freehand.cpp

Go to the documentation of this file.
00001 // $Id: freehand.cpp 1750 2006-09-12 08:59:56Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // The FreeHand Tool
00099 // Created by Rik on 2/9/93
00100 
00101 /*
00102 */
00103 
00104 #include "camtypes.h"
00105 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "resource.h" // _R(IDS_OUTOFMEMORY)
00107 //#include "barsdlgs.h"
00108 //#include "mario.h"
00109 //#include "rik.h"
00110 //#include "markn.h"
00111 //#include "viewrc.h"
00112 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 //#include "mainfrm.h"
00115 #include "infobar.h"
00116 #include "csrstack.h"
00117 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "osrndrgn.h"
00119 #include "oilfiles.h"
00120 #include "blobs.h"
00121 #include "freeinfo.h"
00122 #include "opfree.h"
00123 #include "freehand.h"
00124 
00125 #include "fitcurve.h"
00126 #include "opretro.h"
00127 #include "module.h"
00128 #include "ezmodule.h"
00129 //#include "freehres.h"
00130 #include "progress.h"
00131 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00132 #include "nodepath.h"
00133 
00134 #include "opdrbrsh.h"
00135 #include "opbevel.h"
00136 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00137 
00138 
00139 //#include "will2.h"
00140 #include "nodeblnd.h"
00141 #include "ndbldpth.h"
00142 #include "nodetxts.h"
00143 #include "ndtxtpth.h"
00144 #include "attrmap.h"
00145 #include "nodershp.h"
00146 #include "brushref.h"
00147 #include "brshcomp.h"
00148 #include "ppbrush.h"
00149 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00150 #include "brshattr.h"
00151 #include "sgline.h"
00152 #include "progress.h"
00153 #include "brshdata.h"
00154 #include "brshgdgt.h"
00155 #include "lineattr.h"
00156 #include "brushmsg.h"
00157 #include "coldlog.h"
00158 #include "comattrmsg.h"
00159 #include "pen.h"
00160 #include "brshname.h"
00161 #include "brshbeca.h"
00162 
00163 //#include "grndbrsh.h"
00164 #include "layer.h"
00165 
00166 // Set things up so that the tool will be listed in the Dialog box
00167 DECLARE_SOURCE("$Revision: 1750 $");
00168 
00169 
00170 // These are still TCHAR* while we wait for resource technology to be developed for modules
00171 TCHAR* FreeHandTool::FamilyName = _T("Drawing Tools");
00172 TCHAR* FreeHandTool::ToolName   = _T("Free Hand Tool");
00173 TCHAR* FreeHandTool::Purpose    = _T("To Draw arbitrary lines");
00174 TCHAR* FreeHandTool::Author     = _T("Rik");
00175 
00176 CC_IMPLEMENT_MEMDUMP( FreeHandTool, Tool_v1 )
00177 
00178 // Better memory tracking please
00179 #define new CAM_DEBUG_NEW
00180 
00181 
00182 /********************************************************************************************
00183 
00184     Preference: FreehandPtrCrosshair
00185     Section:    Mouse
00186     Range:      FALSE, TRUE
00187     Purpose:    Determine whether freehand pointer has crosshairs or not
00188 
00189 ********************************************************************************************/
00190 BOOL FreeHandTool::FreehandPtrCrosshair = FALSE;
00191                                   
00192 
00193 /********************************************************************************************
00194 
00195 >   FreeHandTool::FreeHandTool()
00196 
00197     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00198     Created:    21/6/93
00199     Purpose:    Dummp Constructor - It does nothing. All the real initialisation is done
00200                 in FreeHandTool::Init which is called by the Tool Manager
00201     SeeAlso:    FreeHandTool::Init
00202 
00203 ********************************************************************************************/
00204 
00205 FreeHandTool::FreeHandTool()
00206 {
00207     // The Default smoothness
00208     Smoothness = 50;
00209     
00210     // No info bar or previous path by default
00211     pInfoBarOp = NULL;
00212     PreviousPath = NULL;
00213 
00214     // The previous path is not valid
00215     IsPreviousPathValid = FALSE;
00216     IsRetroPathValid = FALSE;
00217     AreWeRetroFitting = FALSE;
00218 
00219     // The actual paths
00220     TrackData = NULL;
00221     RetroPath = NULL;
00222 
00223     // Positions in the previous path to default values
00224     StartSlot = 0;
00225     NumSlots = 0;
00226     
00227     // Set the cursor pointers to null
00228     pNormalCursor = NULL;
00229     pActiveCursor = NULL;
00230     pJoinCursor = NULL;
00231 
00232     // Set the vars to deal with the Joining of paths
00233     JoinInfo.pJoinPath = NULL;
00234     JoinInfo.IsNearEndPoint = FALSE;
00235     JoinInfo.CloseSlot = 0;
00236     JoinInfo.Into = 0.0;
00237     JoinInfo.pAttrBrush = NULL;
00238     JoinInfo.FirstBrushSpacing = 25;
00239 
00240     // Make sure that the status line text is a valid string
00241     StatusMsg = String_256("");
00242     
00243     m_pBlendRef     = NULL;
00244     m_BrushSpacing  = 10000;
00245     m_NumInkNodes   = 0;
00246     m_pGRenderBrush = NULL;
00247     m_BrushHandle   = BrushHandle_NoBrush;
00248     
00249     m_LastBrushDocument = TEXT("No document");
00250 }
00251 
00252 
00253 
00254 /********************************************************************************************
00255 
00256 >   FreeHandTool::~FreeHandTool()
00257 
00258     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00259     Created:    21/6/93
00260     Purpose:    Destructor (Virtual). Does nothing.
00261 
00262 ********************************************************************************************/
00263 
00264 FreeHandTool::~FreeHandTool()
00265 {
00266     //Clear out the old paths
00267     DeletePaths();
00268 PORTNOTE("other", "Removed m_pGRenderBrush support");
00269 #ifndef EXCLUDE_FROM_XARALX
00270     if (m_pGRenderBrush != NULL)
00271         delete m_pGRenderBrush;
00272 #endif
00273 }
00274 
00275 
00276 
00277 /********************************************************************************************
00278 
00279 >   BOOL FreeHandTool::Init( INT32 Pass )
00280 
00281     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00282     Created:    21/6/93
00283     Returns:    FALSE if it does not want to be created, TRUE otherwise
00284     Purpose:    Used to check if the Tool was properly constructed
00285     SeeAlso:    FreeHandTool::FreeHandTool
00286 
00287 ********************************************************************************************/
00288 
00289 BOOL FreeHandTool::Init()
00290 {
00291     // This should be set to NULL by default. It will be set properly below, if
00292     // everthing is working as it should
00293     pInfoBarOp = NULL;
00294 
00295     if (!OpRetroFit::Init())
00296         return FALSE;
00297 
00298     // Now we have to declare all our operations and if that works, try to find
00299     // the freehand tools info bar and create it
00300     if (OpFreeHand::Declare())
00301     {
00302         pInfoBarOp = new FreeHandInfoBarOp();
00303         if (pInfoBarOp)
00304             pInfoBarOp->pTool = this;
00305 PORTNOTE("dialog", "Removed Bar reading")
00306 #if 0
00307         // Resource File and Object that creates FreeHandInfoBarOp objects
00308         CCResTextFile           ResFile;
00309         FreeHandInfoBarOpCreate BarCreate;
00310 
00311         if (ResFile.open(_R(IDM_FREEHAND_BAR), _R(IDT_INFO_BAR_RES)))
00312         {
00313             // Found the file and opened it
00314             if (DialogBarOp::ReadBarsFromFile(ResFile,BarCreate))
00315             {
00316                 // read it in ok, so close it
00317                 ResFile.close();
00318 
00319                 // Info bar now exists.  Now get a pointer to it
00320                 String_32 str = String_32(_R(IDS_FREEHAND_INFOBARNAME));
00321                 DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str);
00322 
00323                 // Should have a dialog bar op by now
00324                 if (pDialogBarOp != NULL)
00325                 {
00326                     // Make sure it is what we were expecting and set it
00327                     if (pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(FreeHandInfoBarOp)))
00328                     {
00329                         pInfoBarOp = (FreeHandInfoBarOp*) pDialogBarOp;
00330                         pInfoBarOp->pTool = this;
00331                     }
00332                 }
00333             }
00334         }
00335 #endif
00336     }
00337 
00338     GetApplication()->DeclareSection(_T("Mouse"), 10);
00339     GetApplication()->DeclarePref(_T("Mouse"), _T("FreehandPtrCrosshair"), &FreehandPtrCrosshair, FALSE, TRUE);
00340 
00341     // See if it all worked and return depending on the result
00342     ENSURE(pInfoBarOp!=NULL, "Failed to create FreeHand Info Bar" );
00343     if (pInfoBarOp==NULL)
00344         return FALSE;
00345     else
00346         return TRUE;
00347 }
00348 
00349 
00350 
00351 
00352 
00353 /********************************************************************************************
00354 
00355 >   void FreeHandTool::Describe(void *InfoPtr)
00356 
00357     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00358     Created:    21/6/93
00359     Inputs:     InfoPtr - A pointer to a tool info block. It is passed cast to void* as
00360                 the version of the tool is unknown at this point. Later versions of the
00361                 Tool class may have more items in this block, that this tool will not use
00362     Outputs:    InfoPtr - The structure pointed to by InfoPtr will have had all the info
00363                 that this version of the Tool knows about
00364     Purpose:    Allows the tool manager to extract information about the tool
00365 
00366 ********************************************************************************************/
00367 
00368 void FreeHandTool::Describe(void *InfoPtr)
00369 {
00370     // Cast structure into the latest one we understand.
00371     ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr;
00372 
00373     Info->InfoVersion = 1;
00374     Info->InterfaceVersion = GetToolInterfaceVersion();  // You should always have this line.
00375         
00376     // These are all arbitrary at present.
00377     Info->Version = 1;
00378     Info->ID      = GetID();
00379     Info->TextID  = _R(IDS_FREE_HAND_TOOL);
00380     Info->BubbleID = _R(IDBBL_FHND_TOOLBOX);
00381 
00382     Info->Family  = FamilyName;
00383     Info->Name    = ToolName;
00384     Info->Purpose = Purpose;
00385     Info->Author  = Author;
00386 
00387     Info->InfoBarDialog = _R(IDD_FREEHANDTOOL);
00388 }
00389 
00390 
00391 UINT32 FreeHandTool::GetID()
00392 {
00393     return TOOLID_FREEHAND;
00394 }
00395 
00396 
00397 
00398 // The Free Hand Tools EventHandlers
00399 
00400 /********************************************************************************************
00401 
00402 >   virtual void FreeHandTool::SelectChange(BOOL isSelected)
00403 
00404     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00405     Created:    24/11/93
00406     Inputs:     IsSelected - TRUE if the tool is becoming selected, FALSE if it is loosing
00407                 the selection
00408     Purpose:    Called when the tool is selected or deselected.  Creates and pushes the
00409                 tool's cursor; pops and destroys it.
00410     Errors:     Sends warning to debugging terminal if creating the cursor fails.
00411 
00412 ********************************************************************************************/
00413 
00414 void FreeHandTool::SelectChange(BOOL isSelected)
00415 {
00416     
00417     if (isSelected)
00418     {
00419         CCPen *pPen = Camelot.GetPressurePen();
00420         if (pPen != NULL)
00421         {
00422             PressureMode NewState = (pPen->IsRealPen()) ? PressureMode_Pen : PressureMode_None;
00423             pPen->SetPressureMode(NewState);
00424         }
00425     
00426         m_UpdateBrushState = UPDATE_ONIDLE;
00427 
00428         BOOL ok = BrushSelected(m_BrushHandle, FALSE);
00429         // if that didn't work then we won't use a brush
00430         if (!ok)
00431             m_BrushHandle = BrushHandle_NoBrush;
00432         
00433         // if we have a brush then default to no smoothness
00434     /*  if (m_BrushHandle != BrushHandle_NoBrush)
00435             Smoothness = 0;
00436         else
00437             Smoothness = 50;
00438 */
00439         m_LastBrushDocument = TEXT("No document");
00440 
00441         // Load the cursors
00442         if (!LoadCursors())
00443             InformError();
00444         
00445         // reset the join path pointer
00446         JoinInfo.pJoinPath = NULL;
00447         JoinInfo.FirstBrushSpacing = 25;
00448         // Find the blob manager ready for later
00449         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00450 
00451         // Clear the sub-selection on all the paths in the current selection
00452         BOOL IsPathWithSubSelection = FALSE;
00453         if (Document::GetSelected() != NULL)
00454         {
00455             // Get the selection
00456             SelRange* Selected = GetApplication()->FindSelection();
00457             Node* pNode = Selected->FindFirst();
00458             while ((pNode!=NULL) && (!IsPathWithSubSelection))
00459             {
00460                 // clear the sub-selection if this is a path
00461                 if (pNode->IsNodePath())
00462                 {
00463                     // If this path has a sub-selection, then set the flag
00464                     if (((NodePath*)pNode)->InkPath.IsSubSelection())
00465                         IsPathWithSubSelection = TRUE;
00466                 }
00467 
00468                 // Now find the next selected node
00469                 pNode = Selected->FindNext(pNode);
00470             }
00471 
00472             // if there was a sub-selection, then get rid of it
00473             if ((IsPathWithSubSelection) && (BlobMgr!=NULL))
00474             {
00475                 // Get rid of the object blobs while we clear the paths sub-selection
00476                 BlobStyle ClearBlobs;
00477                 ClearBlobs.Object = FALSE;
00478                 BlobMgr->ToolInterest(ClearBlobs);
00479 
00480                 // There are now no object blobs on screen, so we can clear the subselection
00481                 pNode = Selected->FindFirst();
00482                 while (pNode!=NULL)
00483                 {
00484                     // clear the sub-selection if this is a path
00485                     if (pNode->IsNodePath())
00486                         ((NodePath*)pNode)->InkPath.ClearSubSelection();
00487 
00488                     // Now find the next selected node
00489                     pNode = Selected->FindNext(pNode);
00490                 }
00491             }
00492         }
00493     
00494     // Diccon 9/99 Code copied from Beztool::SelectChange() complete with original comments
00495 
00496     // If there is are TextStories or Blends with paths selected then select the paths
00497     // (PS. This is a rather disgusting way of doing things, and one you can't blame on me - MarkN 17-5-99)
00498 
00499     Range SelRng(*(GetApplication()->FindSelection()));
00500     Node* pNode = SelRng.FindFirst();
00501     
00502     while (pNode != NULL)
00503     {
00504         if (IS_A(pNode, TextStory))
00505         {
00506             TextStory* pStory = (TextStory*)pNode;
00507             if (pStory->GetTextPath() != NULL)
00508                 pStory->GetTextPath()->Select(TRUE);
00509         }
00510 
00511         if (IS_A(pNode, NodeBlend))
00512         {
00513             NodeBlend* pNodeBlend = (NodeBlend*)pNode;
00514             UINT32 NBPCounter = 0;
00515             while (pNodeBlend->GetNodeBlendPath(NBPCounter) != NULL)
00516                 pNodeBlend->GetNodeBlendPath(NBPCounter++)->Select(TRUE);
00517         }
00518 
00519         pNode = SelRng.FindNext(pNode);
00520     }
00521     
00522         // Create and display my info bar please
00523         if (pInfoBarOp != NULL)
00524         {
00525             pInfoBarOp->Create();
00526             pInfoBarOp->SetToolActiveState(TRUE);
00527         }
00528 
00529         // Make sure that Objects blobs are on
00530         if (BlobMgr != NULL)
00531         {
00532             // Decide which blobs to display
00533             BlobStyle MyBlobs;
00534             MyBlobs.Object = TRUE;
00535 
00536             // tell the blob manager
00537             BlobMgr->ToolInterest(MyBlobs);
00538         }
00539 
00540         // Set the retro field on the info bar
00541         if (pInfoBarOp != NULL)
00542         {
00543             // Find out if we can retro fit and set the field appropriatly
00544             BOOL IsStillValid = IsRetroPathStillValid();
00545             pInfoBarOp->SetRetroState(IsStillValid);
00546         }
00547 
00548     }
00549     else
00550     {
00551         // Deselection of the tool
00552         // Get rid of all the tools cursors
00553         RemoveCursors();
00554 
00555 PORTNOTE("other", "Removed m_pGRenderBrush support");
00556 #ifndef EXCLUDE_FROM_XARALX
00557         if (m_pGRenderBrush != NULL)
00558         {
00559             delete m_pGRenderBrush;
00560             m_pGRenderBrush = NULL;
00561         }
00562 #endif
00563         
00564         // Hide and destroy my info bar please
00565         if (pInfoBarOp != NULL)
00566         {
00567             pInfoBarOp->SetToolActiveState(FALSE);
00568             
00569 PORTNOTE("other", "Removed brush gadget support");
00570 #ifndef EXCLUDE_FROM_XARALX
00571             if (FreeHandInfoBarOp::GetBrushGadget()->IsDialogOpen ())
00572             {
00573                 FreeHandInfoBarOp::GetBrushGadget()->CloseDialog ();
00574             }
00575 #endif          
00576             pInfoBarOp->Delete();
00577         }
00578         // ensure that the colour picker is working
00579         SetColourEditorProcessing(TRUE);
00580 
00581         // ensure any tool object blobs are removed.
00582         BlobManager* BlobMgr = GetApplication()->GetBlobManager();
00583         if (BlobMgr != NULL)
00584         {
00585             BlobStyle bsRemoves;
00586             bsRemoves.ToolObject = TRUE;
00587             BlobMgr->RemoveInterest(bsRemoves);
00588         }
00589     }
00590 }
00591 
00592 
00593 
00594 /********************************************************************************************
00595 
00596 >   BOOL FreeHandTool::LoadCursors()
00597 
00598     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00599     Created:    21/10/94
00600     Returns:    TRUE if it worked, FALSE if it did not
00601     Purpose:    Loads all the cursors used by the freehand tool and pushs the normal
00602                 one onto the top of the cursor stack
00603     Errors:     ERROR1 (_R(IDE_FHAND_BADCURSORS)) if it fails to load the cursors
00604 
00605 ********************************************************************************************/
00606 
00607 BOOL FreeHandTool::LoadCursors()
00608 {
00609     // This tool has just been selected, so it is not displaying a cursor
00610     pActiveCursor = NULL;
00611 
00612     // Try to create all our cursors - The normal cursor
00613     if (FreehandPtrCrosshair)
00614     {
00615         pNormalCursor = new Cursor(this, _R(IDC_FREEHANDTOOLCURSOR_X));
00616         pJoinCursor   = new Cursor(this, _R(IDC_FREEHANDJOINCURSOR_X));
00617         pModifyCursor = new Cursor(this, _R(IDC_FREEHANDMODIFY_X));
00618     }
00619     else
00620     {
00621         pNormalCursor = new Cursor(this, _R(IDC_FREEHANDTOOLCURSOR));
00622         pJoinCursor   = new Cursor(this, _R(IDC_FREEHANDJOINCURSOR));
00623         pModifyCursor = new Cursor(this, _R(IDC_FREEHANDMODIFY));
00624     }
00625 
00626     // did we get all the cursors ok
00627     if ((pNormalCursor==NULL) || (!pNormalCursor->IsValid()) || 
00628         (pJoinCursor==NULL)   || (!pJoinCursor->IsValid()) ||
00629         (pModifyCursor==NULL) || (!pModifyCursor->IsValid()))
00630     {
00631         // No, at least one of them was NULL, so delete them all
00632         // Deleting the null cursor will be ok
00633         delete pNormalCursor;
00634         delete pJoinCursor;
00635         delete pModifyCursor;
00636 
00637         // and ensure that the pointers are set to NULL
00638         pNormalCursor = NULL;
00639         pJoinCursor = NULL;
00640         pModifyCursor = NULL;
00641 
00642         // and set an error and return
00643         ERROR1(FALSE, _R(IDE_FHAND_BADCURSORS));
00644     }
00645 
00646     // All the cursors loaded ok if we got to here
00647     // So push the normal cursor onto the stack and mark it as active
00648     CurrentCursorID = CursorStack::GPush(pNormalCursor, FALSE);
00649     pActiveCursor = pNormalCursor;
00650 
00651     // return that it worked
00652     return TRUE;
00653 }
00654 
00655 
00656 
00657 /********************************************************************************************
00658 
00659 >   void FreeHandTool::RemoveCursors()
00660 
00661     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00662     Created:    21/10/94
00663     Purpose:    Pops the freehand tools cursor off the top of the cursor stack, frees the
00664                 memory used by all the cursors in the freehand tool and sets all the pointers
00665                 to NULL.
00666     SeeAlso:    FreeHandTool::LoadCursors
00667 
00668 ********************************************************************************************/
00669 
00670 void FreeHandTool::RemoveCursors()
00671 {
00672     // If we ever had a cursor
00673     if (pActiveCursor!=NULL)
00674     {
00675         // pop it off the stack
00676         CursorStack::GPop(CurrentCursorID);
00677 
00678         // and free up the cursors we allocated
00679         delete pNormalCursor;
00680         delete pJoinCursor;
00681         delete pModifyCursor;
00682 
00683         // and set them all to NULL
00684         pNormalCursor = NULL;
00685         pJoinCursor = NULL;
00686         pActiveCursor = NULL;
00687         pModifyCursor = NULL;
00688         CurrentCursorID = 0;
00689     }
00690 }
00691 
00692 
00693 
00694 
00695 /********************************************************************************************
00696 
00697 >   void FreeHandTool::ChangeCursor(Cursor* cursor)
00698 
00699     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00700     Created:    16/3/94
00701     Inputs:     ID of the cursor you want to flip to
00702     Purpose:    Changes to the specified cursor. Will only change the cursor if it isn't already
00703                 this cursor, so it doesn't flicker.
00704     Errors:     can fail if the cursor cannot be created - the cursor code will fail.
00705 
00706 ********************************************************************************************/
00707 
00708 void FreeHandTool::ChangeCursor(Cursor* pCursor)
00709 {
00710     // only change if this cursor is different from the current cursor
00711     if ((pCursor!=pActiveCursor) && (pCursor!=NULL))
00712     {
00713         // set this cursor as the current cursor and immediately display it
00714         CursorStack::GSetTop(pCursor, CurrentCursorID);
00715 
00716         // remember this is our current cursor
00717         pActiveCursor = pCursor;
00718     }
00719 }
00720 
00721 
00722 
00723 
00724 
00725 
00726 /********************************************************************************************
00727 
00728 >   void FreeHandTool::OnClick( DocCoord PointerPos, ClickType Click, 
00729                                 ClickModifiers ClickMods, Spread *pSpread )
00730 
00731     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00732     Created:    21/6/93
00733     Inputs:     PointerPos - The Coords (in spread coords) of the point where the mouse 
00734                 button was clicked
00735                 Click - Describes the type of click that was detected. 
00736                 ClickMods - Indicates which buttons caused the click and which modifers were
00737                 pressed at the same time
00738                 pSpread - the spread in which the click happened
00739     Returns:    TRUE if it handled the Click, FALSE otherwise
00740     Purpose:    To handle a Mouse Click event for the FreeHand Tool. It starts up a FreeHand 
00741                 Operation.
00742     SeeAlso:    Tool::MouseClick; ClickType; ClickModifiers
00743 
00744 ********************************************************************************************/
00745 
00746 void FreeHandTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods,
00747                         Spread* pSpread )
00748 {
00749     if (ClickMods.Menu) return;                         // Don't do anything if the user clicked the Menu button
00750 
00751     // See if there is already a drag going on
00752     if (Operation::GetCurrentDragOp()!=NULL)
00753         return;
00754 
00755     // If we get a click, treat it like a mouse move, allowing the tool to set up
00756     // any info that is passed to the operation in the drag
00757     // NEW 17/4/2000 Now we also want to click-select in the Freehand tool
00758     if (Click==CLICKTYPE_SINGLE)
00759     {
00760         OnMouseMove(PointerPos, pSpread, ClickMods);
00761         DragTool::OnClick (PointerPos, Click, ClickMods, pSpread);
00762     }
00763     if (Click == CLICKTYPE_UP)
00764     {
00765         // so long as we are not interfering with any drawing operations we will
00766         // try and change the selection
00767         //if (JoinInfo.pJoinPath == NULL)
00768         {
00769             DragTool::OnClick (PointerPos, Click, ClickMods, pSpread);
00770 
00771             BROADCAST_TO_ALL(CommonAttrsChangedMsg);
00772         }
00773     }
00774     // Make sure this click is one that we want
00775     if (Click==CLICKTYPE_DRAG)
00776     {
00777         // Just what we wanted - Someone is dragging the mouse about
00778         // We need to make an operation to perform the drag with
00779     
00780         // quick safety check - if the current line width is less than 501MP then we want
00781         // to launch a freehand op even if we have a brush selected
00782         MILLIPOINT LineWidth = OpDrawBrush::GetCurrentLineWidthIfNotDefault();
00783         
00784         // Added a condition here - if our join info brush handle indicates no brush
00785         // then we are adding to a normal line, hence don't use the brush
00786         if (m_BrushHandle == BrushHandle_NoBrush || (LineWidth != -1 && LineWidth < 501)
00787             || JoinInfo.m_BrushHandle == BrushHandle_NoBrush)
00788         {
00789             OpFreeHand* pOpFreeHand = new OpFreeHand;
00790             if ((pOpFreeHand!=NULL) && (GetNewPaths()))
00791             {   
00792                 // Start the drag operation and pass in the Anchor Point to the push operation
00793                 pOpFreeHand->DoDrag(StartPos, StartSpread, Smoothness, &JoinInfo, TrackData);
00794             }
00795             else
00796             {
00797                 // Failed to get the memory to do the job
00798                 ToolInformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00799             }
00800         }
00801         else
00802         {
00803         // launch a brush op
00804             Document* pDoc = Document::GetCurrent();
00805             if (pDoc == NULL)
00806             {
00807                 ERROR3("No document in FreeHandTool::OnClick");
00808                 return;
00809             }
00810         
00811             String_256 CurrentDoc = pDoc->GetDocName();
00812             if (CurrentDoc != m_LastBrushDocument)
00813             {
00814                 m_LastBrushDocument = CurrentDoc;
00815                 m_UpdateBrushState = UPDATE_NOW;
00816                 BrushSelected(m_BrushHandle, FALSE);
00817             }
00818         
00819 PORTNOTE("other", "Removed m_pGRenderBrush support");
00820 #ifndef EXCLUDE_FROM_XARALX
00821             // This is a bit of a bodge to try to stop the internal error in 8bpp
00822             if (m_pGRenderBrush && m_pGRenderBrush->GetRenderDC()->GetSafeHdc() == NULL)
00823             {
00824                 delete m_pGRenderBrush;
00825                 m_pGRenderBrush = NULL;
00826             }
00827 #endif
00828 
00829             if (m_pGRenderBrush == NULL || CurrentDoc != m_LastBrushDocument)
00830             {
00831                 // or don't if we are not ready
00832                 String_32 ProgString = _T("Preparing brush, please wait..");
00833                 Progress Hourglass(&ProgString, -1, FALSE);
00834                 m_pGRenderBrush = GetBigGRenderBrush(pSpread);
00835                 
00836 PORTNOTE("other", "Removed m_pGRenderBrush support");
00837 #ifndef EXCLUDE_FROM_XARALX
00838                 if (m_pGRenderBrush == NULL)
00839                 {
00840                     ToolInformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00841                     return;
00842                 }
00843 #endif
00844                 m_LastBrushDocument = CurrentDoc;
00845                 return;
00846             
00847             }
00848             // ensures that if we are over another brush we get the correct info
00849             OnMouseMove(PointerPos, pSpread, ClickMods);
00850 
00851             OpDrawBrush* pOpDrawBrush = new OpDrawBrush(this);
00852             //TRACEUSER( "Diccon", _T("About to start new stroke\n"));
00853             if ((pOpDrawBrush != NULL)&& (GetNewPaths()))
00854             {
00855                 // turn off background rendering whilst we are in progress
00856                 // note that we must turn off the selected docview or else we will 
00857                 // screw up background rendering when we have multiple views (as I just found out)
00858                 DocView* pDocView = DocView::GetCurrent(); //DocView::GetSelected(); //Current();
00859                 if (pDocView != NULL)
00860                     pDocView->SetPreventRenderView(TRUE);
00861 
00862                 // turn off the colour picker, as it munches CPU
00863                 SetColourEditorProcessing(FALSE);
00864 PORTNOTE("other", "Removed m_pGRenderBrush support");
00865 #ifndef EXCLUDE_FROM_XARALX
00866                 m_pGRenderBrush->SetupMainBitmap();
00867 #endif
00868                 // find out if our brush definition wants time stamping, and tell the op
00869                 BrushDefinition* pBrushDef = GetSelectedBrush();
00870                 if (pBrushDef != NULL)
00871                     pOpDrawBrush->SetTimeStamp(pBrushDef->GetTimeStampingPeriod());
00872 
00873                 pOpDrawBrush->DoDrag(StartPos, StartSpread, Smoothness, m_BrushHandle, &JoinInfo, TrackData,
00874                                     m_pGRenderBrush, ClickMods);
00875 
00876                 m_UpdateBrushState = UPDATE_NEVER; // we REALLY don't want to be updating whilst in the middle of drawing
00877             }
00878             else
00879             {
00880                 // Failed to get the memory to do the job
00881                 ToolInformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00882 PORTNOTE("other", "Removed m_pGRenderBrush support");
00883 #ifndef EXCLUDE_FROM_XARALX
00884                 delete m_pGRenderBrush;
00885                 m_pGRenderBrush = NULL;
00886 #endif
00887             }
00888             
00889         }
00890     }
00891 
00892 
00893 }
00894 
00895 
00896 
00897 
00898 /********************************************************************************************
00899 
00900 >   void FreeHandTool::OnMouseMove(DocCoord* coord, Spread* pSpread, ClickModifiers mods)
00901 
00902     Author:     Jim_Lynn (Xara Group Ltd) <camelotdev@xara.com>
00903     Created:    15/3/94
00904     Inputs:     coordinate of mouse move, pointer to spread containing coord; click modifiers
00905     Purpose:    This routine is called whenever the mouse moves while we're in the freehand 
00906                 tool. it sees what is under the pointer, and flips the cursor if clicking 
00907                 will have a different effect. The rules are:
00908 
00909                 Over space, no selection        - freehand cursor
00910                 Over selected endpoint          - add freehand to line
00911 
00912 ********************************************************************************************/
00913 
00914 void FreeHandTool::OnMouseMove(DocCoord Coord, Spread* pSpread, ClickModifiers mods)
00915 {
00916     // Assume that we are not going to find anything of interest, so set the params to good values
00917     JoinInfo.pJoinPath = NULL;
00918     StartPos = Coord;
00919     StartSpread = pSpread;
00920     JoinInfo.FirstBrushSpacing = 25;
00921     // make sure the join info always reflects the brush that we should draw with
00922     JoinInfo.m_BrushHandle = m_BrushHandle;
00923     JoinInfo.pAttrBrush = NULL;
00924     JoinInfo.BrushDistance = 0;
00925     
00926     // We are interested in any selected paths that the cursor is over
00927     // we will also be needing the current docview
00928     SelRange* Selected = GetApplication()->FindSelection();
00929     Node* pNode = Selected->FindFirst();
00930 
00931     // Check to see if the selection is on the same spread as the mouse
00932     if (pNode!=NULL)
00933     {
00934         // Get the spread and return if it is different from the one with the cursor
00935         Spread* pNodeSpread = pNode->FindParentSpread();
00936         if (pNodeSpread!=pSpread)
00937             return;
00938     }
00939 
00940     // Find the Blob manager, so we can find out how big a rect to use
00941     BlobManager* pBlobMgr = GetApplication()->GetBlobManager();
00942     if (pBlobMgr==NULL)
00943         return;
00944     
00945     // Find the Rect round the mouse pos that counts as a hit
00946     DocRect BlobRect;
00947     pBlobMgr->GetBlobRect(Coord, &BlobRect);
00948     
00949     // Work out the square of the distance that we will count as 'close' to the line
00950     INT32 Range = BlobRect.Width() / 2;
00951     Range *= Range;
00952 
00953 
00954     // loop through the selection
00955     while (pNode!=NULL)
00956     {
00957         // We are only interested in NodePaths
00958         // Diccon 9/99 now we are also interested in NodeBlends, as long
00959         // as they are blended on a path..
00960         if (pNode->IsNodePath())
00961         {
00962             // get the node as a NodePath
00963             NodePath* pPath = (NodePath*) pNode;
00964 
00965             // Go and see if we are near any of the endpoints of this path
00966             // (and stop looking if it is)
00967             if (IsCursorNearEndPoint(pPath, BlobRect))
00968                 return;
00969 
00970             // We were not near the open ends of the selected path, so test
00971             // to see if we are anywhere near any point along the path
00972             if (IsCursorNearPath(pPath, Range, Coord))
00973                 return;
00974         }
00975         // Diccon added 9/99
00976         else if (pNode->IS_KIND_OF(NodeBlend))
00977         {
00978             UINT32 NBPCounter = 0;
00979             NodeBlendPath* pNodeBlendPath = ((NodeBlend*)pNode)->GetNodeBlendPath(NBPCounter++);
00980             
00981             while (pNodeBlendPath != NULL) 
00982             {
00983                 NodePath* pPath = (NodePath*)pNodeBlendPath;
00984                 // Go and see if we are near any of the endpoints of this path
00985                 // (and stop looking if it is)
00986                 if (IsCursorNearEndPoint(pPath, BlobRect))
00987                     return;
00988 
00989                 // We were not near the open ends of the selected path, so test
00990                 // to see if we are anywhere near any point along the path
00991                 if (IsCursorNearPath(pPath, Range, Coord))
00992                     return;
00993 
00994                 pNodeBlendPath = ((NodeBlend*)pNode)->GetNodeBlendPath(NBPCounter++);
00995             }
00996         }
00997              
00998         // Now find the next selected node
00999         pNode = Selected->FindNext(pNode);
01000     }
01001 
01002     // if we are in brush mode then check to see if the document we are over is 
01003     // the same as the last document we drew on. If it is different we will need to update
01004     if (m_BrushHandle != BrushHandle_NoBrush)
01005     {
01006         Document* pDoc = Document::GetCurrent();
01007         if (pDoc != NULL)
01008         {
01009             String_256 CurrentDoc = pDoc->GetDocName();
01010             if (CurrentDoc != m_LastBrushDocument)
01011             {
01012                 m_LastBrushDocument = CurrentDoc;
01013                 m_UpdateBrushState = UPDATE_NOW;
01014                 BrushSelected(m_BrushHandle, FALSE);
01015             }
01016         }
01017     }
01018 
01019 
01020     // We did not find anything good, so set the cursor to the normal one
01021     ChangeCursor(pNormalCursor);
01022 
01023     // And set the status bar text
01024     StatusMsg.Load(_R(IDS_FREEHANDSTART), Tool::GetModuleID(GetID()));
01025     GetApplication()->UpdateStatusBarText(&StatusMsg);
01026 }
01027 
01028 
01029 
01030 /********************************************************************************************
01031 
01032 >   BOOL FreeHandTool::GetStatusLineText(String_256* ptext, Spread*, DocCoord, ClickModifiers)
01033 
01034     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01035     Created:    5/1/95
01036     Inputs:     Others not used
01037     Outputs:    pText - pointer to a string that will hold the text
01038     Returns:    TRUE if it set the text, FALSE if not
01039     Purpose:    Sets the status line text on request from the app.
01040 
01041 ********************************************************************************************/
01042 
01043 BOOL FreeHandTool::GetStatusLineText(String_256* ptext, Spread*, DocCoord, ClickModifiers)
01044 {
01045     *ptext = StatusMsg;
01046     return TRUE;
01047 }
01048 
01049 
01050 /********************************************************************************************
01051 
01052 >   BOOL FreeHandTool::IsCursorNearEndPoint(NodePath* pNodePath, const DocRect& BlobRect)
01053 
01054     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01055     Created:    21/10/94
01056     Inputs:     pNodePath - The Node Path that we want to test
01057                 BlobRect - The rectangle that the endpoints have to be in to qualify
01058     Returns:    TRUE if it found an endpoint in the rect, FALSE if it did not
01059     Purpose:    Scans the path in the NodePath to see any of its endpoints lie inside the
01060                 DocRect supplied. The DocRect should be the snapping rectangle around the
01061                 cursor position. If this function finds that one of the endpoints is in the
01062                 rect, it will remember the path, and the position of the endpoint so that
01063                 when a new freehand line is started, this data can be passed in. This
01064                 function also changes the cursor to the Join cursor and sets the status bar
01065                 text if it finds a match.
01066 
01067 ********************************************************************************************/
01068 
01069 BOOL FreeHandTool::IsCursorNearEndPoint(NodePath* pNodePath, const DocRect& BlobRect)
01070 {
01071     // Find the path associated with this NodePath
01072     Path* pPath = &(pNodePath->InkPath);
01073 
01074     // Test to see if the cursor was anywhere near any of the paths endpoints
01075     INT32 CloseSlot;
01076     if (pPath->IsNearOpenEnd(BlobRect, &CloseSlot))
01077     {
01078         // Yep, so set the eventual starting position of any new freehand paths to the
01079         // endpoint in question
01080         pPath->SetPathPosition(CloseSlot);
01081         StartPos = pPath->GetCoord();
01082         
01083         // Also remember the path that we can Join with
01084         JoinInfo.pJoinPath = pNodePath;
01085         JoinInfo.IsNearEndPoint = TRUE;
01086 
01087         // And set the cursor
01088         ChangeCursor(pJoinCursor);
01089 
01090         // And the status bar text
01091         StatusMsg.Load(_R(IDS_FREEHANDSTARTJOIN), Tool::GetModuleID(GetID()));
01092         GetApplication()->UpdateStatusBarText(&StatusMsg);
01093 
01094         // now test to see if our nodepath is brushed
01095         NodeAttribute* pAttr = NULL;
01096         pNodePath->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
01097         if (pAttr != NULL)
01098         {
01099             InitialiseJoinInfoForBrush((AttrBrushType*)pAttr, pNodePath, BlobRect.Centre());
01100         }
01101         
01102         // and tell our caller that we found a match
01103         return TRUE;
01104     }
01105     
01106     // No match was found
01107     return FALSE;
01108 }
01109 
01110 
01111 
01112 
01113 /********************************************************************************************
01114 
01115 >   BOOL FreeHandTool::IsCursorNearPath(NodePath* pNodePath, INT32 Range, const DocCoord& PointerPos)
01116 
01117     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01118     Created:    24/10/94
01119     Inputs:     pNodePath - The Path that we want to see if we are near
01120                 Range - The squared distance away from the path that we count as being
01121                 close to the line
01122                 PointerPos - The position of the mouse cursor
01123     Returns:    TRUE if we find that the path goes through the rectangle, FALSE if not
01124     Purpose:    This function tests to see if any point along the paths length is close to
01125                 the cursor. If it finds that it is, then the cursor and the status
01126                 bar help are changed and the information about the path is remembered in
01127                 case a drag starts soon.
01128 
01129 ********************************************************************************************/
01130 
01131 BOOL FreeHandTool::IsCursorNearPath(NodePath* pNodePath, INT32 Range, const DocCoord& PointerPos)
01132 {
01133     // Find the path associated with this NodePath
01134     Path* pPath = &(pNodePath->InkPath);
01135 
01136     // Test to see if the cursor was anywhere near any of the paths endpoints
01137     INT32 CloseSlot = 0;
01138     double mu;
01139     if (pPath->IsPointCloseTo(PointerPos, Range, &CloseSlot, &mu))
01140     {
01141         // Yep, so set the eventual starting position of any new freehand paths to the
01142         // endpoint in question
01143         StartPos = pPath->ClosestPointTo(mu, CloseSlot);
01144 
01145         // Also remember the path that we can Join with
01146         JoinInfo.pJoinPath = pNodePath;
01147         JoinInfo.IsNearEndPoint = FALSE;
01148         JoinInfo.CloseSlot = CloseSlot;
01149         JoinInfo.Into = mu;
01150 
01151         // And set the cursor
01152         ChangeCursor(pModifyCursor);
01153 
01154         // And the status bar text
01155         StatusMsg.Load(_R(IDS_FREEHANDMODSTART), Tool::GetModuleID(GetID()));
01156         GetApplication()->UpdateStatusBarText(&StatusMsg);
01157 
01158         // now test to see if our nodepath is brushed
01159         NodeAttribute* pAttr = NULL;
01160         pNodePath->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), &pAttr);
01161         if (pAttr != NULL)
01162         {
01163             AttrBrushType* pAttrBrush = (AttrBrushType*)pAttr;
01164             InitialiseJoinInfoForBrush(pAttrBrush, pNodePath, PointerPos);
01165         }
01166 
01167         // tell the caller that some point along the path is close to the pointer
01168         return TRUE;
01169     }
01170 
01171     // This path is not close to the cursor
01172     return FALSE;   
01173 }
01174 
01175 
01176 
01177 /********************************************************************************************
01178 
01179 >   void FreeHandTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01180 
01181     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01182     Created:    17/5/94
01183     Inputs:     pSpread - The spread that the blob is to appear on
01184                 pClipRect - Pointer to the rect that contains the blobs
01185     Purpose:    Renders the Tools Blobs. This will be used to draw paths as they are retro
01186                 fitted live, as the accuracy bar is dragged!
01187 
01188 ********************************************************************************************/
01189 
01190 void FreeHandTool::RenderToolBlobs(Spread* pSpread, DocRect* pClipRect)
01191 {
01192     // Can only draw the path if there is a path to draw
01193     if (IsPreviousPathValid && IsRetroPathValid && RetroPath!=NULL)
01194     {
01195         RenderRegion* pRegion = DocView::RenderOnTop(pClipRect, pSpread, ClippedEOR);
01196         while (pRegion)
01197         {
01198             // Draw a Cross Hair
01199             pRegion->SetLineColour(COLOUR_XOREDIT);
01200             pRegion->SetLineWidth(0);
01201             pRegion->DrawPath(RetroPath);
01202 
01203             // Get the next region in the list
01204             pRegion = DocView::GetNextOnTop(pClipRect);
01205         }
01206     }   
01207 }
01208 
01209 
01210 
01211 
01212 /********************************************************************************************
01213 
01214 >   void FreeHandTool::SetSmoothness(INT32 Smooth)
01215 
01216     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01217     Created:    17/5/94
01218     Inputs:     Smooth - The new value for the smoothness
01219     Purpose:    Sets the freehand tools accuracy setting
01220 
01221 ********************************************************************************************/
01222 
01223 void FreeHandTool::SetSmoothness(INT32 Smooth)
01224 {
01225     Smoothness = Smooth;
01226 }
01227 
01228 
01229 
01230 
01231 /********************************************************************************************
01232 
01233 >   INT32  FreeHandTool::GetSmoothness()
01234 
01235     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01236     Created:    17/5/94
01237     Returns:    The freehand accuracy setting
01238     Purpose:    for finding out the freehand tools current smoothing accuracy value
01239 
01240 ********************************************************************************************/
01241 
01242 INT32  FreeHandTool::GetSmoothness()
01243 {
01244     return Smoothness;
01245 }
01246 
01247 
01248 
01249 /********************************************************************************************
01250 
01251 >   void FreeHandTool::PreviousPathInvalid()
01252 
01253     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01254     Created:    17/5/94
01255     Purpose:    Marks the previous path as invalid. After calling this function, attempts to
01256                 re-fit a path by changing the smoothing values will be ignored. This function
01257                 should be called when something happens that would make it impossible to
01258                 re-fit the path (eg the original is translated or rotated or undone)
01259 
01260 ********************************************************************************************/
01261 
01262 void FreeHandTool::PreviousPathInvalid()
01263 {
01264     // The previous path is no longer valid, so set all
01265     // the flags and vars associated with it
01266     IsPreviousPathValid = FALSE;
01267     IsRetroPathValid = FALSE;
01268     IsSelectBlobsOnScreen = FALSE;
01269     PreviousPath = NULL;
01270     StartSlot = 0;
01271     NumSlots = 0;
01272     PathCRC = 0;
01273 
01274     // Tell the info bar to show that retro fitting is no longer possible
01275     if (pInfoBarOp != NULL)
01276         pInfoBarOp->SetRetroState(FALSE);
01277 }
01278 
01279 
01280 
01281 
01282 /********************************************************************************************
01283 
01284 >   void FreeHandTool::SetPreviousPath(NodePath* PrevPath, INT32 Start, INT32 Len)
01285 
01286     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01287     Created:    19/5/94
01288     Inputs:     PrevPath - The path that has just been added to the tree. ie the one most
01289                 recently drawn with the freehand tool
01290                 Start - The Start slot at which the new section of path begins.
01291                 Len - The number of slots that the new path section contains.
01292     Purpose:    Marks the previously drawn path as valid so that it can be retro fitted.
01293                 Retro fitting will be allowed until PreviousPathInvalid() is called.
01294     SeeAlso:    FreeHandTool::PreviousPathInvalid
01295 
01296 ********************************************************************************************/
01297 
01298 void FreeHandTool::SetPreviousPath(NodePath* PrevPath, INT32 Start, INT32 Len)
01299 {
01300     // Set all the params associated with the previous path
01301     PreviousPath = PrevPath;
01302     StartSlot = Start;
01303     NumSlots = Len;
01304 
01305     // Calculate the CRC for the path.
01306     PathCRC = BuildPathCRC(PreviousPath);
01307 
01308     // Set the flags so that we know what is valid and what is not
01309     IsPreviousPathValid = TRUE;
01310     IsRetroPathValid = FALSE;
01311     IsSelectBlobsOnScreen = TRUE;
01312 
01313     // Tell the info bar to flash up a little indicator that retro fitting is possible
01314     if (pInfoBarOp != NULL)
01315         pInfoBarOp->SetRetroState(TRUE);
01316 
01317     // Clear out the retro fit path
01318     RetroPath->ClearPath();
01319 }
01320 
01321 
01322 
01323 /********************************************************************************************
01324 
01325 >   BOOL FreeHandTool::IsRetroPathStillValid()
01326 
01327     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01328     Created:    31/8/94
01329     Returns:    TRUE if we can Retro fit the currently selected object
01330     Purpose:    Finds out if we are able to do retro fitting and is used by the info bar
01331                 as well as the tool when it becomes the selected tool and when a drag of the
01332                 slider starts.
01333 
01334 ********************************************************************************************/
01335 
01336 BOOL FreeHandTool::IsRetroPathStillValid()
01337 {
01338     // Need to find out if we can retro fit the path or not
01339     BOOL IsStillValid = FALSE;
01340     SelRange* Selected = GetApplication()->FindSelection();
01341     DocView* pDocView = DocView::GetSelected();
01342 
01343     // If there is only one thing selected
01344     if ((pDocView!=NULL) && (Selected->Count()==1))
01345     {
01346         // Get the selected node
01347         Node* pNode = Selected->FindFirst();
01348 
01349         // see if it is our path
01350         if ((pNode==PreviousPath) && (pNode->IS_KIND_OF(NodePath)))
01351         {
01352             // Build the CRC for the path now and if it matches, flag that we can do retro fitting
01353             INT32 CurrentCRC = BuildPathCRC(PreviousPath);
01354             if (CurrentCRC==PathCRC)
01355                 IsStillValid = TRUE;
01356         }
01357     }
01358 
01359     // and return the state of IsStillValid
01360     return IsStillValid;
01361 }
01362 
01363 
01364 /********************************************************************************************
01365 
01366 >   INT32 FreeHandTool::BuildPathCRC(NodePath* PrevPath)
01367 
01368     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01369     Created:    14/6/94
01370     Inputs:     PrevPath - The path to build a CRC checksum for.
01371     Returns:    The CRC checksum for the path, relative to the first coord
01372     Purpose:    Goes though the path and adds all the coordinate offsets together to
01373                 come up with a number that will hopefully identify the path uniquly,
01374                 so that we can tell if this is in fact the path we want, only translated.
01375 
01376 ********************************************************************************************/
01377 
01378 INT32 FreeHandTool::BuildPathCRC(NodePath* PrevPath)
01379 {
01380     INT32 TotalCRC = 0;
01381 
01382     // Get at the path in the NodePath
01383     Path* CRCPath = &(PrevPath->InkPath);
01384 
01385     // Find out about the coords and how many there are
01386     DocCoord* Coords = CRCPath->GetCoordArray();
01387     INT32 NumCoords = StartSlot+NumSlots;
01388 
01389     // go though the coords
01390     for (INT32 i=StartSlot; i<NumCoords; i++)
01391     {
01392         // Get the coords relative from the first coord
01393         // making this process transparent to trnslation
01394         // Add it in to the CRC total
01395         TotalCRC += (Coords[i].x-Coords[StartSlot].x) + (Coords[i].y-Coords[StartSlot].y);
01396     }
01397 
01398     // return the CRC I have
01399     return TotalCRC;
01400 }
01401 
01402 
01403 
01404 /********************************************************************************************
01405 
01406 >   void FreeHandTool::TranslateTrackData()
01407 
01408     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01409     Created:    15/6/94
01410     Purpose:    If the path in the tree has been translated since it was drawn, we have to
01411                 translate the original retro fit data to match. This functions checks if
01412                 a translation is required, and translates all the coords if it is needed.
01413 
01414 ********************************************************************************************/
01415 
01416 void FreeHandTool::TranslateTrackData()
01417 {
01418     // if we do not have any track data then stop now
01419     if (TrackData==NULL)
01420         return;
01421 
01422     // Get the two coord sections of the paths
01423     DocCoord* TrackCoords = TrackData->GetCoordArray();
01424     DocCoord* TreeCoords = PreviousPath->InkPath.GetCoordArray();
01425 
01426     // see if the first coord matches
01427     if ((TrackCoords[0].x!=TreeCoords[StartSlot].x) || (TrackCoords[0].y!=TreeCoords[StartSlot].y))
01428     {
01429         // Nop, so find the offset
01430         INT32 Dx = TreeCoords[StartSlot].x - TrackCoords[0].x;
01431         INT32 Dy = TreeCoords[StartSlot].y - TrackCoords[0].y;
01432 
01433         // Loop through all the coords and add on the offset
01434         INT32 NumCoords = TrackData->GetNumCoords();
01435         for (INT32 i=0; i<NumCoords; i++)
01436         {
01437             // Offset the track data
01438             TrackCoords[i].x += Dx;
01439             TrackCoords[i].y += Dy;
01440         }
01441     }
01442 }
01443 
01444 
01445 /********************************************************************************************
01446 
01447 >   void FreeHandTool::RetroFitChanging()
01448 
01449     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01450     Created:    17/5/94
01451     Purpose:    Tries to re-fit the previous path with the current smoothness setting
01452 
01453 ********************************************************************************************/
01454 
01455 void FreeHandTool::RetroFitChanging()
01456 {
01457     BOOL IsStillValid = IsRetroPathStillValid();
01458     if (IsStillValid)
01459     {
01460         // Find the spread that the previous path was on
01461         Spread* pSpread = PreviousPath->FindParentSpread();
01462 
01463         // Find the docview that we are looking at
01464         DocView* pDocView = DocView::GetSelected();
01465 
01466         // if both of those are ok, carry on
01467         if (pSpread!=NULL && pDocView!=NULL)
01468         {
01469             BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01470             ENSURE(BlobMgr!=NULL, "Blob Manager was not there");
01471 
01472             // Translate the original data if the path moved
01473             TranslateTrackData();
01474 
01475             // Get rid of the Selection blobs if they are still on screen
01476             if (IsSelectBlobsOnScreen)
01477             {
01478                 BlobMgr->RenderMyBlobs(NULL, pSpread, PreviousPath);
01479                 IsSelectBlobsOnScreen = FALSE;
01480             }
01481 
01482             // Erase the old EORed path (if it was there)
01483             BlobMgr->RenderToolBlobsOff(this, pSpread, NULL);
01484 
01485             // Refit the path - First work out the current error factor
01486             double ScaleFactor = (pDocView->GetViewScale()).MakeDouble();
01487             double ErrorLevel = (64 + (160*Smoothness)) / ScaleFactor;
01488             ErrorLevel = ErrorLevel * ErrorLevel;
01489 
01490             // and perform all the maths
01491             if ((RetroPath!=NULL) && (TrackData!=NULL))
01492             {
01493                 CurveFitObject CurveFitter(RetroPath, ErrorLevel);
01494                 if (CurveFitter.Initialise(TrackData, TrackData->GetNumCoords()))
01495                     CurveFitter.FitCurve();
01496             }
01497 
01498             // Draw in the new path
01499             IsRetroPathValid = TRUE;
01500             BlobMgr->RenderToolBlobsOn(this, pSpread, NULL);
01501             AreWeRetroFitting = TRUE;
01502         }
01503         else
01504         {
01505             IsStillValid = FALSE;
01506         }
01507     }
01508 
01509     // If the retro fit is not valid, then invalidate the path properly
01510     if (!IsStillValid)
01511     {
01512         // mark the path as invalid
01513         PreviousPathInvalid();
01514     }
01515 }
01516 
01517 
01518 
01519 /********************************************************************************************
01520 
01521 >   void FreeHandTool::RetroFitFinished()
01522 
01523     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01524     Created:    17/5/94
01525     Purpose:    Should be called when any changes to the accuracy slider have been completed
01526 
01527 ********************************************************************************************/
01528 
01529 void FreeHandTool::RetroFitFinished()
01530 {
01531     // Start an operation here that will hide the original path and put a new one in
01532     // Make sure that we have a path that we are able to change
01533     if ((IsPreviousPathValid && AreWeRetroFitting) && (RetroPath!=NULL))
01534     {
01535         // Find the spread that the previous path was on
01536         Spread* pSpread = PreviousPath->FindParentSpread();
01537         if (pSpread!=NULL)
01538         {
01539             BlobManager* BlobMgr = GetApplication()->GetBlobManager();
01540             ENSURE(BlobMgr!=NULL, "Blob Manager was not there");
01541 
01542             // Draw the selection blobs back on again
01543             if (!IsSelectBlobsOnScreen)
01544             {
01545                 BlobMgr->RenderMyBlobs(NULL, pSpread, PreviousPath);
01546                 IsSelectBlobsOnScreen = TRUE;
01547             }
01548 
01549             // Erase the old EORed path (if it was there)
01550             BlobMgr->RenderToolBlobsOff(this, pSpread, NULL);
01551             
01552             // Build the undo info to change the existing path to the new one
01553             if (IsRetroPathValid)
01554             {
01555                 // Make a copy of the node we are joining with
01556                 Node* pNode;
01557                 if (PreviousPath->NodeCopy(&pNode))
01558                 {
01559                     // Start a slow job
01560                     BeginSlowJob();
01561 
01562                     // Clear out the old data in the path
01563                     NodePath* pNewPath = (NodePath*) pNode;
01564 
01565                     // Go and replace the section that we have changed
01566                     BOOL IsOk;
01567                     if (StartSlot==0)
01568                         IsOk = pNewPath->InkPath.RetroReplaceSection(StartSlot, NumSlots, RetroPath, TRUE);
01569                     else
01570                         IsOk = pNewPath->InkPath.RetroReplaceSection(StartSlot, NumSlots, RetroPath, FALSE);
01571                     
01572                     // If it worked, then build a bit of undo
01573                     if (IsOk)
01574                     {
01575                         // Create a new Retro Fit Operation
01576                         OpRetroFit* pRetroOp = new OpRetroFit;
01577                         if (pRetroOp!=NULL)
01578                         {
01579                             // Make a note of the start slot and length of the section we are
01580                             // changing as the operation causes this information to be invalidated
01581                             INT32 OldStartSlot = StartSlot;
01582                             INT32 OldNumSlots = RetroPath->GetNumCoords();
01583                             if (pRetroOp->BuildUndo(PreviousPath, (NodePath*)pNode))
01584                                 SetPreviousPath(pNewPath, OldStartSlot, OldNumSlots);
01585                         }
01586                         else
01587                         {
01588                             // Did not get the memory for the operation
01589                             IsOk = FALSE;
01590                         }
01591                     }
01592 
01593                     // End the slow job we started
01594                     EndSlowJob();
01595 
01596                     // If things generally did not work, tell the happy user
01597                     if (!IsOk)
01598                         ToolInformError(_R(IDS_RETROOUTOFMEM));
01599                 }
01600             }
01601 
01602             // Clear out the data in the fitted version of the path, as this is no longer needed
01603             // We do keep the Original track data though.
01604             RetroPath->ClearPath();
01605             IsRetroPathValid = FALSE;
01606         }
01607     }
01608 
01609     // We are no longer retro fitting the path, so reset the flag to indicate this
01610     AreWeRetroFitting = FALSE;
01611 }
01612 
01613 
01614 
01615 /********************************************************************************************
01616 
01617 >   BOOL FreeHandTool::GetNewPaths()
01618 
01619     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01620     Created:    1/5/95
01621     Returns:    TRUE if everything worked like a dream, FALSE if it screwed up big time
01622     Purpose:    Gets rid of the old path data and creates new paths. One of the useful
01623                 side effects of doing this is that it helps to keep the path data near the
01624                 top of the heap, make the freehand tool seem more responsive.
01625 
01626 ********************************************************************************************/
01627 
01628 BOOL FreeHandTool::GetNewPaths()
01629 {
01630     // Delete the paths that we have
01631     DeletePaths();
01632 
01633     // Create some new paths
01634     if ((RetroPath==NULL) && (TrackData==NULL))
01635     {
01636         // Create a new path for the track data
01637         TrackData = new Path();
01638         if (TrackData==NULL)
01639             return FALSE;
01640     
01641         // Create a new path for the smoothed data
01642         RetroPath = new Path();
01643         if (RetroPath==NULL)
01644         {
01645             delete TrackData;
01646             TrackData = NULL;
01647             return FALSE;
01648         }
01649     
01650         // init the track buffer and fail if it does not work
01651         if (!TrackData->Initialise(48,48))
01652         {
01653             DeletePaths();
01654             return FALSE;
01655         }
01656     
01657         // Same with the retro fitting path
01658         if (!RetroPath->Initialise(12, 12))
01659         {
01660             DeletePaths();
01661             return FALSE;
01662         }
01663 
01664     }
01665 
01666     // All Worked
01667     return TRUE;
01668 }
01669 
01670 
01671 
01672 /********************************************************************************************
01673 
01674 >   BOOL FreeHandTool::DeletePaths()
01675 
01676     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01677     Created:    1/5/95
01678     Returns:    TRUE - always
01679     Purpose:    Deletes the paths that hold the track data and the smoothed path and sets
01680                 thier params to NULL.
01681 
01682 ********************************************************************************************/
01683 
01684 BOOL FreeHandTool::DeletePaths()
01685 {
01686     // Retro fitting is no longer an option
01687     PreviousPathInvalid();
01688 
01689     // Clear out the old Track Data
01690     if (TrackData!=NULL)
01691     {
01692         delete TrackData;
01693         TrackData = NULL;
01694     }
01695     // Clear out the smoothed version of the path
01696     if (RetroPath!=NULL)
01697     {
01698         delete RetroPath;
01699         RetroPath = NULL;
01700     }
01701     // always return TRUE at present
01702     return TRUE;
01703 }
01704 
01705 
01706 /********************************************************************************************
01707 
01708 >   void FreeHandTool::DrawWithBrush()
01709 
01710     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01711     Created:    10/10/99
01712     Returns:    -
01713     Purpose:    Sets the brush handle member to that of the current default brush attribute.
01714 
01715 ********************************************************************************************/
01716 
01717 void FreeHandTool::SetCurrentBrushHandle()
01718 {
01719     Document* pDoc = Document::GetCurrent();
01720     if (pDoc == NULL)
01721     {
01722         ERROR3("No document");
01723         return;
01724     }
01725 
01726     AttributeManager* pAttrMgr = &(pDoc->GetAttributeMgr());
01727     if (pAttrMgr == NULL)
01728     {
01729         ERROR3("No attribute manager");
01730         return;
01731     }
01732 
01733     AttrBrushType* pAttr = (AttrBrushType*)(pAttrMgr->GetCurrentAttribute(CC_RUNTIME_CLASS(NodeRenderableInk), CC_RUNTIME_CLASS(AttrBrushType)));
01734 
01735     if (pAttr == NULL)
01736     {
01737         ERROR3("No current brush attribute");
01738         return;
01739     }
01740     m_BrushHandle = pAttr->GetBrushHandle();
01741     
01742 }
01743 
01744 /********************************************************************************************
01745 
01746 >   void FreeHandTool::CreateBrush()
01747 
01748     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01749     Created:    10/10/99
01750     Returns:    -
01751     Purpose:    Called from the infobar, creates a brush definition from the ink objects in 
01752                 the selection and passes it over to the BrushComponent.  Also creates a 
01753                 GRenderBrush render region which is used for rendering the brush into when 
01754                 the user begins to draw.
01755                 
01756 ********************************************************************************************/
01757 
01758 void FreeHandTool::CreateBrush()
01759 {
01760     //TRACEUSER( "Diccon", _T("\nCreating Brush"));
01761 
01762 
01763         SelRange* pSel = GetApplication()->FindSelection();
01764 
01765         if (pSel == NULL)
01766         {
01767             return ;
01768         }
01769 
01770         m_NumInkNodes = 0;
01771 
01772         Node* pNode = pSel->FindFirst();
01773         
01774         if (pNode == NULL)
01775         {
01776             InformWarning(_R(IDS_BRUSHCREATENOSEL));
01777             return;
01778         }
01779 
01780         // first count the number of objects
01781         while (pNode != NULL)
01782         {
01783             if (pNode->IsAnObject())
01784             {
01785                 if (BrushDefinition::ObjectCanCreateBrush((NodeRenderableInk*)pNode))
01786                     m_NumInkNodes++;
01787 
01788                 if (m_NumInkNodes >= MAX_BRUSH_OBJECTS)
01789                 {
01790                     InformWarning(_R(IDS_BRUSHCREATE_TOOMANY));
01791                     break;
01792                 }
01793             }
01794             pNode = pSel->FindNext(pNode);
01795         }
01796 
01797         if (m_NumInkNodes == 0)
01798         {
01799             InformWarning(_R(IDS_BRUSHCREATE_INVALID));
01800             return;
01801         }
01802 
01803         InitialiseBrushInkNodeArray((UINT32)m_NumInkNodes);
01804         
01805         DocRect BBox; // 
01806 
01807         // iterate through again and assign the selection pointers to the new array
01808         UINT32 Counter = 0;
01809         pNode = pSel->FindFirst();
01810 
01811         while (pNode != NULL && Counter < m_NumInkNodes)
01812         {
01813             if (pNode->IsAnObject())
01814             {               
01815                 // check to see that its not too big
01816                 BBox = ((NodeRenderableInk*)pNode)->GetBoundingRect();
01817                 if (BBox.Height() > MAX_BRUSH_SPACING || BBox.Width() > MAX_BRUSH_SPACING)
01818                 {
01819                     InformWarning(_R(IDS_BRUSH_TOOBIG));
01820                     return;
01821                 }
01822                 if (BrushDefinition::ObjectCanCreateBrush((NodeRenderableInk*)pNode))
01823                 {
01824                     m_BrushInkNodeArray[Counter++] = (NodeRenderableInk*)pNode;
01825                 }
01826             }
01827             pNode = pSel->FindNext(pNode);
01828         }
01829     
01830         Spread *pBrushSpread = new Spread;
01831         if (pBrushSpread == NULL)
01832         {
01833             ERROR3("Couldn't create new brush spread");
01834             return;
01835         }
01836 
01837         Layer *pBrushLayer = new Layer(pBrushSpread, FIRSTCHILD, String_256(TEXT("Diccon did this")));
01838         if (pBrushLayer == NULL)
01839         {
01840             ERROR3("Couldn't create new brush (5)");
01841             delete pBrushSpread;
01842             return;
01843         }
01844 
01845         BOOL LineWidth = FALSE;
01846         BOOL Compound = FALSE;
01847         // And attach the clipart tree to our new layer
01848         Node* pLastNode = NULL;
01849 //      AttrBrushType* pAttrBrush = NULL;
01850 
01851         for (UINT32 i = 0; i < m_NumInkNodes; i++)
01852         {
01853             NodeRenderableInk* pInk = m_BrushInkNodeArray[i];
01854             
01855             // do a quick line width check - we need this info later
01856             if (NodeHasLineWidth(pInk))
01857                 LineWidth = TRUE;
01858             if (pInk->IsCompound())
01859                 Compound = TRUE;
01860             pInk->MakeAttributeComplete();
01861 
01862             Node* pInsertNode = NULL;
01863             // OK if we're trying to make a brush out of an object that is already a brush then 
01864             // convert the object to normal shapes first.
01865         /*  pInk->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), 
01866                                                     (NodeAttribute**)&pAttrBrush);
01867 
01868             if (pAttrBrush && pAttrBrush->GetBrushHandle() != BrushHandle_NoBrush)
01869             {
01870                 //Turn the brush into a group with lots of nodepaths
01871                 BrushBecomeAGroup BecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL);
01872                 pAttrBrush->DoBecomeA(&BecomeA, pInk);
01873         
01874                 // the brush will create a group out of itself and we want to retrieve that
01875                 NodeGroup* pBrushGroup = BecomeA.GetNodeGroup();
01876                 if (pBrushGroup != NULL)
01877                     pInsertNode = pBrushGroup;
01878                 else
01879                 {
01880                     ERROR3("Unable to make group from brush");
01881                     return;
01882                 }
01883 
01884                 pAttrBrush = NULL;
01885             }
01886             else*/
01887             {   
01888                 // the normal case
01889                 pInsertNode = pInk->PublicCopy();
01890                 
01891                 if (pInsertNode == NULL)
01892                 {
01893                     ERROR3("Error copying node");
01894                     return;
01895                 }
01896 
01897                 pInk->CopyChildrenTo(pInsertNode);
01898             
01899             }
01900             pInk->NormaliseAttributes();
01901             if (i==0)
01902                 pInsertNode->AttachNode(pBrushLayer, FIRSTCHILD, FALSE, TRUE);
01903             else
01904                 pInsertNode->AttachNode(pLastNode, NEXT, FALSE, TRUE);
01905 
01906             pLastNode = pInsertNode;
01907         }
01908 
01909         // convert any indexed colours in our subtree
01910         LineDefinition::ConvertIndexedColours(pBrushSpread);
01911 
01912         // now create the brush definition
01913         BrushDefinition* pBrushDef = new BrushDefinition(pBrushSpread);
01914 
01915         if (pBrushDef == NULL)
01916         {
01917             ERROR3("Couldn't allocate brush def");
01918             delete pBrushSpread;
01919             delete pBrushLayer;
01920             return;
01921         }
01922 
01923         // just double check
01924         if (!pBrushDef->IsActivated())
01925         {
01926             ERROR3("Brush failed to initialise correctly, aborting Create Brush");
01927             delete pBrushDef;
01928             delete pBrushSpread;
01929             delete pBrushLayer;
01930             return;
01931         }
01932 
01933         // set the default spacing
01934         DocRect BRect = pBrushDef->GetLargestBoundingBox();
01935         MILLIPOINT Width = BRect.Width();
01936         if (pBrushDef->GetNumBrushObjects() > 1 || LineWidth || Compound)
01937         {
01938             MILLIPOINT Spacing = (MILLIPOINT)(Width * 1.1);
01939             if (Spacing > MAX_BRUSH_SPACING)
01940                 Spacing = MAX_BRUSH_SPACING;
01941 
01942             pBrushDef->SetSpacing(Spacing);
01943         }
01944         else
01945             pBrushDef->SetSpacing(Width / 5);
01946 
01947         // get the brush component of the document
01948         Document* pDoc = Document::GetCurrent();
01949         if (pDoc == NULL)
01950         {
01951             ERROR3("No document");
01952             delete pBrushSpread;
01953             delete pBrushLayer;
01954             return;
01955         }
01956         BrushComponent *pBrushComp = (BrushComponent*)(pDoc->GetDocComponent(CC_RUNTIME_CLASS(BrushComponent)));
01957         if (pBrushComp == NULL)
01958         {
01959             ERROR3("Couldn't get brush component");
01960             delete pBrushSpread;
01961             delete pBrushLayer;
01962             return;
01963         }
01964         BrushHandle NewHandle = pBrushComp->AddNewItem(pBrushDef, TRUE);
01965         
01966         if (NewHandle != BrushHandle_NoBrush)
01967         {
01968             if (pInfoBarOp != NULL)
01969                 pInfoBarOp->AddBrush();
01970 
01971             // we want this brush selected
01972             BrushSelected(NewHandle, FALSE);
01973 
01974             // and we want to clear the document selection
01975             NodeRenderableInk::DeselectAll();
01976         }
01977         else
01978             delete pBrushDef;
01979         
01980 }
01981 
01982 
01983 
01984 /***********************************************************************************************
01985 
01986 >   void FreeHandTool::LaunchBrushDefinitionDialog(BrushHandle Handle)
01987 
01988     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
01989     Created:    6/10/99
01990     Inputs:     Handle of the brush definition to edit
01991     Returns:    -
01992     Purpose:    launches the brush edit dialog for the brush handle supplied.  A good idea to call
01993                 this just after creating a brush so the user can edit it before using it.
01994 
01995 ***********************************************************************************************/
01996 
01997 void FreeHandTool::LaunchBrushDefinitionDialog(BrushHandle Handle)
01998 {
01999     // first get the brush definition from the component
02000     // get the brush component from the document
02001     Document* pDoc = Document::GetCurrent();
02002     if (!pDoc) return;
02003     BrushComponent* pBrushComp = (BrushComponent*)pDoc->GetDocComponent(CC_RUNTIME_CLASS(BrushComponent));
02004     if (!pBrushComp) return;
02005 
02006     BrushDefinition* pBrushDef = pBrushComp->FindBrushDefinition(Handle);
02007     if (!pBrushDef) return;
02008 
02009     // now launch the edit dialog
02010     BrushData* pData = pBrushDef->GetBrushData();
02011     
02012     if (pData != NULL)
02013     {
02014 PORTNOTE("other", "Removed CBrushGadget support");
02015 #ifndef EXCLUDE_FROM_XARALX
02016         // get the line gallery, as we want to edit the definition version
02017         LineGallery* pLineGallery = LineGallery::GetInstance();
02018         if (pLineGallery != NULL)
02019         {
02020             // get a pointer to the gadget
02021             CBrushGadget* pGadget = pLineGallery->GetBrushGadget();
02022             if (pGadget != NULL)
02023             {
02024                 // brush definitions don't know their own handle so we need to tell the data
02025                 pData->m_BrushHandle = Handle;
02026             
02027                 pGadget->EnableEditingBrushDefinition ();
02028                 pGadget->HandleBrushButtonClick(pData->m_BrushHandle);
02029             }
02030             // we need to delete the data
02031             delete pData;
02032         }
02033 #endif
02034     }
02035 }
02036 
02037 /***********************************************************************************************
02038 
02039 >   void FreeHandTool::InitialiseBrushInkNodeArray(UINT32 NumObjects)
02040 
02041     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com> 
02042     Created:    6/10/99
02043     Inputs:     Number of objects to size the array at
02044     Returns:    -
02045     Purpose:    Clears out the m_BrushRefPtrArray if it is not empty, and sets the size 
02046 
02047 ***********************************************************************************************/
02048 
02049 void FreeHandTool::InitialiseBrushInkNodeArray(UINT32 NumObjects)
02050 {
02051     UINT32 i = 0;
02052     // we don't wish to delete any pointers as they point to objects
02053     // that are actually in the document.
02054     m_BrushInkNodeArray.clear();
02055 
02056     m_BrushInkNodeArray.resize(NumObjects);
02057 
02058     // fill the array with NULL objects so that we can check later
02059     // to see if our allocations have worked
02060     i = 0;
02061     while (i < m_BrushInkNodeArray.size())
02062     {
02063         m_BrushInkNodeArray[i++] = NULL;
02064     }
02065 
02066 }
02067 
02068 
02069 /********************************************************************************************
02070 
02071 >   GRenderBrush* FreeHandTool::GetBigGRenderBrush()
02072 
02073     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02074     Created:    14/11/99
02075     Returns:    -
02076     Purpose:    Gets a GRenderBrush the size of the current view, then renders into it the 
02077                 contents of the current view
02078 
02079 ********************************************************************************************/
02080 
02081 GRenderBrush* FreeHandTool::GetBigGRenderBrush(Spread* pSpread)
02082 {
02083 PORTNOTE("other", "Removed m_pGRenderBrush support");
02084 #ifndef EXCLUDE_FROM_XARALX
02085     m_bBrushReady = FALSE;
02086     Document* pDoc = Document::GetSelected();
02087     if (!pDoc)
02088         return NULL;
02089 
02090     DocView * pDocView = DocView::GetSelected();
02091     
02092     // get the docview rect and convert to spread coords 
02093     DocRect ViewRect = pDocView->GetDocViewRect(pSpread);
02094     pSpread->DocCoordToSpreadCoord(&ViewRect);
02095     
02096     // find out about our device 
02097     //View* pView = View::GetSelectedView();
02098     CCamView* pCCamView = pDocView->GetConnectionToOilView(); 
02099     CDC* pDevContext = ((ScreenCamView*)pCCamView)->GetRenderDC();
02100     HDC DeviceHdc = pDevContext->GetSafeHdc();
02101     INT32 DeviceBPP = GetDeviceCaps(DeviceHdc, BITSPIXEL);
02102     
02103     // a couple more variables we need to get the GRenderBrush
02104     Matrix  RenderMat = pDocView->ConstructRenderingMatrix(pSpread);    
02105     FIXED16 Scale = 1;
02106 //  INT32 i = 0;
02107     GRenderBrush* pGRender = NULL;  
02108     
02109     INT32 DeviceRes = GetDeviceCaps( DeviceHdc, LOGPIXELSX );
02110     // create a new GRenderBrush at 32BPP and 96 dpi
02111     pGRender = new GRenderBrush(ViewRect, RenderMat, Scale, 32, DeviceRes); 
02112 
02113     // Check to see if the Render Region is valid!
02114     if (pGRender == NULL)
02115     {
02116         ERROR3("Failed to allocate DIB");
02117         return NULL;
02118     }
02119 
02120     BOOL ok = pGRender->AttachDevice(pDocView, pDevContext, pSpread);
02121     if (!ok)
02122     {   
02123         delete pGRender;
02124         pGRender = NULL;    
02125         return NULL;
02126     }
02127     
02128     if (DeviceBPP < 24)
02129         pGRender->SetDeepDIB(FALSE);
02130     else
02131         pGRender->SetDeepDIB(TRUE);
02132 
02133     
02134     pGRender->InitDevice();
02135     pGRender->m_DoCompression = TRUE;
02136     
02137     pGRender->InitAttributes();
02138     pGRender->RRQuality.SetQuality(QUALITY_MAX);
02139     pGRender->SetQualityLevel();
02140     pGRender->SetLineAttributes();
02141     pGRender->SetFillAttributes();
02142 
02143 
02144     // lleeeets geeet ready to render!  
02145     if (!pGRender->StartRender())
02146     {
02147         ERROR3("Unable to setup bitmap");
02148         delete pGRender;
02149         return NULL;
02150     }
02151     
02152     // if we're less than 24 bit then we need to allocate smaller brush bitmaps as well
02153     if (DeviceBPP < 24)
02154     {
02155         if (!InitialiseBrushBitmaps(pGRender))
02156         {
02157             InformWarning(_R(IDS_BRUSHBITMAP_INVALID));
02158             delete pGRender;
02159             return NULL;
02160         }
02161     }
02162 
02163     // or blit, in this case, as it is quicker than rendering the view
02164     if (!pGRender->CaptureView(pSpread))
02165     {
02166         ERROR3("Unable to capture view");
02167         delete pGRender;
02168         return NULL;
02169     }
02170 
02171 
02172     m_bBrushReady = TRUE;
02173     return pGRender;
02174 #else
02175     return NULL;
02176 #endif
02177 }
02178 
02179 
02180 /********************************************************************************************
02181 
02182 >   MILLIPOINT  FreeHandTool::GetBrushSpacing()
02183 
02184     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02185     Created:    30/11/99
02186     Returns:    The current brush spacing
02187     Purpose:    for finding out the freehand tools current brush spacing
02188 
02189 ********************************************************************************************/
02190 
02191 MILLIPOINT FreeHandTool::GetBrushSpacing()
02192 {
02193     return m_BrushSpacing;
02194 }
02195 
02196 
02197 /********************************************************************************************
02198 
02199 >   void  FreeHandTool::SetBrushSpacing(MILLIPOINT Spacing)
02200 
02201     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02202     Created:    30/11/99
02203     inputs:     the spacing value to set
02204     Returns:    -
02205     Purpose:    for setting the freehand tools current brush spacing
02206 
02207 ********************************************************************************************/
02208 
02209 void FreeHandTool::SetBrushSpacing(MILLIPOINT Spacing)
02210 {
02211     m_BrushSpacing = Spacing;
02212 }
02213 
02214 
02215 
02216 /********************************************************************************************
02217 
02218 >   BOOL FreeHandTool::InitialiseBlendRef(NodeRenderableInk* pInk)
02219 
02220     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02221     Created:    14/11/99
02222     Inputs:     pInk - inknode to generate the blendref from
02223     Outputs:    -
02224     Returns:    TRUE if successful, false otherwise
02225     Purpose:    To initialise a BlendRef object
02226 
02227 ********************************************************************************************/
02228 
02229 BOOL FreeHandTool::InitialiseBlendRef(NodeRenderableInk* pInk)
02230 {
02231     ERROR2IF(pInk == NULL,FALSE,"InkNode is NULL");
02232     
02233     if (m_pBlendRef != NULL)
02234     {   
02235         delete m_pBlendRef;
02236         m_pBlendRef = NULL;
02237     }
02238     m_pBlendRef = new BlendRef;
02239     
02240     BOOL ok = (m_pBlendRef != NULL);
02241     // initialise the blender
02242     Progress* pProgress = NULL;
02243     if (ok)  ok = m_pBlendRef->Initialise(pInk, -1, NULL, pProgress, FALSE, NULL);  
02244 
02245     return ok;
02246 }
02247 
02248 
02249 
02250 /********************************************************************************************
02251 
02252 >   DocRect FreeHandTool::GetLargestInkBoundingRect()
02253 
02254     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02255     Created:    14/11/99
02256     Inputs:     -
02257     Outputs:    -
02258     Returns:    the bounding rect of the largest brush ink object, or an empty rect
02259     Purpose:    as above, note that the m_brushinknodes pointer must be initialised
02260 
02261 ********************************************************************************************/
02262 
02263 DocRect FreeHandTool::GetLargestInkBoundingRect()
02264 {
02265     DocRect LargestBRect;  //create an empty rect
02266 
02267     
02268 
02269     INT32 BiggestArea = 0;
02270     for (UINT32 i = 0; i < m_NumInkNodes; i++)
02271     {
02272         DocRect TempRect = m_BrushInkNodeArray[i]->GetBoundingRect();
02273         INT32 TempArea = TempRect.Height() * TempRect.Width();
02274         if (TempArea > BiggestArea)
02275         {
02276             LargestBRect = TempRect;
02277             BiggestArea  = TempArea;
02278         }
02279     }
02280     return LargestBRect;
02281 }
02282 
02283 
02284 /********************************************************************************************
02285 
02286 >   BOOL FreeHandTool::NodeHasLineWidth(NodeRenderableInk* pInk)
02287     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02288     Created:    14/11/99
02289     Inputs:     pNode - inknode to test
02290     Outputs:    -
02291     Returns:    TRUE if this node has an applied linewidth > 0, FALSE if not
02292     Purpose:    To determine if this node has a linewidth applied to it that is greater than zero
02293                 It turns out that the best way to find this out is to find out if it has an
02294                 applied stroke colour, rather than look for line width.
02295 ********************************************************************************************/
02296 
02297 BOOL FreeHandTool::NodeHasLineWidth(NodeRenderableInk* pNode)
02298 {
02299     ERROR2IF(pNode == NULL, FALSE, "pNode is NULL in FreeHandTool::NodeHasLineWidth");
02300     NodeAttribute* pAttr = NULL;
02301     pNode->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), &pAttr);
02302     
02303     if (pAttr == NULL)
02304         return FALSE;
02305 
02306     // ok so we've got an attribute, lets see if it has the NoColour flag set
02307     StrokeColourAttribute* pVal = (StrokeColourAttribute*)pAttr->GetAttributeValue();
02308     if (pVal != NULL)
02309     {
02310         DocColour Col = pVal->Colour;
02311         if (Col.IsTransparent())
02312             return FALSE;
02313     }
02314 
02315     return TRUE;
02316 }
02317 
02318 
02319 /********************************************************************************************
02320 
02321 >   BrushDefinition FreeHandTool::GetSelectedBrush()
02322 
02323     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02324     Created:    14/11/99
02325     Inputs:     -
02326     Outputs:    -
02327     Returns:    pointer to the brush definition of the current default brush attribute 
02328                 this can be NULL.
02329     Purpose:    In order to determine which brush to draw with when performing a real time
02330                 draw brush operation.  
02331 
02332 ********************************************************************************************/
02333 
02334 BrushDefinition* FreeHandTool::GetSelectedBrush()
02335 {
02336     Document* pDoc = Document::GetCurrent();
02337     ERROR2IF(pDoc == NULL, NULL, "No document");
02338     
02339     BrushComponent* pBrushComp = (BrushComponent*)pDoc->GetDocComponent(CC_RUNTIME_CLASS(BrushComponent));
02340     if (pBrushComp == NULL)
02341     {
02342         ERROR3("No brush component");
02343         return NULL;
02344     }
02345 
02346     return pBrushComp->FindBrushDefinition(m_BrushHandle);
02347     
02348 }
02349 
02350 
02351 
02352 /********************************************************************************************
02353 
02354 >   BOOL FreeHandTool::SetBrushUpdateState(UINT32 Update)
02355 
02356     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02357     Created:    14/11/99
02358     Inputs:     Update - when we want the brush to update next upon entering brush selected
02359                          UPDATE_NOW: updates immediately
02360                          UPDATE_ONIDLE: updates on the next idle
02361                          UPDATE_NEVER: will not update until the flag is changed
02362                 
02363     Outputs:    -
02364     Returns:    TRUE if successful, FALSE if Update is invalid
02365     Purpose:    Tells us when we should next update the offscreen buffer used by the brush.
02366                 Often it is wise ot wait until the next idle to update because that gives time
02367                 for the screen to redraw after the brush combo has closed.
02368 
02369 ********************************************************************************************/
02370 
02371 BOOL FreeHandTool::SetBrushUpdateState(UPDATE_STATE Update)
02372 {
02373     if (Update < UPDATE_NOW || Update > UPDATE_NEVER)
02374     {
02375         ERROR3("Invalid update flag");
02376         return FALSE;
02377     }
02378     m_UpdateBrushState = Update;
02379     return TRUE;
02380 }
02381 
02382 /********************************************************************************************
02383 
02384 >   void FreeHandTool::BrushSelected(BrushHandle Handle, BOOL ApplyToSelection)
02385 
02386     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02387     Created:    14/11/99
02388     Inputs:     Handle to the selected brush
02389                 ApplyToSelection - whether or not we wish to apply this brush to the selection
02390     Outputs:    -
02391     Returns:    TRUE if successful, FALSE if something went wrong
02392     Purpose:    sets the brushhandle member of the tool to Handle. Designed to be called by the
02393                 infobar when it receives a brushmessage.  Also initiates a number of steps that
02394                 must be taken to ensure that real time drawing with this brush can proceed smoothly
02395 
02396 ********************************************************************************************/
02397 
02398 BOOL FreeHandTool::BrushSelected(BrushHandle Handle, BOOL ApplyToSelection)
02399 {
02400     if (m_UpdateBrushState == UPDATE_NEVER)
02401         return TRUE;
02402 
02403     // first apply what we've got to the selection
02404     if (ApplyToSelection)
02405     {
02406         // if this fails then quit
02407         if (!ApplyBrushToSelection(Handle))
02408             return FALSE;
02409 
02410         m_UpdateBrushState = UPDATE_ONIDLE; // So that we update on the next idle rather than immediately
02411     }
02412 
02413     //TRACEUSER( "Diccon", _T("Start Brush selected\n"));
02414     m_BrushHandle = Handle;
02415     
02416     // note that we do not do the update straight unless the flag is set.  If it is not set then
02417     // we set the flag and then do the update on the next idle event.  The reason for this is that
02418     // if we do not wait then the brush combo will still be on the screen whilst we do our screen grab
02419 
02420     if (m_UpdateBrushState == UPDATE_NOW)
02421         m_UpdateBrushState = UPDATE_NEVER;
02422     else
02423     {   
02424         m_UpdateBrushState = UPDATE_ONIDLE;
02425         return TRUE;
02426     }
02427 
02428     // if its the 'null' brush then just kill the render region and leave
02429     if (m_BrushHandle == BrushHandle_NoBrush)
02430     {
02431 PORTNOTE("other", "Removed m_pGRenderBrush support");
02432 #ifndef EXCLUDE_FROM_XARALX
02433         if (m_pGRenderBrush != NULL)
02434         {
02435             delete m_pGRenderBrush;
02436             m_pGRenderBrush = NULL;
02437         }
02438 #endif
02439         return TRUE;
02440     }
02441 
02442 PORTNOTE("other", "Removed m_pGRenderBrush support");
02443 #ifndef EXCLUDE_FROM_XARALX
02444     // if we are <24BPP display then make the whole thing again from scratch
02445     // as we will need different sized brush bitmaps and everything
02446     if (m_pGRenderBrush != NULL && m_pGRenderBrush->GetScreenDepth() < 24)
02447     {
02448         delete m_pGRenderBrush;
02449         m_pGRenderBrush = NULL;
02450     }
02451 
02452     String_32 ProgString = _T("Preparing brush, please wait..");
02453     Progress Hourglass(&ProgString, -1, FALSE);
02454     
02455     // set smoothness to zero
02456 //  SetSmoothness(0);
02457 
02458     Spread* pCurrentSpread = Document::GetSelectedSpread();
02459     // make a new GRenderBrush for the new brush
02460     if (pCurrentSpread == NULL)
02461     {
02462         ERROR3("No Spread in FreeHandTool::BrushSelected");
02463         return FALSE;
02464     }
02465         
02466     // inform the join info of what we're going to draw
02467     JoinInfo.m_BrushHandle = m_BrushHandle;
02468     
02469     if (m_pGRenderBrush == NULL)
02470     {
02471         m_pGRenderBrush = GetBigGRenderBrush(pCurrentSpread);
02472     
02473         if (m_pGRenderBrush != NULL)
02474         {
02475             if (!InitialiseBrushBitmaps(m_pGRenderBrush))
02476             {
02477                 InformWarning(_R(IDS_BRUSHBITMAP_INVALID));
02478                 delete m_pGRenderBrush;
02479                 m_pGRenderBrush = NULL;
02480 
02481                 return FALSE;
02482             }
02483 
02484             m_bBrushReady = TRUE;
02485 
02486         }
02487         else
02488         {
02489             ToolInformError(_R(IDS_OUT_OF_MEMORY));
02490         
02491             return FALSE;
02492         }
02493     }
02494     else
02495     {
02496         // grab whats on the screen
02497         BOOL ok = m_pGRenderBrush->CaptureView(pCurrentSpread);
02498 
02499         // tell GDraw to use this bitmap
02500         if (ok) m_pGRenderBrush->SetupMainBitmap();
02501         return ok; //m_pGRenderBrush->CaptureView(pCurrentSpread);
02502     }
02503 #endif
02504 
02505     return TRUE;
02506 }
02507 
02508 
02509 /********************************************************************************************
02510 
02511 >   BOOL FreeHandTool::InitialiseBrushBitmaps(GRenderBrush* pGRender)
02512 
02513     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02514     Created:    14/11/99
02515     Inputs:     pGRender - the render region for which we want brush bitmaps for
02516     Outputs:    -
02517     Returns:    TRUE if successful, FALSE if something went wrong
02518     Purpose:    To work out how large our brush bitmaps must be and allocate them to the
02519                 render region supplied
02520 
02521 ********************************************************************************************/
02522 
02523 BOOL FreeHandTool::InitialiseBrushBitmaps(GRenderBrush* pGRender)
02524 {
02525     ERROR2IF(pGRender == NULL, FALSE, "GRenderBrush is NULL in FreeHandTool::InitialiseBrushBitmaps");
02526 
02527     // first work out the largest possible size of our brush        
02528     BrushDefinition* pBrushDef = GetSelectedBrush();
02529     if (pBrushDef == NULL)
02530     {
02531         ERROR3("No brush definition");
02532         return FALSE;
02533     }
02534     // find out how big the biggest shape is
02535     DocRect LargestRect = pBrushDef->GetLargestPossibleRect(TRUE);
02536     if (LargestRect.IsEmpty())
02537     {
02538         ERROR3("Empty bounding rect");  
02539         return FALSE;
02540     }
02541 
02542     // Oh dear it still isn't big enough, guess I'll just have to resort to 
02543     // this nasty hack (please look away)
02544 /*  INT32 Height = LargestRect.Height();
02545     INT32 Width = LargestRect.Width();
02546 
02547     LargestRect.Inflate((INT32)(Height * 0.1), (INT32)(Width *0.1));
02548 */
02549 PORTNOTE("other", "Removed m_pGRenderBrush support");
02550 #ifndef EXCLUDE_FROM_XARALX
02551     if (!pGRender->InitialiseBrushBitmaps(LargestRect))
02552     {
02553         return FALSE;
02554     }
02555 #endif
02556     return TRUE;
02557 }
02558 
02559 
02560 
02561 
02562 
02563 /********************************************************************************************
02564 
02565 >   void FreeHandTool::BrushDeleted(BrushHandle Handle)
02566 
02567     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02568     Created:    14/11/99
02569     Inputs:     Handle to the brush that has been deleted
02570     Outputs:    -
02571     Returns:    -
02572     Purpose:    Lets the tool know that a brush has been deactivated (we call it deleted in the UI
02573                 but its actually still there).  If this brush is the current brush then we need
02574                 to set the current brush to default.
02575                 Also informs the infobar
02576 
02577 ********************************************************************************************/
02578 
02579 void FreeHandTool::BrushDeleted(BrushHandle Handle)
02580 {
02581     if (Handle == m_BrushHandle)
02582         BrushSelected(BrushHandle_NoBrush, FALSE);
02583 
02584     if (pInfoBarOp != NULL)
02585         pInfoBarOp->RemoveBrush(Handle);
02586 }
02587 
02588 /********************************************************************************************
02589 
02590 >   void FreeHandTool::BrushFinished()
02591 
02592     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02593     Created:    14/11/99
02594     Inputs:     -
02595     Outputs:    -
02596     Returns:    -
02597     Purpose:    Called by OpDrawBrush to tell the tool that the previous draw brush op has 
02598                 finished.  Therefore the tool must prepare for the next one (if any)
02599 ********************************************************************************************/
02600 
02601 void FreeHandTool::BrushFinished()
02602 {
02603     // turn the colour picker back on
02604     SetColourEditorProcessing(TRUE);
02605     
02606     // turn background rendering back on 
02607     Document* pDoc = Document::GetSelected();
02608     DocView* pDocView = pDoc->GetFirstDocView();
02609     
02610     while (pDocView)
02611     {
02612         pDocView->SetPreventRenderView(FALSE);
02613         pDocView = pDoc->GetNextDocView(pDocView);
02614     }
02615 
02616     /*
02617     DocView* pDocView = DocView::GetSelected(); //gotta be getselected in case we have multiple views
02618     if (pDocView != NULL)
02619         pDocView->SetPreventRenderView(FALSE);
02620     */
02621     m_bBrushReady = FALSE;
02622     m_UpdateBrushState = UPDATE_ONIDLE;
02624     BrushSelected(m_BrushHandle, FALSE);
02625 
02626 
02627 }
02628 
02629 /********************************************************************************************
02630 
02631 >   void FreeHandTool::ScreenChanged()
02632 
02633     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02634     Created:    14/11/99
02635     Inputs:     -
02636     Outputs:    -
02637     Returns:    -
02638     Purpose:    Called by the infobar to let the tool know that the screen has changed.  
02639                 Essentially this means that if we are in brush drawing mode then we need to 
02640                 blit the contents of the screen into our big render region
02641 ********************************************************************************************/
02642 
02643 void FreeHandTool::ScreenChanged(BOOL WipeRegion)
02644 {
02645     //TRACEUSER( "Diccon", _T("FreehandTool::ScreenChanged\n"));
02646 
02647     if (WipeRegion == TRUE)
02648     {
02649 PORTNOTE("other", "Removed m_pGRenderBrush support");
02650 #ifndef EXCLUDE_FROM_XARALX
02651         if (m_pGRenderBrush != NULL)
02652         {
02653             delete m_pGRenderBrush;
02654             m_pGRenderBrush = NULL;
02655         }
02656 #endif
02657     }
02658 
02659     m_bBrushReady = FALSE;
02660     
02661     m_UpdateBrushState = UPDATE_ONIDLE;
02662 }
02663 
02664 
02665 /********************************************************************************************
02666 
02667 >   BOOL FreeHandTool::ApplyBrushToSelection(BrushHandle Handle)
02668 
02669     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02670     Created:    14/11/99
02671     Inputs:     Handle to the selected brush
02672     Outputs:    -
02673     Returns:    TRUE if successful, FALSE otherwise
02674     Purpose:    Applies the chosen brush to all items in the selection via the attribute manager.
02675                 However we do not want to make this a default attribute so if the selection is
02676                 empty then nothing happens.
02677 
02678 ********************************************************************************************/
02679 
02680 BOOL FreeHandTool::ApplyBrushToSelection(BrushHandle Handle)
02681 {
02682     // first check to see if we have a selection
02683     SelRange* pSel = GetApplication()->FindSelection();
02684     if (pSel == NULL)
02685     {
02686         ERROR3("No selection in FreeHandTool::ApplyBrushToSelection");
02687         return FALSE;
02688     }
02689 
02690     // we have decided that if the selection is not empty then we will not try to 
02691     // go ahead and apply the attribute, as this gets annoying when you only want to 
02692     // draw with a different brush
02693     if (pSel->IsEmpty())
02694         return TRUE;
02695 
02696     // ok so we have a selection, now get an attribute node from the brush component
02697     NodeAttribute* pNewAttr = BrushComponent::CreateNode(Handle);
02698     if (pNewAttr != NULL)
02699         AttributeManager::AttributeSelected(pNewAttr);
02700 
02701     // we may have a scenario where we wish to apply the default brush
02702     if (Handle == BrushHandle_NoBrush)
02703     {
02704         // in this case we just need to make an attribute node
02705         NodeAttribute* pAttr = new AttrBrushType;
02706         if (pAttr != NULL)
02707             AttributeManager::AttributeSelected(pAttr);
02708     }
02709     return TRUE;
02710     
02711 }
02712     
02713 
02714 /********************************************************************************************
02715 
02716 >   virtual BOOL FreeHandTool::OnIdle()
02717 
02718     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02719     Created:    14/11/99
02720     Inputs:     -
02721     Outputs:    -
02722     Returns:    FALSE always as we need more idles
02723     Purpose:
02724 ********************************************************************************************/
02725 
02726 BOOL FreeHandTool::OnIdle()
02727 {
02728     // just check that we have a document
02729     if (Document::GetCurrent() == NULL)
02730     {
02731         m_UpdateBrushState = UPDATE_NEVER;
02732         return FALSE;
02733     }
02734 
02735     if (m_UpdateBrushState == UPDATE_ONIDLE)
02736     {
02737 #ifdef NEWFASTBRUSHES
02738         static Application* pApp = GetApplication ();
02739 
02740         BOOL doCaptureNow = TRUE;
02741 
02742         if (pApp)
02743         {
02744             if (!pApp->IsRenderingComplete ())
02745             {
02746                 // TEST
02747                 // there are still regions that are rendering,
02748                 // we probably should NOT grab the screen yet!
02749                 doCaptureNow = FALSE;
02750                 TRACEUSER( "ChrisS", _T("Brush System:  Not finished rendering - just ignored screen capture request ...."));
02751             }
02752         }
02753 
02754         if (doCaptureNow)
02755         {
02756 #endif
02757             // bodge so that we do not end up with the freehand cursor
02758 PORTNOTE("other", "Removed cursor bodge");
02759 #ifndef EXCLUDE_FROM_XARALX
02760             HCURSOR Cursor = GetCursor();
02761 #endif
02762             m_UpdateBrushState = UPDATE_NOW;
02763             BrushSelected(m_BrushHandle, FALSE);
02764 PORTNOTE("other", "Removed cursor bodge");
02765 #ifndef EXCLUDE_FROM_XARALX
02766             SetCursor(Cursor);
02767 #endif
02768 #ifdef NEWFASTBRUSHES
02769         }
02770 #endif
02771 
02772     }
02773     return FALSE;
02774 }
02775 
02776 
02777 /********************************************************************************************
02778 
02779 >   void FreeHandTool::SetColourEditorProcessing(BOOL Value)
02780 
02781     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02782     Created:    14/11/99
02783     Inputs:     -
02784     Outputs:    -
02785     Returns:    -
02786     Purpose:    Tells the colour editor whether or not to do timer processing, we need to turn
02787                 it off in order to draw a brush stroke.
02788 ********************************************************************************************/
02789 
02790 void FreeHandTool::SetColourEditorProcessing(BOOL Value)
02791 {
02792     ColourEditDlg* pColourEditor = ColourEditDlg::GetColourEditDlg();
02793     if (pColourEditor != NULL)
02794         pColourEditor->SetDoTimerProcessing(Value);
02795 }
02796 
02797 
02798 /********************************************************************************************
02799 
02800 >   void FreeHandTool::InitialiseJoinInfoForBrush(AttrBrushType* pAttrBrush, Path* pPath, 
02801                                                     DocCoord JoinPoint)
02802 
02803     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
02804     Created:    23/5/2000
02805     Inputs:     pAttrBrush - the brush that we are going to join to
02806                 NodepPath      - the path that we are going to join to 
02807                 JoinPoint  - the point where we will join
02808     Returns:    -
02809     Purpose:    Initialises the JoinInfo struct so that we correctly join the existing brush.
02810 
02811 ********************************************************************************************/
02812 
02813 void FreeHandTool::InitialiseJoinInfoForBrush(AttrBrushType* pAttrBrush, NodePath* pPath, DocCoord JoinPoint)
02814 {
02815     if (pAttrBrush == NULL || pPath == NULL)
02816     {
02817         ERROR3("NULL entry pointers in FreeHandTool::InitialiseJoinInfoForBrush");
02818         return;
02819     }
02820 
02821     // initialise joininfo with the data
02822     JoinInfo.m_BrushHandle = pAttrBrush->GetBrushHandle();
02823 
02824     // Now we need to find out the spacing of the first brush object.
02825 
02826     //find out how far along the path we are
02827     MILLIPOINT Distance = 0;
02828     if (!pPath->InkPath.GetDistanceToPoint(JoinPoint, &Distance))
02829     {
02830         ERROR3("Unable to find distance to point in FreeHandTool::InitialiseJoinInfoForBrush");
02831         return;
02832     }
02833 
02834     // didn't bother to write an access fn. for this, ask the path processor to work
02835     // out the spacing.
02836     BrushAttrValue* pVal = (BrushAttrValue*)pAttrBrush->GetAttributeValue();
02837     if (pVal != NULL)
02838     {
02839         PathProcessorBrush* pPPB = pVal->GetPathProcessor();
02840         if (pPPB != NULL)
02841         {
02842             // we want to get the scaling and spacing at this distance along the path
02843             double Scaling = 1.0;
02844             MILLIPOINT Spacing = pPPB->GetSpacing();
02845             pPPB->GetSpacingAndScalingAtDistance(Distance, &Spacing, &Scaling);
02846             JoinInfo.FirstBrushSpacing = (MILLIPOINT)((double)Spacing * Scaling);
02847             JoinInfo.BrushDistance = Distance;
02848 
02849             pPPB->CopyBrushData(&JoinInfo.m_BrushData);
02850 
02851             // get the stroke colour
02852             JoinInfo.UseLocalColour = pPPB->GetUseLocalFillColour();
02853             JoinInfo.UseNamedColour = pPPB->GetUseNamedColours();
02854             if ( JoinInfo.UseLocalColour == TRUE || JoinInfo.UseNamedColour == FALSE)
02855             {
02856                 AttrStrokeColour *pStrokeCol = NULL;
02857                 pPath->FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), (NodeAttribute**)&pStrokeCol);
02858                 if (pStrokeCol)
02859                 {
02860                     JoinInfo.StrokeColour = pStrokeCol->Value.Colour;
02861                 }
02862             }
02863 
02864         }
02865 
02866         JoinInfo.pAttrBrush = pAttrBrush;
02867         
02868     }
02869 
02870 
02871 
02872 }
02873 
02874 
02875 IMPLEMENT_SIMPLE_MODULE( FreeHandModule, MODULEID_FREEHAND, FreeHandTool, 
02876                             "Freehand Tool", "To draw things", "Rik" );
02877 
02878 
02879 
02880 
02881 
02882 
02883 
02884 

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