opdrbrsh.cpp

Go to the documentation of this file.
00001 // $Id: opdrbrsh.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 // Implementation of OpDrawBrush
00100 
00101 #include "camtypes.h"
00102 #include "opdrbrsh.h"
00103 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 #include "pen.h"
00105 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "opfree.h"
00109 //#include "resource.h"
00110 #include "freehand.h"
00111 #include "csrstack.h"
00112 //#include "undoop.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "combshps.h"
00114 //#include "rik.h"
00115 //#include "bubbleid.h"
00116 #include "ndbldpth.h"
00117 //#include "mario.h"
00118 #include "progress.h"
00119 //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 //#include "grndbrsh.h"
00121 #include "objchge.h"
00122 //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 #include "attrmap.h"
00124 #include "nodershp.h"
00125 #include "opbevel.h"
00126 #include "nodebev.h"
00127 //#include "markn.h"
00128 #include "blndtool.h"
00129 #include "brshattr.h"
00130 #include "brshcomp.h"
00131 #include "pathproc.h"
00132 #include "lineattr.h"
00133 #include "freehand.h"
00134 //#include "freehres.h"
00135 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00136 //#include "ccobject.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00137 #include "rsmooth.h"
00138 #include "camelot.h"
00139 #include "nodeshad.h"
00140 #include "nodecntr.h"
00141 #include "lineattr.h"
00142 #include "samplist.h"
00143 #include "linwthop.h"
00144 //#include "scrcamvw.h"
00145 #include "ophist.h"
00146 #include "ppbrush.h"
00147 
00148 #include "qualattr.h"
00149 
00150 // Declare the class
00151 CC_IMPLEMENT_DYNCREATE( OpDrawBrush, SelOperation );
00152 CC_IMPLEMENT_DYNCREATE( OpChangeBrush, SelOperation );
00153 CC_IMPLEMENT_DYNAMIC(ChangeBrushOpParam, OpParam);
00154 CC_IMPLEMENT_DYNCREATE(ChangeBrushAction, Action);
00155 CC_IMPLEMENT_DYNCREATE(RemoveTimeStampPointsAction, Action);
00156 CC_IMPLEMENT_DYNCREATE(AddTimeStampPointsAction, Action);
00157 CC_IMPLEMENT_DYNCREATE(UpdateBrushAction, Action);
00158 CC_IMPLEMENT_DYNCREATE(SetTimeStampUpdateTypeAction, Action);
00159 CC_IMPLEMENT_DYNCREATE(RemovePressurePointsAction, Action);
00160 CC_IMPLEMENT_DYNCREATE(AddPressurePointsAction, Action);
00161 
00162 #define DELPTR(p) if (p != NULL) { delete p; p = NULL; }
00163 #define SWAP(type,a,b) { type x=a; a=b; b=x; }
00164 
00165 // Declare smart memory handling in Debug builds
00166 #define new CAM_DEBUG_NEW
00167 
00168 const UINT32 PressureValsThreshold = 3; //basically the first 3 or so values coming out of the 
00169 
00170                                       // pressure pen are always zero, so we must ignore them
00171 
00172 
00173 
00174 /********************************************************************************************
00175 
00176 >   OpDrawBrush::OpDrawBrush()
00177 
00178     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00179     Created:    6/10/99
00180     Purpose:    Constructor.    
00181 ********************************************************************************************/
00182 
00183 OpDrawBrush::OpDrawBrush()
00184 {
00185     ResetMembers();
00186 }
00187 
00188 /********************************************************************************************
00189 
00190 >   OpDrawBrush::OpDrawBrush()
00191 
00192     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00193     Created:    6/10/99
00194     Purpose:    Constructor. This simply sets a few of the operation flags.
00195 
00196 ********************************************************************************************/
00197 
00198 OpDrawBrush::OpDrawBrush(FreeHandTool *pTool)
00199 {
00200     ResetMembers();
00201     if (pTool != NULL)
00202         m_pTool = pTool;
00203 }
00204 
00205 
00206 /********************************************************************************************
00207 
00208 >   OpDrawBrush::~OpDrawBrush()
00209 
00210     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00211     Created:    6/10/99
00212     Purpose:    destructor.
00213 
00214 ********************************************************************************************/
00215 
00216 OpDrawBrush::~OpDrawBrush()
00217 {
00218 //  if (m_pGRenderBrush != NULL)
00219 //      delete m_pGRenderBrush;
00220     if (m_pProcPathDistance != NULL)
00221         delete m_pProcPathDistance;
00222 
00223 
00224     if (m_pPathProcBrush != NULL)
00225         delete m_pPathProcBrush;
00226 
00227     if (m_pPressureList != NULL)
00228         delete m_pPressureList;
00229 
00230     if (m_pPointsCache != NULL)
00231         delete m_pPointsCache;
00232 
00233     if (m_pPressureSampler != NULL)
00234         delete m_pPressureSampler;
00235 }
00236 
00237 
00238 /********************************************************************************************
00239 
00240 >   void OpDrawBrush::ResetMembers()
00241 
00242     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00243     Created:    6/10/99
00244     Purpose:    Initialises all our member variables to something sensible
00245 
00246 ********************************************************************************************/
00247 
00248 void OpDrawBrush::ResetMembers()
00249 {
00250     // Set all our cursors to NULL to start with
00251     pFreeHandCursor = NULL;
00252     pJoinCursor = NULL;
00253     pStraightCursor = NULL;
00254     pRubOutCursor = NULL;
00255     pModifyCursor = NULL;
00256     
00257     // Set other default values
00258     TrackData = NULL;
00259     StartSpread = NULL;
00260     PreviousSpread = NULL;
00261     Smoothness = 512;
00262     LineSegmentCount = 0;
00263     CanLineJoin = FALSE;
00264     IsStraightLineMode = FALSE;
00265     AddPressureToPath = FALSE;
00266     FreeHandPressure = 0;
00267 
00268     // The paths that we are joined to, or NULL if we are joined to none
00269     pJoinInfo = NULL;
00270     StartPath = NULL;
00271     EndPath = NULL;
00272     CloseTo = 0;
00273     Mu = 0.0;
00274     IsEndNearEndpoint = FALSE;
00275     CurrentCursorID = 0;
00276 
00277     m_pGRenderBrush = NULL;
00278     m_pPathProcBrush = NULL;
00279 //#ifdef NEWFASTBRUSHES
00280     m_pBrushDefinition = NULL;
00281     mustClearStatusBar = TRUE;
00282     pApp = GetApplication ();
00283     ERROR3IF (!pApp, "Could not find the application!");
00284 //#endif
00285     m_pProcPathDistance = NULL;
00286     m_BrushScaling = 1.0;
00287 
00288 #ifdef OPBRUSHPOINTSCACHE
00289     m_pPointsCache = NULL;
00290 #endif
00291     
00292     m_pTimeStampList = NULL;
00293     m_TimeStamp      = 0;
00294     m_LastTimeStamp  = -1;
00295 
00296     m_pPressureList   = NULL;
00297 
00298     m_LastPathIndexRendered = 1;
00299 
00300     m_FirstSpacing = 25;
00301 
00302     m_pPressureSampler = NULL;
00303     m_NumPressureVals = 0;
00304 
00305 
00306 }
00307 
00308 /********************************************************************************************
00309 
00310 >   void OpDrawBrush::DoDrag( DocCoord Anchor, Spread *pSpread, INT32 Smooth, BrushHandle Handle, 
00311                             NodePath* pPath, Path* ToolPath, GRenderBitmap* pGRndBMP )
00312 
00313     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00314     Created:    6/10/99
00315     Inputs:     Anchor  - The position of the mouse at the start of the Drag
00316                 pSpread - The spread that the drag started on
00317                 Smooth  - Closeness of fit value
00318                 Handle - the handle of the brush we want to use
00319                 pFreeHandInfo - Pointer to the info about the Joining set up by the
00320                 freehand tool
00321                 ToolPath - The path to store the mouse tracking info in
00322                 pGRndBMP - pointer to the big bitmap used to store the view
00323                 
00324     Purpose:    Starts up the drag operation by storing all start positions and setting
00325                 up a Path to store the mouse movement in
00326 
00327 ********************************************************************************************/
00328 
00329 void OpDrawBrush::DoDrag(DocCoord Anchor, Spread *pSpread, INT32 Smooth, BrushHandle Handle,
00330                         FreeHandJoinInfo* pFreeHandInfo, Path* ToolPath,
00331                         GRenderBrush* pGRndRgn, ClickModifiers ClickMods)
00332 {
00333     TRACEUSER( "Diccon", _T("Drag started\n"));
00334     if (pSpread == NULL)
00335     {
00336         ERROR3("No spread");
00337         FailAndExecute();
00338         End();
00339         return;
00340     }
00341     if (ToolPath == NULL)
00342     {
00343         ERROR3("Tool path is NULL");
00344         FailAndExecute();
00345         End();
00346         return;
00347     }
00348 // WEBSTER - markn 25/4/97
00349 // No pen stuff required in Webster
00350 // Taken out by vector stroking code Neville 2/10/97
00351 #ifdef VECTOR_STROKING
00352     // Tell the pressure pen that we're starting a new stroke
00353     CCPen *pPen = pApp->GetPressurePen();
00354     if (pPen != NULL)
00355         pPen->StartStroke();
00356 #endif // VECTOR_STROKING
00357 
00358     // Snap the starting coord if we are not joining to something else
00359     if ((pFreeHandInfo==NULL) || (pFreeHandInfo->pJoinPath==NULL))
00360         DocView::SnapCurrent(pSpread, &Anchor, FALSE, TRUE);
00361 
00362     // Make a note of various starting conditions
00363     TrackData   = ToolPath;
00364     Smoothness  = Smooth;
00365     pJoinInfo   = pFreeHandInfo;
00366     StartPath   = pJoinInfo->pJoinPath;
00367     EndPath     = NULL;
00368 
00369     // Make a mental note of the start point
00370     StartPoint    = Anchor;
00371     StartSpread   = pSpread;
00372     PreviousSpread= pSpread;
00373     PreviousPoint = Anchor;
00374     LineSegmentCount = 0;
00375     IsStraightLineMode = FALSE;
00376     CanLineJoin = FALSE;
00377 
00378     // Prepare the Track data path and add in the initial click point to the path
00379     if (!PrepareTrackDataPath())
00380     {
00381         // We failed to get the memory to initialise the track data
00382         InformError(_R(IDS_OUT_OF_MEMORY), _R(IDS_OK));
00383         FailAndExecute();
00384         End();
00385         return;
00386     }
00387     
00388     // Create some cursors that I might need
00389     if (!LoadCursors())
00390     {
00391         // The cursors did not load, so fail?
00392         FailAndExecute();
00393         End();
00394         return;
00395     }
00396 
00397 
00398     // Push my new cursor onto the stack
00399     CurrentCursorID = CursorStack::GPush(pFreeHandCursor, TRUE);
00400     MyCurrentCursor = pFreeHandCursor;
00401 
00402     // Diccon new brush stuff
00403     TRACEUSER( "Diccon", _T("DRAG STARTED\n"));
00404     if (pGRndRgn == NULL)
00405     {
00406         ERROR3("No GRenderBrush");
00407         FailAndExecute();
00408         End();
00409         return;
00410     }
00411     else
00412         m_pGRenderBrush = pGRndRgn;
00413 
00414     // if we are joining a brush then make sure we know that handle
00415     if (pFreeHandInfo->m_BrushHandle != BrushHandle_NoBrush)
00416         Handle = pFreeHandInfo->m_BrushHandle;
00417 
00418     if (!InitialisePathProcessorBrush(Handle, pFreeHandInfo->BrushDistance))
00419     {
00420         FailAndExecute();
00421         End();
00422         return;
00423     }   
00424 
00425     // if out freehand info indicates that we are joining to an existing brush then 
00426     // copy out the data from that brush
00427     if (pFreeHandInfo->m_BrushHandle != BrushHandle_NoBrush && pFreeHandInfo->pAttrBrush != NULL)
00428     {   
00429 //      MILLIPOINT Spacing = 0;
00430         m_pPathProcBrush->GetSpacingAndScalingAtDistance(pFreeHandInfo->BrushDistance, &m_FirstSpacing, &m_BrushScaling);
00431     
00432         m_pPathProcBrush->CopyDataFromObject(&(pFreeHandInfo->m_BrushData));
00433         m_LastSpacing = m_pPathProcBrush->GetSpacing();
00434     
00435         // set the scaling, this can vary as we may be adding to a line
00436         // of any width
00437         m_BrushScaling = pFreeHandInfo->m_BrushData.m_BrushScaling; 
00438         
00439         m_pPathProcBrush->AdvanceBrushToDistance(pFreeHandInfo->BrushDistance);
00440 
00441         m_LastInkObjectRendered = m_pPathProcBrush->GetLastObject();
00442 
00443         // if we're overriding colours then tell the ppb what colour to use
00444         m_pPathProcBrush->SetUseLocalFillColour(pFreeHandInfo->UseLocalColour);
00445         m_pPathProcBrush->SetUseNamedColours(pFreeHandInfo->UseNamedColour);
00446         if (pFreeHandInfo->UseLocalColour || !pFreeHandInfo->UseNamedColour)
00447             m_pPathProcBrush->SetStrokeColour(pFreeHandInfo->StrokeColour);
00448 
00449     //  m_FirstSpacing = m_pPathProcBrush->GetLastSpacing() - pFreeHandInfo->FirstBrushSpacing;
00450 
00451     //  TRACEUSER( "Diccon", _T("JOINING EXISTING BRUSH\n"));
00452     }
00453 
00454     if (!InitialiseProcessPathDistance())
00455     {
00456         FailAndExecute();
00457         End();
00458         return;
00459     }
00460 
00461     if (!InitialisePressureSampler())
00462     {
00463         FailAndExecute();
00464         End();
00465         return;
00466     }
00467     if (AddPressureToPath == TRUE)
00468     {
00469     //  TRACEUSER( "Diccon", _T("Pressure = %d\n"), ClickMods.Pressure);
00470     //  m_pPressureSampler->CollectData(Anchor, ClickMods.Pressure);  //?? not sure this is right
00471     }
00472 
00473     if (m_pPathProcBrush != NULL)
00474     {
00475         m_NumInkObjects = m_pPathProcBrush->GetNumBrushObjects();
00476         m_BrushSpacing = m_pPathProcBrush->GetSpacing();
00477         m_pPathProcBrush->SetParentAttribute(pFreeHandInfo->pAttrBrush);
00478     }
00479     else
00480     {
00481         FailAndExecute();
00482         End();
00483         return;
00484     }
00485     if (m_NumInkObjects < 1)
00486     {
00487         ERROR3("No brush ink objects");
00488         FailAndExecute();
00489         End();
00490         return;
00491     }
00492 
00493     // if we are not timestamping then we want a regular point-at-distance map to
00494     // use as a cache, so long as we are not joining an existing brush
00495 #ifdef OPBRUSHPOINTSCACHE   
00496     if (m_TimeStamp <= 0 && pFreeHandInfo->m_BrushHandle == BrushHandle_NoBrush)
00497     {
00498         // allocate the map for caching
00499         m_pPointsCache = new PointsMap;
00500 
00501         //m_pPointsList = new List;
00502         // not a disaster if we don't get it
00503 //      if (m_pPointsCache != NULL)
00504 //          m_pPointsCache->InitHashTable(1277);    
00505 //      else
00506 //          ERROR3("Failed to allocate cache");
00507     }
00508 #endif
00509     // if we are timestamping then we want a list
00510     if (m_TimeStamp > 0)
00511     {
00512         if (!InitialiseTimeStampList())
00513         {
00514             // can't do anything if we don't get our cache
00515             FailAndExecute();
00516             End();
00517             return;
00518         }
00519     }
00520 
00521     // initialise needs to have correct start point
00522     m_StartPoint = Anchor;
00523     
00524     // set up the member variables
00525     m_LastPoint = Anchor;
00526     m_LastPointRendered = Anchor;
00527     m_LastDistanceRendered = 0;
00528     m_DistanceSinceLastObject = 0;
00529     m_LastSpacing = m_BrushSpacing;
00530     m_NumBrushObjects = 0;
00531 
00532     AddPointsToPath(Anchor, pSpread);
00533     m_bFirstPointRendered = FALSE;
00534 
00535     StartDrag( DRAGTYPE_NOSCROLL );
00536 
00537 //  TRACEUSER( "Diccon", _T("Drag initialised"));
00538 
00539 PORTNOTE("other", "Removed m_pRenderGBrush");
00540 #ifndef EXCLUDE_FROM_XARALX
00541 //#ifdef NEWFASTBRUSHES
00542     View*   pView = View::GetCurrent();
00543     CCamView* pCCamView = pView->GetConnectionToOilView();
00544     CDC* pDevContext = pCCamView->GetRenderDC();
00545     HDC DeviceHdc = pDevContext->GetSafeHdc();
00546 
00547     m_pGRenderBrush->SetView (pView);
00548     m_pGRenderBrush->SetCamView (pView->GetConnectionToOilView());
00549     m_pGRenderBrush->SetCDC (pCCamView->GetRenderDC());
00550     m_pGRenderBrush->SetHDC (pDevContext->GetSafeHdc());
00551 //#endif
00552 #endif
00553 }
00554 
00555 
00556 
00557 /********************************************************************************************
00558 
00559 >   void OpDrawBrush::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, 
00560                                   Spread *pSpread, BOOL bSolidDrag)
00561 
00562     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00563     Created:    7/10/99
00564     Inputs:     PointerPos - The current position of the mouse in Doc Coords
00565                 ClickMods - Which key modifiers are being pressed
00566                 pSpread - The spread that the mouse is moving over
00567     Purpose:    Handles the event of the mouse moving during a drag
00568     SeeAlso:    ClickModifiers
00569 
00570 ********************************************************************************************/
00571 
00572 void OpDrawBrush::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, 
00573                                   Spread *pSpread, BOOL bSolidDrag)
00574 {
00575     // Lets whip out the current pressure value
00576     if (AddPressureToPath)
00577         FreeHandPressure = ClickMods.Pressure;
00578 
00579     // If drag has moved onto a different spread, convert the coord to be relative to the
00580     // original spread.
00581     if (pSpread != StartSpread)
00582 #ifdef NEWFASTBRUSHES
00583         PointerPos = MakeRelativeToSpreadNoOverhead(StartSpread, pSpread, PointerPos);  // min variable allocation overhead version
00584 #else
00585         PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);    // min variable allocation overhead version
00586 #endif
00587 
00588     // I'm not allowing rubbing out or straightlines whilst drawing a brush
00589     ClickMods.Alternative1 = FALSE;
00590     ClickMods.Adjust = FALSE;
00591 
00592     // Change the Cursor to display the appropriate thing.
00593     SetCursorOnMove(ClickMods, StartSpread, &PointerPos);
00594 
00595     AddPointsToPath(PointerPos, StartSpread);
00596     // Set the last spread to something appropriate
00597     PreviousSpread = pSpread;
00598 
00599     //  brush specific code
00600     double Distance = (PointerPos.Distance(m_LastPoint));
00601     m_DistanceSinceLastObject += (MILLIPOINT)Distance;
00602         // cache the point for this brush object
00603     m_LastPoint = PointerPos;
00604     // pass the data to our pressure sampler
00605     if (m_pPressureSampler != NULL)
00606     {
00607         m_NumPressureVals++;
00608         if (m_NumPressureVals > PressureValsThreshold)
00609         {
00610             TRACEUSER( "Diccon", _T("Pressure = %d\n"), ClickMods.Pressure);
00611             
00612             if (!m_pPressureSampler->CollectData(PointerPos, FreeHandPressure))
00613             {
00614                 ERROR3("Error in Pressure sampler, aborting..");
00615                 FailAndExecute();
00616                 End();
00617                 return;
00618             }
00619         }
00620         else
00621             return;
00622         
00623     }
00624         // --- If the quality is set low enough, strokes are just rendered as centrelines
00625         // "low enough" is defined as the same point that Blends show their middle parts
00626         // BLOCK
00627         {
00628 PORTNOTE("other", "Removed m_pRenderGBrush");
00629 #ifndef EXCLUDE_FROM_XARALX
00630             if(m_pGRenderBrush != NULL)
00631             {
00632                 QualityAttribute *pQuality = (QualityAttribute *) m_pGRenderBrush->GetCurrentAttribute(ATTR_QUALITY);
00633                 if (pQuality != NULL && pQuality->QualityValue.GetBlendQuality() != Quality::FullBlend)
00634                 {
00635                     RenderRegion* pRegion = DocView::RenderOnTop(NULL, pSpread, UnclippedEOR );
00636                     while ( pRegion )
00637                     {
00638                         RenderLine(pRegion, TrackData, TrackData->GetNumCoords()-1, FALSE);
00639                         // get the next region to draw in
00640                         pRegion = DocView::GetNextOnTop(NULL);
00641                     }   
00642                     return;
00643                 }
00644             }
00645 #endif
00646         }
00647 
00648 //  if (pJoinInfo->BrushHandle != BrushHandle_NoBrush)
00649 //      RenderLine(m_pGRenderBrush, TrackData, TrackData->GetNumCoords()-1);
00650     // Render brush objects for the specified period
00651     RenderStepsForTime(50, pSpread);
00652 
00653     // if we are editing then render the path too
00654 /*  if (pJoinInfo->BrushHandle != BrushHandle_NoBrush)
00655     {
00656         RenderLine(NULL, pSpread, TrackData, TrackData->GetNumCoords() - 1);
00657         RenderLine(m_pGRenderBrush, TrackData, TrackData->GetNumCoords()-1);
00658     }*/
00659 }   
00660 
00661 
00662 
00663 /********************************************************************************************
00664 
00665 >   void OpDrawBrush::DragPointerIdle( DocCoord PointerPos, ClickModifiers ClickMods, 
00666                                   Spread *pSpread, BOOL bSolidDrag)
00667 
00668     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00669     Created:    7/10/99
00670     Inputs:     PointerPos - The current position of the mouse in Doc Coords
00671                 ClickMods - Which key modifiers are being pressed
00672                 pSpread - The spread that the mouse is moving over
00673     Purpose:    Handles the mouse idle events
00674     SeeAlso:    ClickModifiers
00675 
00676 ********************************************************************************************/
00677 
00678 void OpDrawBrush::DragPointerIdle(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag)
00679 {
00680     BOOL bAllowRender = TRUE;
00681     
00682     // --- If the quality is set low enough, strokes are just rendered as centrelines
00683     // "low enough" is defined as the same point that Blends show their middle parts
00684     // BLOCK
00685     {
00686 PORTNOTE("other", "Removed m_pRenderGBrush");
00687 #ifndef EXCLUDE_FROM_XARALX
00688         if(m_pGRenderBrush != NULL)
00689         {
00690             QualityAttribute *pQuality = (QualityAttribute *) m_pGRenderBrush->GetCurrentAttribute(ATTR_QUALITY);
00691             if (pQuality != NULL && pQuality->QualityValue.GetBlendQuality() != Quality::FullBlend)
00692             {
00693                 bAllowRender = FALSE;
00694                 // return;
00695             }
00696         }
00697 #endif
00698     }
00699 
00700     // because we are throwing away the first few pressure values we want to make sure we have 
00701     // got enough before we render here 
00702     if (AddPressureToPath)
00703     {
00704         if (m_NumPressureVals > PressureValsThreshold && bAllowRender)
00705             RenderStepsForTime(75, pSpread); // 75 is the time to render for in ms, as determined by trial and error
00706     }
00707     else
00708     {
00709         if(bAllowRender)
00710             RenderStepsForTime(75, pSpread);
00711     }
00712     
00713     //TRACEUSER( "Diccon", _T("Drag pointer idle\n"));  
00714 }
00715 
00716 /********************************************************************************************
00717 
00718 >   void OpDrawBrush::RenderStepsForTime(double TimeSlice, Spread* pSpread)
00719 
00720     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00721     Created:    20/10/99
00722     Inputs:     TimeSlice - the length of time to render for
00723                 pSpread - the spread to render on to
00724     Returns:    -
00725     Purpose:    Renders as many steps in the brush as possible in the time available.
00726                 The reason for having the code here rather than in the processor is twofold:
00727                 1) I feel the property of rendering for a certain timeslice is a property of
00728                 the Op rather than the PPB;
00729                 2) In order to make use of the caching ProcessPathDistance, which belongs
00730                 to the Op rather than the PPB.
00731 ********************************************************************************************/
00732 
00733 /* Notes: In case its not clear from the code what is happening here we go: the member variable
00734 m_DistanceSinceLastObject records how far the mouse has travelled since we last rendered an object.
00735 So long as this distance is greater than our spacing value then we wish to keep rendering points, and
00736 reducing m_DistanceSinceLastObject until it becomes less than our spacing value.
00737 */
00738 
00739 void OpDrawBrush::RenderStepsForTime(double TimeSlice, Spread* pSpread)
00740 {
00741     if ( (m_pProcPathDistance == NULL) || (m_pPathProcBrush == NULL)
00742         || (m_pGRenderBrush == NULL) || (TrackData == NULL))
00743     {
00744         ERROR3("One or more members are NULL");
00745         return;
00746     }
00747 
00748     MonotonicTime timer;
00749     
00750     // declare locals outside the loop
00751     DocCoord Point;
00752     double Angle;
00753     UINT32 Pressure = 0;
00754     CSampleItem TheItem;
00755     BrushPointInfo PathPoint;
00756 
00757     // records how far along the path we are
00758     MILLIPOINT DistanceToGetPoint = m_LastDistanceRendered;
00759 
00760     // spacing records the spacing between objects (duh)
00761     // this little hack is here to make sure our first object is a few millipoints in from the 
00762     // start of the path, otherwise we will always have a tangetial angle of zero
00763 
00764     MILLIPOINT Spacing = m_LastSpacing;
00765     if (!m_bFirstPointRendered)
00766     {
00767         Spacing = m_FirstSpacing;
00768         if (m_pPressureSampler != NULL)
00769         {
00770             if (m_pPressureSampler->GetAt(0, &TheItem))
00771                 Pressure = TheItem.m_Pressure;
00772             else
00773                 Pressure =127;
00774             m_pPathProcBrush->SetCurrentPressure(Pressure);
00775         }
00776     }
00777 
00778 
00779     BOOL Found;
00780     //m_BrushScaling = m_pPathProcBrush->GetLastScaling();
00781 #ifdef SCALESPACING
00782     double ActualSpacing = (double)Spacing * m_BrushScaling;
00783 #else
00784     double ActualSpacing = (double)Spacing * m_pPathProcBrush->GetBrushScaling();
00785 #endif
00786 //  if (TrackData->GetNumCoords() > 2)
00787 //      RenderLine(m_pGRenderBrush, TrackData, TrackData->GetNumCoords() - 1);
00788     UINT32 Dummy = 0;
00789     // render objects at m_BrushSpacing intervals starting at the last point rendered
00790     while (m_DistanceSinceLastObject >= (MILLIPOINT)ActualSpacing)
00791     {
00792         // the path processor needs to set up some variables to deal with offsets
00793         // before we come to actually render
00794         m_pPathProcBrush->SetNextOffsetType();
00795 
00796         //TRACEUSER( "Diccon", _T("Scaled Spacing = %f\n"), double(Spacing * m_BrushScaling));
00797         DistanceToGetPoint += (MILLIPOINT)ActualSpacing;
00798         
00799         // get the point from the nodebrushpath
00800         m_pProcPathDistance->GetCoordAndTangentWithCache(&Point, &Angle, &Found,
00801                                                         DistanceToGetPoint, TrackData, &Dummy);
00802         if (Found)
00803         {
00804             
00805             //TRACEUSER( "Diccon", _T("Point found at: %d, %d\n"), Point.x, Point.y);
00806             PathPoint.m_Point = Point;
00807             PathPoint.m_Tangent = Angle;
00808             PathPoint.m_Pressure = Dummy;
00809 
00810             // pass to the pathprocessor which does the work of rendering, but not if our object is zero-sized
00811 PORTNOTE("other", "Removed m_pRenderGBrush");
00812 #ifndef EXCLUDE_FROM_XARALX
00813             if (m_BrushScaling > 0)
00814             {
00815                 m_pPathProcBrush->RenderBrushAtPoint(Point, Angle, m_LastInkObjectRendered, m_NumBrushObjects,
00816                                                     m_pGRenderBrush, NULL, TRUE, m_pBrushDefinition);
00817             }
00818 #endif          
00819             // get the next pressure value
00820             if (m_pPressureSampler != NULL && m_bFirstPointRendered)
00821             {
00822                 m_pPressureSampler->SetSampleRateFromSpacing((MILLIPOINT)ActualSpacing);
00823                 
00824                 if (m_pPressureSampler->GetNext(&TheItem))
00825                     Pressure = TheItem.m_Pressure;
00826                 else
00827                     Pressure = 127;
00828                 m_pPathProcBrush->SetCurrentPressure(Pressure);
00829                 //TRACEUSER( "Diccon", _T("SETTING PRESSURE %d = %d\n"), m_NumBrushObjects, Pressure);
00830             }
00831 
00832 PORTNOTE("other", "Removed m_pRenderGBrush");
00833 #ifndef EXCLUDE_FROM_XARALX
00834             // get it to the screen ASAP
00835             m_pGRenderBrush->DrawToScreenNow();
00836 #endif
00837 
00838             m_DistanceSinceLastObject -= (MILLIPOINT)(ActualSpacing); //m_BrushSpacing;
00839             m_LastDistanceRendered = DistanceToGetPoint;
00840 
00841             // get the next spacing (spacing only changes once we have drawn an object)
00842             Spacing = m_pPathProcBrush->GetNextSpacing();
00843             m_BrushScaling = m_pPathProcBrush->GetLastScaling();
00844 #ifdef SCALESPACING
00845             ActualSpacing = Spacing * m_BrushScaling;
00846 #else
00847             ActualSpacing = (double)Spacing * m_pPathProcBrush->GetBrushScaling(); //m_BrushSpacing; //Spacing * m_BrushScaling;
00848 #endif
00849             // switch our alternating offset 
00850             m_pPathProcBrush->SwitchAlternateValue();
00851 
00852 #ifdef OPBRUSHPOINTSCACHE
00853             // stash this point in the cache
00854             if (m_pPointsCache != NULL)
00855                 (*m_pPointsCache)[DistanceToGetPoint]=PathPoint;
00856 #endif
00857          
00858             // get the next object in the sequence
00859             m_LastInkObjectRendered = m_pPathProcBrush->GetNextInkObject(m_LastInkObjectRendered,
00860                                                                      m_NumInkObjects);
00861 
00862             m_pPathProcBrush->DecideWhetherToUseActualScaling(m_LastInkObjectRendered);
00863             // increment our counter
00864             m_NumBrushObjects++;
00865             m_bFirstPointRendered = TRUE;
00866         //  TRACEUSER( "Diccon", _T("Pressure at end of loop = %d\n"), Pressure);
00867         }
00868 
00869         // if we hit the time constraint then just get out now
00870         if (timer.Elapsed(UINT32(TimeSlice * 1000.0)))
00871             break;
00872     }
00873 //  TRACEUSER( "Diccon", _T("Exit RenderStepsForTime\n"));
00874     // remember the spacing for next time
00875     m_LastSpacing = Spacing;
00876 }
00877 
00878 
00879 /********************************************************************************************
00880 
00881 >   void OpDrawBrush::RenderTimeStamp()
00882 
00883     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00884     Created:    20/10/99
00885     Inputs:     -
00886     Returns:    -
00887     Purpose:    To be used in timestamp mode, obviously.  This fn. determines whether or not 
00888                 enough time has elapsed for us to render a point.  If so we render a point
00889                 and cache it, updating the necessary members.
00890 ********************************************************************************************/
00891 
00892 void OpDrawBrush::RenderTimeStamp()
00893 {
00894     if (m_TimeStamp <=0)
00895     {
00896         ERROR3("Invalid timestamp value");
00897         return;
00898     }
00899 
00900     if (m_Timer.Elapsed((UINT32)(m_TimeStamp * 1000.0)))
00901     {   
00902         m_Timer.Sample();
00903         
00904         // find out how far along the path we are
00905         MILLIPOINT DistanceAlongPath = m_LastDistanceRendered + m_DistanceSinceLastObject;
00906         
00907         DocCoord Point;
00908         double Angle = 0;
00909         BOOL Found = FALSE;
00910         // get the point from the path
00911         m_pProcPathDistance->GetCoordAndTangentWithCache(&Point, &Angle, &Found,
00912                                                         DistanceAlongPath, TrackData);
00913     
00914         if (Found)
00915         {   
00916             TimeStampBrushPoint TSP(Point, Angle, DistanceAlongPath);
00917             
00918                 // get the next object in the sequence
00919         m_LastInkObjectRendered = m_pPathProcBrush->GetNextInkObject(m_LastInkObjectRendered,
00920                                                                      m_NumInkObjects);
00921 
00922 PORTNOTE("other", "Removed m_pRenderGBrush");
00923 #ifndef EXCLUDE_FROM_XARALX
00924             // pass to the pathprocessor which does the work of rendering
00925             if (m_pGRenderBrush != NULL)
00926             {
00927                 m_pPathProcBrush->RenderBrushAtPoint(Point, Angle, m_LastInkObjectRendered++, 1,
00928                                                 m_pGRenderBrush, NULL, TRUE);
00929                 // get it to the screen ASAP
00930                 m_pGRenderBrush->DrawToScreenNow();
00931                 TRACEUSER( "Diccon", _T("OpDrawBrush Rendered TimeStamp"));
00932             }
00933 #endif
00934             // update our member variables
00935             m_DistanceSinceLastObject = 0;
00936             m_LastDistanceRendered = DistanceAlongPath;
00937 
00938             // cache the point
00939             if (m_pTimeStampList != NULL)
00940                 m_pTimeStampList->push_back(TSP);
00941         }
00942     }
00943 }
00944 
00945 
00946 
00947 /********************************************************************************************
00948 
00949 >   void OpDrawBrush::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
00950                                Spread *pSpread, BOOL Success, BOOL bSolidDrag)
00951 
00952     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
00953     Created:    7/10/99
00954     Inputs:     PointerPos - The position of the mouse at the end of the drag
00955                 ClickMods - the key modifiers being pressed
00956                 pSpread - The spread that the drag finished on
00957                 Success - TRUE if the drag was terminated properly, FALSE if it
00958                 was ended with the escape key being pressed
00959     Purpose:    Handles the drag finishing by rubbing out an EOR on the screen and
00960                 adding the path to the tree and building all the undo that will be
00961                 needed
00962     SeeAlso:    ClickModifiers
00963 
00964 ********************************************************************************************/
00965 
00966 void OpDrawBrush::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods,
00967                                Spread *pSpread, BOOL Success, BOOL bSolidDrag)
00968 {   
00969     TRACEUSER( "Diccon", _T("Drag finished\n"));
00970     // Erase the whole EORed line
00971     DocRect ClipRect(0,0,0,0);
00972     RenderDragBlobs(ClipRect, StartSpread, bSolidDrag);
00973     
00974     // Get rid of all the cursors
00975     RemoveCursors();
00976 
00977     // Put the hour glass up as we have to and end the drag
00978     //BeginSlowJob();
00979     String_32 ProgString = _T("Calculating brush, please wait..");
00980     Progress Hourglass(&ProgString, -1, FALSE);
00981 
00982     EndDrag();
00983     
00984     
00985     // Add the new path to the tree if it was valid
00986     BOOL Worked = FALSE;
00987     if (Success)
00988     {
00989         // were we in the middle of drawing a straight line
00990         if (IsStraightLineMode)
00991         {
00992             // we were so add it in to the track data
00993             AddStraightLine();
00994             PreviousPoint = StraightLinePos;
00995             IsStraightLineMode = FALSE;
00996         }
00997         BeginSlowJob(-1, TRUE, NULL);
00998         
00999         // invalidate the rect that we calculated (this may be different to the bounding 
01000         // rect of the node due to smoothing)
01001         DocRect BRect = m_pPathProcBrush->GetCachedRect();
01002 // Get rid of cached bitmaps here!
01003 Node* pANode = NULL;
01004 if (m_pPathProcBrush->GetParentAttribute())
01005 {
01006     pANode = m_pPathProcBrush->GetParentAttribute()->FindParent();
01007     if (pANode->IsBounded())
01008     {
01009         ((NodeRenderableBounded*)pANode)->ReleaseCached();
01010     }
01011 }
01012         DoInvalidateRegion(pSpread, BRect);
01013         
01014         // clean up some memory we used
01015         m_pPathProcBrush->CleanUpAfterRender();
01016 
01017         BrushDefinition* pDef = BrushComponent::FindBrushDefinition(m_pPathProcBrush->GetBrushDefinitionHandle());
01018         if (pDef)
01019             pDef->StopRender();
01020         else
01021             ERROR3("Unable to get brush definition in OpDrawBrush::DragFinished");
01022 
01023         // try and smooth the path and insert it into the tree
01024         Worked = CompleteOperation();
01025         EndSlowJob();   
01026     }
01027 // WEBSTER - markn 25/4/97
01028 // No pen stuff required in Webster
01029 // Taken out by vector stroking code Neville 2/10/97
01030 #ifdef VECTOR_STROKING
01031     // Inform the pressure pen that we've finished the stroke
01032     CCPen *pPen = pApp->GetPressurePen();
01033     if (pPen != NULL)
01034         pPen->EndStroke();
01035 #endif // VECTOR_STROKING
01036     
01037 
01038     if (m_pTool != NULL)
01039         m_pTool->BrushFinished();
01040 
01041     // point the pointer away from the render region (the tool still needs it so we don't delete)
01042     m_pGRenderBrush = NULL;
01043 
01044     // If the operation failed, then tidy up
01045     if (Worked==FALSE)
01046         FailAndExecute();
01047     else
01048     {
01049         // Update all the parents of the effected paths.
01050         ObjChangeFlags cFlags;
01051         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
01052         ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
01053         UpdateChangedNodes(&ObjChange);
01054     }
01055     // End the operation properly
01056     End();
01057 }
01058 
01059 
01060 /********************************************************************************************
01061 
01062 >   BOOL OpDrawBrush::ApplyAttributes(NodePath* NewPath, Document *pDocument)
01063 
01064     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01065     Created:    31/1/2000
01066     Inputs:     NewPath - The NodePath to apply the current attributes to
01067                 pDocument - the document this path will be placed into.
01068     Returns:    TRUE if it worked, FALSE if it did not
01069     Purpose:    Applies the current attributes to the Node passed in. It has to find the
01070                 current document to do this and will ENSURE if it fails to find one.
01071                 This overridden version also finds the newly applied brush attribute and 
01072                 gives it the cache of points that we have been recording
01073 
01074                 This fn. has changed slightly as we now have to comply with the "most recently
01075                 applied attributes" option.  In order to do this both the line width and the
01076                 brush attribute have to be applied via the attribute manager.
01077 
01078                 Andy Hills, 18-09-00
01079                 Note - I have temporarily reverted this function to the 24-08-00 version.
01080                 This fixes the problem where, if the above-mentioned option is disabled,
01081                 an access violation occurs.
01082 ********************************************************************************************/
01083 
01084 BOOL OpDrawBrush::ApplyAttributes(NodePath* pNewPath, Document *pDocument)
01085 {
01086     ERROR2IF(pNewPath == NULL, FALSE, "pNewNodePath is NULL in OpDrawBrush::ApplyAttributes");
01087     ERROR2IF(pDocument == NULL, FALSE, "pDocument is NULL in OpDrawBrush::ApplyAttributes");
01088     
01089     //ApplyRetroSmoother(pNewPath, 50);
01090     
01091     // Find the current document to get the current attributes from
01092     ENSURE(pDocument!=NULL, "Null Document while setting attributes for new NodePath");
01093     if (pDocument!=NULL)
01094     {   
01095         // we make out own attributes here
01096         AttrBrushType* pNewAttr = CreateNewAttribute();
01097         if (pNewAttr == NULL)
01098             return FALSE;
01099 #ifdef NEWFASTBRUSHES
01100         BrushAttrValue* pAttrVal = (BrushAttrValue*) pNewAttr->GetAttributeValue();
01101         pAttrVal->SetBoundsParent (pNewPath);
01102 #endif
01103         // now make the line the correct width, if someone has selected a line width then we will make it
01104         // that size, otherwise we will use the default provided by the brush
01105         MILLIPOINT LineWidth = GetCurrentLineWidthIfNotDefault();
01106         if (LineWidth == -1)
01107             LineWidth = pNewAttr->GetDefaultLineWidth(!AddPressureToPath);
01108     
01109     
01110         // Apply the current attributes to the path, changing the line width if necessary
01111         if (!pDocument->GetAttributeMgr().ApplyCurrentAttribsToNode((NodeRenderableInk*)pNewPath))
01112             ERROR3("Failed to apply current attributes in OpDrawBrush::ApplyAttributes");
01113         
01114         // change the line width value
01115         AttrLineWidth* pLineWidth = (AttrLineWidth*)pNewPath->FindFirstChild(CC_RUNTIME_CLASS(AttrLineWidth));
01116         if (pLineWidth != NULL)
01117             pLineWidth->Value.LineWidth = LineWidth;
01118         else
01119             ERROR3("Unable to find line width in OpDrawBrush::ApplyAttributes");
01120         
01121         // if the document inserted a brush then get rid of it
01122         Node* pDocBrush = pNewPath->FindFirstChild(CC_RUNTIME_CLASS(AttrBrushType));
01123         if (pDocBrush)
01124         {
01125             pDocBrush->CascadeDelete();
01126             delete pDocBrush;
01127             pDocBrush = NULL;
01128         }
01129 
01130 
01131         // apply our brush attribute here, in case the document already applied a default
01132         pNewPath->ApplyAttributeToObject(pNewAttr, FALSE);
01133         
01134         // tell it about the freehand tool
01135         pNewAttr->SetFreeHandTool(m_pTool);
01136 
01137         if (pNewAttr->GetBrushHandle() != BrushHandle_NoBrush)      
01138         {
01139                     
01140             #ifdef OPBRUSHPOINTSCACHE
01141             if (m_TimeStamp <= 0)
01142                 pNewAttr->SetCache(m_pPointsCache);
01143             
01144             // make sure we don't try to use the cache, the attribute is now responsible for
01145             // deleting it
01146             m_pPointsCache = NULL;
01147             #endif
01148             BrushAttrValue* pVal = (BrushAttrValue*)pNewAttr->GetAttributeValue();
01149             // if we're timestamping then set the timestamp cache instead
01150             if (m_TimeStamp > 0)
01151             {   
01152                 if (pVal !=