opgrad.cpp

Go to the documentation of this file.
00001 // $Id: opgrad.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 // The Grad Fill creation and Editing operations
00099 
00100 /********************************************************************************************
00101 
00102     NOTE: 
00103 
00104     OpCreateFill is no longer used.
00105     OpEditFill, is now used for fill Creation and Editing.
00106 
00107 ********************************************************************************************/
00108 
00109 /*
00110 */
00111 
00112 
00113 #include "camtypes.h"
00114 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 #include "opgrad.h"
00116 #include "filltool.h"
00117 //#include "will.h"
00118 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00119 //#include "attr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 #include "lineattr.h"
00121 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00124 //#include "stockcol.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00125 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00126 #include "blobs.h"
00127 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00128 #include "toollist.h"
00129 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 #include "bitmpinf.h"
00131 #include "progress.h"
00132 #include "ndoptmz.h"
00133 #include "csrstack.h"
00134 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00135 #include "objchge.h"
00136 #include "keypress.h"
00137 #include "nodeblnd.h"
00138 #include "fillramp.h"
00139 
00140 //#include "will2.h"
00141 #include "nodecont.h"
00142 
00143 #ifndef STANDALONE
00144 CC_IMPLEMENT_DYNCREATE( OpCreateFill, Operation )
00145 CC_IMPLEMENT_DYNCREATE( OpEditFill,   OpApplyAttribToSelected )
00146 CC_IMPLEMENT_DYNCREATE( RestoreFillRampAction,   Action)
00147 #endif
00148 //CC_IMPLEMENT_DYNCREATE( OpMutateFill, Operation )
00149 //CC_IMPLEMENT_DYNCREATE( OpChangeFillColour, SelOperation )
00150 //CC_IMPLEMENT_DYNCREATE( OpChangeSelectionColours, SelOperation )
00151 //CC_IMPLEMENT_DYNCREATE( ModifyFillCoordsAction, Action)
00152 //CC_IMPLEMENT_DYNCREATE( ModifyFillColoursAction, Action)
00153 
00154 // Declare smart memory handling in Debug builds
00155 #define new CAM_DEBUG_NEW
00156 
00157 // Fill editing preferences
00158 BOOL    OpEditFill::InteractiveDragUpdate   = TRUE;
00159 BOOL    OpEditFill::ContinuousEOR           = TRUE;
00160 UINT32  OpEditFill::IdleFillDelay           = 200;  // Fill redraw delay (used to be 300 in Xara X release)
00161 
00162 BOOL OpEditFill::CreateFill = FALSE;
00163 
00164 static BOOL EditFinished = FALSE;
00165 
00166 //  some pointers that are useful within this stuff ....
00167 
00168 static NodeRenderableInk* pParentOfFill = NULL;
00169 static AttrFillGeometry* pTheFill = NULL;
00170 
00171 #ifndef STANDALONE
00172 
00173 /********************************************************************************************
00174 
00175 >   OpEditFill::OpEditFill()
00176 
00177     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00178     Created:    1/6/94
00179     Purpose:    Dummy Contrustor
00180 
00181 ********************************************************************************************/
00182 
00183 OpEditFill::OpEditFill()
00184 {
00185     // Initialise everything
00186     AlwaysFail          = TRUE;
00187     ApplyAtEnd          = TRUE;
00188     ForceAspectLock     = FALSE;
00189     CreateFill          = FALSE;
00190     IsRequired          = TRUE;
00191     ShouldApplyNewFill  = FALSE;
00192     CheckForCompound    = TRUE;
00193     CallAllowOp         = FALSE;
00194 
00195     InvalidateAll       = TRUE;
00196     InvalidateCompound  = FALSE;
00197 
00198     DragIsIdle          = FALSE;
00199     DoneIdleRedraw      = FALSE;
00200 
00201     ShowDragBlobs       = TRUE;
00202     DontDrawBlobs       = FALSE;
00203 
00204     EditFinished        = FALSE;
00205 
00206     AspectRatio = 1;
00207 }
00208 
00209 /********************************************************************************************
00210 
00211 >   void OpEditFill::DoCreate(DocCoord &Start, DocCoord &End, Spread* pSpread, AttrFillGeometry* pGrad)
00212 
00213     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00214     Created:    20/7/94
00215     Inputs:     Start - The starting position of the drag
00216                 pSpread - The spread that the drag started over
00217                 pElip - The NodeGradFill that is being edited
00218     Purpose:    -
00219 
00220 ********************************************************************************************/
00221 
00222 void OpEditFill::DoCreate(DocCoord &Start, Spread* pSpread, AttrFillGeometry* pGrad)
00223 {
00224     ERROR3IF(pGrad == NULL, "Fill pointer is NULL in OpEditFill::DoCreate()");
00225 
00226     if (pGrad == NULL)
00227     {
00228         // Nothing to create !!
00229         FailAndExecute();
00230         End();
00231         return;
00232     }
00233 
00234     // DMc - do the attribute to change
00235     m_pAttr = (NodeAttribute *)pGrad;
00236 
00237     AlwaysFail = FALSE;
00238     
00239     // DMc - set this to TRUE
00240     ApplyAtEnd = FALSE;
00241     ForceAspectLock = TRUE;
00242     CreateFill = TRUE;
00243 
00244     // Get a description of the attribute being applied so that we can use it to create the 
00245     // undo string. 
00246     UndoAttribStrID = pGrad->GetAttrNameID();
00247     AttrFillGeometry::HitList.DeleteAll();
00248 
00249     SelRange* pSel = GetApplication()->FindSelection(); 
00250     if (pSel && pSel->Count() > 0)
00251     {
00252         // Check it's ok to apply to this object
00253         ObjChangeFlags cFlags;
00254         cFlags.Attribute = TRUE;
00255         ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
00256 ObjChange.SetRetainCachedData(TRUE);        // Don't let nodes releasecached data - we will handle that!
00257         if (!pSel->AllowOp(&ObjChange))
00258         {
00259             delete pGrad;
00260             FailAndExecute();
00261             End();
00262             return;
00263         }
00264     }
00265 
00266     AttrFillGeometry* pFillToEdit = pGrad;
00267 
00268     // Now check to make sure someone in the selection will accept this attribute
00269     if (AttributeManager::CanBeAppliedToSelection(pGrad, &AttrGroups))
00270     {
00271         // We're gunna apply to at least one object
00272         IsRequired = TRUE;
00273 
00274         // Flag that we need to apply the fill, either when we try
00275         // and do an interective redraw, or at the end
00276         ShouldApplyNewFill = TRUE;
00277 
00278         // Remove all the existing selection fill blobs
00279         DocRect SelBounds = pSel->GetBlobBoundingRect();
00280         RenderInitSelectionBlobs(pSel, SelBounds, pSpread);
00281     }
00282     else
00283     {
00284         // This attribute is not required by any of the selection,
00285         // but we will continue, without actually applying anything,
00286         // and set the 'current' attribute instead
00287         IsRequired = FALSE;
00288         AttributeManager::pLastNodeAppliedTo = NULL;
00289     }
00290 
00291     // Snap the actual mouse position to the grid if needed
00292     DocView::SnapSelected(pSpread, &Start);
00293 
00294     // Initialise the Control Points
00295     pFillToEdit->SetStartPoint(&Start);
00296     pFillToEdit->SetEndPoint(&Start);
00297     pFillToEdit->SetEndPoint2(&Start);
00298 
00299     // Call the edit code
00300     DoDrag(Start, pSpread, pFillToEdit, FILLCONTROL_ENDPOINT);
00301 }
00302 
00303 /********************************************************************************************
00304 
00305 >   void OpEditFill::DoDrag(DocCoord &Start, DocCoord &End, Spread* pSpread, AttrFillGeometry* pGrad, INT32 BlobHit)
00306 
00307     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00308     Created:    20/7/94
00309     Inputs:     Start - The starting position of the drag
00310                 pSpread - The spread that the drag started over
00311                 pElip - The NodeGradFill that is being edited
00312     Purpose:    -
00313     Notes:      Phil, 16-09-2005
00314                 This code relies on the attributea pplication code using DoHideNode and DoShowNode
00315                 to record changes to the attributes. By recording the attributes in this way when
00316                 they are first applied, the undo history contains pointers to them and the 
00317                 dragging code can then modify those attributes in the knowledge that the op
00318                 history is now pointing at the modified attributes.
00319                 It should really be changed to use OpApplyAttrInteractive. That would reduce
00320                 the complexity of the following functions enormously and would be more
00321                 consistent, smaller and possibly faster.
00322                 There are too many different ways of "solid dragging" things and they should all:
00323                 * Record current tree state at start of drag
00324                 * Modify tree during drag
00325                 * Produce permanent Undo information at the end, using the same logic as would
00326                   be used in the simple, parametric or outline drag case
00327 
00328 ********************************************************************************************/
00329 
00330 void OpEditFill::DoDrag(DocCoord &Start, Spread* pSpread, AttrFillGeometry* pGrad, FillControl ControlHit)
00331 {
00332     ERROR3IF(ControlHit > NUMCONTROLPOINTS && 
00333         !ISA_RAMPINDEX(ControlHit), "Invalid control point in OpEditFill::DoDrag()");
00334     ERROR3IF(pGrad == NULL, "Fill pointer is NULL in OpEditFill::DoDrag()");
00335 
00336     if (pGrad == NULL)
00337     {
00338         // Nothing to edit !!
00339         FailAndExecute();
00340         End();
00341         return;
00342     }
00343 
00344     // Various starting positions
00345     StartSpread = pSpread;
00346     DragControl = ControlHit;
00347 
00348     // Keep a copy of the old fill, so we can put it back later if necessary
00349     CCRuntimeClass* ObjectType = pGrad->GetRuntimeClass();
00350     FillClone = (AttrFillGeometry*)ObjectType->CreateObject();
00351     *FillClone = *pGrad;
00352 
00353     // and make another copy to actually edit
00354     ObjectType = pGrad->GetRuntimeClass();
00355     GradFill = (AttrFillGeometry*)ObjectType->CreateObject();
00356     *GradFill = *pGrad;
00357 
00358     pParentOfFill = (NodeRenderableInk*) pGrad->FindParent ();
00359     pTheFill = pGrad;
00360 
00361     if (CreateFill)
00362         delete pGrad;   // Not needed any more
00363 
00364     AspectRatio = FindAspectRatio();
00365 
00366     AnchorPos = Start;
00367     LastMousePosition = Start;
00368 
00369     DontDrawBlobs = FALSE;
00370     DragIsIdle = FALSE;
00371     DoneIdleRedraw = FALSE;
00372 
00373 //  ShowDragBlobs = ContinuousEOR;
00374     ShowDragBlobs = !DocView::SolidDragging;
00375 
00376     if (!IsRequired)            // Always show blobs if we're gunna
00377         ShowDragBlobs = TRUE;   // set the current attribute
00378 
00379     AttrFillGeometry::DraggedFill = NULL;
00380     AttrFillGeometry::EditedFill  = GradFill;
00381 
00382     // And tell the Dragging system that we need drags to happen
00383     StartDrag(DRAGTYPE_AUTOSCROLL, NULL, NULL, TRUE, TRUE);         // We support solid dragging
00384 }
00385     
00386 /********************************************************************************************
00387 
00388 >   void OpEditFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods,
00389                                         Spread* pSpread, BOOL bSolidDrag)
00390 
00391     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00392     Created:    20/7/94
00393     Inputs:     PointerPos - The current position of the mouse
00394                 ClickMods - Which buttons and modifiers are held down
00395                 pSpread - The Spread that the mouse is over now.
00396     Purpose:    Recalculates the GradFill according to the new position of the corner that
00397                 is being dragged, EORs it to the screen and keeps the Parallelogram up
00398                 to date.
00399 
00400 ********************************************************************************************/
00401 
00402 void OpEditFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag)
00403 {
00404     if (ISA_RAMPINDEX(DragControl) && !bSolidDrag)
00405     {
00406         GradFill->DisableRampRedraw();
00407     }
00408     
00409     if (EditFinished)
00410         return;
00411 
00412     if (ForceAspectLock)
00413         ClickMods.Adjust = TRUE;
00414 
00415     DragIsIdle = FALSE;
00416     DoneIdleRedraw = FALSE;
00417 
00418     // Snap the actual mouse position to the grid if needed
00419     DocView::SnapSelected(pSpread, &PointerPos);
00420 
00421     if (AttrFillGeometry::DraggedFill == NULL)
00422     {
00423         double APixel = (DocView::GetSelected()->GetScaledPixelWidth()).MakeDouble();
00424 
00425         // If the pointer has moved less than a few pixels, then do nothing
00426         if (PointerPos.Distance(AnchorPos) <= APixel*2)
00427         {
00428             if (!(ISA_RAMPINDEX(DragControl)))
00429             {
00430                 return;
00431             }
00432         }
00433 
00434         // This is the first time the pointer has moved ....
00435         if (!CreateFill)
00436         {
00437             // If we are editing a fill, the check with it's parent
00438             // to make sure it's ok (Mould's will say 'no').
00439 
00440             ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
00441             AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
00442 
00443             if (pGrad != NULL)
00444             {
00445                 Node* pParent = pGrad->FindParent();
00446 
00447                 if (pParent != NULL)
00448                 {
00449                     // Check it's ok to edit this fill
00450                     ObjChangeFlags cFlags;
00451                     cFlags.Attribute = TRUE;
00452                     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
00453 ObjChange.SetRetainCachedData(TRUE);
00454 
00455                     if (!pParent->AllowOp(&ObjChange))
00456                     {
00457                         // Parent said 'no', so abort the edit
00458     
00459                         EndDrag();
00460                         delete FillClone;
00461                         delete GradFill;
00462 
00463                         FailAndExecute();
00464                         End();
00465                         return;
00466                     }
00467                 }
00468             }
00469         }
00470 
00471         AttrFillGeometry::EditedFill  = GradFill;
00472 
00473         AttrFillGeometry::LastRenderedStartBlob = DocCoord(0,0);
00474         AttrFillGeometry::LastRenderedEndBlob = DocCoord(0,0);
00475         AttrFillGeometry::LastRenderedEnd2Blob = DocCoord(0,0);
00476         AttrFillGeometry::LastRenderedEnd3Blob = DocCoord(0,0);
00477 
00478         // First Rub out the old Fill blobs
00479         RenderInitBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag);
00480 
00481         GradFill->SetBlobState(DragControl, TRUE);
00482 
00483         // And stop any more from being drawn for now
00484         AttrFillGeometry::DraggedFill = GradFill;
00485 
00486         LastDragRect = GradFill->GetBlobBoundingRect();
00487         DontDrawBlobs = !ShowDragBlobs;
00488 
00489         // Now draw in the new 'Drag' blobs
00490         RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag);
00491 
00492         // Update the status line to show we are dragging a fill
00493         String_256 DragText;
00494         GetStatusLineText(&DragText, pSpread, PointerPos, ClickMods);
00495         GetApplication()->UpdateStatusBarText(&DragText);
00496     }
00497 
00498     // If the mouse is in a different position then do something
00499     if (PointerPos != LastMousePosition)
00500     {
00501         // First Rub out the old Drag blobs
00502         RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag);
00503 
00504         // Make sure that the coords are relative to the correct spread
00505         if (pSpread != StartSpread)
00506             PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
00507 
00508         // Update the last mouse position and re-calc the bounding rect
00509         GradFill->OnControlDrag(PointerPos, DragControl, ClickMods);
00510 
00511         LastMousePosition = PointerPos;
00512 
00513         // If we are creating a fill, then we'll try and create it with the
00514         // correct aspect ratio
00515         if (ForceAspectLock)
00516             GradFill->SetAspectRatio(AspectRatio);
00517 
00518         LastDragRect = GradFill->GetBlobBoundingRect();
00519         DontDrawBlobs = !ShowDragBlobs;
00520 
00521         // Render 'On' the blobs in the new position
00522         RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag);
00523 
00524         if (DontDrawBlobs)
00525             RedrawFills(ClickMods);     // We're in 'Solid update' mode
00526     }
00527 
00528     if (ISA_RAMPINDEX(DragControl))
00529     {
00530         GradFill->EnableRampRedraw ();
00531     }
00532 }
00533 
00534 /********************************************************************************************
00535 
00536 >   double OpEditFill::FindAspectRatio()
00537 
00538     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00539     Created:    3/5/95
00540     Purpose:    Find the aspect ratio of the bitmap we are creating.
00541 
00542 ********************************************************************************************/
00543 
00544 double OpEditFill::FindAspectRatio()
00545 {
00546     // Are we creating a Bitmap fill ?
00547     if (!GradFill->IsABitmapFill())
00548         return 1;
00549 
00550     SelRange* pSel = GetApplication()->FindSelection();
00551 
00552     NodeAttribute* pCommonAttr;
00553     SelRange::CommonAttribResult Result;
00554     
00555     // Find the common attr in the selection
00556     Result = pSel->FindCommonAttribute(GradFill->GetAttributeType(), &pCommonAttr);
00557 
00558     KernelBitmap* Bmp = NULL;
00559 
00560     if (Result == SelRange::ATTR_COMMON)
00561     {
00562         // There is a common attr, so if it's a Bitmap Fill,
00563         // then use that bitmaps aspect ratio
00564         AttrFillGeometry* pFill = (AttrFillGeometry*)pCommonAttr;
00565 
00566         if (pFill->IsABitmapFill())
00567             Bmp = pFill->GetBitmap();
00568 
00569         GradFill->AttachBitmap(Bmp);
00570     }
00571     else
00572     {
00573         // Otherwise just use the default
00574         Bmp = GradFill->GetBitmap();
00575     }
00576 
00577     if (Bmp == NULL)
00578         return 1;
00579 
00580     BitmapInfo Info;
00581 
00582     if (Bmp->ActualBitmap &&
00583         Bmp->ActualBitmap->GetInfo( &Info ))
00584     {
00585         INT32 BitmapWidth  = Info.RecommendedWidth;
00586         INT32 BitmapHeight = Info.RecommendedHeight;
00587 
00588         // Calculate the aspect ratio of the bitmap
00589         return double(BitmapHeight)/double(BitmapWidth);
00590     }
00591 
00592     // Default to square
00593     return 1;
00594 }
00595 
00596 /********************************************************************************************
00597 
00598 >   void OpEditFill::RedrawFills(ClickModifiers& ClickMods)
00599 
00600     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00601     Created:    3/5/95
00602     Purpose:    Forces a redraw on the edited fills.
00603 
00604 ********************************************************************************************/
00605 
00606 void OpEditFill::RedrawFills(ClickModifiers& ClickMods)
00607 {
00608     if (ClickMods.Alternative1)
00609         return;         // Alt key stops interactive updates
00610 
00611     // Prevent slow renderers from doing their stuff while we try to
00612     // show the results of the feather op interactively.
00613     Operation::SetQuickRender(TRUE);
00614 
00615     if (ShouldApplyNewFill)
00616     {
00617         // If we haven't applied the new fill yet, then we should
00618         // do so now.
00619         ApplyNewFill();
00620     }
00621 
00622     if (CheckForCompound)
00623     {
00624         // If we haven't already done so, we check to see if we need
00625         // to invalidate any compound parents.
00626         CheckForCompoundRedraw();
00627     }
00628 
00629     if (!AttrFillGeometry::HitList.IsEmpty())
00630     {
00631         DocView* pSelView = DocView::GetSelected();
00632 
00633         // Scan the List of Fills
00634         ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
00635 
00636         DocRect SelBounds = GetApplication()->FindSelection()->GetBoundingRect();
00637 
00638         while (pAttrPtr != NULL)
00639         {
00640             AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
00641             ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list");
00642 
00643             if (pAttr != GradFill)
00644             {
00645                 if (ISA_RAMPINDEX(DragControl))
00646                 {
00647                     AttrFillGeometry* CurrentFill = AttrFillGeometry::DraggedFill;
00648                     
00649                     AttrFillGeometry::DraggedFill = pAttr;
00650                     DocRect Current = pAttr->GetBlobBoundingRect ();
00651                     SelBounds = SelBounds.Union (Current);
00652 
00653                     AttrFillGeometry::DraggedFill = CurrentFill;
00654                 }
00655                 
00656                 // Update the control points on the other fills
00657                 pAttr->SetStartPoint(GradFill->GetStartPoint());
00658                 pAttr->SetEndPoint(GradFill->GetEndPoint());
00659                 pAttr->SetEndPoint2(GradFill->GetEndPoint2());
00660                 pAttr->SetColourRamp(GradFill->GetColourRamp());
00661                 //pAttr->DisableRampRedraw ();
00662 
00663                 pAttr->RecalcFractal();
00664             }
00665 
00666             if (!InvalidateAll)
00667             {
00668                 // Force the fills parent object to redraw
00669                 NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent();
00670                 ERROR3IF(pParent==NULL, "Can't find parent of edited attribute");
00671                 if (pParent && pAttr->IsEffectAttribute())  // IsValidEffectAttr?
00672                 {
00673 //                  Node* pGrandParent = pParent->FindParent();
00674 //                  if (pGrandParent && pGrandParent->IsBounded())
00675 //                      ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only
00676                     pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE);   // Parents and derived data only
00677                 }
00678                 else
00679                     pParent->ReleaseCached();
00680                 pSelView->ForceRedraw(StartSpread, pParent->GetBoundingRect(), FALSE, pAttr);
00681                 GetApplication ()->ServiceRendering ();
00682             }
00683 
00684             pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
00685         }
00686 
00687         if (InvalidateAll)
00688         {
00689             // Either there were too many objects to invalidate individually,
00690             // or there is a Compound Parent to invalidate
00691             if (InvalidateCompound)
00692             {
00693                 pAttrPtr = AttrFillGeometry::HitList.GetHead();
00694                 AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
00695 
00696                 if (CallAllowOp && !pGrad->IsEffectAttribute())
00697                 {
00698                     // For a blend, we need to make sure it re-blends itself,
00699                     // so we need to call AllowOp on it ...
00700 
00701                     ObjChangeFlags cFlags;
00702                     cFlags.Attribute = TRUE;        // Needed to make Blends re-calc themselves
00703 
00704                     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL, this);
00705 
00706                     pGrad->AllowOp(&ObjChange);
00707 
00708                     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL, this);
00709                     UpdateAllChangedNodes(&ObjChange);
00710                 }
00711 
00712                 if (ISA_RAMPINDEX (DragControl))
00713                 {
00714                     GradFill->DisableBoundsRedraw();
00715                 }
00716 
00717                 NodeRenderableInk* pParent = (NodeRenderableInk*)pCompound;
00718                 ERROR3IF(pParent==NULL, "Can't find parent of edited attribute");
00719                 if (pParent && pGrad->IsEffectAttribute())  // IsValidEffectAttr?
00720                 {
00721 //                  Node* pGrandParent = pParent->FindParent();
00722 //                  if (pGrandParent && pGrandParent->IsBounded())
00723 //                      ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only
00724                     pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE);   // Parents and derived data only
00725                 }
00726                 else
00727                     pParent->ReleaseCached();
00728                 pSelView->ForceRedraw(StartSpread, pParent->GetBoundingRect(), FALSE, pGrad);
00729                 GetApplication ()->ServiceRendering();
00730 
00731                 if (ISA_RAMPINDEX (DragControl))
00732                 {
00733                     GradFill->EnableBoundsRedraw();
00734                 }
00735             }
00736             else
00737             {           
00738                 if (ISA_RAMPINDEX (DragControl))
00739                 {
00740                     GradFill->DisableBoundsRedraw();
00741                 }
00742                 
00743                 // Oh sod it. Just invalidate the whole blinking lot of them,
00744                 // and be done with it.
00745                 //DocRect SelBounds = GetApplication()->FindSelection()->GetBoundingRect();
00746 
00747                 // Scan the List of Fills and ensure that none of them are cached...
00748                 ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
00749                 Node* pBackMost = NULL;
00750                 while (pAttrPtr != NULL)
00751                 {
00752                     AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
00753 
00754                     // Force the fills parent object to redraw
00755                     NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent();
00756                     ERROR3IF(pParent==NULL, "Can't find parent of edited attribute");
00757                     if (pParent && pAttr->IsEffectAttribute())  // IsValidEffectAttr?
00758                     {
00759 //                      Node* pGrandParent = pParent->FindParent();
00760 //                      if (pGrandParent && pGrandParent->IsBounded())
00761 //                          ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only
00762                         pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE);   // Parents and derived data only
00763                     }
00764                     else
00765                         pParent->ReleaseCached();
00766 
00767                     // Work out the backmost attr
00768                     if (pBackMost==NULL || pAttr->IsUnder(pBackMost))
00769                         pBackMost = pAttr;
00770 
00771                     pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
00772                 }
00773 
00774                 pSelView->ForceRedraw(StartSpread, SelBounds, FALSE, pBackMost);
00775 
00776                 GetApplication()->ServiceRendering();
00777 
00778                 if (ISA_RAMPINDEX (DragControl))
00779                 {
00780                     GradFill->EnableBoundsRedraw();
00781                 }
00782             }
00783         }
00784     }
00785 
00786     // Re-enable slow renderers
00787     Operation::SetQuickRender(FALSE);
00788 }
00789 
00790 /********************************************************************************************
00791 
00792 >   void OpEditFill::CheckForCompoundRedraw()
00793 
00794     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00795     Created:    3/5/95
00796     Purpose:    Check to see if the redraw needs to invalidate a compound parent.
00797                 (eg. a blend).
00798 
00799 ********************************************************************************************/
00800 
00801 void OpEditFill::CheckForCompoundRedraw()
00802 {
00803     CheckForCompound = FALSE;
00804 
00805     pFillsDoc = StartSpread->FindOwnerDoc();
00806 
00807     if (AttrFillGeometry::HitList.GetCount() == GetApplication()->FindSelection()->Count() ||
00808         AttrFillGeometry::HitList.GetCount() > 10)
00809     {
00810         // If we are dragging all selected fills, or if there are
00811         // more than 10 of them, then we'll just invalidate the 
00812         // whole selection, 'cus it's much quicker
00813         InvalidateAll = TRUE;
00814     }
00815     else
00816     {
00817         // We will invalidate just the edited fills
00818         InvalidateAll = FALSE;
00819     }
00820 
00821     InvalidateCompound = FALSE;
00822 
00823     if (AttrFillGeometry::HitList.GetCount() == 1)
00824     {
00825         // If just one Fill is being dragged, then check to see if
00826         // it's a 'Select Inside' of a Compound object.
00827 
00828         // If it is, then we'll need to update the whole compound parent.
00829 
00830         ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
00831         AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
00832 
00833         Node* pParent = pGrad->FindParent();
00834         if (pParent == NULL)
00835         {
00836             InvalidateAll = TRUE;
00837             return;
00838         }
00839     
00840         if (pParent->IsCompound())
00841         {
00842             // The parent is compound, so we'll just update that
00843             InvalidateAll = TRUE;
00844             InvalidateCompound = TRUE;
00845             pCompound = pParent;
00846         }
00847 
00848         // Is the parent a blend object, or is there one
00849         // further up the tree ?
00850         while (pParent != NULL && !pParent->IsLayer())
00851         {
00852             if (pParent->IS_KIND_OF(NodeBlend) || pParent->ShouldITransformWithChildren())
00853             {
00854                 // Found a blend, so we'll have to invalidate it
00855                 // each time we want to re-draw
00856                 InvalidateAll = TRUE;
00857                 InvalidateCompound = TRUE;
00858                 pCompound = pParent;
00859 
00860                 // We will call allowop, to invalidate the blend
00861                 CallAllowOp = TRUE;
00862 
00863                 // Don't break out yet, 'cus we need to invalidate
00864                 // the highest blend in the tree
00865             }
00866             
00867             pParent = pParent->FindParent();
00868         }
00869     }
00870 }
00871 
00872 /********************************************************************************************
00873 
00874 >   void OpEditFill::DragPointerIdle(DocCoord PointerPos, ClickModifiers ClickMods,
00875                                         Spread* pSpread, BOOL bSolidDrag)
00876 
00877     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00878     Created:    20/7/94
00879     Inputs:     PointerPos - The current position of the mouse
00880                 ClickMods - Which buttons and modifiers are held down
00881                 pSpread - The Spread that the mouse is over now.
00882     Purpose:    Recalculates the GradFill according to the new position of the corner that
00883                 is being dragged, EORs it to the screen and keeps the Parallelogram up
00884                 to date.
00885 
00886 ********************************************************************************************/
00887 
00888 void OpEditFill::DragPointerIdle(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag)
00889 {   
00890     if (!DragIsIdle)
00891     {
00892         // The Mouse has just become idle, so we'll reset the timer.
00893         DragIsIdle = TRUE;
00894         DoneIdleRedraw = FALSE;
00895         Time.Sample();
00896     }       
00897     else
00898     {
00899         // Has the Mouse been idle for long enough ?
00900         if (Time.Elapsed(IdleFillDelay))
00901         {
00902             if (DontDrawBlobs)
00903             {
00904                 // If we are not redrawing the EOR blobs constantly,
00905                 // then we will stick them back on when the mouse
00906                 // has been idle for a while.
00907                 DontDrawBlobs = FALSE;
00908                 DragIsIdle = FALSE;
00909                 RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag);
00910             }
00911             else
00912             {
00913                 // If we ARE redrawing the blobs, then we'll try and redraw
00914                 // the fills when the mouse has been idle for a while.
00915                 if (InteractiveDragUpdate && !DoneIdleRedraw)
00916                 {
00917                     RedrawFills(ClickMods);
00918                     DoneIdleRedraw = TRUE;
00919                 }
00920             }
00921         }
00922     }
00923 }
00924 
00925 /********************************************************************************************
00926 
00927 >   BOOL OpEditFill::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag)
00928 
00929     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
00930     Created:    4/5/95
00931     Inputs:     pKeyPress - Object describing the nature of the keypress.
00932     Purpose:    Allows the Fill Edit operation to respond to key presses.
00933 
00934 ********************************************************************************************/
00935 
00936 BOOL OpEditFill::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag)
00937 {
00938     if (pKeyPress->GetVirtKey() == CAMKEY(TAB) && 
00939         pKeyPress->IsPress() &&
00940         !pKeyPress->IsRepeat())
00941     {
00942         // Toggle the continuous EOR flag
00943         ContinuousEOR = !ContinuousEOR;
00944         ShowDragBlobs = ContinuousEOR;
00945 
00946         String_256 DragText;
00947         ClickModifiers ClickMods;
00948 
00949         // Now update the Status line to show the mode change
00950         GetStatusLineText(&DragText, StartSpread, LastMousePosition, ClickMods);
00951         GetApplication()->UpdateStatusBarText(&DragText);
00952         
00953         return TRUE;
00954     }
00955     return FALSE;
00956 }
00957 
00958 
00959 
00960 
00961 /********************************************************************************************
00962 
00963 >   void OpEditFill::DragModeChanged()
00964 
00965     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00966     Created:    23/12/2003
00967     Inputs:     pKeyPress - pointer to a keypress object
00968     Returns:    TRUE if it handled the keypress, FALSE otherwise
00969     Purpose:    Find out if any of the key modifiers are being pressed when the mouse is
00970                 not moving.
00971 
00972 ********************************************************************************************/
00973 
00974 void OpEditFill::DragModeChanged(BOOL bSolidDrag)
00975 {
00976 /*  DocView* pDocView = DocView::GetSelected();
00977     BlobManager* pBlobManager = Camelot.GetBlobManager();
00978     ENSURE(pBlobManager, "Can't get BlobManager");
00979 
00980     // Toggle the continuous EOR flag
00981     ContinuousEOR = !bSolidDrag;
00982     ShowDragBlobs = ContinuousEOR;
00983 
00984     String_256 DragText;
00985     ClickModifiers ClickMods;
00986 
00987     // Now update the Status line to show the mode change
00988     GetStatusLineText(&DragText, StartSpread, LastMousePosition, ClickMods);
00989     GetApplication()->UpdateStatusBarText(&DragText);
00990 */
00991 }
00992 
00993 
00994 
00995 
00996 /********************************************************************************************
00997 
00998 >   void OpEditFill::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods,
00999                                     Spread* pSpread, BOOL Success, BOOL bSolidDrag)
01000 
01001     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01002     Created:    20/7/94
01003     Inputs:     PointerPos - The current position of the mouse
01004                 ClickMods - Which buttons and modifiers are held down
01005                 pSpread - The Spread that the mouse is over now.
01006                 Success - TRUE if the drag was completed sucessfully.
01007     Purpose:    If the drag was a success then a copy of the original node in the tree is
01008                 created and updated GradFill built. The original NodeGradFill is hidden and
01009                 the new one if inserted into the tree. If any of these things fail then
01010                 the operation will fail.
01011 
01012 ********************************************************************************************/
01013 
01014 void OpEditFill::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread*, BOOL Success, BOOL bSolidDrag)
01015 {   
01016     EditFinished = TRUE;
01017     
01018     double APixel;
01019 
01020     // Flag to say if everything has worked
01021     BOOL IsOk = FALSE;
01022 
01023     // First Rub out the old Drag blobs
01024     if (!DontDrawBlobs && AttrFillGeometry::DraggedFill != NULL)
01025     {
01026         if (!(ISA_RAMPINDEX(DragControl)))
01027         {
01028             RenderDragBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag);
01029         }
01030         else
01031         {
01032             if (ISA_RAMPINDEX (DragControl))
01033             {
01034                 GradFill->DisableBoundsRedraw ();
01035             }
01036             RenderDragBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag);
01037             if (ISA_RAMPINDEX (DragControl))
01038             {
01039                 GradFill->EnableBoundsRedraw ();
01040             }
01041         }
01042     }
01043 
01044     DontDrawBlobs = FALSE;
01045     BOOL HaveFailed = FALSE;
01046 
01047     Document* pDoc = Document::GetSelected();
01048 
01049     // check for a fill ramp drag
01050     if (ISA_RAMPINDEX(DragControl))
01051     {
01052         if (Success)
01053         {
01054             if (GradFill->GetColourRamp())
01055             {
01056                 GradFill->GetColourRamp()->SortRamp();
01057             }
01058         }
01059     }
01060 
01061     // End the Drag                             
01062     if (!EndDrag())
01063     {
01064         delete FillClone;
01065         delete GradFill;
01066         goto EndOperation;
01067     }
01068 
01069     if (*GradFill->GetStartPoint() == *GradFill->GetEndPoint())
01070         Success = FALSE;
01071     
01072     APixel = (DocView::GetSelected()->GetScaledPixelWidth()).MakeDouble();
01073 
01074     // If the pointer has moved less than a few pixels, then just do a click
01075     if (PointerPos.Distance(AnchorPos) <= APixel*2)
01076         Success = FALSE;
01077 
01078     // If PointerPos is equal to AnchorPos, then it must have just been a click
01079     if (!Success || (PointerPos == AnchorPos))
01080     {
01081         if (AttrFillGeometry::DraggedFill)
01082         {
01083             // We must have actually dragged a bit,
01084             // so make sure we tidy everying, and then render
01085             // the old fill blobs back on
01086             AttrFillGeometry::DraggedFill = NULL;
01087             AttrFillGeometry::EditedFill = NULL;
01088 
01089             // Failed, so put all the fills back like they were,
01090             // if we have changed them.
01091             if (!(*GradFill == *FillClone))
01092             {
01093                 *GradFill = *FillClone;
01094 
01095                 ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
01096                 while (pAttrPtr != NULL)
01097                 {
01098                     AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
01099 ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list");
01100 
01101                     pAttr->SetStartPoint(FillClone->GetStartPoint());
01102                     pAttr->SetEndPoint(FillClone->GetEndPoint());
01103                     pAttr->SetEndPoint2(FillClone->GetEndPoint2());
01104 
01105                     NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent();
01106                     ERROR3IF(pParent==NULL, "Can't find parent of edited attribute");
01107                     if (pParent && pAttr->IsEffectAttribute())  // IsValidEffectAttr?
01108                     {
01109 //                      Node* pGrandParent = pParent->FindParent();
01110 //                      if (pGrandParent && pGrandParent->IsBounded())
01111 //                          ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only
01112                         pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE);   // Parents and derived data only
01113                     }
01114                     else
01115                         pParent->ReleaseCached();
01116 
01117                     pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
01118                 }
01119 
01120                 // Redraw the old fills back
01121                 DocRect SelBounds = GetApplication()->FindSelection()->GetBlobBoundingRect();
01122 
01123                 DocView::GetSelected()->ForceRedraw(StartSpread, SelBounds);
01124             }
01125 
01126         }
01127         else
01128         {
01129             // We didn't drag anything, so it must have just been a click
01130             if (ClickMods.Adjust && ClickMods.Constrain)
01131             {
01132                 // Cntrl/Shift/Click will select all Start or End blobs
01133                 SelectAllBlobs();
01134             }
01135 
01136             // Tell the world that a fill blob has been selected
01137             BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); 
01138         }
01139 
01140         AttrFillGeometry::LastRenderedMesh = NULL;
01141         AttrFillGeometry::HitList.DeleteAll();
01142 
01143         delete FillClone;
01144         delete GradFill;
01145         goto EndOperation;      // This will fail the op
01146     }
01147 
01148     // We have edited the fill ok, so we'll draw the blobs back on
01149     AttrFillGeometry::DraggedFill = NULL;
01150 
01151     if (IsRequired)
01152         RenderFinalBlobs(GetDragBlobRect(), StartSpread, bSolidDrag);
01153 
01154     if (AlwaysFail)
01155     {
01156         // If we just edited a fill, then we don't want any undo info,
01157         // so we'll fail the op now (and discard the Actions), before we
01158         // apply the edited fill
01159         FailAndExecute();
01160         HaveFailed = TRUE;  // no need to fail at the end
01161     }
01162 
01163     // if the drag was a sucess then make a grad fill
01164     if (Success && (!GetDragBlobRect().IsEmpty()))
01165     {
01166         AttrFillGeometry::EditedFill = NULL;
01167         AttrFillGeometry::DraggedFill = NULL;
01168     
01169         if (ApplyAtEnd)
01170         {
01171             // Update all the edited fills
01172             ApplyEditedFill();
01173 
01174             delete FillClone;
01175             delete GradFill;
01176         }
01177         else
01178         {
01179             if (ShouldApplyNewFill)
01180             {
01181                 ApplyNewFill();
01182             }
01183 
01184             BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); 
01185             BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); 
01186 
01187             SelRange *Selection = Camelot.FindSelection();
01188             DocRect Bounds;
01189             if (Selection != NULL && Selection->Count()>0)
01190                 Bounds = Selection->GetBoundingRect();
01191             else
01192                 Bounds = GetDragBlobRect();
01193 
01194             GradFill->SetBoundingRect(Bounds);
01195 
01196             // Ensure we update all the fills
01197             if (!AttrFillGeometry::HitList.IsEmpty())
01198             {
01199                 ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
01200 
01201                 while (pAttrPtr != NULL)
01202                 {
01203                     AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
01204 ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list");
01205 
01206                     // Update the control points on the other fills
01207                     pAttr->SetStartPoint(GradFill->GetStartPoint());
01208                     pAttr->SetEndPoint(GradFill->GetEndPoint());
01209                     pAttr->SetEndPoint2(GradFill->GetEndPoint2());
01210 
01211                     // Give the attribute a bounding rect
01212                     pAttr->SetBoundingRect(Bounds);
01213 
01214                     // We need to ensure that we force a redraw at the end of the drag
01215                     // because redraws during the drag will have been done with QuickRender turned
01216                     // on and so some slow objects may not have rendered themselves
01217                     // fully.
01218                     if (bSolidDrag)
01219                     {
01220                         Node* pParent = pAttr->FindParent();
01221                         if (pParent && pParent->IsBounded())
01222                         {
01223 ((NodeRenderableBounded*)pParent)->ReleaseCached(TRUE, FALSE, !pAttr->IsEffectAttribute(), TRUE);
01224                             DoInvalidateNodeRegion((NodeRenderableBounded*)pParent, FALSE, TRUE, FALSE, FALSE); // Do not recache
01225                             // (SelOperation::DoApply will have called ReleaseCached as needed)
01226                         }
01227                     }
01228 
01229                     pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
01230                 }
01231             }
01232             AttributeManager& AttrMgr = pDoc->GetAttributeMgr();
01233 
01234             if (AttrMgr.WeShouldMakeAttrCurrent(IsRequired,GradFill,&AttrGroups))
01235             {
01236                 AttributeManager::UpdateCurrentAttr(GradFill, TRUE, &AttrGroups);
01237             }
01238 
01239             delete FillClone;
01240             delete GradFill;
01241         }
01242 
01243         IsOk = TRUE;
01244     }
01245 
01246 EndOperation:
01247 
01248     // DMc - should inform all nodes at the end of a drag too
01249     AttrFillGeometry::EditedFill = NULL;
01250     AttrFillGeometry::DraggedFill = NULL;
01251     AttrFillGeometry::HitList.DeleteAll();
01252     CreateFill = FALSE;
01253 
01254     // Don't forget the AttrGroups List
01255     AttrGroups.DeleteAll();
01256 
01257     // If something went wrong, then fail (if we haven't already)
01258     if (!IsOk && !HaveFailed)
01259     {
01260         FailAndExecute();
01261     }
01262 
01263     // always call end
01264     End();
01265 }
01266 
01267 /********************************************************************************************
01268 
01269 >   BOOL OpEditFill::ApplyNewFill()
01270 
01271     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01272     Created:    14/5/95
01273     Returns:    TRUE if fill was applied ok.
01274     Purpose:    Applies the newly created fill to the selection.
01275 
01276 ********************************************************************************************/
01277 
01278 BOOL OpEditFill::ApplyNewFill()
01279 {
01280     ShouldApplyNewFill = FALSE;
01281 
01282     SelRange* pSel = GetApplication()->FindSelection();
01283     Range SelRng(*pSel);
01284     // >>Webster
01285     BOOL bShowProgress = TRUE;
01286 #ifndef EXCLUDE_FROM_XARALX 
01287     if (GetKeyState(CAMKEY(LBUTTON)) & 0x80)
01288         bShowProgress = FALSE;
01289 #endif
01290     // << Webster
01291 
01292     if (bShowProgress)
01293         BeginSlowJob(-1, FALSE);
01294 
01295     ObjChangeFlags cFlags;
01296     cFlags.Attribute = TRUE;
01297     ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this);
01298     ObjChange.SetRetainCachedData(TRUE);
01299 
01300     if (!pSel->AllowOp(&ObjChange))
01301     {
01302         if (bShowProgress)
01303             EndSlowJob();
01304         return FALSE;
01305     }
01306 
01307     DoStartSelOp(FALSE,FALSE, TRUE,TRUE);
01308 
01309     // Before we apply the attribute to the selection we must localise all attributes
01310     // with the same type that we are going to apply. If we don't do this then the
01311     // tree will be left in an invalid state. 
01312 
01313     AttrTypeSet AttrTypes; 
01314     AttrTypes.AddToSet((GradFill->GetAttributeType())); 
01315 
01316     // Invalidate before
01317     if (!DoInvalidateRegions(&SelRng, 
01318                              GradFill, 
01319                              TRUE,
01320                              NULL,
01321                              FALSE))  //Mutators have to include bounds
01322     {
01323         if (bShowProgress)
01324             EndSlowJob();
01325         return FALSE;
01326     }
01327     
01328     if (!DoLocaliseForAttrChange(&SelRng, &AttrTypes))
01329     {
01330         if (bShowProgress)
01331             EndSlowJob();
01332         return FALSE;
01333     }
01334 
01335     // Apply the new fill to the selection
01336     //GradFill->SetColourRamp (
01337     
01338     //pAttr->SetColourRamp ((ColourRamp*) GradFill->GetFillRamp ());
01339     ApplyToSelection(GradFill, TRUE);
01340 
01341     // Having applied the attributes, we  must now try and factor out the newly 
01342     // applied attributes
01343     if (!DoFactorOutAfterAttrChange(&SelRng, 
01344                                     &AttrTypes))
01345     {
01346         if (bShowProgress)
01347             EndSlowJob();
01348         return FALSE;
01349     }
01350 
01351     // Invalidate after
01352     if (!DoInvalidateRegions(&SelRng, 
01353                              GradFill, 
01354                              TRUE,
01355                              NULL,
01356                              FALSE))  //Mutators have to include bounds
01357     {
01358         if (bShowProgress)
01359             EndSlowJob();
01360         return FALSE;
01361     }
01362 
01363     AttrTypes.DeleteAll();
01364 
01365     if (Document::GetSelected())
01366         Document::GetSelected()->SetModified(TRUE);
01367 
01368     ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this);
01369 ObjChange.SetRetainCachedData(TRUE);    // Tell TextStory not to invalidate everything in OnChildChange
01370     if (!UpdateChangedNodes(&ObjChange))
01371     {
01372         if (bShowProgress)
01373             EndSlowJob();
01374         return FALSE;
01375     }
01376 
01377     // Get the 'actual' fills back, so we can continue to drag them
01378     AttrFillGeometry* pFill = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType());
01379 //  AttrFillGeometry* pFillToEdit = pFill;
01380 
01381     // Build a list of them
01382     while (pFill)
01383     {
01384         BOOL InList = FALSE;
01385 
01386         // We may have already hit this attribute if the selection is
01387         // inside a parent and have common attributes, so we need to
01388         // check and make sure this attribute is NOT in the list
01389         // already.
01390 
01391         if (!AttrFillGeometry::HitList.IsEmpty())
01392         {
01393             ListItem* pItem = AttrFillGeometry::HitList.GetHead();
01394 
01395             while (pItem)
01396             {
01397                 NodeAttributePtrItem* NodePtr = (NodeAttributePtrItem*)pItem;
01398 
01399                 if (NodePtr->NodeAttribPtr == pFill)
01400                 {
01401                     // Ignore this one, we've hit it already
01402                     InList = TRUE;
01403                     break;
01404                 }
01405 
01406                 pItem = AttrFillGeometry::HitList.GetNext(pItem);
01407             }
01408         }
01409 
01410         if (!InList)
01411         {
01412             // Add this to the list of fills to edit
01413             ListItem* pAttr = new NodeAttributePtrItem;
01414             if (pAttr == NULL)
01415             {
01416                 if (bShowProgress)
01417                     EndSlowJob();
01418                 return FALSE;
01419             }
01420 
01421             ERROR3IF(pFill->IsADefaultAttr(), "Default attr in hit list");
01422             ((NodeAttributePtrItem*)pAttr)->NodeAttribPtr = pFill;
01423 
01424             AttrFillGeometry::HitList.AddTail(pAttr);
01425         }
01426 
01427         pFill = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType());
01428     }
01429 
01430     if (bShowProgress)
01431         EndSlowJob();
01432 
01433     return TRUE;
01434 }
01435 
01436 /********************************************************************************************
01437 
01438 >   BOOL OpEditFill::ApplyEditedFill()
01439 
01440     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01441     Created:    14/5/95
01442     Returns:    TRUE if fill was applied ok.
01443     Purpose:    Applies the edited fill, back to the editing objects.
01444 
01445 ********************************************************************************************/
01446 
01447 BOOL OpEditFill::ApplyEditedFill()
01448 {
01449     // Put all the fills back like they were so undo will work
01450     ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead();
01451     while (pAttrPtr != NULL)
01452     {
01453         AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr;
01454 
01455         if (pAttr != GradFill)
01456         {
01457             pAttr->SetStartPoint(FillClone->GetStartPoint());
01458             pAttr->SetEndPoint(FillClone->GetEndPoint());
01459             pAttr->SetEndPoint2(FillClone->GetEndPoint2());
01460             pAttr->SetColourRamp(FillClone->GetColourRamp());
01461         }
01462 
01463         pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr);
01464     }
01465 
01466     SelRange *Selection = Camelot.FindSelection();
01467 
01468     DocRect Bounds;
01469     if (Selection != NULL && Selection->Count()>0)
01470         Bounds = Selection->GetBoundingRect();
01471     else
01472         Bounds = GetDragBlobRect();
01473 
01474     // Give the attribute a bounding rect
01475     GradFill->SetBoundingRect(Bounds);
01476 
01477     BeginSlowJob(-1, FALSE);
01478     AttributeManager::SendMessages = FALSE;
01479 
01480     // Now replace all the edited attributes with the new one
01481     AttributeManager::ReplaceAttributes(GradFill, &AttrFillGeometry::HitList);
01482     AttrFillGeometry::HitList.DeleteAll();
01483 
01484 //  BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); 
01485 //  BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); 
01486     AttributeManager::SendMessages = TRUE;
01487     EndSlowJob();
01488 
01489     return TRUE;
01490 }
01491 
01492 /********************************************************************************************
01493 
01494 >   void OpEditFill::SelectAllBlobs()
01495 
01496     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01497     Created:    14/5/95
01498     Purpose:    Selects all the Start or End fill blobs.
01499 
01500 ********************************************************************************************/
01501 
01502 void OpEditFill::SelectAllBlobs()
01503 {
01504     // It's a 'Select all Blobs' thingy
01505     AttrFillGeometry::LastRenderedStartBlob = DocCoord(0,0);
01506     AttrFillGeometry::LastRenderedEndBlob = DocCoord(0,0);
01507     AttrFillGeometry::LastRenderedEnd2Blob = DocCoord(0,0);
01508 
01509     // Start again at the beginning
01510     AttrFillGeometry* pSelAttr = AttrFillGeometry::FindFirstSelectedAttr(FillClone->GetAttributeType());
01511     while (pSelAttr != NULL)
01512     {
01513         if (pSelAttr)
01514         {
01515             pSelAttr->SelectBlob(DragControl);
01516             pSelAttr->DeselectAllBut(DragControl);
01517         }
01518 
01519         // Move onto the next.
01520         pSelAttr = AttrFillGeometry::FindNextSelectedAttr(FillClone->GetAttributeType());
01521     }                   
01522 }
01523 
01524 /********************************************************************************************
01525 
01526 >   DocRect OpEditFill::GetDragBlobRect()
01527 
01528     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01529     Created:    20/7/94
01530     Returns:    The bounding rect of the NodeGradFill parallelogram
01531     Purpose:    This function finds the bounding rect that the parallelogram of the 
01532                 NodeGradFill we are using for EOR renderings parallelogram fits in.
01533 
01534 ********************************************************************************************/
01535 
01536 DocRect OpEditFill::GetDragBlobRect()
01537 {
01538     return GradFill->GetBlobBoundingRect();
01539 }
01540 
01541 /********************************************************************************************
01542 
01543 >   void OpEditFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01544 
01545     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01546     Created:    20/7/94
01547     Inputs:     Rect - the rect that needs redrawing
01548                 pSpread  - the spread that is being rendered
01549     Purpose:    Renders the GradFill as it will look if the drag were to end. If we failed
01550                 to create the GradFill earlier, it will draw a bounding rect
01551 
01552 ********************************************************************************************/
01553 
01554 void OpEditFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01555 {
01556     if (DontDrawBlobs)
01557         return;
01558 
01559     // If being called from DocView::RenderView, then the spread could be wrong - so
01560     // convert the rectangle if necessary.
01561     if (pSpread != StartSpread)
01562     {
01563         Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo);
01564         Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi);
01565     }
01566 
01567     AttrFillGeometry::EditedFill  = NULL;
01568 
01569     RenderRegion* pRegion = DocView::RenderOnTop(&Rect, StartSpread, UnclippedEOR);
01570     while (pRegion)
01571     {
01572         GradFill->RenderFillBlobs(pRegion);
01573 
01574         // Get the Next render region
01575         pRegion = DocView::GetNextOnTop(&Rect);
01576     }
01577 
01578     AttrFillGeometry::EditedFill  = GradFill;
01579 
01580     // Bodge to stop fill meshes EOR each other out.
01581     AttrFillGeometry::LastRenderedMesh = NULL;
01582 }
01583 
01584 /********************************************************************************************
01585 
01586 >   void OpEditFill::RenderSelectionFillBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread)
01587 
01588     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01589     Created:    9/6/95
01590     Inputs:     Rect - the rect that needs redrawing
01591                 pSpread  - the spread that is being rendered
01592     Purpose:    Renders the GradFill as it will look if the drag were to end. If we failed
01593                 to create the GradFill earlier, it will draw a bounding rect
01594 
01595 ********************************************************************************************/
01596 
01597 void OpEditFill::RenderSelectionFillBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread)
01598 {
01599     // Render all the selection fill blobs
01600     RenderRegion* pRegion = DocView::RenderOnTop(&Rect, pSpread, UnclippedEOR);
01601     while (pRegion)
01602     {
01603         Node* pNode = pSel->FindFirst();
01604     
01605         while (pNode)
01606         {
01607             ((NodeRenderable*)pNode)->RenderAppliedFillBlobs(pRegion);
01608             pNode = pSel->FindNext(pNode);
01609         }
01610 
01611         // Get the Next render region
01612         pRegion = DocView::GetNextOnTop(&Rect);
01613     }
01614 
01615     AttrFillGeometry::LastRenderedMesh = NULL;
01616 }
01617 
01618 /********************************************************************************************
01619 
01620 >   void OpEditFill::RenderInitSelectionBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread)
01621 
01622     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01623     Created:    9/6/95
01624     Inputs:     Rect - the rect that needs redrawing
01625                 pSpread  - the spread that is being rendered
01626     Purpose:    Renders the GradFill as it will look if the drag were to end. If we failed
01627                 to create the GradFill earlier, it will draw a bounding rect
01628 
01629 ********************************************************************************************/
01630 
01631 void OpEditFill::RenderInitSelectionBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread)
01632 {
01633     // EOR on in all the render regions that are still rendering,
01634     // so that the Blob rendering when the region is finished, 
01635     // will put them back
01636     RenderRegionList* pRegionList = GetApplication()->GetRegionList();
01637     
01638     if (!pRegionList->IsEmpty())
01639     {
01640         RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead();  
01641         
01642         while (pRegion)
01643         {
01644             // Check the RenderRegion is for the same spread.
01645             if (pRegion->GetRenderSpread() == pSpread &&
01646                 (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper))
01647             {
01648                 // Render the blobs 'clipped' to this Render Region.
01649                 DocRect ClipRect = pRegion->GetRegionRect();
01650         
01651                 if (ClipRect.IsIntersectedWith(Rect))
01652                 {
01653                     ClipRect = ClipRect.Intersection(Rect);
01654                     RenderSelectionFillBlobs(pSel, ClipRect, pSpread);
01655                 }
01656             }
01657 
01658             // Get the Next render region
01659             pRegion = (RenderRegion*)pRegionList->GetNext(pRegion);
01660         }
01661     }
01662 
01663     // Render the blobs 'Off'
01664     RenderSelectionFillBlobs(pSel, Rect, pSpread);
01665 }
01666 
01667 /********************************************************************************************
01668 
01669 >   void OpEditFill::RenderFinalBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01670 
01671     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01672     Created:    9/6/95
01673     Inputs:     Rect - the rect that needs redrawing
01674                 pSpread  - the spread that is being rendered
01675     Purpose:    Renders the GradFill as it will look if the drag were to end. If we failed
01676                 to create the GradFill earlier, it will draw a bounding rect
01677 
01678 ********************************************************************************************/
01679 
01680 void OpEditFill::RenderFinalBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01681 {
01682     // Render the blobs 'On'
01683     RenderDragBlobs(Rect, pSpread, bSolidDrag);
01684 
01685     // Now EOR off in all the render regions that are still rendering,
01686     // so that the Blob rendering when the region is finished, 
01687     // will put them back
01688     RenderRegionList* pRegionList = GetApplication()->GetRegionList();
01689     
01690     if (!pRegionList->IsEmpty())
01691     {
01692         RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead();  
01693         
01694         while (pRegion)
01695         {
01696             // Check the RenderRegion is for the same spread.
01697             if (pRegion->GetRenderSpread() == pSpread &&
01698                 (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper))
01699             {
01700                 // Render the blobs 'clipped' to this Render Region.
01701                 DocRect ClipRect = pRegion->GetRegionRect();
01702         
01703                 if (ClipRect.IsIntersectedWith(Rect))
01704                 {
01705                     ClipRect = ClipRect.Intersection(Rect);
01706                     RenderDragBlobs(ClipRect, pSpread, bSolidDrag);
01707                 }
01708             }
01709 
01710             // Get the Next render region
01711             pRegion = (RenderRegion*)pRegionList->GetNext(pRegion);
01712         }
01713     } 
01714 }
01715 
01716 /********************************************************************************************
01717 
01718 >   void OpEditFill::RenderInitBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01719 
01720     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01721     Created:    9/6/95
01722     Inputs:     Rect - the rect that needs redrawing
01723                 pSpread  - the spread that is being rendered
01724     Purpose:    Renders the GradFill as it will look if the drag were to end. If we failed
01725                 to create the GradFill earlier, it will draw a bounding rect
01726 
01727 ********************************************************************************************/
01728 
01729 void OpEditFill::RenderInitBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
01730 {
01731     // Now EOR off in all the render regions that are still rendering,
01732     // so that the Blob rendering when the region is finished, 
01733     // will put them back
01734     RenderRegionList* pRegionList = GetApplication()->GetRegionList();
01735     
01736     if (!pRegionList->IsEmpty())
01737     {
01738         RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead();  
01739         
01740         while (pRegion)
01741         {
01742             // Check the RenderRegion is for the same spread.
01743             if (pRegion->GetRenderSpread() == pSpread &&
01744                 (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper))
01745             {
01746                 // Render the blobs 'clipped' to this Render Region.
01747                 DocRect ClipRect = pRegion->GetRegionRect();
01748         
01749                 if (ClipRect.IsIntersectedWith(Rect))
01750                 {
01751                     ClipRect = ClipRect.Intersection(Rect);
01752                     RenderDragBlobs(ClipRect, pSpread, bSolidDrag);
01753                 }
01754             }
01755 
01756             // Get the Next render region
01757             pRegion = (RenderRegion*)pRegionList->GetNext(pRegion);
01758         }
01759     }
01760 
01761     // Render the blobs 'On'
01762     RenderDragBlobs(Rect, pSpread, bSolidDrag);
01763 }
01764 
01765 /********************************************************************************************
01766 
01767 >   BOOL OpEditFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers Mods)
01768 
01769     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01770     Created:    29/1/95
01771     Inputs:     pSpread - pioter to spread mouse is over
01772                 DocPos  - position of mouse in doc
01773                 Mods    - click modifiers
01774     Outputs:    pText   - buffer for StatusLine text
01775     Returns:    FALSE if fails
01776     Purpose:    Gets the status text during the editing of a fill.
01777 
01778 ********************************************************************************************/
01779 
01780 BOOL OpEditFill::GetStatusLineText(String_256* pText, Spread* pSpread, 
01781                                     DocCoord DocPos, ClickModifiers ClickMods)
01782 {
01783     if (GradFill == NULL)
01784     {
01785         *pText="";      // We don't seem to be doing anything at the moment !
01786         return TRUE;
01787     }
01788 
01789     pText->Load(_R(IDS_K_OPGRAD_EDITING));
01790 
01791     String_256 FillName;
01792     if (FillName.Load(GradFill->GetAttrNameID()))
01793     {
01794         // Add the description of the attribute onto the base string
01795         *pText += FillName;
01796 
01797         if (ContinuousEOR)
01798         {
01799             *pText += String_256(_R(IDS_K_OPGRAD_IMMEDIATE));
01800         }
01801         else
01802         {
01803             if (InteractiveDragUpdate)
01804             {
01805                 *pText += String_256(_R(IDS_K_OPGRAD_BACKGROUND));
01806             }
01807             else
01808             {
01809                 *pText += String_256(_R(IDS_K_OPGRAD_NOUPDATE));
01810             }
01811         }
01812 
01813         return TRUE;
01814     }
01815 
01816     return FALSE;
01817 }
01818 
01819 /********************************************************************************************
01820 
01821 >   BOOL OpEditFill::Init()
01822 
01823     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01824     Created:    20/7/94
01825     Returns:    TRUE if it works, FALSE if not
01826     Purpose:    Register this operation into the list of all operations
01827 
01828 ********************************************************************************************/
01829 
01830 BOOL OpEditFill::Init()
01831 {
01832     return (RegisterOpDescriptor(   0, 
01833                                     _R(IDS_EDITFILLOP),
01834                                     CC_RUNTIME_CLASS(OpEditFill), 
01835                                     OPTOKEN_EDITFILL,
01836                                     OpEditFill::GetState,
01837                                     0,  /* help ID */
01838                                     0,
01839                                     0   /* bitmap ID */));
01840 }
01841 
01842 /********************************************************************************************
01843 
01844 >   OpState OpEditFill::GetState(String_256* Description, OpDescriptor*)
01845 
01846     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01847     Created:    20/7/94
01848     Inputs:     Description - the description of the operation
01849     Returns:    The State of the operation
01850     Purpose:    Used for greying etc of an operation.
01851 
01852 ********************************************************************************************/
01853 
01854 OpState OpEditFill::GetState(String_256* Description, OpDescriptor*)
01855 {
01856     OpState Blobby;
01857     
01858     return Blobby;
01859 }
01860 
01861 
01862 
01863 
01864 
01865 
01866 /********************************************************************************************
01867 
01868 >   OpChangeFillColour::OpChangeFillColour()
01869 
01870     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01871     Created:    1/6/94
01872     Purpose:    Dummy Contrustor
01873 
01874 ********************************************************************************************/
01875 
01876 /*
01877 OpChangeFillColour::OpChangeFillColour()
01878 {
01879 }
01880 */
01881 
01882 
01883 
01884 /********************************************************************************************
01885 
01886 >   void OpChangeFillColour::Do(AttrFillGeometry* pGrad, DocColour &StartColour)
01887 
01888     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01889     Created:    20/7/94
01890     Inputs:     -
01891     Purpose:    -
01892 
01893 ********************************************************************************************/
01894 
01895 /*
01896 void OpChangeFillColour::Do( DocColour &StartColour, DocColour &EndColour,
01897                                 AttrFillGeometry* GradFill )
01898 {
01899     DoStartSelOp(FALSE, FALSE);
01900 
01901     ModifyFillColoursAction* ModAction;
01902     
01903     ActionCode Act;
01904     Act = ModifyFillColoursAction::Init(this, &UndoActions, 2, (Action**)(&ModAction));
01905     if (Act == AC_FAIL)
01906     {
01907         FailAndExecute();
01908         End();
01909         return;
01910     }
01911             
01912     DocColour* OldColours;
01913     BOOL* OldSelect;
01914 
01915     ALLOC_WITH_FAIL(OldColours, (DocColour*) CCMalloc(2 * sizeof DocColour), this);
01916     if (!OldColours)
01917     {
01918         FailAndExecute();
01919         End();
01920         return;
01921     }
01922 
01923     // Strangeness
01924     new(&OldColours[0]) DocColour;
01925     new(&OldColours[1]) DocColour;
01926 
01927     OldColours[0] = (GradFill->GetAttributeValue())->Colour;
01928     OldColours[1] = (GradFill->GetAttributeValue())->EndColour;
01929 
01930     ALLOC_WITH_FAIL(OldSelect, (BOOL*) CCMalloc(2 * sizeof BOOL), this);
01931     if (!OldSelect)
01932     {
01933         CCFree(OldColours);
01934         FailAndExecute();
01935         End();
01936         return;
01937     }
01938 
01939     GradFill->GetSelectionState(OldSelect, 2);
01940 
01941     ModAction->StoreChanges(GradFill, OldColours, OldSelect);
01942 
01943     (GradFill->GetAttributeValue())->Colour    = StartColour;
01944     (GradFill->GetAttributeValue())->EndColour = EndColour;
01945 
01946     DocView* pDocView = DocView::GetCurrent();
01947     ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillColours" );
01948 
01949     Node* ParentNode = GradFill->FindParent();
01950     DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE);
01951 
01952     // always call end
01953     End();
01954 }
01955 */
01956 
01957 /********************************************************************************************
01958 
01959 >   BOOL OpChangeFillColour::Init()
01960 
01961     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01962     Created:    20/7/94
01963     Returns:    TRUE if it works, FALSE if not
01964     Purpose:    Register this operation into the list of all operations
01965 
01966 ********************************************************************************************/
01967 
01968 /*
01969 BOOL OpChangeFillColour::Init()
01970 {
01971     return (RegisterOpDescriptor(   0, 
01972                                     _R(IDS_CHANGECOLOP),
01973                                     CC_RUNTIME_CLASS(OpChangeFillColour), 
01974                                     OPTOKEN_CHANGEFILL,
01975                                     OpChangeFillColour::GetState,
01976                                     0,  // help ID 
01977                                     0,
01978                                     0   // bitmap ID
01979                                 ));
01980 }
01981 */
01982 
01983 
01984 
01985 /********************************************************************************************
01986 
01987 >   OpState OpChangeFillColour::GetState(String_256* Description, OpDescriptor*)
01988 
01989     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01990     Created:    20/7/94
01991     Inputs:     Description - the description of the operation
01992     Returns:    The State of the operation
01993     Purpose:    Used for greying etc of an operation.
01994 
01995 ********************************************************************************************/
01996 
01997 /*
01998 OpState OpChangeFillColour::GetState(String_256* Description, OpDescriptor*)
01999 {
02000     OpState Blobby;
02001     
02002     return Blobby;
02003 }
02004 */
02005 
02006 
02007 
02008 
02009 
02010 /********************************************************************************************
02011 
02012 >   OpChangeSelectionColours::OpChangeSelectionColours()
02013 
02014     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02015     Created:    1/6/94
02016     Purpose:    Dummy Contrustor
02017 
02018 ********************************************************************************************/
02019 
02020 /*
02021 OpChangeSelectionColours::OpChangeSelectionColours()
02022 {
02023 }
02024 */
02025 
02026 /********************************************************************************************
02027 
02028 >   void OpChangeSelectionColours::Do(DocColour &NewColour)
02029 
02030     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02031     Created:    20/7/94
02032     Inputs:     -
02033     Purpose:    -
02034 
02035 ********************************************************************************************/
02036 /*
02037 BOOL OpChangeSelectionColours::Do( DocColour &NewColour )
02038 {
02039     DoStartSelOp(FALSE, FALSE);
02040 
02041     BOOL ColourChanged = FALSE;
02042 
02043     Node *SelectedNode = NULL;
02044     SelRange *Selection = Camelot.FindSelection();
02045 
02046     if (Selection != NULL)
02047     {
02048         // Scan though all the Nodes in the Selection (and their Children)
02049         SelectedNode = Selection->FindFirst();
02050         while (SelectedNode != NULL)
02051         {
02052             // Is it a Fill Attribute ?
02053             if ( SelectedNode->IsKindOf(CC_RUNTIME_CLASS(AttrFillGeometry)) )
02054             {
02055                 // Yes, so lets ask if it has any Control Points Selected ?
02056                 if ( ((AttrFillGeometry*)SelectedNode)->ChangeControlPointColour( NewColour, this ) )
02057                 {
02058                     if (!ColourChanged)
02059                     {
02060                         CCRuntimeClass* CurrentAttribGroup = 
02061                             Tool::GetCurrent()->Parent->ToolInfo.CurrentAttributeGroup;
02062 
02063                         Document* CurrentDoc = Document::GetSelected();
02064 
02065                         Node* AttribClone;
02066                         SelectedNode->NodeCopy(&AttribClone); 
02067 
02068                         CurrentDoc->GetAttributeMgr()
02069                             .UpdateCurrentAttribute(CurrentAttribGroup, (NodeAttribute*)AttribClone);
02070                     }
02071 
02072                     Node* ParentNode = SelectedNode->FindParent();
02073                     DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE);
02074 
02075                     // Yep, Someone has changed their colour.
02076                     ColourChanged = TRUE;
02077 
02078                 }
02079             }
02080             SelectedNode = Selection->FindNextAndChild(SelectedNode);
02081         }
02082     }
02083 
02084     if (!ColourChanged)
02085         FailAndExecute();
02086 
02087     // always call end
02088     End();
02089 
02090     return ColourChanged;
02091 }
02092 */
02093 
02094 /********************************************************************************************
02095 
02096 >   BOOL OpChangeSelectionColours::Init()
02097 
02098     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02099     Created:    20/7/94
02100     Returns:    TRUE if it works, FALSE if not
02101     Purpose:    Register this operation into the list of all operations
02102 
02103 ********************************************************************************************/
02104 /*
02105 BOOL OpChangeSelectionColours::Init()
02106 {
02107     return (RegisterOpDescriptor(   0, 
02108                                     _R(IDS_CHANGECOLOP),
02109                                     CC_RUNTIME_CLASS(OpChangeSelectionColours), 
02110                                     OPTOKEN_SELECTCOLS,
02111                                     OpChangeSelectionColours::GetState,
02112                                     0,  // help ID
02113                                     0,
02114                                     0   // bitmap ID
02115                                 ));
02116 }
02117 */
02118 
02119 
02120 
02121 /********************************************************************************************
02122 
02123 >   OpState OpChangeSelectionColours::GetState(String_256* Description, OpDescriptor*)
02124 
02125     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02126     Created:    20/7/94
02127     Inputs:     Description - the description of the operation
02128     Returns:    The State of the operation
02129     Purpose:    Used for greying etc of an operation.
02130 
02131 ********************************************************************************************/
02132 /*
02133 OpState OpChangeSelectionColours::GetState(String_256* Description, OpDescriptor*)
02134 {
02135     OpState Blobby;
02136     
02137     return Blobby;
02138 }
02139 */
02140 
02142 // The ModifyFillCoordsAction class                                                              //
02144 
02145 /********************************************************************************************
02146 
02147 >   ModifyFillCoordsAction::ModifyFillCoordsAction()
02148 
02149     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02150     Created:    24/7/94
02151     Inputs:     -
02152     Outputs:    -
02153     Returns:    -
02154     Purpose:    Constructor for the action to undo fill modification
02155     Errors:     -
02156     SeeAlso:    -
02157 
02158 ********************************************************************************************/
02159 /*
02160 ModifyFillCoordsAction::ModifyFillCoordsAction()
02161 {
02162     ChangedCoords = NULL;
02163     SelectionState = NULL;
02164 }
02165 */
02166 
02167 /********************************************************************************************
02168 
02169 >   ActionCode ModifyFillCoordsAction::Init(  Operation* pOp,
02170                                         ActionList* pActionList,
02171                                         Action** NewAction)
02172 
02173     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02174     Created:    24/7/94
02175     Inputs:     pOp is the pointer to the operation to which this action belongs
02176                 pActionList is the action list to which this action should be added
02177     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to return
02178                 a pointer to the created action
02179     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
02180     Purpose:    This is the function which creates an instance of this action. If there is no room 
02181                 in the undo buffer (which is determined by the base class Init function called within)
02182                 the function will either return AC_NO_RECORD which means the operation can continue, 
02183                 but no undo information needs to be stored, or AC_OK which means the operation should
02184                 continue AND record undo information. If the function returns AC_FAIL, there was not 
02185                 enough memory to record the undo information, and the user has decided not to continue
02186                 with the operation.
02187     Errors:     -
02188     SeeAlso:    Action::Init()
02189 
02190 ********************************************************************************************/
02191 
02192 
02193 /*
02194 ActionCode ModifyFillCoordsAction::Init(  Operation* pOp,
02195                                     ActionList* pActionList,
02196                                     INT32 NumChanged,
02197                                     Action** NewAction)
02198 {
02199     UINT32 ActSize = sizeof(ModifyFillCoordsAction) + (NumChanged * sizeof(DocCoord)) + (NumChanged * sizeof(BOOL));
02200     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ModifyFillCoordsAction), NewAction);
02201 
02202     ((ModifyFillCoordsAction*)*NewAction)->NumChangedCoords = NumChanged;
02203 
02204     if (IsUserName("Will")) TRACE( _T("Creating ModifyFillCoordsAction"));
02205 
02206     return Ac;
02207 }
02208 */
02209 
02210 /********************************************************************************************
02211 
02212 >   void ModifyFillCoordsAction::StoreChanges(DocCoord* Coords, DocColour* Colours)
02213 
02214     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02215     Created:    24/7/94
02216     Inputs:     Coords is a pointer to an array of DocCoords
02217                 Colours is a pointer to an array of DocColours
02218     Outputs:    -
02219     Returns:    -
02220     Purpose:    This function initialises the array pointers in this action. Note that the 
02221                 variable NumElements is initialised in the Init function
02222     Errors:     -
02223     SeeAlso:    -
02224 
02225 ********************************************************************************************/
02226 
02227 /*
02228 void ModifyFillCoordsAction::StoreChanges(AttrFillGeometry* Fill, DocCoord* Coords, BOOL* SelState)
02229 {
02230     ChangedFill = Fill;
02231     ChangedCoords = Coords;
02232     SelectionState = SelState;
02233 }
02234 */
02235 
02236 /********************************************************************************************
02237 
02238 >   ActionCode ModifyFillCoordsAction::Execute()
02239 
02240     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02241     Created:    24/7/94
02242     Inputs:     -
02243     Outputs:    -
02244     Returns:    Action code, one of AC_OK, AC_NORECORD or AC_FAIL.
02245     Purpose:    This is the virtual function that is called when the action is executed
02246                 by the Undo/Redo system. This is the function that actually undoes the 
02247                 ModifyFillCoords action by changing the attribute values, and
02248                 records redo information from the current values.
02249     Errors:     -
02250     SeeAlso:    -
02251 
02252 ********************************************************************************************/
02253 
02254 /*
02255 ActionCode ModifyFillCoordsAction::Execute()
02256 {
02257 
02258     ModifyFillCoordsAction* ModAction;
02259     
02260     ActionCode Act;
02261     Act = ModifyFillCoordsAction::Init(pOperation, pOppositeActLst, NumChangedCoords, (Action**)(&ModAction));
02262     if (Act == AC_FAIL)
02263         return AC_FAIL;
02264 
02265     DocView* pDocView = DocView::GetCurrent();
02266     ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillCoords" );
02267 
02268     DocCoord StartPointTemp = ChangedCoords[0];
02269     DocCoord EndPointTemp   = ChangedCoords[1];
02270     DocCoord EndPoint2Temp  = ChangedCoords[2];
02271 
02272     BOOL StartSelTemp = SelectionState[0];
02273     BOOL EndSelTemp   = SelectionState[1];
02274     BOOL End2SelTemp  = SelectionState[2];
02275 
02276     ChangedCoords[0] = (ChangedFill->GetAttributeValue())->StartPoint;
02277     ChangedCoords[1] = (ChangedFill->GetAttributeValue())->EndPoint;
02278     ChangedCoords[2] = (ChangedFill->GetAttributeValue())->EndPoint2;
02279 
02280     BOOL CurrentSelState[3];
02281     ChangedFill->GetSelectionState(CurrentSelState, 3);
02282 
02283     SelectionState[0] = CurrentSelState[0];
02284     SelectionState[1] = CurrentSelState[1];
02285     SelectionState[2] = CurrentSelState[2];
02286 
02287     CurrentSelState[0] = StartSelTemp;
02288     CurrentSelState[1] = EndSelTemp;
02289     CurrentSelState[2] = End2SelTemp;
02290 
02291     ChangedFill->SetSelectionState(CurrentSelState, 3);
02292 
02293     (ChangedFill->GetAttributeValue())->StartPoint = StartPointTemp;
02294     (ChangedFill->GetAttributeValue())->EndPoint = EndPointTemp;
02295     (ChangedFill->GetAttributeValue())->EndPoint2 = EndPoint2Temp;
02296 
02297     ModAction->StoreChanges(ChangedFill, ChangedCoords, SelectionState);
02298 
02299     ChangedCoords = NULL;
02300     SelectionState = NULL;
02301 
02302     Node* ParentNode = ChangedFill->FindParent();
02303     ((UndoableOperation*)pOperation)->
02304             DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE);
02305 
02306     return Act;
02307 }
02308 
02309 ModifyFillCoordsAction::~ModifyFillCoordsAction()
02310 {
02311     if (ChangedCoords)
02312         CCFree(ChangedCoords);
02313 
02314     if (SelectionState)
02315         CCFree(SelectionState);
02316 }
02317 */
02318 
02320 // The ModifyFillColoursAction class                                                                 //
02322 
02323 /********************************************************************************************
02324 
02325 >   ModifyFillColoursAction::ModifyFillColoursAction()
02326 
02327     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02328     Created:    24/7/94
02329     Inputs:     -
02330     Outputs:    -
02331     Returns:    -
02332     Purpose:    Constructor for the action to undo fill modification
02333     Errors:     -
02334     SeeAlso:    -
02335 
02336 ********************************************************************************************/
02337 /*
02338 ModifyFillColoursAction::ModifyFillColoursAction()
02339 {
02340     ChangedColours = NULL;
02341     SelectionState = NULL;
02342 }
02343 */
02344 
02345 /********************************************************************************************
02346 
02347 >   ActionCode ModifyFillColoursAction::Init(  Operation* pOp,
02348                                         ActionList* pActionList,
02349                                         Action** NewAction)
02350 
02351     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02352     Created:    24/7/94
02353     Inputs:     pOp is the pointer to the operation to which this action belongs
02354                 pActionList is the action list to which this action should be added
02355     Outputs:    NewAction is a pointer to a pointer to an action, allowing the function to return
02356                 a pointer to the created action
02357     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
02358     Purpose:    This is the function which creates an instance of this action. If there is no room 
02359                 in the undo buffer (which is determined by the base class Init function called within)
02360                 the function will either return AC_NO_RECORD which means the operation can continue, 
02361                 but no undo information needs to be stored, or AC_OK which means the operation should
02362                 continue AND record undo information. If the function returns AC_FAIL, there was not 
02363                 enough memory to record the undo information, and the user has decided not to continue
02364                 with the operation.
02365     Errors:     -
02366     SeeAlso:    Action::Init()
02367 
02368 ********************************************************************************************/
02369 
02370 /*
02371 ActionCode ModifyFillColoursAction::Init(  Operation* pOp,
02372                                     ActionList* pActionList,
02373                                     INT32 NumChanged,
02374                                     Action** NewAction)
02375 {
02376     UINT32 ActSize = sizeof(ModifyFillColoursAction) + (NumChanged * sizeof(DocColour)) + (NumChanged * sizeof(BOOL));
02377     ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ModifyFillColoursAction), NewAction);
02378 
02379     ((ModifyFillColoursAction*)*NewAction)->NumChangedColours = NumChanged;
02380 
02381     if (IsUserName("Will")) TRACE( _T("Creating ModifyFillColoursAction"));
02382 
02383     return Ac;
02384 }
02385 */
02386 
02387 /********************************************************************************************
02388 
02389 >   void ModifyFillColoursAction::StoreChanges(AttrFillGeometry* Fill, DocColour* Colours)
02390 
02391     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02392     Created:    24/7/94
02393     Inputs:     Colours is a pointer to an array of DocColours
02394     Outputs:    -
02395     Returns:    -
02396     Purpose:    This function initialises the array pointers in this action. Note that the 
02397                 variable NumElements is initialised in the Init function
02398     Errors:     -
02399     SeeAlso:    -
02400 
02401 ********************************************************************************************/
02402 
02403 /*
02404 void ModifyFillColoursAction::StoreChanges(AttrFillGeometry* Fill, DocColour* Colours, BOOL* SelState)
02405 {
02406     ChangedFill = Fill;
02407     ChangedColours = Colours;
02408     SelectionState = SelState;
02409 }
02410 */
02411 
02412 /********************************************************************************************
02413 
02414 >   ActionCode ModifyFillColoursAction::Execute()
02415 
02416     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02417     Created:    24/7/94
02418     Inputs:     -
02419     Outputs:    -
02420     Returns:    Action code, one of AC_OK, AC_NORECORD or AC_FAIL.
02421     Purpose:    This is the virtual function that is called when the action is executed
02422                 by the Undo/Redo system. This is the function that actually undoes the 
02423                 ModifyFillColours action by changing the attribute values, and
02424                 records redo information from the current values.
02425     Errors:     -
02426     SeeAlso:    -
02427 
02428 ********************************************************************************************/
02429 
02430 /*
02431 ActionCode ModifyFillColoursAction::Execute()
02432 {
02433 
02434     ModifyFillColoursAction* ModAction;
02435     
02436     ActionCode Act;
02437     Act = ModifyFillColoursAction::Init(pOperation, pOppositeActLst, NumChangedColours, (Action**)(&ModAction));
02438     if (Act == AC_FAIL)
02439         return AC_FAIL;
02440 
02441     DocView* pDocView = DocView::GetCurrent();
02442     ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillColours" );
02443 
02444     DocColour StartTemp = ChangedColours[0];
02445     DocColour EndTemp   = ChangedColours[1];
02446 
02447     BOOL StartSelTemp = SelectionState[0];
02448     BOOL EndSelTemp   = SelectionState[1];
02449 
02450     ChangedColours[0] = (ChangedFill->GetAttributeValue())->Colour;
02451     ChangedColours[1] = (ChangedFill->GetAttributeValue())->EndColour;
02452 
02453     BOOL CurrentSelState[2];
02454     ChangedFill->GetSelectionState(CurrentSelState, 2);
02455 
02456     SelectionState[0] = CurrentSelState[0];
02457     SelectionState[1] = CurrentSelState[1];
02458 
02459     CurrentSelState[0] = StartSelTemp;
02460     CurrentSelState[1] = EndSelTemp;
02461 
02462     ChangedFill->SetSelectionState(CurrentSelState, 2);
02463 
02464     (ChangedFill->GetAttributeValue())->Colour = StartTemp;
02465     (ChangedFill->GetAttributeValue())->EndColour = EndTemp;
02466 
02467     ModAction->StoreChanges(ChangedFill, ChangedColours, SelectionState);
02468 
02469     ChangedColours = NULL;
02470     SelectionState = NULL;
02471 
02472     Node* ParentNode = ChangedFill->FindParent();
02473     ((UndoableOperation*)pOperation)->
02474             DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE);
02475 
02476     return Act;
02477 }
02478 
02479 ModifyFillColoursAction::~ModifyFillColoursAction()
02480 {
02481     if (ChangedColours)
02482     {
02483         for (INT32 i=0; i < NumChangedColours; i++)
02484         {
02485             ChangedColours[i].~DocColour();
02486         }
02487         CCFree(ChangedColours);
02488     }
02489 
02490     if (SelectionState)
02491         CCFree(SelectionState);
02492 }
02493 */
02494 
02495 
02496 /********************************************************************************************
02497 
02498 >   OpCreateFill::OpCreateFill()
02499 
02500     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02501     Created:    20/7/94
02502     Purpose:    Dummy Constructor
02503 
02504 ********************************************************************************************/
02505 
02506 OpCreateFill::OpCreateFill()
02507 {
02508     GradFill = NULL;
02509 }
02510 
02511 /********************************************************************************************
02512 
02513 >   void OpCreateFill::DoDrag( AttrFillGeometry* Fill, Spread* pSpread, DocCoord Anchor)
02514 
02515     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02516     Created:    20/7/94
02517     Inputs:     pSpread - The Spread the drag is starting on
02518                 Anchor - The coord of the start of the drag
02519     Purpose:    Starts the drag and records the initial position of the mouse etc.
02520                 It also snaps the start position to the grid if it needs to.
02521 
02522 ********************************************************************************************/
02523 
02524 void OpCreateFill::DoDrag( AttrFillGeometry* Fill, Spread* pSpread, DocCoord Anchor)
02525 {
02526     DocView::SnapSelected(pSpread, &Anchor);
02527 
02528     // We had better take a note of the starting point of the drag
02529     StartSpread = pSpread;
02530     StartPoint = Anchor;
02531     
02532     // init the old and the new mouse position needed in drawing the rubber box
02533     LastMousePosition = Anchor;
02534 
02535     GradFill = Fill;
02536 
02537     GradFill->SetStartPoint(&Anchor);
02538     GradFill->SetEndPoint(&Anchor);
02539     
02540     if (GradFill->GetEndPoint2() != NULL)
02541         GradFill->SetEndPoint2(&Anchor);
02542 
02543     // Remove other fill meshs whilst we are dragging   
02544     BlobStyle MyBlobs;
02545     MyBlobs.Fill = TRUE;
02546     (Camelot.GetBlobManager())->RemoveInterest(MyBlobs);
02547 
02548     AttrFillGeometry::DraggedFill = GradFill;
02549 
02550     // And tell the Dragging system that we need drags to happen
02551 //  StartDrag( DRAGTYPE_AUTOSCROLL );
02552     StartDrag(DRAGTYPE_AUTOSCROLL, NULL, NULL, TRUE, TRUE);         // We support solid dragging
02553 }   
02554 
02555 /********************************************************************************************
02556 
02557 >   void OpCreateFill::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods,
02558                                             Spread* pSpread, BOOL bSolidDrag)
02559 
02560     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02561     Created:    20/7/94
02562     Inputs:     PointerPos - The current mouse position
02563                 ClickMods - Which modifier keys were being pressed at the time
02564                 pSpread - The Spread that the mouse is over now
02565     Purpose:    re-draws the drag blobs as the mouse moves about the document.
02566 
02567 ********************************************************************************************/
02568 
02569 void OpCreateFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag)
02570 {
02571     // Snap the actual mouse position to the grid if needed
02572     DocView::SnapSelected(pSpread, &PointerPos);
02573 
02574     // if we need to constrain the drag, then make our GradFill into a circle
02575     if (ClickMods.Constrain)
02576         DocView::ConstrainToAngle(StartPoint, PI/4, &PointerPos);
02577 
02578     // If the mouse is in a different position then do something
02579     if (PointerPos != LastMousePosition)
02580     {
02581         // First Rub out the old Drag blobs
02582         DocRect BoundingRect = GetDragBlobRect();
02583         RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag);
02584 
02585         // Make sure that the coords are relative to the coorect spread
02586         if (pSpread != StartSpread)
02587             PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos);
02588 
02589         // Update the last mouse position and re-calc the bounding rect
02590         LastMousePosition = PointerPos;
02591 
02592         DocCoord Start  = StartPoint;
02593         DocCoord End    = PointerPos;
02594         DocCoord End2   = MakeLineAtAngle(StartPoint, PointerPos);
02595 
02596         // If we are creating a Bitmap Fill, then try to create it 
02597         // with the correct aspect ratio 
02598         if (GradFill->GetBitmap() != NULL)
02599         {
02600             BitmapInfo Info;
02601             KernelBitmap* Bmp = GradFill->GetBitmap();
02602 
02603             AttrFillGeometry* Attr = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType());
02604 
02605             while (Attr != NULL)
02606             {
02607                 if (Attr->GetBitmap() != NULL)
02608                 {
02609                     Bmp = Attr->GetBitmap();
02610                     break; 
02611                 }
02612                 
02613                 Attr = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType());
02614             }
02615 
02616             // if possible we base our default size on the bitmaps preferred size
02617             if (Bmp && Bmp->ActualBitmap)
02618             {
02619                 if (Bmp->ActualBitmap->GetInfo( &Info ))
02620                 {
02621                     INT32 BitmapWidth  = Info.RecommendedWidth;
02622                     INT32 BitmapHeight = Info.RecommendedHeight;
02623 
02624                     // Calculate the spect ratio of the bitmap
02625                     double Ratio = double(BitmapHeight)/double(BitmapWidth);
02626 
02627                     End2 = MakeLineAtAngle(Start, End, 90, INT32(Start.Distance(End) * Ratio));
02628                 }
02629             }
02630         }
02631 
02632         if (GradFill->IsKindOf(CC_RUNTIME_CLASS(AttrBitmapFill)))
02633         {
02634             // Get the Real Control Points
02635             // (The user only sees the virtual points)
02636             GetBitmapRealPoints(Start, End, End2,
02637                                 &Start, &End, &End2);
02638         }
02639 
02640         // Update the new Fill's points
02641         GradFill->SetStartPoint(&Start);
02642         GradFill->SetEndPoint(&End);
02643         if (GradFill->GetEndPoint2() != NULL)
02644             GradFill->SetEndPoint2(&End2);
02645 
02646         BoundingRect = GetDragBlobRect();
02647 
02648         // Draw the Fill Mesh
02649         RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag);
02650     }
02651 }
02652 
02653 /********************************************************************************************
02654 
02655 >   void OpCreateFill::DragFinished(DocCoord PointerPos, ClickModifiers, Spread* pSpread,
02656                                         BOOL Success, BOOL bSolidDrag)
02657 
02658     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02659     Created:    1/6/94
02660     Inputs:     PointerPos - The position of the mouse at the end of the drag
02661                 ClickModifiers - Not Used
02662                 pSpread - The spread that the drag ended over
02663                 Success - TRUE if the drag worked
02664     Purpose:    Called when the drag ends. This function rubs out the drag blobs and then
02665                 builds a new GradFill Node and adds it to the end of the tree.
02666 
02667 ********************************************************************************************/
02668 
02669 void OpCreateFill::DragFinished(DocCoord PointerPos, ClickModifiers, Spread* pSpread, BOOL Success, BOOL bSolidDrag)
02670 {
02671     // Put the hour glass up as we have to
02672     BeginSlowJob();
02673 
02674     DocRect DragRect;
02675 
02676     // First Rub out the old Drag blobs
02677     DocRect BoundingRect = GetDragBlobRect();
02678     RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag);
02679 
02680     // Get the scaled pixel size for the view
02681     FIXED16 ScaledPixelWidth,
02682             ScaledPixelHeight;
02683     GetWorkingView()->GetScaledPixelSize(&ScaledPixelWidth, &ScaledPixelHeight);
02684 
02685     INT32 PixWidth  = ScaledPixelWidth.MakeLong();
02686     INT32 PixHeight = ScaledPixelHeight.MakeLong();
02687 
02688     // Flag to say if everything has worked
02689     BOOL IsOk = FALSE;
02690 
02691     // End the Drag                             
02692     if (!EndDrag())
02693         goto EndOperation;
02694 
02695     if (PointerPos == StartPoint)
02696         goto EndOperation;
02697 
02698     if (*GradFill->GetStartPoint() == *GradFill->GetEndPoint())
02699         goto EndOperation;
02700 
02701     DragRect.IncludePoint(StartPoint);
02702     DragRect.IncludePoint(PointerPos);
02703 
02704     if (DragRect.Width() < PixWidth*2 && DragRect.Height() < PixHeight*2)
02705     {
02706         TRACEUSER( "Will", _T("Drag rect too small\n"));
02707         goto EndOperation;
02708     }
02709 
02710     // if the drag was a sucess then make a grad fill
02711     if (Success && (!DragRect.IsEmpty()))
02712     {
02713         if (GradFill != NULL)
02714         {               
02715              CCRuntimeClass* CurrentAttribGroup = 
02716                 Tool::GetCurrent()->Parent->m_ToolInfo.CurrentAttributeGroup;
02717 
02718             Document* CurrentDoc = Document::GetSelected();
02719 
02720             AttrFillGeometry* CurrentAttr = (AttrFillGeometry*)(CurrentDoc->GetAttributeMgr()
02721                     .GetCurrentAttribute(CurrentAttribGroup, GradFill->GetAttributeType()));
02722 
02723             // Is there a current attribute of this type ?
02724             if (CurrentAttr != NULL)
02725             {
02726                 BOOL Locked = ((FillGeometryAttribute*)GradFill->GetAttributeValue())->IsAspectLocked();
02727                 
02728                 // Use the current Attributes Colours
02729                 *((FillGeometryAttribute*)GradFill->GetAttributeValue()) = 
02730                             *((FillGeometryAttribute*)CurrentAttr->GetAttributeValue());
02731 
02732                 ((FillGeometryAttribute*)GradFill->GetAttributeValue())->SetAspectLock(Locked);
02733 
02734                 DocCoord Start  = StartPoint;
02735                 DocCoord End    = LastMousePosition;
02736                 DocCoord End2   = MakeLineAtAngle(StartPoint, LastMousePosition);
02737 
02738                 // Ensure the Aspect ratio is correct for a new
02739                 // bitmap fill
02740                 if (GradFill->GetBitmap() != NULL)
02741                 {
02742                     BitmapInfo Info;
02743                     KernelBitmap* Bmp = GradFill->GetBitmap();
02744 
02745                     AttrFillGeometry* Attr = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType());
02746 
02747                     while (Attr != NULL)
02748                     {
02749                         if (Attr->GetBitmap() != NULL)
02750                         {
02751                             Bmp = Attr->GetBitmap();
02752                             break; 
02753                         }
02754                 
02755                         Attr = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType());
02756                     }
02757 
02758                     // if possible we base our default size on the bitmaps preferred size
02759                     if (Bmp && Bmp->ActualBitmap)
02760                     {
02761                         if (Bmp->ActualBitmap->GetInfo( &Info ))
02762                         {
02763                             INT32 BitmapWidth  = Info.RecommendedWidth;
02764                             INT32 BitmapHeight = Info.RecommendedHeight;
02765 
02766                             // Calculate the spect ratio of the bitmap
02767                             double Ratio = double(BitmapHeight)/double(BitmapWidth);
02768 
02769                             End2 = MakeLineAtAngle(Start, End, 90, INT32(Start.Distance(End) * Ratio));
02770                         }
02771                     }
02772                 }
02773 
02774                 if (GradFill->IsKindOf(CC_RUNTIME_CLASS(AttrBitmapFill)))
02775                 {
02776                     // Get the Real Control Points
02777                     // (The user only sees the virtual points)
02778                     GetBitmapRealPoints(Start, End, End2,
02779                                         &Start, &End, &End2);
02780                 }
02781 
02782                 // Set the new fills points
02783                 GradFill->SetStartPoint(&Start);
02784                 GradFill->SetEndPoint(&End);
02785 
02786                 if (GradFill->GetEndPoint2() != NULL)
02787                     GradFill->SetEndPoint2(&End2);
02788             }
02789             else
02790             {
02791                 // No current fill so use default colours
02792                 *GradFill->GetStartColour() = DocColour(COLOUR_WHITE);
02793                 *GradFill->GetEndColour()   = DocColour(COLOUR_BLACK);
02794             }
02795 
02796             if (GradFill->GetAttributeType() == CC_RUNTIME_CLASS(AttrTranspFillGeometry))
02797             {
02798                 UINT32 Start = 0;
02799                 UINT32 End = 255;
02800     
02801                 GradFill->SetStartTransp(&Start);
02802                 GradFill->SetEndTransp(&End);
02803             }
02804 
02805             SelRange *Selection = Camelot.FindSelection();
02806             DocRect Bounds;
02807             if (Selection != NULL && Selection->Count()>0)
02808                 Bounds = Selection->GetBoundingRect();
02809             else
02810                 Bounds = GetDragBlobRect();
02811 
02812             // Give the attribute a bounding rect
02813             GradFill->SetBoundingRect(Bounds);
02814 
02815             GradFill->SelectBlob(FILLCONTROL_ENDPOINT);
02816 
02817             // And pass it to the attribute manager to sort the rest out
02818             AttributeManager::SendMessages = FALSE;
02819             AttributeManager::AttributeSelected(GradFill); 
02820             AttributeManager::SendMessages = TRUE;
02821 
02822             IsOk = TRUE;
02823         }
02824     }
02825 
02826 EndOperation:
02827     // If something went wrong, then fail
02828     if (!IsOk)
02829     {
02830         delete GradFill;
02831         FailAndExecute();
02832     }
02833 
02834     AttrFillGeometry::DraggedFill = NULL;
02835 
02836     // Turn the fill meshes back on
02837     BlobStyle MyBlobs;
02838     MyBlobs.Fill = TRUE;
02839     (Camelot.GetBlobManager())->AddInterest(MyBlobs);
02840 
02841     AttrFillGeometry::SelectionCount = AttrFillGeometry::CountSelectionControlPoints();
02842     // We've probably changed a Colour
02843     BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); 
02844     // and the Attrib may have changed shape or summit. Who knows ?
02845     BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); 
02846 
02847     // always call end
02848     End();
02849 }
02850 
02851 /********************************************************************************************
02852 
02853 >   void OpCreateFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
02854 
02855     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02856     Created:    20/7/94
02857     Inputs:     Rect - The rectangle that needs to be redrawn
02858                 pSpread - the spread that it should be drawn on
02859     Purpose:    Renders the EOR blobs during a drag. It can also get called from OnDraw()
02860 
02861 ********************************************************************************************/
02862 
02863 void OpCreateFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag)
02864 {
02865     // If being called from DocView::RenderView, then the spread could be wrong - so
02866     // convert the rectangle if necessary.
02867     if (pSpread != StartSpread)
02868     {
02869         Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo);
02870         Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi);
02871     }
02872 
02873     RenderRegion* pRegion = DocView::RenderOnTop(&Rect, StartSpread, UnclippedEOR);
02874     while (pRegion)
02875     {
02876         GradFill->RenderFillBlobs(pRegion);
02877 
02878         // Get the Next render region
02879         pRegion = DocView::GetNextOnTop(&Rect);
02880     }
02881 
02882     // Bodge to stop fill meshes EOR each other out.
02883     AttrFillGeometry::LastRenderedMesh = NULL;
02884 }
02885 
02886 /********************************************************************************************
02887 
02888 >   DocRect OpCreateFill::GetDragBlobRect()
02889 
02890     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02891     Created:    20/7/94
02892     Returns:    The bounding rect of the NodeGradFill parallelogram
02893     Purpose:    This function finds the bounding rect that the parallelogram of the 
02894                 NodeGradFill we are using for EOR renderings parallelogram fits in.
02895 
02896 ********************************************************************************************/
02897 
02898 DocRect OpCreateFill::GetDragBlobRect()
02899 {
02900     return GradFill->GetBlobBoundingRect();
02901 }
02902 
02903 /********************************************************************************************
02904 
02905 >   BOOL OpCreateFill::Init()
02906 
02907     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02908     Created:    20/7/94
02909     Returns:    TRUE if the operation was initialised sucessfully
02910     Purpose:    Declare the operations OpDescriptor
02911 
02912 ********************************************************************************************/
02913 
02914 BOOL OpCreateFill::Init()
02915 {
02916     return (RegisterOpDescriptor(   0, 
02917                                     _R(IDS_CREATEFILLOP),
02918                                     CC_RUNTIME_CLASS(OpCreateFill), 
02919                                     OPTOKEN_GRADFILL,
02920                                     OpCreateFill::GetState,
02921                                     0,  /* help ID */
02922                                     0,
02923                                     0   /* bitmap ID */));
02924 }
02925 
02926 /********************************************************************************************
02927 
02928 >   OpState OpCreateFill::GetState(String_256* Description, OpDescriptor*)
02929 
02930     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02931     Created:    20/7/94
02932     Inputs:     Description - The Description of the operation
02933     Purpose:    Finds out the operations current state for greying etc in menus
02934 
02935 ********************************************************************************************/
02936 
02937 OpState OpCreateFill::GetState(String_256* Description, OpDescriptor*)
02938 {
02939     OpState Blobby;
02940     
02941     return Blobby;
02942 }
02943 
02944 /********************************************************************************************
02945 
02946 >   BOOL OpCreateFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers Mods)
02947 
02948     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
02949     Created:    29/1/95
02950     Inputs:     pSpread - pioter to spread mouse is over
02951                 DocPos  - position of mouse in doc
02952                 Mods    - click modifiers
02953     Outputs:    pText   - buffer for StatusLine text
02954     Returns:    FALSE if fails
02955     Purpose:    Gets the status text during the creation of a fill.
02956 
02957 ********************************************************************************************/
02958 
02959 BOOL OpCreateFill::GetStatusLineText(String_256* pText, Spread* pSpread, 
02960                                         DocCoord DocPos, ClickModifiers ClickMods)
02961 {
02962     if (GradFill == NULL)
02963     {
02964         *pText="";      // We don't seem to be doing anything at the moment !
02965         return TRUE;
02966     }
02967 
02968     pText->Load(_R(IDS_K_OPGRAD_CREATENEW));
02969 
02970     String_256 FillName;
02971     if (FillName.Load(GradFill->GetAttrNameID()))
02972     {
02973         // Add the description of the attribute onto the base string
02974         *pText += FillName;
02975 
02976         return TRUE;
02977     }
02978 
02979     return FALSE;
02980 }
02981 
02982 
02983 #endif    // STANDALONE
02984 
02985 
02986 
02987 
02988 
02989 
02990 /********************************************************************************************   
02991 
02992     These are some Global Functions, used by the Grad Fill stuff to render it's fill mesh
02993     bits.  They are only tempory.
02994 
02995 ********************************************************************************************/
02996 
02997 /********************************************************************************************
02998 
02999 >   void MakeMeshArrow(Path* ArrowPath, DocCoord &Start, DocCoord &End, DocCoord* LineEnd)
03000 
03001     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03002     Created:    21/07/94
03003     Inputs:     -
03004     Returns:    -
03005     Purpose:    Makes an Arrow for the Fill Mesh.
03006                 This is a Tempory Function, used until ArrowHeads work properly. 
03007 
03008 ********************************************************************************************/
03009 
03010 void MakeMeshArrow(Path* ArrowPath, DocCoord &Start, DocCoord &End, DocCoord* LineEnd)
03011 {
03012 #if !defined(EXCLUDE_FROM_RALPH)
03013     // First we Must caculate the angle of the arrow from the line angle
03014     const INT32 dx = Start.x - End.x;
03015     const INT32 dy = Start.y - End.y;
03016 
03017     if ( (dy==0) && (dx==0) )
03018     {
03019         ArrowPath->InsertMoveTo(End);
03020         ArrowPath->InsertLineTo(End);
03021         ArrowPath->TryToClose();
03022 
03023         *LineEnd = End;
03024         return;                             // draw nothing as too small
03025     }
03026 
03027     double ArrowAngle;
03028 
03029     if ( (dx>=0) && (dy>=0) )
03030         ArrowAngle = PI - atan2( (double)dx, (double)dy );
03031     else if ( (dx<0) && (dy>=0) )
03032         ArrowAngle = -PI + atan2( (double)-dx, (double)dy );
03033     else if ( (dx<0) && (dy<0) )
03034         ArrowAngle = -atan2( (double)-dx, (double)-dy );
03035     else if ( (dx>=0) && (dy<0) )
03036         ArrowAngle = atan2( (double)dx, (double)-dy );
03037     else
03038     {
03039         TRACE( _T("invalid angle on arrowhead\n"));     // this should be impossible
03040         return;
03041     }
03042 
03043     ANGLE RotateAngle = 360 * (ArrowAngle / (2*PI));    // Convert from radians to degrees
03044 
03045     Matrix ArrowMatrix = Matrix(RotateAngle);           // Rotate it
03046     ArrowMatrix *= Matrix(End);                         // Move it to the line point
03047 
03048     const UINT32 PointCount = 4;
03049     Coord ArrowPts[PointCount];
03050 
03051     INT32 BlobSize = (Camelot.GetBlobManager())->GetBlobSize();
03052 
03053     INT32 ArrowWidth  = (BlobSize  * 3)/2;
03054     INT32 ArrowHeight = (BlobSize  * 3)/2;
03055     
03056     ArrowPts[0] = Coord(             0,            0);
03057     ArrowPts[1] = Coord( -ArrowWidth/2, -ArrowHeight);
03058     ArrowPts[2] = Coord(  ArrowWidth/2, -ArrowHeight);
03059     ArrowPts[3] = Coord(             0, -ArrowHeight);
03060 
03061     ArrowMatrix.transform(ArrowPts, PointCount);
03062 
03063     ArrowPath->InsertMoveTo(DocCoord(ArrowPts[0].x, ArrowPts[0].y));
03064     ArrowPath->InsertLineTo(DocCoord(ArrowPts[1].x, ArrowPts[1].y));
03065     ArrowPath->InsertLineTo(DocCoord(ArrowPts[2].x, ArrowPts[2].y));
03066     ArrowPath->InsertLineTo(DocCoord(ArrowPts[0].x, ArrowPts[0].y));
03067     ArrowPath->TryToClose();
03068 
03069     *LineEnd = DocCoord(ArrowPts[3].x, ArrowPts[3].y);
03070 #endif
03071 }
03072 
03073 /********************************************************************************************
03074 
03075 >   void MakeMeshDottedLine(Path* ArrowPath, DocCoord &Start, DocCoord &End)
03076 
03077     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03078     Created:    22/07/94
03079     Inputs:     -
03080     Returns:    -
03081     Purpose:    Makes a Dotted Line for the Fill Mesh. 
03082                 This is a Tempory Function, used until Stroke Providers work properly. 
03083 
03084 ********************************************************************************************/
03085 
03086 void MakeMeshDottedLine(Path* ArrowPath, DocCoord &Start, DocCoord &End)
03087 {
03088 #if !defined(EXCLUDE_FROM_RALPH)
03089     ArrowPath->InsertMoveTo(Start);
03090     ArrowPath->InsertLineTo(End);
03091 #endif
03092 }
03093 
03094 /********************************************************************************************
03095 
03096 >   void MakeMeshSemiCircle(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord ArrowPoint)
03097 
03098     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03099     Created:    22/07/94
03100     Inputs:     -
03101     Returns:    -
03102     Purpose:    Make a Semi-Circle for the Conical Fill Mesh. 
03103 
03104 ********************************************************************************************/
03105 
03106 void MakeMeshSemiCircle(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord ArrowPoint)
03107 {
03108 #if !defined(EXCLUDE_FROM_RALPH)
03109     End  = MakeLineAtAngle(Start, End, 180);
03110     DocCoord End2 = MakeLineAtAngle(Start, End);
03111 
03112     ANGLE LineAngle = CalcLineAngle(Start, End);
03113     Matrix Rotate = Matrix(-LineAngle);
03114 
03115     End.translate(-Start.x, -Start.y);
03116     End2.translate(-Start.x, -Start.y);
03117 
03118     Rotate.transform(&End);
03119     Rotate.transform(&End2);
03120 
03121     End.translate(Start.x, Start.y);
03122     End2.translate(Start.x, Start.y);
03123 
03124     DocCoord Parallel[4];
03125 
03126     INT32 dx = End2.x - Start.x;
03127     INT32 dy = Start.y - End.y;
03128 
03129     // Copy the rectangle into the parallelogram
03130     Parallel[0] = DocCoord(Start.x - dx, Start.y - dy);
03131     Parallel[1] = DocCoord(Start.x + dx, Start.y - dy);
03132     Parallel[2] = DocCoord(Start.x + dx, Start.y + dy);
03133     Parallel[3] = DocCoord(Start.x - dx, Start.y + dy);
03134 
03135     // Get an array to put the 12 different coords needed to specify an ellipse
03136     DocCoord NewCoords[12];
03137 
03138     // Calculate the 3 coordinates along each side of the parallelogram
03139     CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]);
03140     CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]);
03141     CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]);
03142     CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]);
03143 
03144     Matrix Rotate2 = Matrix(LineAngle);
03145 
03146     for (INT32 i=0; i<12; i++)
03147     {
03148         NewCoords[i].translate(-Start.x, -Start.y);
03149         Rotate2.transform(&NewCoords[i]);
03150         NewCoords[i].translate(Start.x, Start.y);
03151     }
03152 
03153     // build a path
03154     EllipsePath->ClearPath();
03155     EllipsePath->FindStartOfPath();
03156             
03157     // Start at bottom left corner
03158 //  EllipsePath->InsertMoveTo(NewCoords[0]);
03159 //  EllipsePath->InsertCurveTo(NewCoords[1], NewCoords[2], NewCoords[3]);
03160 //  EllipsePath->InsertCurveTo(NewCoords[4], NewCoords[5], NewCoords[6]);
03161 
03162     EllipsePath->InsertMoveTo(ArrowPoint);
03163     EllipsePath->InsertCurveTo(NewCoords[7], NewCoords[8], NewCoords[9]);
03164     EllipsePath->InsertCurveTo(NewCoords[10], NewCoords[11], NewCoords[0]);
03165 
03166     // Close the path properly
03167 //  EllipsePath->CloseSubPath();
03168 #endif
03169 }
03170 
03171 /********************************************************************************************
03172 
03173 >   DocRect GetMeshSemiCircleBounds(DocCoord Start, DocCoord End, DocCoord EndPoint)
03174 
03175     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03176     Created:    22/07/94
03177     Inputs:     -
03178     Returns:    -
03179     Purpose:    Gets the Bounding rect of the Conical Fill Mesh. 
03180 
03181 ********************************************************************************************/
03182 
03183 DocRect GetMeshSemiCircleBounds(DocCoord Start, DocCoord End, DocCoord EndPoint)
03184 {
03185 #if !defined(EXCLUDE_FROM_RALPH)
03186     DocCoord End2 = MakeLineAtAngle(Start, End);
03187 
03188     ANGLE LineAngle = CalcLineAngle(Start, End);
03189     Matrix Rotate = Matrix(-LineAngle);
03190 
03191     End.translate(-Start.x, -Start.y);
03192     End2.translate(-Start.x, -Start.y);
03193 
03194     Rotate.transform(&End);
03195     Rotate.transform(&End2);
03196 
03197     End.translate(Start.x, Start.y);
03198     End2.translate(Start.x, Start.y);
03199 
03200     DocCoord Parallel[4];
03201 
03202     INT32 dx = End2.x - Start.x;
03203     INT32 dy = Start.y - End.y;
03204 
03205     // Copy the rectangle into the parallelogram
03206     Parallel[0] = DocCoord(Start.x - dx, Start.y - dy);
03207     Parallel[1] = DocCoord(Start.x + dx, Start.y - dy);
03208     Parallel[2] = DocCoord(Start.x + dx, Start.y + dy);
03209     Parallel[3] = DocCoord(Start.x - dx, Start.y + dy);
03210 
03211     // Get an array to put the 12 different coords needed to specify an ellipse
03212     DocCoord NewCoords[12];
03213 
03214     // Calculate the 3 coordinates along each side of the parallelogram
03215     CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]);
03216     CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]);
03217     CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]);
03218     CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]);
03219 
03220     Matrix Rotate2 = Matrix(LineAngle);
03221 
03222     for (INT32 i=0; i<12; i++)
03223     {
03224         NewCoords[i].translate(-Start.x, -Start.y);
03225         Rotate2.transform(&NewCoords[i]);
03226         NewCoords[i].translate(Start.x, Start.y);
03227     }
03228 
03229     DocRect Bounds = DocRect(EndPoint, EndPoint);
03230     Bounds.IncludePoint(NewCoords[6]);
03231     Bounds.IncludePoint(NewCoords[7]);
03232     Bounds.IncludePoint(NewCoords[8]);
03233     Bounds.IncludePoint(NewCoords[9]);
03234     Bounds.IncludePoint(NewCoords[10]);
03235     Bounds.IncludePoint(NewCoords[11]);
03236 
03237     return Bounds;
03238 #else
03239     return DocRect(0,0,0,0);
03240 #endif
03241 }
03242 
03243 /********************************************************************************************
03244 
03245 >   void MakeMeshEllipse(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord End2)
03246 
03247     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03248     Created:    22/07/94
03249     Inputs:     -
03250     Returns:    -
03251     Purpose:    Makes an Elliptical Fill Mesh. 
03252 
03253 ********************************************************************************************/
03254 
03255 void MakeMeshEllipse(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord End2)
03256 {
03257 #if !defined(EXCLUDE_FROM_RALPH)
03258     if (End2 == DocCoord(0,0))
03259     {
03260         End2 = MakeLineAtAngle(Start, End);
03261     }
03262 
03263     ANGLE LineAngle = CalcLineAngle(Start, End);
03264     Matrix Rotate = Matrix(-LineAngle);
03265 
03266     End.translate(-Start.x, -Start.y);
03267     End2.translate(-Start.x, -Start.y);
03268 
03269     Rotate.transform(&End);
03270     Rotate.transform(&End2);
03271 
03272     End.translate(Start.x, Start.y);
03273     End2.translate(Start.x, Start.y);
03274 
03275     DocCoord Parallel[4];
03276 
03277     INT32 dx = End2.x - Start.x;
03278     INT32 dy = Start.y - End.y;
03279 
03280     // Copy the rectangle into the parallelogram
03281     Parallel[0] = DocCoord(Start.x - dx, Start.y - dy);
03282     Parallel[1] = DocCoord(Start.x + dx, Start.y - dy);
03283     Parallel[2] = DocCoord(Start.x + dx, Start.y + dy);
03284     Parallel[3] = DocCoord(Start.x - dx, Start.y + dy);
03285 
03286     // Get an array to put the 12 different coords needed to specify an ellipse
03287     DocCoord NewCoords[12];
03288 
03289     // Calculate the 3 coordinates along each side of the parallelogram
03290     CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]);
03291     CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]);
03292     CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]);
03293     CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]);
03294 
03295     Matrix Rotate2 = Matrix(LineAngle);
03296 
03297     for (INT32 i=0; i<12; i++)
03298     {
03299         NewCoords[i].translate(-Start.x, -Start.y);
03300         Rotate2.transform(&NewCoords[i]);
03301         NewCoords[i].translate(Start.x, Start.y);
03302     }
03303 
03304     // build a path
03305     EllipsePath->ClearPath();
03306     EllipsePath->FindStartOfPath();
03307             
03308     // Start at bottom left corner
03309     EllipsePath->InsertMoveTo(NewCoords[0]);
03310     EllipsePath->InsertCurveTo(NewCoords[1], NewCoords[2], NewCoords[3]);
03311     EllipsePath->InsertCurveTo(NewCoords[4], NewCoords[5], NewCoords[6]);
03312     EllipsePath->InsertCurveTo(NewCoords[7], NewCoords[8], NewCoords[9]);
03313     EllipsePath->InsertCurveTo(NewCoords[10], NewCoords[11], NewCoords[0]);
03314 
03315     // Close the path properly
03316     EllipsePath->CloseSubPath();
03317 #endif
03318 }
03319 
03320 /********************************************************************************************
03321 
03322 >   void CalcMeshEllipseEdge(const DocCoord& P0, const DocCoord& P1, DocCoord* NewCoordA,
03323                                   DocCoord* NewCoordB, DocCoord* NewCoordC)
03324 
03325     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03326     Created:    22/07/94
03327     Inputs:     -
03328     Returns:    -
03329     Purpose:    Used when making an Elliptical Fill Mesh. 
03330 
03331 ********************************************************************************************/
03332 
03333 void CalcMeshEllipseEdge(const DocCoord& P0, const DocCoord& P1, DocCoord* NewCoordA,
03334                                   DocCoord* NewCoordB, DocCoord* NewCoordC)
03335 {
03336 #if !defined(EXCLUDE_FROM_RALPH)
03337     // Do the maths. This is described in the document camelot\docs\ellipse.doc
03338     // Find the length of the first side (pythag)
03339     INT32 dx = P1.x - P0.x;
03340     INT32 dy = P1.y - P0.y;
03341     //double SideLength = sqrt(dx*dx + dy*dy);
03342     //ENSURE(SideLength!=0.0, "CalcMeshEllipseEdge was thinking of doing a div 0!");
03343 
03344     // This would be different if we were calculating a rounded rectangle
03345     //double ArcRadius = SideLength / 2.0;
03346     //double ControlDist = ArcRadius/(SideLength*2.2336);
03347     // This is equivalent to the above 2 lines, only less maths is needed
03348     // (ie none as the compiler should do it)
03349     double ControlDist = 1.0 / (2 * 2.2336);
03350 
03351     // Find the control point closest to P0
03352     NewCoordA->x = (INT32) (P0.x + (dx * ControlDist));
03353     NewCoordA->y = (INT32) (P0.y + (dy * ControlDist));
03354 
03355     // Find the mid point of the side, for the middle control point
03356     NewCoordB->x = (INT32) (P0.x + (dx / 2));
03357     NewCoordB->y = (INT32) (P0.y + (dy / 2));
03358 
03359     // Find the Control point closest to p1
03360     NewCoordC->x = (INT32) (P1.x - dx * ControlDist);
03361     NewCoordC->y = (INT32) (P1.y - dy * ControlDist);
03362 #endif
03363 }
03364 
03365 /********************************************************************************************
03366 
03367 >   DocRect GetMeshEllipseBounds(DocCoord Start, DocCoord End, DocCoord End2)
03368 
03369     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03370     Created:    22/07/94
03371     Inputs:     -
03372     Returns:    -
03373     Purpose:    Gets the Bounding rect of the Radial Fill Mesh. 
03374 
03375 ********************************************************************************************/
03376 
03377 DocRect GetMeshEllipseBounds(DocCoord Start, DocCoord End, DocCoord End2)
03378 {
03379 #if !defined(EXCLUDE_FROM_RALPH)
03380     if (End2 == DocCoord(0,0))
03381     {
03382         End2 = MakeLineAtAngle(Start, End);
03383     }
03384 
03385     ANGLE LineAngle = CalcLineAngle(Start, End);
03386     Matrix Rotate = Matrix(-LineAngle);
03387 
03388     End.translate(-Start.x, -Start.y);
03389     End2.translate(-Start.x, -Start.y);
03390 
03391     Rotate.transform(&End);
03392     Rotate.transform(&End2);
03393 
03394     End.translate(Start.x, Start.y);
03395     End2.translate(Start.x, Start.y);
03396 
03397     DocCoord Parallel[4];
03398 
03399     INT32 dx = End2.x - Start.x;
03400     INT32 dy = Start.y - End.y;
03401 
03402     // Copy the rectangle into the parallelogram
03403     Parallel[0] = DocCoord(Start.x - dx, Start.y - dy);
03404     Parallel[1] = DocCoord(Start.x + dx, Start.y - dy);
03405     Parallel[2] = DocCoord(Start.x + dx, Start.y + dy);
03406     Parallel[3] = DocCoord(Start.x - dx, Start.y + dy);
03407 
03408     Matrix Rotate2 = Matrix(LineAngle);
03409 
03410     for (INT32 i=0; i<4; i++)
03411     {
03412         Parallel[i].translate(-Start.x, -Start.y);
03413         Rotate2.transform(&Parallel[i]);
03414         Parallel[i].translate(Start.x, Start.y);
03415     }
03416 
03417     DocRect Bounds = DocRect(Parallel[0], Parallel[0]);
03418     Bounds.IncludePoint(Parallel[1]);
03419     Bounds.IncludePoint(Parallel[2]);
03420     Bounds.IncludePoint(Parallel[3]);
03421 
03422     return Bounds;
03423 #else
03424     return DocRect(0,0,0,0);
03425 #endif
03426 }
03427 
03428 /********************************************************************************************
03429 
03430 >   void MakePerpendicularMeshLines(DocCoord Start, DocCoord End, 
03431                                         DocRect Bounds, DocCoord* PointArray)
03432 
03433     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03434     Created:    22/07/94
03435     Inputs:     -
03436     Returns:    -
03437     Purpose:    Makes lines perpendicular to a fill line. 
03438 
03439 ********************************************************************************************/
03440 
03441 void MakePerpendicularMeshLines(DocCoord Start, DocCoord End, 
03442                                         DocRect Bounds, DocCoord* PointArray)
03443 {
03444 #if !defined(EXCLUDE_FROM_RALPH)
03445     INT32 Infinity = max(Bounds.Width(), Bounds.Height());
03446 
03447     PointArray[0] = MakeLineAtAngle(Start, End,  90, Infinity);
03448     PointArray[1] = MakeLineAtAngle(Start, End, -90, Infinity);
03449     PointArray[2] = MakeLineAtAngle(End, Start,  90, Infinity);
03450     PointArray[3] = MakeLineAtAngle(End, Start, -90, Infinity);
03451 #endif
03452 }
03453 
03454 /********************************************************************************************
03455 
03456 >   DocCoord MakeLineAtAngle(DocCoord Start, DocCoord End, ANGLE Offset, INT32 Length)
03457                                     DocRect Bounds, DocCoord* PointArray)
03458 
03459     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03460     Created:    22/07/94
03461     Inputs:     -
03462     Returns:    -
03463     Purpose:    Make a line at a givel angle to another line. 
03464 
03465 ********************************************************************************************/
03466 
03467 DocCoord MakeLineAtAngle(DocCoord Start, DocCoord End, ANGLE Offset, INT32 Length)
03468 {
03469     const INT32 dx = End.x - Start.x;
03470     const INT32 dy = End.y - Start.y;
03471 
03472     if ( (dy==0) && (dx==0) )
03473     {
03474         return End;
03475     }
03476 
03477     DocCoord End2;
03478 
03479     if (Length == 0 && Offset == ANGLE(90))
03480     {
03481         End2.x = Start.x - (End.y - Start.y);
03482         End2.y = Start.y + (End.x - Start.x);
03483 
03484         return End2;
03485     }
03486     else if (Length == 0 && Offset == ANGLE(-90))
03487     {
03488         End2.x = Start.x + (End.y - Start.y);
03489         End2.y = Start.y - (End.x - Start.x);
03490 
03491         return End2;
03492     }
03493     else if (Length == 0 && (Offset == ANGLE(180) || Offset == ANGLE(-180)))
03494     {
03495         End2.x = Start.x - (End.x - Start.x);
03496         End2.y = Start.y - (End.y - Start.y);
03497 
03498         return End2;
03499     }
03500 
03501     double LineAngle=0;
03502 
03503     if ( (dx>=0) && (dy>=0) )
03504         LineAngle = PI - atan2( (double)dx, (double)dy );
03505     else if ( (dx<0) && (dy>=0) )
03506         LineAngle = -PI + atan2( (double)-dx, (double)dy );
03507     else if ( (dx<0) && (dy<0) )
03508         LineAngle = -atan2( (double)-dx, (double)-dy );
03509     else if ( (dx>=0) && (dy<0) )
03510         LineAngle = atan2( (double)dx, (double)-dy );
03511 
03512     ANGLE RotateAngle = 360 * (LineAngle / (2*PI)); // Convert from radians to degrees
03513 
03514     double Length1 = Length;
03515     double Length2 = Start.Distance(End);
03516 
03517     if ( INT32(Length2) == 0)
03518         return End;
03519 
03520     if ( INT32(Length1) == 0)
03521         Length1 = Length2;
03522 
03523     FIXED16 LineScale = FIXED16(Length1/Length2);
03524 
03525     Matrix Rotate1 = Matrix(-RotateAngle);
03526     Matrix Rotate2 = Matrix(RotateAngle + Offset);
03527     Matrix Scale = Matrix(LineScale, LineScale);
03528 
03529     End2 = End;
03530 
03531     End2.translate(-Start.x, -Start.y);
03532     Rotate1.transform(&End2);
03533     Scale.transform(&End2);
03534     Rotate2.transform(&End2);
03535     End2.translate(Start.x, Start.y);
03536 
03537     return End2;
03538 }
03539 
03540 /********************************************************************************************
03541 
03542 >   ANGLE CalcLineAngle(DocCoord Start, DocCoord End)
03543 
03544     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03545     Created:    22/07/94
03546     Inputs:     -
03547     Returns:    -
03548     Purpose:    Calculates the Angle of a Line. 0 is Straight Down.
03549 
03550 ********************************************************************************************/
03551 
03552 ANGLE CalcLineAngle(DocCoord Start, DocCoord End)
03553 {
03554     const INT32 dx = End.x - Start.x;
03555     const INT32 dy = End.y - Start.y;
03556 
03557     if ( (dy==0) && (dx==0) )
03558     {
03559         return 0;
03560     }
03561 
03562     double LineAngle=0;
03563 
03564     if ( (dx>=0) && (dy>=0) )
03565         LineAngle = PI - atan2( (double)dx, (double)dy );
03566     else if ( (dx<0) && (dy>=0) )
03567         LineAngle = -PI + atan2( (double)-dx, (double)dy );
03568     else if ( (dx<0) && (dy<0) )
03569         LineAngle = -atan2( (double)-dx, (double)-dy );
03570     else if ( (dx>=0) && (dy<0) )
03571         LineAngle = atan2( (double)dx, (double)-dy );
03572 
03573     return 360 * (LineAngle / (2*PI));  // Convert from radians to degrees
03574 }
03575 
03576 /********************************************************************************************
03577 
03578 >   DocCoord CentreOf(DocRect Rect)
03579 
03580     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03581     Created:    22/07/94
03582     Inputs:     -
03583     Returns:    -
03584     Purpose:    Calculates the centre of a rectangle.
03585 
03586 ********************************************************************************************/
03587 
03588 DocCoord CentreOf(DocRect Rect)
03589 {
03590     return DocCoord(Rect.lo.x + (Rect.Width()/2), Rect.lo.y + (Rect.Height()/2));
03591 }
03592 
03593 /********************************************************************************************
03594 
03595 >   void MakeMeshParallelagram(Path* ParaPath, DocCoord Start, DocCoord End, DocCoord End2)
03596 
03597     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03598     Created:    22/07/94
03599     Inputs:     -
03600     Returns:    -
03601     Purpose:    Makes a Parallelagram for the Bitmap fill mesh.
03602 
03603 ********************************************************************************************/
03604 
03605 void MakeMeshParallelagram(Path* ParaPath, DocCoord Start, DocCoord End, DocCoord End2)
03606 {
03607 #if !defined(EXCLUDE_FROM_RALPH)
03608     INT32 dx1 = (End2.x - Start.x);
03609     INT32 dy1 = (End2.y - Start.y);
03610 
03611     INT32 dx2 = (End.x - Start.x);
03612     INT32 dy2 = (Start.y - End.y);
03613 
03614     // build a path
03615     ParaPath->ClearPath();
03616     ParaPath->FindStartOfPath();
03617             
03618     ParaPath->InsertMoveTo(DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2));
03619     ParaPath->InsertLineTo(DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2));
03620     ParaPath->InsertLineTo(DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2));
03621     ParaPath->InsertLineTo(DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2));
03622     ParaPath->InsertLineTo(DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2));
03623 #endif
03624 }
03625 
03626 /********************************************************************************************
03627 
03628 >   void GetMeshParallelagram(DocCoord Start, DocCoord End, DocCoord End2,
03629                             DocCoord* P1, DocCoord* P2, DocCoord* P3, DocCoord* P4)
03630 
03631     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03632     Created:    22/07/94
03633     Inputs:     -
03634     Returns:    -
03635     Purpose:    Makes a Parallelagram for the Bitmap fill mesh.
03636                 Not used any more.
03637 
03638 ********************************************************************************************/
03639 
03640 void GetMeshParallelagram(DocCoord Start, DocCoord End, DocCoord End2,
03641                             DocCoord* P1, DocCoord* P2, DocCoord* P3, DocCoord* P4)
03642 {
03643 #if !defined(EXCLUDE_FROM_RALPH)
03644     INT32 dx1 = (End2.x - Start.x);
03645     INT32 dy1 = (End2.y - Start.y);
03646 
03647     INT32 dx2 = (End.x - Start.x);
03648     INT32 dy2 = (Start.y - End.y);
03649 
03650     *P1 = DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2);
03651     *P2 = DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2);
03652     *P3 = DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2);
03653     *P4 = DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2);
03654 #endif
03655 }
03656 
03657 /********************************************************************************************
03658 
03659 >   DocRect GetParallelagramBounds(DocCoord Start, DocCoord End, DocCoord End2)
03660 
03661     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03662     Created:    22/07/94
03663     Inputs:     -
03664     Returns:    -
03665     Purpose:    Gets the Bounding rect of the Bitmap Fill Mesh. 
03666 
03667 ********************************************************************************************/
03668 
03669 DocRect GetParallelagramBounds(DocCoord Start, DocCoord End, DocCoord End2)
03670 {
03671     DocCoord P1(0,0);
03672     DocCoord P2;
03673     DocCoord P3;
03674     DocCoord P4;
03675 
03676     GetMeshParallelagram(Start, End, End2, &P1, &P2, &P3, &P4);
03677 
03678     DocRect Bounds = DocRect(P1, P1);
03679 
03680     Bounds.IncludePoint(P2);
03681     Bounds.IncludePoint(P3);
03682     Bounds.IncludePoint(P4);
03683     
03684     return Bounds;
03685 }
03686 
03687 /********************************************************************************************
03688 
03689 >   void GetBitmapVirtualPoints(DocCoord Start, DocCoord End, DocCoord End2,
03690                             DocCoord* P1, DocCoord* P2, DocCoord* P3)
03691 
03692     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03693     Created:    22/07/94
03694     Inputs:     -
03695     Returns:    -
03696     Purpose:    Calculates the Virtual points of a bitmap from the actual bitmap control
03697                 points given. 
03698 
03699 ********************************************************************************************/
03700 
03701 void GetBitmapVirtualPoints(DocCoord Start, DocCoord End, DocCoord End2,
03702                             DocCoord* P1, DocCoord* P2, DocCoord* P3)
03703 {
03704 #if !defined(EXCLUDE_FROM_RALPH)
03705     double dx1 = (End2.x - Start.x);
03706     double dy1 = (End2.y - Start.y);
03707 
03708     double dx2 = (End.x - Start.x);
03709     double dy2 = (Start.y - End.y);
03710 
03711     if (P1 != NULL && (Start != DocCoord(0,0)))
03712         *P1 = DocCoord(INT32(double(Start.x) + dx1/2 + dx2/2), INT32(double(Start.y) + dy1/2 - dy2/2));
03713     if (P2 != NULL && (End != DocCoord(0,0)))
03714         *P2 = DocCoord(INT32(double(Start.x) + dx1/2 + dx2),   INT32(double(Start.y) + dy1/2 - dy2));
03715     if (P3 != NULL && (End2 != DocCoord(0,0)))
03716         *P3 = DocCoord(INT32(double(Start.x) + dx1   + dx2/2), INT32(double(Start.y) + dy1   - dy2/2));
03717 #endif
03718 }
03719 
03720 /********************************************************************************************
03721 
03722 >   void GetBitmapRealPoints(DocCoord Start, DocCoord End, DocCoord End2,
03723                             DocCoord* P1, DocCoord* P2, DocCoord* P3)
03724 
03725     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03726     Created:    22/07/94
03727     Inputs:     -
03728     Returns:    -
03729     Purpose:    Calculates the Real Control points of a bitmap from the vitual points given. 
03730 
03731 ********************************************************************************************/
03732 
03733 void GetBitmapRealPoints(DocCoord Start, DocCoord End, DocCoord End2,
03734                             DocCoord* P1, DocCoord* P2, DocCoord* P3)
03735 {
03736 #if !defined(EXCLUDE_FROM_RALPH)
03737     INT32 dx1 = (End2 == DocCoord(0,0)) ? 0 : (End2.x - Start.x);
03738     INT32 dy1 = (End2 == DocCoord(0,0)) ? 0 : (End2.y - Start.y);
03739 
03740     INT32 dx2 = (End.x - Start.x);
03741     INT32 dy2 = (Start.y - End.y);
03742 
03743     if (P1 != NULL && (Start != DocCoord(0,0)))
03744         *P1 = DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2);
03745     if (P2 != NULL && (End != DocCoord(0,0)))
03746         *P2 = DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2);
03747     if (P3 != NULL && (End2 != DocCoord(0,0)))
03748         *P3 = DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2);
03749 #endif
03750 }
03751 
03752 /********************************************************************************************
03753 
03754 >   void IncludeArrowHead(DocRect* Bounds, DocCoord Start, DocCoord End)
03755 
03756     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03757     Created:    8/9/94
03758     Inputs:     -
03759     Returns:    -
03760     Purpose:    Includes the Arrow head in the bounds calculations.
03761 
03762 ********************************************************************************************/
03763 
03764 void IncludeArrowHead(DocRect* Bounds, DocCoord Start, DocCoord End)
03765 {
03766 #if !defined(EXCLUDE_FROM_RALPH)
03767     // First we Must caculate the angle of the arrow from the line angle
03768     const INT32 dx = Start.x - End.x;
03769     const INT32 dy = Start.y - End.y;
03770 
03771     if ( (dy==0) && (dx==0) )
03772     {
03773         return;                             // draw nothing as too small
03774     }
03775 
03776     double ArrowAngle;
03777 
03778     if ( (dx>=0) && (dy>=0) )
03779         ArrowAngle = PI - atan2( (double)dx, (double)dy );
03780     else if ( (dx<0) && (dy>=0) )
03781         ArrowAngle = -PI + atan2( (double)-dx, (double)dy );
03782     else if ( (dx<0) && (dy<0) )
03783         ArrowAngle = -atan2( (double)-dx, (double)-dy );
03784     else if ( (dx>=0) && (dy<0) )
03785         ArrowAngle = atan2( (double)dx, (double)-dy );
03786     else
03787     {
03788         TRACE( _T("invalid angle on arrowhead\n"));     // this should be impossible
03789         return;
03790     }
03791 
03792     ANGLE RotateAngle = 360 * (ArrowAngle / (2*PI));    // Convert from radians to degrees
03793 
03794     Matrix ArrowMatrix = Matrix(RotateAngle);           // Rotate it
03795     ArrowMatrix *= Matrix(End);                         // Move it to the line point
03796 
03797     const UINT32 PointCount = 3;
03798     Coord ArrowPts[PointCount];
03799 
03800     INT32 BlobSize = (Camelot.GetBlobManager())->GetBlobSize();
03801 
03802     INT32 ArrowWidth  = (BlobSize  * 3)/2;
03803     INT32 ArrowHeight = (BlobSize  * 3)/2;
03804     
03805     ArrowPts[0] = Coord(             0,            0);
03806     ArrowPts[1] = Coord( -ArrowWidth/2, -ArrowHeight);
03807     ArrowPts[2] = Coord(  ArrowWidth/2, -ArrowHeight);
03808 
03809     ArrowMatrix.transform(ArrowPts, PointCount);
03810 
03811     Bounds->IncludePoint(DocCoord(ArrowPts[0].x, ArrowPts[0].y));
03812     Bounds->IncludePoint(DocCoord(ArrowPts[1].x, ArrowPts[1].y));
03813     Bounds->IncludePoint(DocCoord(ArrowPts[2].x, ArrowPts[2].y));
03814 #endif
03815 }
03816 
03817 /********************************************************************************************
03818 
03819 >   BOOL AreLinesPerpendicular(DocCoord* Start, DocCoord* End, DocCoord* End2)
03820 
03821     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
03822     Created:    16/4/95
03823     Inputs:     -
03824     Returns:    -
03825     Purpose:    Tests to see if two fill control lines are perpendicular or not.
03826                 Uses the 'Similar Triangles' method, but allows a 5 millipoint error.
03827 
03828 ********************************************************************************************/
03829 
03830 BOOL AreLinesPerpendicular(DocCoord* Start, DocCoord* End, DocCoord* End2)
03831 {
03832     INT32 dx1 = (*End).x - (*Start).x;
03833     INT32 dy1 = (*End).y - (*Start).y;
03834 
03835     INT32 dx2 = (*End2).x - (*Start).x;
03836     INT32 dy2 = (*End2).y - (*Start).y;
03837 
03838     if ( (ABS(dx1 - dy2)) > 5 ) // Allow a few millipoints error
03839         return FALSE;
03840 
03841     if ( (ABS(dy1 + dx2)) > 5 ) // Allow a few millipoints error
03842         return FALSE;
03843     
03844     return TRUE;
03845 }
03846 
03848 // RestoreFillRampAction class
03849 /********************************************************************************************
03850 
03851 >   RestoreFillRampAction::RestoreFillRampAction()
03852 
03853     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03854     Created:    24/10/99
03855     Inputs:     -
03856     Outputs:    -
03857     Returns:    -
03858     Purpose:    Constructor for the action
03859     Errors:     -
03860     SeeAlso:    -
03861 
03862 ********************************************************************************************/
03863 
03864 RestoreFillRampAction::RestoreFillRampAction()
03865 {
03866     m_pAttr  = NULL;
03867 }
03868 
03869 
03870 /********************************************************************************************
03871 
03872 >   static ActionCode Init( Operation* pOp,
03873                             ActionList* pActionList,
03874                             AttrFillGeometry* pFill,
03875                             RestoreFillRampAction** NewAction,
03876     
03877 
03878     Author:     David_McClarnon (Xara Group Ltd) <camelotdev@xara.com>
03879     Created:    10/6/99
03880     Inputs:     pOp             = ptr to the operation to which this action belongs
03881                 pActionList     =  ptr to action list to which this action should be added
03882                 pFill           = ptr to the fill geometry attribute to store its ramp
03883     Outputs:    ppNewAction     = ptr to a ptr to an action, allowing the function to return
03884                                   a pointer to the created action
03885     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
03886     Purpose:    Stores the fill ramp of the given attribute for restoring on undo
03887     Errors:     -
03888     SeeAlso:    Action::Init()
03889 
03890 ********************************************************************************************/
03891 
03892 
03893 
03894 ActionCode RestoreFillRampAction::Init( Operation* pOp,
03895                             ActionList* pActionList,
03896                             AttrFillGeometry* pFill,
03897                             RestoreFillRampAction** NewAction)
03898 {
03899     UINT32 ActSize = sizeof(RestoreFillRampAction);
03900 
03901     ActionCode Ac = Action::Init(pOp,pActionList,ActSize,
03902         CC_RUNTIME_CLASS(RestoreFillRampAction),
03903         (Action**)NewAction);
03904 
03905 //  Document * pDoc = Document::GetCurrent();   
03906 
03907     DocRect dr;
03908 
03909     if (Ac != AC_FAIL)
03910     {
03911         if (pFill->GetColourRamp() != NULL)
03912         {
03913             (*NewAction)->m_LastRamp = *pFill->GetColourRamp();
03914         }       
03915         // bugfix #6835 (Marc 22/09/04) - make sure we store the fill even if there is no ramp
03916         (*NewAction)->m_pAttr    = pFill;
03917     }
03918 
03919     return Ac;
03920 }
03921 
03922 /********************************************************************************************
03923 
03924 >   ActionCode RestoreFillRampAction::Execute();
03925 
03926     Author:     Olivier_Gascoin (Xara Group Ltd) <camelotdev@xara.com>
03927     Created:    07/01/96
03928     Inputs:     -
03929     Outputs:    -
03930     Returns:    ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL
03931     Purpose:    Executes the action.  This will reset the num blend steps in pThisNodeBlend to OldNumSteps,
03932                 after creating another action to record the current num steps of pThisNodeBlend
03933     Errors:     -
03934     SeeAlso:    Action::Init()
03935 
03936 ********************************************************************************************/
03937 
03938 ActionCode RestoreFillRampAction::Execute()
03939 {
03940     ActionCode Act;
03941     RestoreFillRampAction* pAction;
03942 
03943     Act = RestoreFillRampAction::Init(  pOperation, 
03944                                         pOppositeActLst,
03945                                         m_pAttr,
03946                                         &pAction
03947                                         );
03948 
03949     if (Act != AC_FAIL)
03950     {
03951         Document * pDoc = Document::GetCurrent();
03952         
03953         if (pDoc)
03954         {
03955             /*
03956             DocRect dr = pController->GetBoundingRect();
03957             
03958             pDoc->ForceRedraw(pController->FindParentSpread(), 
03959                 dr, FALSE);
03960                 */
03961 
03962             // CGS - we need to check for a NULL colour ramp - cause we may have deleted it!
03963             
03964             ColourRamp* ColRamp = m_pAttr->GetColourRamp();
03965 
03966             if (ColRamp)
03967             {
03968                 *m_pAttr->GetColourRamp() = m_LastRamp;
03969             }
03970             else
03971             {
03972                 // if it is NULL, then we need to recreate one BEFORE making the above call!
03973 
03974                 ColRamp = new ColourRamp ();
03975                 m_pAttr->SetColourRamp (ColRamp);
03976                 *m_pAttr->GetColourRamp() = m_LastRamp;
03977             }
03978         }
03979     }
03980     
03981     return Act;
03982 }
03983 
03984 RestoreFillRampAction::~RestoreFillRampAction()
03985 {
03986 }
03987 

Generated on Sat Nov 10 03:46:15 2007 for Camelot by  doxygen 1.4.4