// $Id: opgrad.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // The Grad Fill creation and Editing operations /******************************************************************************************** NOTE: OpCreateFill is no longer used. OpEditFill, is now used for fill Creation and Editing. ********************************************************************************************/ /* */ #include "camtypes.h" //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "opgrad.h" #include "filltool.h" //#include "will.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "attr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "lineattr.h" //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "stockcol.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "blobs.h" //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "toollist.h" //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "bitmpinf.h" #include "progress.h" #include "ndoptmz.h" #include "csrstack.h" //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "objchge.h" #include "keypress.h" #include "nodeblnd.h" #include "fillramp.h" //#include "will2.h" #include "nodecont.h" #ifndef STANDALONE CC_IMPLEMENT_DYNCREATE( OpCreateFill, Operation ) CC_IMPLEMENT_DYNCREATE( OpEditFill, OpApplyAttribToSelected ) CC_IMPLEMENT_DYNCREATE( RestoreFillRampAction, Action) #endif //CC_IMPLEMENT_DYNCREATE( OpMutateFill, Operation ) //CC_IMPLEMENT_DYNCREATE( OpChangeFillColour, SelOperation ) //CC_IMPLEMENT_DYNCREATE( OpChangeSelectionColours, SelOperation ) //CC_IMPLEMENT_DYNCREATE( ModifyFillCoordsAction, Action) //CC_IMPLEMENT_DYNCREATE( ModifyFillColoursAction, Action) // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW // Fill editing preferences BOOL OpEditFill::InteractiveDragUpdate = TRUE; BOOL OpEditFill::ContinuousEOR = TRUE; UINT32 OpEditFill::IdleFillDelay = 200; // Fill redraw delay (used to be 300 in Xara X release) BOOL OpEditFill::CreateFill = FALSE; static BOOL EditFinished = FALSE; // some pointers that are useful within this stuff .... static NodeRenderableInk* pParentOfFill = NULL; static AttrFillGeometry* pTheFill = NULL; #ifndef STANDALONE /******************************************************************************************** > OpEditFill::OpEditFill() Author: Will_Cowling (Xara Group Ltd) Created: 1/6/94 Purpose: Dummy Contrustor ********************************************************************************************/ OpEditFill::OpEditFill() { // Initialise everything AlwaysFail = TRUE; ApplyAtEnd = TRUE; ForceAspectLock = FALSE; CreateFill = FALSE; IsRequired = TRUE; ShouldApplyNewFill = FALSE; CheckForCompound = TRUE; CallAllowOp = FALSE; InvalidateAll = TRUE; InvalidateCompound = FALSE; DragIsIdle = FALSE; DoneIdleRedraw = FALSE; ShowDragBlobs = TRUE; DontDrawBlobs = FALSE; EditFinished = FALSE; AspectRatio = 1; } /******************************************************************************************** > void OpEditFill::DoCreate(DocCoord &Start, DocCoord &End, Spread* pSpread, AttrFillGeometry* pGrad) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Start - The starting position of the drag pSpread - The spread that the drag started over pElip - The NodeGradFill that is being edited Purpose: - ********************************************************************************************/ void OpEditFill::DoCreate(DocCoord &Start, Spread* pSpread, AttrFillGeometry* pGrad) { ERROR3IF(pGrad == NULL, "Fill pointer is NULL in OpEditFill::DoCreate()"); if (pGrad == NULL) { // Nothing to create !! FailAndExecute(); End(); return; } // DMc - do the attribute to change m_pAttr = (NodeAttribute *)pGrad; AlwaysFail = FALSE; // DMc - set this to TRUE ApplyAtEnd = FALSE; ForceAspectLock = TRUE; CreateFill = TRUE; // Get a description of the attribute being applied so that we can use it to create the // undo string. UndoAttribStrID = pGrad->GetAttrNameID(); AttrFillGeometry::HitList.DeleteAll(); SelRange* pSel = GetApplication()->FindSelection(); if (pSel && pSel->Count() > 0) { // Check it's ok to apply to this object ObjChangeFlags cFlags; cFlags.Attribute = TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); ObjChange.SetRetainCachedData(TRUE); // Don't let nodes releasecached data - we will handle that! if (!pSel->AllowOp(&ObjChange)) { delete pGrad; FailAndExecute(); End(); return; } } AttrFillGeometry* pFillToEdit = pGrad; // Now check to make sure someone in the selection will accept this attribute if (AttributeManager::CanBeAppliedToSelection(pGrad, &AttrGroups)) { // We're gunna apply to at least one object IsRequired = TRUE; // Flag that we need to apply the fill, either when we try // and do an interective redraw, or at the end ShouldApplyNewFill = TRUE; // Remove all the existing selection fill blobs DocRect SelBounds = pSel->GetBlobBoundingRect(); RenderInitSelectionBlobs(pSel, SelBounds, pSpread); } else { // This attribute is not required by any of the selection, // but we will continue, without actually applying anything, // and set the 'current' attribute instead IsRequired = FALSE; AttributeManager::pLastNodeAppliedTo = NULL; } // Snap the actual mouse position to the grid if needed DocView::SnapSelected(pSpread, &Start); // Initialise the Control Points pFillToEdit->SetStartPoint(&Start); pFillToEdit->SetEndPoint(&Start); pFillToEdit->SetEndPoint2(&Start); // Call the edit code DoDrag(Start, pSpread, pFillToEdit, FILLCONTROL_ENDPOINT); } /******************************************************************************************** > void OpEditFill::DoDrag(DocCoord &Start, DocCoord &End, Spread* pSpread, AttrFillGeometry* pGrad, INT32 BlobHit) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Start - The starting position of the drag pSpread - The spread that the drag started over pElip - The NodeGradFill that is being edited Purpose: - Notes: Phil, 16-09-2005 This code relies on the attributea pplication code using DoHideNode and DoShowNode to record changes to the attributes. By recording the attributes in this way when they are first applied, the undo history contains pointers to them and the dragging code can then modify those attributes in the knowledge that the op history is now pointing at the modified attributes. It should really be changed to use OpApplyAttrInteractive. That would reduce the complexity of the following functions enormously and would be more consistent, smaller and possibly faster. There are too many different ways of "solid dragging" things and they should all: * Record current tree state at start of drag * Modify tree during drag * Produce permanent Undo information at the end, using the same logic as would be used in the simple, parametric or outline drag case ********************************************************************************************/ void OpEditFill::DoDrag(DocCoord &Start, Spread* pSpread, AttrFillGeometry* pGrad, FillControl ControlHit) { ERROR3IF(ControlHit > NUMCONTROLPOINTS && !ISA_RAMPINDEX(ControlHit), "Invalid control point in OpEditFill::DoDrag()"); ERROR3IF(pGrad == NULL, "Fill pointer is NULL in OpEditFill::DoDrag()"); if (pGrad == NULL) { // Nothing to edit !! FailAndExecute(); End(); return; } // Various starting positions StartSpread = pSpread; DragControl = ControlHit; // Keep a copy of the old fill, so we can put it back later if necessary CCRuntimeClass* ObjectType = pGrad->GetRuntimeClass(); FillClone = (AttrFillGeometry*)ObjectType->CreateObject(); *FillClone = *pGrad; // and make another copy to actually edit ObjectType = pGrad->GetRuntimeClass(); GradFill = (AttrFillGeometry*)ObjectType->CreateObject(); *GradFill = *pGrad; pParentOfFill = (NodeRenderableInk*) pGrad->FindParent (); pTheFill = pGrad; if (CreateFill) delete pGrad; // Not needed any more AspectRatio = FindAspectRatio(); AnchorPos = Start; LastMousePosition = Start; DontDrawBlobs = FALSE; DragIsIdle = FALSE; DoneIdleRedraw = FALSE; // ShowDragBlobs = ContinuousEOR; ShowDragBlobs = !DocView::SolidDragging; if (!IsRequired) // Always show blobs if we're gunna ShowDragBlobs = TRUE; // set the current attribute AttrFillGeometry::DraggedFill = NULL; AttrFillGeometry::EditedFill = GradFill; // And tell the Dragging system that we need drags to happen StartDrag(DRAGTYPE_AUTOSCROLL, NULL, NULL, TRUE, TRUE); // We support solid dragging } /******************************************************************************************** > void OpEditFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: PointerPos - The current position of the mouse ClickMods - Which buttons and modifiers are held down pSpread - The Spread that the mouse is over now. Purpose: Recalculates the GradFill according to the new position of the corner that is being dragged, EORs it to the screen and keeps the Parallelogram up to date. ********************************************************************************************/ void OpEditFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) { if (ISA_RAMPINDEX(DragControl) && !bSolidDrag) { GradFill->DisableRampRedraw(); } if (EditFinished) return; if (ForceAspectLock) ClickMods.Adjust = TRUE; DragIsIdle = FALSE; DoneIdleRedraw = FALSE; // Snap the actual mouse position to the grid if needed DocView::SnapSelected(pSpread, &PointerPos); if (AttrFillGeometry::DraggedFill == NULL) { double APixel = (DocView::GetSelected()->GetScaledPixelWidth()).MakeDouble(); // If the pointer has moved less than a few pixels, then do nothing if (PointerPos.Distance(AnchorPos) <= APixel*2) { if (!(ISA_RAMPINDEX(DragControl))) { return; } } // This is the first time the pointer has moved .... if (!CreateFill) { // If we are editing a fill, the check with it's parent // to make sure it's ok (Mould's will say 'no'). ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; if (pGrad != NULL) { Node* pParent = pGrad->FindParent(); if (pParent != NULL) { // Check it's ok to edit this fill ObjChangeFlags cFlags; cFlags.Attribute = TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); ObjChange.SetRetainCachedData(TRUE); if (!pParent->AllowOp(&ObjChange)) { // Parent said 'no', so abort the edit EndDrag(); delete FillClone; delete GradFill; FailAndExecute(); End(); return; } } } } AttrFillGeometry::EditedFill = GradFill; AttrFillGeometry::LastRenderedStartBlob = DocCoord(0,0); AttrFillGeometry::LastRenderedEndBlob = DocCoord(0,0); AttrFillGeometry::LastRenderedEnd2Blob = DocCoord(0,0); AttrFillGeometry::LastRenderedEnd3Blob = DocCoord(0,0); // First Rub out the old Fill blobs RenderInitBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag); GradFill->SetBlobState(DragControl, TRUE); // And stop any more from being drawn for now AttrFillGeometry::DraggedFill = GradFill; LastDragRect = GradFill->GetBlobBoundingRect(); DontDrawBlobs = !ShowDragBlobs; // Now draw in the new 'Drag' blobs RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag); // Update the status line to show we are dragging a fill String_256 DragText; GetStatusLineText(&DragText, pSpread, PointerPos, ClickMods); GetApplication()->UpdateStatusBarText(&DragText); } // If the mouse is in a different position then do something if (PointerPos != LastMousePosition) { // First Rub out the old Drag blobs RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag); // Make sure that the coords are relative to the correct spread if (pSpread != StartSpread) PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos); // Update the last mouse position and re-calc the bounding rect GradFill->OnControlDrag(PointerPos, DragControl, ClickMods); LastMousePosition = PointerPos; // If we are creating a fill, then we'll try and create it with the // correct aspect ratio if (ForceAspectLock) GradFill->SetAspectRatio(AspectRatio); LastDragRect = GradFill->GetBlobBoundingRect(); DontDrawBlobs = !ShowDragBlobs; // Render 'On' the blobs in the new position RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag); if (DontDrawBlobs) RedrawFills(ClickMods); // We're in 'Solid update' mode } if (ISA_RAMPINDEX(DragControl)) { GradFill->EnableRampRedraw (); } } /******************************************************************************************** > double OpEditFill::FindAspectRatio() Author: Will_Cowling (Xara Group Ltd) Created: 3/5/95 Purpose: Find the aspect ratio of the bitmap we are creating. ********************************************************************************************/ double OpEditFill::FindAspectRatio() { // Are we creating a Bitmap fill ? if (!GradFill->IsABitmapFill()) return 1; SelRange* pSel = GetApplication()->FindSelection(); NodeAttribute* pCommonAttr; SelRange::CommonAttribResult Result; // Find the common attr in the selection Result = pSel->FindCommonAttribute(GradFill->GetAttributeType(), &pCommonAttr); KernelBitmap* Bmp = NULL; if (Result == SelRange::ATTR_COMMON) { // There is a common attr, so if it's a Bitmap Fill, // then use that bitmaps aspect ratio AttrFillGeometry* pFill = (AttrFillGeometry*)pCommonAttr; if (pFill->IsABitmapFill()) Bmp = pFill->GetBitmap(); GradFill->AttachBitmap(Bmp); } else { // Otherwise just use the default Bmp = GradFill->GetBitmap(); } if (Bmp == NULL) return 1; BitmapInfo Info; if (Bmp->ActualBitmap && Bmp->ActualBitmap->GetInfo( &Info )) { INT32 BitmapWidth = Info.RecommendedWidth; INT32 BitmapHeight = Info.RecommendedHeight; // Calculate the aspect ratio of the bitmap return double(BitmapHeight)/double(BitmapWidth); } // Default to square return 1; } /******************************************************************************************** > void OpEditFill::RedrawFills(ClickModifiers& ClickMods) Author: Will_Cowling (Xara Group Ltd) Created: 3/5/95 Purpose: Forces a redraw on the edited fills. ********************************************************************************************/ void OpEditFill::RedrawFills(ClickModifiers& ClickMods) { if (ClickMods.Alternative1) return; // Alt key stops interactive updates // Prevent slow renderers from doing their stuff while we try to // show the results of the feather op interactively. Operation::SetQuickRender(TRUE); if (ShouldApplyNewFill) { // If we haven't applied the new fill yet, then we should // do so now. ApplyNewFill(); } if (CheckForCompound) { // If we haven't already done so, we check to see if we need // to invalidate any compound parents. CheckForCompoundRedraw(); } if (!AttrFillGeometry::HitList.IsEmpty()) { DocView* pSelView = DocView::GetSelected(); // Scan the List of Fills ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); DocRect SelBounds = GetApplication()->FindSelection()->GetBoundingRect(); while (pAttrPtr != NULL) { AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list"); if (pAttr != GradFill) { if (ISA_RAMPINDEX(DragControl)) { AttrFillGeometry* CurrentFill = AttrFillGeometry::DraggedFill; AttrFillGeometry::DraggedFill = pAttr; DocRect Current = pAttr->GetBlobBoundingRect (); SelBounds = SelBounds.Union (Current); AttrFillGeometry::DraggedFill = CurrentFill; } // Update the control points on the other fills pAttr->SetStartPoint(GradFill->GetStartPoint()); pAttr->SetEndPoint(GradFill->GetEndPoint()); pAttr->SetEndPoint2(GradFill->GetEndPoint2()); pAttr->SetColourRamp(GradFill->GetColourRamp()); //pAttr->DisableRampRedraw (); pAttr->RecalcFractal(); } if (!InvalidateAll) { // Force the fills parent object to redraw NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent(); ERROR3IF(pParent==NULL, "Can't find parent of edited attribute"); if (pParent && pAttr->IsEffectAttribute()) // IsValidEffectAttr? { // Node* pGrandParent = pParent->FindParent(); // if (pGrandParent && pGrandParent->IsBounded()) // ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only } else pParent->ReleaseCached(); pSelView->ForceRedraw(StartSpread, pParent->GetBoundingRect(), FALSE, pAttr); GetApplication ()->ServiceRendering (); } pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr); } if (InvalidateAll) { // Either there were too many objects to invalidate individually, // or there is a Compound Parent to invalidate if (InvalidateCompound) { pAttrPtr = AttrFillGeometry::HitList.GetHead(); AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; if (CallAllowOp && !pGrad->IsEffectAttribute()) { // For a blend, we need to make sure it re-blends itself, // so we need to call AllowOp on it ... ObjChangeFlags cFlags; cFlags.Attribute = TRUE; // Needed to make Blends re-calc themselves ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL, this); pGrad->AllowOp(&ObjChange); ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL, this); UpdateAllChangedNodes(&ObjChange); } if (ISA_RAMPINDEX (DragControl)) { GradFill->DisableBoundsRedraw(); } NodeRenderableInk* pParent = (NodeRenderableInk*)pCompound; ERROR3IF(pParent==NULL, "Can't find parent of edited attribute"); if (pParent && pGrad->IsEffectAttribute()) // IsValidEffectAttr? { // Node* pGrandParent = pParent->FindParent(); // if (pGrandParent && pGrandParent->IsBounded()) // ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only } else pParent->ReleaseCached(); pSelView->ForceRedraw(StartSpread, pParent->GetBoundingRect(), FALSE, pGrad); GetApplication ()->ServiceRendering(); if (ISA_RAMPINDEX (DragControl)) { GradFill->EnableBoundsRedraw(); } } else { if (ISA_RAMPINDEX (DragControl)) { GradFill->DisableBoundsRedraw(); } // Oh sod it. Just invalidate the whole blinking lot of them, // and be done with it. //DocRect SelBounds = GetApplication()->FindSelection()->GetBoundingRect(); // Scan the List of Fills and ensure that none of them are cached... ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); Node* pBackMost = NULL; while (pAttrPtr != NULL) { AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; // Force the fills parent object to redraw NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent(); ERROR3IF(pParent==NULL, "Can't find parent of edited attribute"); if (pParent && pAttr->IsEffectAttribute()) // IsValidEffectAttr? { // Node* pGrandParent = pParent->FindParent(); // if (pGrandParent && pGrandParent->IsBounded()) // ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only } else pParent->ReleaseCached(); // Work out the backmost attr if (pBackMost==NULL || pAttr->IsUnder(pBackMost)) pBackMost = pAttr; pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr); } pSelView->ForceRedraw(StartSpread, SelBounds, FALSE, pBackMost); GetApplication()->ServiceRendering(); if (ISA_RAMPINDEX (DragControl)) { GradFill->EnableBoundsRedraw(); } } } } // Re-enable slow renderers Operation::SetQuickRender(FALSE); } /******************************************************************************************** > void OpEditFill::CheckForCompoundRedraw() Author: Will_Cowling (Xara Group Ltd) Created: 3/5/95 Purpose: Check to see if the redraw needs to invalidate a compound parent. (eg. a blend). ********************************************************************************************/ void OpEditFill::CheckForCompoundRedraw() { CheckForCompound = FALSE; pFillsDoc = StartSpread->FindOwnerDoc(); if (AttrFillGeometry::HitList.GetCount() == GetApplication()->FindSelection()->Count() || AttrFillGeometry::HitList.GetCount() > 10) { // If we are dragging all selected fills, or if there are // more than 10 of them, then we'll just invalidate the // whole selection, 'cus it's much quicker InvalidateAll = TRUE; } else { // We will invalidate just the edited fills InvalidateAll = FALSE; } InvalidateCompound = FALSE; if (AttrFillGeometry::HitList.GetCount() == 1) { // If just one Fill is being dragged, then check to see if // it's a 'Select Inside' of a Compound object. // If it is, then we'll need to update the whole compound parent. ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); AttrFillGeometry* pGrad = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; Node* pParent = pGrad->FindParent(); if (pParent == NULL) { InvalidateAll = TRUE; return; } if (pParent->IsCompound()) { // The parent is compound, so we'll just update that InvalidateAll = TRUE; InvalidateCompound = TRUE; pCompound = pParent; } // Is the parent a blend object, or is there one // further up the tree ? while (pParent != NULL && !pParent->IsLayer()) { if (pParent->IS_KIND_OF(NodeBlend) || pParent->ShouldITransformWithChildren()) { // Found a blend, so we'll have to invalidate it // each time we want to re-draw InvalidateAll = TRUE; InvalidateCompound = TRUE; pCompound = pParent; // We will call allowop, to invalidate the blend CallAllowOp = TRUE; // Don't break out yet, 'cus we need to invalidate // the highest blend in the tree } pParent = pParent->FindParent(); } } } /******************************************************************************************** > void OpEditFill::DragPointerIdle(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: PointerPos - The current position of the mouse ClickMods - Which buttons and modifiers are held down pSpread - The Spread that the mouse is over now. Purpose: Recalculates the GradFill according to the new position of the corner that is being dragged, EORs it to the screen and keeps the Parallelogram up to date. ********************************************************************************************/ void OpEditFill::DragPointerIdle(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) { if (!DragIsIdle) { // The Mouse has just become idle, so we'll reset the timer. DragIsIdle = TRUE; DoneIdleRedraw = FALSE; Time.Sample(); } else { // Has the Mouse been idle for long enough ? if (Time.Elapsed(IdleFillDelay)) { if (DontDrawBlobs) { // If we are not redrawing the EOR blobs constantly, // then we will stick them back on when the mouse // has been idle for a while. DontDrawBlobs = FALSE; DragIsIdle = FALSE; RenderDragBlobs(LastDragRect, StartSpread, bSolidDrag); } else { // If we ARE redrawing the blobs, then we'll try and redraw // the fills when the mouse has been idle for a while. if (InteractiveDragUpdate && !DoneIdleRedraw) { RedrawFills(ClickMods); DoneIdleRedraw = TRUE; } } } } } /******************************************************************************************** > BOOL OpEditFill::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 4/5/95 Inputs: pKeyPress - Object describing the nature of the keypress. Purpose: Allows the Fill Edit operation to respond to key presses. ********************************************************************************************/ BOOL OpEditFill::DragKeyPress(KeyPress* pKeyPress, BOOL bSolidDrag) { if (pKeyPress->GetVirtKey() == CAMKEY(TAB) && pKeyPress->IsPress() && !pKeyPress->IsRepeat()) { // Toggle the continuous EOR flag ContinuousEOR = !ContinuousEOR; ShowDragBlobs = ContinuousEOR; String_256 DragText; ClickModifiers ClickMods; // Now update the Status line to show the mode change GetStatusLineText(&DragText, StartSpread, LastMousePosition, ClickMods); GetApplication()->UpdateStatusBarText(&DragText); return TRUE; } return FALSE; } /******************************************************************************************** > void OpEditFill::DragModeChanged() Author: Phil_Martin (Xara Group Ltd) Created: 23/12/2003 Inputs: pKeyPress - pointer to a keypress object Returns: TRUE if it handled the keypress, FALSE otherwise Purpose: Find out if any of the key modifiers are being pressed when the mouse is not moving. ********************************************************************************************/ void OpEditFill::DragModeChanged(BOOL bSolidDrag) { /* DocView* pDocView = DocView::GetSelected(); BlobManager* pBlobManager = Camelot.GetBlobManager(); ENSURE(pBlobManager, "Can't get BlobManager"); // Toggle the continuous EOR flag ContinuousEOR = !bSolidDrag; ShowDragBlobs = ContinuousEOR; String_256 DragText; ClickModifiers ClickMods; // Now update the Status line to show the mode change GetStatusLineText(&DragText, StartSpread, LastMousePosition, ClickMods); GetApplication()->UpdateStatusBarText(&DragText); */ } /******************************************************************************************** > void OpEditFill::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL Success, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: PointerPos - The current position of the mouse ClickMods - Which buttons and modifiers are held down pSpread - The Spread that the mouse is over now. Success - TRUE if the drag was completed sucessfully. Purpose: If the drag was a success then a copy of the original node in the tree is created and updated GradFill built. The original NodeGradFill is hidden and the new one if inserted into the tree. If any of these things fail then the operation will fail. ********************************************************************************************/ void OpEditFill::DragFinished(DocCoord PointerPos, ClickModifiers ClickMods, Spread*, BOOL Success, BOOL bSolidDrag) { EditFinished = TRUE; double APixel; // Flag to say if everything has worked BOOL IsOk = FALSE; // First Rub out the old Drag blobs if (!DontDrawBlobs && AttrFillGeometry::DraggedFill != NULL) { if (!(ISA_RAMPINDEX(DragControl))) { RenderDragBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag); } else { if (ISA_RAMPINDEX (DragControl)) { GradFill->DisableBoundsRedraw (); } RenderDragBlobs(GradFill->GetBlobBoundingRect(), StartSpread, bSolidDrag); if (ISA_RAMPINDEX (DragControl)) { GradFill->EnableBoundsRedraw (); } } } DontDrawBlobs = FALSE; BOOL HaveFailed = FALSE; Document* pDoc = Document::GetSelected(); // check for a fill ramp drag if (ISA_RAMPINDEX(DragControl)) { if (Success) { if (GradFill->GetColourRamp()) { GradFill->GetColourRamp()->SortRamp(); } } } // End the Drag if (!EndDrag()) { delete FillClone; delete GradFill; goto EndOperation; } if (*GradFill->GetStartPoint() == *GradFill->GetEndPoint()) Success = FALSE; APixel = (DocView::GetSelected()->GetScaledPixelWidth()).MakeDouble(); // If the pointer has moved less than a few pixels, then just do a click if (PointerPos.Distance(AnchorPos) <= APixel*2) Success = FALSE; // If PointerPos is equal to AnchorPos, then it must have just been a click if (!Success || (PointerPos == AnchorPos)) { if (AttrFillGeometry::DraggedFill) { // We must have actually dragged a bit, // so make sure we tidy everying, and then render // the old fill blobs back on AttrFillGeometry::DraggedFill = NULL; AttrFillGeometry::EditedFill = NULL; // Failed, so put all the fills back like they were, // if we have changed them. if (!(*GradFill == *FillClone)) { *GradFill = *FillClone; ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); while (pAttrPtr != NULL) { AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list"); pAttr->SetStartPoint(FillClone->GetStartPoint()); pAttr->SetEndPoint(FillClone->GetEndPoint()); pAttr->SetEndPoint2(FillClone->GetEndPoint2()); NodeRenderableInk* pParent = (NodeRenderableInk*)pAttr->FindParent(); ERROR3IF(pParent==NULL, "Can't find parent of edited attribute"); if (pParent && pAttr->IsEffectAttribute()) // IsValidEffectAttr? { // Node* pGrandParent = pParent->FindParent(); // if (pGrandParent && pGrandParent->IsBounded()) // ((NodeRenderableBounded*)pGrandParent)->ReleaseCached(TRUE, FALSE); // Parents only pParent->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only } else pParent->ReleaseCached(); pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr); } // Redraw the old fills back DocRect SelBounds = GetApplication()->FindSelection()->GetBlobBoundingRect(); DocView::GetSelected()->ForceRedraw(StartSpread, SelBounds); } } else { // We didn't drag anything, so it must have just been a click if (ClickMods.Adjust && ClickMods.Constrain) { // Cntrl/Shift/Click will select all Start or End blobs SelectAllBlobs(); } // Tell the world that a fill blob has been selected BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); } AttrFillGeometry::LastRenderedMesh = NULL; AttrFillGeometry::HitList.DeleteAll(); delete FillClone; delete GradFill; goto EndOperation; // This will fail the op } // We have edited the fill ok, so we'll draw the blobs back on AttrFillGeometry::DraggedFill = NULL; if (IsRequired) RenderFinalBlobs(GetDragBlobRect(), StartSpread, bSolidDrag); if (AlwaysFail) { // If we just edited a fill, then we don't want any undo info, // so we'll fail the op now (and discard the Actions), before we // apply the edited fill FailAndExecute(); HaveFailed = TRUE; // no need to fail at the end } // if the drag was a sucess then make a grad fill if (Success && (!GetDragBlobRect().IsEmpty())) { AttrFillGeometry::EditedFill = NULL; AttrFillGeometry::DraggedFill = NULL; if (ApplyAtEnd) { // Update all the edited fills ApplyEditedFill(); delete FillClone; delete GradFill; } else { if (ShouldApplyNewFill) { ApplyNewFill(); } BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); SelRange *Selection = Camelot.FindSelection(); DocRect Bounds; if (Selection != NULL && Selection->Count()>0) Bounds = Selection->GetBoundingRect(); else Bounds = GetDragBlobRect(); GradFill->SetBoundingRect(Bounds); // Ensure we update all the fills if (!AttrFillGeometry::HitList.IsEmpty()) { ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); while (pAttrPtr != NULL) { AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; ERROR3IF(pAttr->IsADefaultAttr(), "Default attr in hit list"); // Update the control points on the other fills pAttr->SetStartPoint(GradFill->GetStartPoint()); pAttr->SetEndPoint(GradFill->GetEndPoint()); pAttr->SetEndPoint2(GradFill->GetEndPoint2()); // Give the attribute a bounding rect pAttr->SetBoundingRect(Bounds); // We need to ensure that we force a redraw at the end of the drag // because redraws during the drag will have been done with QuickRender turned // on and so some slow objects may not have rendered themselves // fully. if (bSolidDrag) { Node* pParent = pAttr->FindParent(); if (pParent && pParent->IsBounded()) { ((NodeRenderableBounded*)pParent)->ReleaseCached(TRUE, FALSE, !pAttr->IsEffectAttribute(), TRUE); DoInvalidateNodeRegion((NodeRenderableBounded*)pParent, FALSE, TRUE, FALSE, FALSE); // Do not recache // (SelOperation::DoApply will have called ReleaseCached as needed) } } pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr); } } AttributeManager& AttrMgr = pDoc->GetAttributeMgr(); if (AttrMgr.WeShouldMakeAttrCurrent(IsRequired,GradFill,&AttrGroups)) { AttributeManager::UpdateCurrentAttr(GradFill, TRUE, &AttrGroups); } delete FillClone; delete GradFill; } IsOk = TRUE; } EndOperation: // DMc - should inform all nodes at the end of a drag too AttrFillGeometry::EditedFill = NULL; AttrFillGeometry::DraggedFill = NULL; AttrFillGeometry::HitList.DeleteAll(); CreateFill = FALSE; // Don't forget the AttrGroups List AttrGroups.DeleteAll(); // If something went wrong, then fail (if we haven't already) if (!IsOk && !HaveFailed) { FailAndExecute(); } // always call end End(); } /******************************************************************************************** > BOOL OpEditFill::ApplyNewFill() Author: Will_Cowling (Xara Group Ltd) Created: 14/5/95 Returns: TRUE if fill was applied ok. Purpose: Applies the newly created fill to the selection. ********************************************************************************************/ BOOL OpEditFill::ApplyNewFill() { ShouldApplyNewFill = FALSE; SelRange* pSel = GetApplication()->FindSelection(); Range SelRng(*pSel); // >>Webster BOOL bShowProgress = TRUE; #ifndef EXCLUDE_FROM_XARALX if (GetKeyState(CAMKEY(LBUTTON)) & 0x80) bShowProgress = FALSE; #endif // << Webster if (bShowProgress) BeginSlowJob(-1, FALSE); ObjChangeFlags cFlags; cFlags.Attribute = TRUE; ObjChangeParam ObjChange(OBJCHANGE_STARTING,cFlags,NULL,this); ObjChange.SetRetainCachedData(TRUE); if (!pSel->AllowOp(&ObjChange)) { if (bShowProgress) EndSlowJob(); return FALSE; } DoStartSelOp(FALSE,FALSE, TRUE,TRUE); // Before we apply the attribute to the selection we must localise all attributes // with the same type that we are going to apply. If we don't do this then the // tree will be left in an invalid state. AttrTypeSet AttrTypes; AttrTypes.AddToSet((GradFill->GetAttributeType())); // Invalidate before if (!DoInvalidateRegions(&SelRng, GradFill, TRUE, NULL, FALSE)) //Mutators have to include bounds { if (bShowProgress) EndSlowJob(); return FALSE; } if (!DoLocaliseForAttrChange(&SelRng, &AttrTypes)) { if (bShowProgress) EndSlowJob(); return FALSE; } // Apply the new fill to the selection //GradFill->SetColourRamp ( //pAttr->SetColourRamp ((ColourRamp*) GradFill->GetFillRamp ()); ApplyToSelection(GradFill, TRUE); // Having applied the attributes, we must now try and factor out the newly // applied attributes if (!DoFactorOutAfterAttrChange(&SelRng, &AttrTypes)) { if (bShowProgress) EndSlowJob(); return FALSE; } // Invalidate after if (!DoInvalidateRegions(&SelRng, GradFill, TRUE, NULL, FALSE)) //Mutators have to include bounds { if (bShowProgress) EndSlowJob(); return FALSE; } AttrTypes.DeleteAll(); if (Document::GetSelected()) Document::GetSelected()->SetModified(TRUE); ObjChange.Define(OBJCHANGE_FINISHED,cFlags,NULL,this); ObjChange.SetRetainCachedData(TRUE); // Tell TextStory not to invalidate everything in OnChildChange if (!UpdateChangedNodes(&ObjChange)) { if (bShowProgress) EndSlowJob(); return FALSE; } // Get the 'actual' fills back, so we can continue to drag them AttrFillGeometry* pFill = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType()); // AttrFillGeometry* pFillToEdit = pFill; // Build a list of them while (pFill) { BOOL InList = FALSE; // We may have already hit this attribute if the selection is // inside a parent and have common attributes, so we need to // check and make sure this attribute is NOT in the list // already. if (!AttrFillGeometry::HitList.IsEmpty()) { ListItem* pItem = AttrFillGeometry::HitList.GetHead(); while (pItem) { NodeAttributePtrItem* NodePtr = (NodeAttributePtrItem*)pItem; if (NodePtr->NodeAttribPtr == pFill) { // Ignore this one, we've hit it already InList = TRUE; break; } pItem = AttrFillGeometry::HitList.GetNext(pItem); } } if (!InList) { // Add this to the list of fills to edit ListItem* pAttr = new NodeAttributePtrItem; if (pAttr == NULL) { if (bShowProgress) EndSlowJob(); return FALSE; } ERROR3IF(pFill->IsADefaultAttr(), "Default attr in hit list"); ((NodeAttributePtrItem*)pAttr)->NodeAttribPtr = pFill; AttrFillGeometry::HitList.AddTail(pAttr); } pFill = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType()); } if (bShowProgress) EndSlowJob(); return TRUE; } /******************************************************************************************** > BOOL OpEditFill::ApplyEditedFill() Author: Will_Cowling (Xara Group Ltd) Created: 14/5/95 Returns: TRUE if fill was applied ok. Purpose: Applies the edited fill, back to the editing objects. ********************************************************************************************/ BOOL OpEditFill::ApplyEditedFill() { // Put all the fills back like they were so undo will work ListItem* pAttrPtr = AttrFillGeometry::HitList.GetHead(); while (pAttrPtr != NULL) { AttrFillGeometry* pAttr = (AttrFillGeometry*)((NodeAttributePtrItem*)pAttrPtr)->NodeAttribPtr; if (pAttr != GradFill) { pAttr->SetStartPoint(FillClone->GetStartPoint()); pAttr->SetEndPoint(FillClone->GetEndPoint()); pAttr->SetEndPoint2(FillClone->GetEndPoint2()); pAttr->SetColourRamp(FillClone->GetColourRamp()); } pAttrPtr = AttrFillGeometry::HitList.GetNext(pAttrPtr); } SelRange *Selection = Camelot.FindSelection(); DocRect Bounds; if (Selection != NULL && Selection->Count()>0) Bounds = Selection->GetBoundingRect(); else Bounds = GetDragBlobRect(); // Give the attribute a bounding rect GradFill->SetBoundingRect(Bounds); BeginSlowJob(-1, FALSE); AttributeManager::SendMessages = FALSE; // Now replace all the edited attributes with the new one AttributeManager::ReplaceAttributes(GradFill, &AttrFillGeometry::HitList); AttrFillGeometry::HitList.DeleteAll(); // BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); // BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); AttributeManager::SendMessages = TRUE; EndSlowJob(); return TRUE; } /******************************************************************************************** > void OpEditFill::SelectAllBlobs() Author: Will_Cowling (Xara Group Ltd) Created: 14/5/95 Purpose: Selects all the Start or End fill blobs. ********************************************************************************************/ void OpEditFill::SelectAllBlobs() { // It's a 'Select all Blobs' thingy AttrFillGeometry::LastRenderedStartBlob = DocCoord(0,0); AttrFillGeometry::LastRenderedEndBlob = DocCoord(0,0); AttrFillGeometry::LastRenderedEnd2Blob = DocCoord(0,0); // Start again at the beginning AttrFillGeometry* pSelAttr = AttrFillGeometry::FindFirstSelectedAttr(FillClone->GetAttributeType()); while (pSelAttr != NULL) { if (pSelAttr) { pSelAttr->SelectBlob(DragControl); pSelAttr->DeselectAllBut(DragControl); } // Move onto the next. pSelAttr = AttrFillGeometry::FindNextSelectedAttr(FillClone->GetAttributeType()); } } /******************************************************************************************** > DocRect OpEditFill::GetDragBlobRect() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: The bounding rect of the NodeGradFill parallelogram Purpose: This function finds the bounding rect that the parallelogram of the NodeGradFill we are using for EOR renderings parallelogram fits in. ********************************************************************************************/ DocRect OpEditFill::GetDragBlobRect() { return GradFill->GetBlobBoundingRect(); } /******************************************************************************************** > void OpEditFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Rect - the rect that needs redrawing pSpread - the spread that is being rendered Purpose: Renders the GradFill as it will look if the drag were to end. If we failed to create the GradFill earlier, it will draw a bounding rect ********************************************************************************************/ void OpEditFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) { if (DontDrawBlobs) return; // If being called from DocView::RenderView, then the spread could be wrong - so // convert the rectangle if necessary. if (pSpread != StartSpread) { Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo); Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi); } AttrFillGeometry::EditedFill = NULL; RenderRegion* pRegion = DocView::RenderOnTop(&Rect, StartSpread, UnclippedEOR); while (pRegion) { GradFill->RenderFillBlobs(pRegion); // Get the Next render region pRegion = DocView::GetNextOnTop(&Rect); } AttrFillGeometry::EditedFill = GradFill; // Bodge to stop fill meshes EOR each other out. AttrFillGeometry::LastRenderedMesh = NULL; } /******************************************************************************************** > void OpEditFill::RenderSelectionFillBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread) Author: Will_Cowling (Xara Group Ltd) Created: 9/6/95 Inputs: Rect - the rect that needs redrawing pSpread - the spread that is being rendered Purpose: Renders the GradFill as it will look if the drag were to end. If we failed to create the GradFill earlier, it will draw a bounding rect ********************************************************************************************/ void OpEditFill::RenderSelectionFillBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread) { // Render all the selection fill blobs RenderRegion* pRegion = DocView::RenderOnTop(&Rect, pSpread, UnclippedEOR); while (pRegion) { Node* pNode = pSel->FindFirst(); while (pNode) { ((NodeRenderable*)pNode)->RenderAppliedFillBlobs(pRegion); pNode = pSel->FindNext(pNode); } // Get the Next render region pRegion = DocView::GetNextOnTop(&Rect); } AttrFillGeometry::LastRenderedMesh = NULL; } /******************************************************************************************** > void OpEditFill::RenderInitSelectionBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread) Author: Will_Cowling (Xara Group Ltd) Created: 9/6/95 Inputs: Rect - the rect that needs redrawing pSpread - the spread that is being rendered Purpose: Renders the GradFill as it will look if the drag were to end. If we failed to create the GradFill earlier, it will draw a bounding rect ********************************************************************************************/ void OpEditFill::RenderInitSelectionBlobs(SelRange* pSel, DocRect Rect, Spread* pSpread) { // EOR on in all the render regions that are still rendering, // so that the Blob rendering when the region is finished, // will put them back RenderRegionList* pRegionList = GetApplication()->GetRegionList(); if (!pRegionList->IsEmpty()) { RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead(); while (pRegion) { // Check the RenderRegion is for the same spread. if (pRegion->GetRenderSpread() == pSpread && (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper)) { // Render the blobs 'clipped' to this Render Region. DocRect ClipRect = pRegion->GetRegionRect(); if (ClipRect.IsIntersectedWith(Rect)) { ClipRect = ClipRect.Intersection(Rect); RenderSelectionFillBlobs(pSel, ClipRect, pSpread); } } // Get the Next render region pRegion = (RenderRegion*)pRegionList->GetNext(pRegion); } } // Render the blobs 'Off' RenderSelectionFillBlobs(pSel, Rect, pSpread); } /******************************************************************************************** > void OpEditFill::RenderFinalBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 9/6/95 Inputs: Rect - the rect that needs redrawing pSpread - the spread that is being rendered Purpose: Renders the GradFill as it will look if the drag were to end. If we failed to create the GradFill earlier, it will draw a bounding rect ********************************************************************************************/ void OpEditFill::RenderFinalBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) { // Render the blobs 'On' RenderDragBlobs(Rect, pSpread, bSolidDrag); // Now EOR off in all the render regions that are still rendering, // so that the Blob rendering when the region is finished, // will put them back RenderRegionList* pRegionList = GetApplication()->GetRegionList(); if (!pRegionList->IsEmpty()) { RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead(); while (pRegion) { // Check the RenderRegion is for the same spread. if (pRegion->GetRenderSpread() == pSpread && (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper)) { // Render the blobs 'clipped' to this Render Region. DocRect ClipRect = pRegion->GetRegionRect(); if (ClipRect.IsIntersectedWith(Rect)) { ClipRect = ClipRect.Intersection(Rect); RenderDragBlobs(ClipRect, pSpread, bSolidDrag); } } // Get the Next render region pRegion = (RenderRegion*)pRegionList->GetNext(pRegion); } } } /******************************************************************************************** > void OpEditFill::RenderInitBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 9/6/95 Inputs: Rect - the rect that needs redrawing pSpread - the spread that is being rendered Purpose: Renders the GradFill as it will look if the drag were to end. If we failed to create the GradFill earlier, it will draw a bounding rect ********************************************************************************************/ void OpEditFill::RenderInitBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) { // Now EOR off in all the render regions that are still rendering, // so that the Blob rendering when the region is finished, // will put them back RenderRegionList* pRegionList = GetApplication()->GetRegionList(); if (!pRegionList->IsEmpty()) { RenderRegion* pRegion = (RenderRegion*)pRegionList->GetHead(); while (pRegion) { // Check the RenderRegion is for the same spread. if (pRegion->GetRenderSpread() == pSpread && (pRegion->IsInkRenderStarted || pRegion->NeedsOSPaper)) { // Render the blobs 'clipped' to this Render Region. DocRect ClipRect = pRegion->GetRegionRect(); if (ClipRect.IsIntersectedWith(Rect)) { ClipRect = ClipRect.Intersection(Rect); RenderDragBlobs(ClipRect, pSpread, bSolidDrag); } } // Get the Next render region pRegion = (RenderRegion*)pRegionList->GetNext(pRegion); } } // Render the blobs 'On' RenderDragBlobs(Rect, pSpread, bSolidDrag); } /******************************************************************************************** > BOOL OpEditFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers Mods) Author: Will_Cowling (Xara Group Ltd) Created: 29/1/95 Inputs: pSpread - pioter to spread mouse is over DocPos - position of mouse in doc Mods - click modifiers Outputs: pText - buffer for StatusLine text Returns: FALSE if fails Purpose: Gets the status text during the editing of a fill. ********************************************************************************************/ BOOL OpEditFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods) { if (GradFill == NULL) { *pText=""; // We don't seem to be doing anything at the moment ! return TRUE; } pText->Load(_R(IDS_K_OPGRAD_EDITING)); String_256 FillName; if (FillName.Load(GradFill->GetAttrNameID())) { // Add the description of the attribute onto the base string *pText += FillName; if (ContinuousEOR) { *pText += String_256(_R(IDS_K_OPGRAD_IMMEDIATE)); } else { if (InteractiveDragUpdate) { *pText += String_256(_R(IDS_K_OPGRAD_BACKGROUND)); } else { *pText += String_256(_R(IDS_K_OPGRAD_NOUPDATE)); } } return TRUE; } return FALSE; } /******************************************************************************************** > BOOL OpEditFill::Init() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: TRUE if it works, FALSE if not Purpose: Register this operation into the list of all operations ********************************************************************************************/ BOOL OpEditFill::Init() { return (RegisterOpDescriptor( 0, _R(IDS_EDITFILLOP), CC_RUNTIME_CLASS(OpEditFill), OPTOKEN_EDITFILL, OpEditFill::GetState, 0, /* help ID */ 0, 0 /* bitmap ID */)); } /******************************************************************************************** > OpState OpEditFill::GetState(String_256* Description, OpDescriptor*) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Description - the description of the operation Returns: The State of the operation Purpose: Used for greying etc of an operation. ********************************************************************************************/ OpState OpEditFill::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } /******************************************************************************************** > OpChangeFillColour::OpChangeFillColour() Author: Will_Cowling (Xara Group Ltd) Created: 1/6/94 Purpose: Dummy Contrustor ********************************************************************************************/ /* OpChangeFillColour::OpChangeFillColour() { } */ /******************************************************************************************** > void OpChangeFillColour::Do(AttrFillGeometry* pGrad, DocColour &StartColour) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: - Purpose: - ********************************************************************************************/ /* void OpChangeFillColour::Do( DocColour &StartColour, DocColour &EndColour, AttrFillGeometry* GradFill ) { DoStartSelOp(FALSE, FALSE); ModifyFillColoursAction* ModAction; ActionCode Act; Act = ModifyFillColoursAction::Init(this, &UndoActions, 2, (Action**)(&ModAction)); if (Act == AC_FAIL) { FailAndExecute(); End(); return; } DocColour* OldColours; BOOL* OldSelect; ALLOC_WITH_FAIL(OldColours, (DocColour*) CCMalloc(2 * sizeof DocColour), this); if (!OldColours) { FailAndExecute(); End(); return; } // Strangeness new(&OldColours[0]) DocColour; new(&OldColours[1]) DocColour; OldColours[0] = (GradFill->GetAttributeValue())->Colour; OldColours[1] = (GradFill->GetAttributeValue())->EndColour; ALLOC_WITH_FAIL(OldSelect, (BOOL*) CCMalloc(2 * sizeof BOOL), this); if (!OldSelect) { CCFree(OldColours); FailAndExecute(); End(); return; } GradFill->GetSelectionState(OldSelect, 2); ModAction->StoreChanges(GradFill, OldColours, OldSelect); (GradFill->GetAttributeValue())->Colour = StartColour; (GradFill->GetAttributeValue())->EndColour = EndColour; DocView* pDocView = DocView::GetCurrent(); ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillColours" ); Node* ParentNode = GradFill->FindParent(); DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE); // always call end End(); } */ /******************************************************************************************** > BOOL OpChangeFillColour::Init() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: TRUE if it works, FALSE if not Purpose: Register this operation into the list of all operations ********************************************************************************************/ /* BOOL OpChangeFillColour::Init() { return (RegisterOpDescriptor( 0, _R(IDS_CHANGECOLOP), CC_RUNTIME_CLASS(OpChangeFillColour), OPTOKEN_CHANGEFILL, OpChangeFillColour::GetState, 0, // help ID 0, 0 // bitmap ID )); } */ /******************************************************************************************** > OpState OpChangeFillColour::GetState(String_256* Description, OpDescriptor*) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Description - the description of the operation Returns: The State of the operation Purpose: Used for greying etc of an operation. ********************************************************************************************/ /* OpState OpChangeFillColour::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } */ /******************************************************************************************** > OpChangeSelectionColours::OpChangeSelectionColours() Author: Will_Cowling (Xara Group Ltd) Created: 1/6/94 Purpose: Dummy Contrustor ********************************************************************************************/ /* OpChangeSelectionColours::OpChangeSelectionColours() { } */ /******************************************************************************************** > void OpChangeSelectionColours::Do(DocColour &NewColour) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: - Purpose: - ********************************************************************************************/ /* BOOL OpChangeSelectionColours::Do( DocColour &NewColour ) { DoStartSelOp(FALSE, FALSE); BOOL ColourChanged = FALSE; Node *SelectedNode = NULL; SelRange *Selection = Camelot.FindSelection(); if (Selection != NULL) { // Scan though all the Nodes in the Selection (and their Children) SelectedNode = Selection->FindFirst(); while (SelectedNode != NULL) { // Is it a Fill Attribute ? if ( SelectedNode->IsKindOf(CC_RUNTIME_CLASS(AttrFillGeometry)) ) { // Yes, so lets ask if it has any Control Points Selected ? if ( ((AttrFillGeometry*)SelectedNode)->ChangeControlPointColour( NewColour, this ) ) { if (!ColourChanged) { CCRuntimeClass* CurrentAttribGroup = Tool::GetCurrent()->Parent->ToolInfo.CurrentAttributeGroup; Document* CurrentDoc = Document::GetSelected(); Node* AttribClone; SelectedNode->NodeCopy(&AttribClone); CurrentDoc->GetAttributeMgr() .UpdateCurrentAttribute(CurrentAttribGroup, (NodeAttribute*)AttribClone); } Node* ParentNode = SelectedNode->FindParent(); DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE); // Yep, Someone has changed their colour. ColourChanged = TRUE; } } SelectedNode = Selection->FindNextAndChild(SelectedNode); } } if (!ColourChanged) FailAndExecute(); // always call end End(); return ColourChanged; } */ /******************************************************************************************** > BOOL OpChangeSelectionColours::Init() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: TRUE if it works, FALSE if not Purpose: Register this operation into the list of all operations ********************************************************************************************/ /* BOOL OpChangeSelectionColours::Init() { return (RegisterOpDescriptor( 0, _R(IDS_CHANGECOLOP), CC_RUNTIME_CLASS(OpChangeSelectionColours), OPTOKEN_SELECTCOLS, OpChangeSelectionColours::GetState, 0, // help ID 0, 0 // bitmap ID )); } */ /******************************************************************************************** > OpState OpChangeSelectionColours::GetState(String_256* Description, OpDescriptor*) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Description - the description of the operation Returns: The State of the operation Purpose: Used for greying etc of an operation. ********************************************************************************************/ /* OpState OpChangeSelectionColours::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } */ /////////////////////////////////////////////////////////////////////////////////////////////// // The ModifyFillCoordsAction class // /////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > ModifyFillCoordsAction::ModifyFillCoordsAction() Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action to undo fill modification Errors: - SeeAlso: - ********************************************************************************************/ /* ModifyFillCoordsAction::ModifyFillCoordsAction() { ChangedCoords = NULL; SelectionState = NULL; } */ /******************************************************************************************** > ActionCode ModifyFillCoordsAction::Init( Operation* pOp, ActionList* pActionList, Action** NewAction) Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: pOp is the pointer to the operation to which this action belongs pActionList is the action list to which this action should be added Outputs: NewAction is a pointer to a pointer to an action, allowing the function to return a pointer to the created action Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: This is the function which creates an instance of this action. If there is no room in the undo buffer (which is determined by the base class Init function called within) the function will either return AC_NO_RECORD which means the operation can continue, but no undo information needs to be stored, or AC_OK which means the operation should continue AND record undo information. If the function returns AC_FAIL, there was not enough memory to record the undo information, and the user has decided not to continue with the operation. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ /* ActionCode ModifyFillCoordsAction::Init( Operation* pOp, ActionList* pActionList, INT32 NumChanged, Action** NewAction) { UINT32 ActSize = sizeof(ModifyFillCoordsAction) + (NumChanged * sizeof(DocCoord)) + (NumChanged * sizeof(BOOL)); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ModifyFillCoordsAction), NewAction); ((ModifyFillCoordsAction*)*NewAction)->NumChangedCoords = NumChanged; if (IsUserName("Will")) TRACE( _T("Creating ModifyFillCoordsAction")); return Ac; } */ /******************************************************************************************** > void ModifyFillCoordsAction::StoreChanges(DocCoord* Coords, DocColour* Colours) Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: Coords is a pointer to an array of DocCoords Colours is a pointer to an array of DocColours Outputs: - Returns: - Purpose: This function initialises the array pointers in this action. Note that the variable NumElements is initialised in the Init function Errors: - SeeAlso: - ********************************************************************************************/ /* void ModifyFillCoordsAction::StoreChanges(AttrFillGeometry* Fill, DocCoord* Coords, BOOL* SelState) { ChangedFill = Fill; ChangedCoords = Coords; SelectionState = SelState; } */ /******************************************************************************************** > ActionCode ModifyFillCoordsAction::Execute() Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: - Outputs: - Returns: Action code, one of AC_OK, AC_NORECORD or AC_FAIL. Purpose: This is the virtual function that is called when the action is executed by the Undo/Redo system. This is the function that actually undoes the ModifyFillCoords action by changing the attribute values, and records redo information from the current values. Errors: - SeeAlso: - ********************************************************************************************/ /* ActionCode ModifyFillCoordsAction::Execute() { ModifyFillCoordsAction* ModAction; ActionCode Act; Act = ModifyFillCoordsAction::Init(pOperation, pOppositeActLst, NumChangedCoords, (Action**)(&ModAction)); if (Act == AC_FAIL) return AC_FAIL; DocView* pDocView = DocView::GetCurrent(); ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillCoords" ); DocCoord StartPointTemp = ChangedCoords[0]; DocCoord EndPointTemp = ChangedCoords[1]; DocCoord EndPoint2Temp = ChangedCoords[2]; BOOL StartSelTemp = SelectionState[0]; BOOL EndSelTemp = SelectionState[1]; BOOL End2SelTemp = SelectionState[2]; ChangedCoords[0] = (ChangedFill->GetAttributeValue())->StartPoint; ChangedCoords[1] = (ChangedFill->GetAttributeValue())->EndPoint; ChangedCoords[2] = (ChangedFill->GetAttributeValue())->EndPoint2; BOOL CurrentSelState[3]; ChangedFill->GetSelectionState(CurrentSelState, 3); SelectionState[0] = CurrentSelState[0]; SelectionState[1] = CurrentSelState[1]; SelectionState[2] = CurrentSelState[2]; CurrentSelState[0] = StartSelTemp; CurrentSelState[1] = EndSelTemp; CurrentSelState[2] = End2SelTemp; ChangedFill->SetSelectionState(CurrentSelState, 3); (ChangedFill->GetAttributeValue())->StartPoint = StartPointTemp; (ChangedFill->GetAttributeValue())->EndPoint = EndPointTemp; (ChangedFill->GetAttributeValue())->EndPoint2 = EndPoint2Temp; ModAction->StoreChanges(ChangedFill, ChangedCoords, SelectionState); ChangedCoords = NULL; SelectionState = NULL; Node* ParentNode = ChangedFill->FindParent(); ((UndoableOperation*)pOperation)-> DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE); return Act; } ModifyFillCoordsAction::~ModifyFillCoordsAction() { if (ChangedCoords) CCFree(ChangedCoords); if (SelectionState) CCFree(SelectionState); } */ /////////////////////////////////////////////////////////////////////////////////////////////// // The ModifyFillColoursAction class // /////////////////////////////////////////////////////////////////////////////////////////////// /******************************************************************************************** > ModifyFillColoursAction::ModifyFillColoursAction() Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action to undo fill modification Errors: - SeeAlso: - ********************************************************************************************/ /* ModifyFillColoursAction::ModifyFillColoursAction() { ChangedColours = NULL; SelectionState = NULL; } */ /******************************************************************************************** > ActionCode ModifyFillColoursAction::Init( Operation* pOp, ActionList* pActionList, Action** NewAction) Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: pOp is the pointer to the operation to which this action belongs pActionList is the action list to which this action should be added Outputs: NewAction is a pointer to a pointer to an action, allowing the function to return a pointer to the created action Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: This is the function which creates an instance of this action. If there is no room in the undo buffer (which is determined by the base class Init function called within) the function will either return AC_NO_RECORD which means the operation can continue, but no undo information needs to be stored, or AC_OK which means the operation should continue AND record undo information. If the function returns AC_FAIL, there was not enough memory to record the undo information, and the user has decided not to continue with the operation. Errors: - SeeAlso: Action::Init() ********************************************************************************************/ /* ActionCode ModifyFillColoursAction::Init( Operation* pOp, ActionList* pActionList, INT32 NumChanged, Action** NewAction) { UINT32 ActSize = sizeof(ModifyFillColoursAction) + (NumChanged * sizeof(DocColour)) + (NumChanged * sizeof(BOOL)); ActionCode Ac = Action::Init( pOp, pActionList, ActSize, CC_RUNTIME_CLASS(ModifyFillColoursAction), NewAction); ((ModifyFillColoursAction*)*NewAction)->NumChangedColours = NumChanged; if (IsUserName("Will")) TRACE( _T("Creating ModifyFillColoursAction")); return Ac; } */ /******************************************************************************************** > void ModifyFillColoursAction::StoreChanges(AttrFillGeometry* Fill, DocColour* Colours) Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: Colours is a pointer to an array of DocColours Outputs: - Returns: - Purpose: This function initialises the array pointers in this action. Note that the variable NumElements is initialised in the Init function Errors: - SeeAlso: - ********************************************************************************************/ /* void ModifyFillColoursAction::StoreChanges(AttrFillGeometry* Fill, DocColour* Colours, BOOL* SelState) { ChangedFill = Fill; ChangedColours = Colours; SelectionState = SelState; } */ /******************************************************************************************** > ActionCode ModifyFillColoursAction::Execute() Author: Will_Cowling (Xara Group Ltd) Created: 24/7/94 Inputs: - Outputs: - Returns: Action code, one of AC_OK, AC_NORECORD or AC_FAIL. Purpose: This is the virtual function that is called when the action is executed by the Undo/Redo system. This is the function that actually undoes the ModifyFillColours action by changing the attribute values, and records redo information from the current values. Errors: - SeeAlso: - ********************************************************************************************/ /* ActionCode ModifyFillColoursAction::Execute() { ModifyFillColoursAction* ModAction; ActionCode Act; Act = ModifyFillColoursAction::Init(pOperation, pOppositeActLst, NumChangedColours, (Action**)(&ModAction)); if (Act == AC_FAIL) return AC_FAIL; DocView* pDocView = DocView::GetCurrent(); ENSURE( pDocView != NULL, "There was no current document when undoing ModifyFillColours" ); DocColour StartTemp = ChangedColours[0]; DocColour EndTemp = ChangedColours[1]; BOOL StartSelTemp = SelectionState[0]; BOOL EndSelTemp = SelectionState[1]; ChangedColours[0] = (ChangedFill->GetAttributeValue())->Colour; ChangedColours[1] = (ChangedFill->GetAttributeValue())->EndColour; BOOL CurrentSelState[2]; ChangedFill->GetSelectionState(CurrentSelState, 2); SelectionState[0] = CurrentSelState[0]; SelectionState[1] = CurrentSelState[1]; CurrentSelState[0] = StartSelTemp; CurrentSelState[1] = EndSelTemp; ChangedFill->SetSelectionState(CurrentSelState, 2); (ChangedFill->GetAttributeValue())->Colour = StartTemp; (ChangedFill->GetAttributeValue())->EndColour = EndTemp; ModAction->StoreChanges(ChangedFill, ChangedColours, SelectionState); ChangedColours = NULL; SelectionState = NULL; Node* ParentNode = ChangedFill->FindParent(); ((UndoableOperation*)pOperation)-> DoInvalidateNodeRegion((NodeRenderableInk*)ParentNode, TRUE); return Act; } ModifyFillColoursAction::~ModifyFillColoursAction() { if (ChangedColours) { for (INT32 i=0; i < NumChangedColours; i++) { ChangedColours[i].~DocColour(); } CCFree(ChangedColours); } if (SelectionState) CCFree(SelectionState); } */ /******************************************************************************************** > OpCreateFill::OpCreateFill() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Purpose: Dummy Constructor ********************************************************************************************/ OpCreateFill::OpCreateFill() { GradFill = NULL; } /******************************************************************************************** > void OpCreateFill::DoDrag( AttrFillGeometry* Fill, Spread* pSpread, DocCoord Anchor) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: pSpread - The Spread the drag is starting on Anchor - The coord of the start of the drag Purpose: Starts the drag and records the initial position of the mouse etc. It also snaps the start position to the grid if it needs to. ********************************************************************************************/ void OpCreateFill::DoDrag( AttrFillGeometry* Fill, Spread* pSpread, DocCoord Anchor) { DocView::SnapSelected(pSpread, &Anchor); // We had better take a note of the starting point of the drag StartSpread = pSpread; StartPoint = Anchor; // init the old and the new mouse position needed in drawing the rubber box LastMousePosition = Anchor; GradFill = Fill; GradFill->SetStartPoint(&Anchor); GradFill->SetEndPoint(&Anchor); if (GradFill->GetEndPoint2() != NULL) GradFill->SetEndPoint2(&Anchor); // Remove other fill meshs whilst we are dragging BlobStyle MyBlobs; MyBlobs.Fill = TRUE; (Camelot.GetBlobManager())->RemoveInterest(MyBlobs); AttrFillGeometry::DraggedFill = GradFill; // And tell the Dragging system that we need drags to happen // StartDrag( DRAGTYPE_AUTOSCROLL ); StartDrag(DRAGTYPE_AUTOSCROLL, NULL, NULL, TRUE, TRUE); // We support solid dragging } /******************************************************************************************** > void OpCreateFill::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: PointerPos - The current mouse position ClickMods - Which modifier keys were being pressed at the time pSpread - The Spread that the mouse is over now Purpose: re-draws the drag blobs as the mouse moves about the document. ********************************************************************************************/ void OpCreateFill::DragPointerMove(DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) { // Snap the actual mouse position to the grid if needed DocView::SnapSelected(pSpread, &PointerPos); // if we need to constrain the drag, then make our GradFill into a circle if (ClickMods.Constrain) DocView::ConstrainToAngle(StartPoint, PI/4, &PointerPos); // If the mouse is in a different position then do something if (PointerPos != LastMousePosition) { // First Rub out the old Drag blobs DocRect BoundingRect = GetDragBlobRect(); RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag); // Make sure that the coords are relative to the coorect spread if (pSpread != StartSpread) PointerPos = MakeRelativeToSpread(StartSpread, pSpread, PointerPos); // Update the last mouse position and re-calc the bounding rect LastMousePosition = PointerPos; DocCoord Start = StartPoint; DocCoord End = PointerPos; DocCoord End2 = MakeLineAtAngle(StartPoint, PointerPos); // If we are creating a Bitmap Fill, then try to create it // with the correct aspect ratio if (GradFill->GetBitmap() != NULL) { BitmapInfo Info; KernelBitmap* Bmp = GradFill->GetBitmap(); AttrFillGeometry* Attr = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType()); while (Attr != NULL) { if (Attr->GetBitmap() != NULL) { Bmp = Attr->GetBitmap(); break; } Attr = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType()); } // if possible we base our default size on the bitmaps preferred size if (Bmp && Bmp->ActualBitmap) { if (Bmp->ActualBitmap->GetInfo( &Info )) { INT32 BitmapWidth = Info.RecommendedWidth; INT32 BitmapHeight = Info.RecommendedHeight; // Calculate the spect ratio of the bitmap double Ratio = double(BitmapHeight)/double(BitmapWidth); End2 = MakeLineAtAngle(Start, End, 90, INT32(Start.Distance(End) * Ratio)); } } } if (GradFill->IsKindOf(CC_RUNTIME_CLASS(AttrBitmapFill))) { // Get the Real Control Points // (The user only sees the virtual points) GetBitmapRealPoints(Start, End, End2, &Start, &End, &End2); } // Update the new Fill's points GradFill->SetStartPoint(&Start); GradFill->SetEndPoint(&End); if (GradFill->GetEndPoint2() != NULL) GradFill->SetEndPoint2(&End2); BoundingRect = GetDragBlobRect(); // Draw the Fill Mesh RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag); } } /******************************************************************************************** > void OpCreateFill::DragFinished(DocCoord PointerPos, ClickModifiers, Spread* pSpread, BOOL Success, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 1/6/94 Inputs: PointerPos - The position of the mouse at the end of the drag ClickModifiers - Not Used pSpread - The spread that the drag ended over Success - TRUE if the drag worked Purpose: Called when the drag ends. This function rubs out the drag blobs and then builds a new GradFill Node and adds it to the end of the tree. ********************************************************************************************/ void OpCreateFill::DragFinished(DocCoord PointerPos, ClickModifiers, Spread* pSpread, BOOL Success, BOOL bSolidDrag) { // Put the hour glass up as we have to BeginSlowJob(); DocRect DragRect; // First Rub out the old Drag blobs DocRect BoundingRect = GetDragBlobRect(); RenderDragBlobs(BoundingRect, StartSpread, bSolidDrag); // Get the scaled pixel size for the view FIXED16 ScaledPixelWidth, ScaledPixelHeight; GetWorkingView()->GetScaledPixelSize(&ScaledPixelWidth, &ScaledPixelHeight); INT32 PixWidth = ScaledPixelWidth.MakeLong(); INT32 PixHeight = ScaledPixelHeight.MakeLong(); // Flag to say if everything has worked BOOL IsOk = FALSE; // End the Drag if (!EndDrag()) goto EndOperation; if (PointerPos == StartPoint) goto EndOperation; if (*GradFill->GetStartPoint() == *GradFill->GetEndPoint()) goto EndOperation; DragRect.IncludePoint(StartPoint); DragRect.IncludePoint(PointerPos); if (DragRect.Width() < PixWidth*2 && DragRect.Height() < PixHeight*2) { TRACEUSER( "Will", _T("Drag rect too small\n")); goto EndOperation; } // if the drag was a sucess then make a grad fill if (Success && (!DragRect.IsEmpty())) { if (GradFill != NULL) { CCRuntimeClass* CurrentAttribGroup = Tool::GetCurrent()->Parent->m_ToolInfo.CurrentAttributeGroup; Document* CurrentDoc = Document::GetSelected(); AttrFillGeometry* CurrentAttr = (AttrFillGeometry*)(CurrentDoc->GetAttributeMgr() .GetCurrentAttribute(CurrentAttribGroup, GradFill->GetAttributeType())); // Is there a current attribute of this type ? if (CurrentAttr != NULL) { BOOL Locked = ((FillGeometryAttribute*)GradFill->GetAttributeValue())->IsAspectLocked(); // Use the current Attributes Colours *((FillGeometryAttribute*)GradFill->GetAttributeValue()) = *((FillGeometryAttribute*)CurrentAttr->GetAttributeValue()); ((FillGeometryAttribute*)GradFill->GetAttributeValue())->SetAspectLock(Locked); DocCoord Start = StartPoint; DocCoord End = LastMousePosition; DocCoord End2 = MakeLineAtAngle(StartPoint, LastMousePosition); // Ensure the Aspect ratio is correct for a new // bitmap fill if (GradFill->GetBitmap() != NULL) { BitmapInfo Info; KernelBitmap* Bmp = GradFill->GetBitmap(); AttrFillGeometry* Attr = AttrFillGeometry::FindFirstSelectedAttr(GradFill->GetAttributeType()); while (Attr != NULL) { if (Attr->GetBitmap() != NULL) { Bmp = Attr->GetBitmap(); break; } Attr = AttrFillGeometry::FindNextSelectedAttr(GradFill->GetAttributeType()); } // if possible we base our default size on the bitmaps preferred size if (Bmp && Bmp->ActualBitmap) { if (Bmp->ActualBitmap->GetInfo( &Info )) { INT32 BitmapWidth = Info.RecommendedWidth; INT32 BitmapHeight = Info.RecommendedHeight; // Calculate the spect ratio of the bitmap double Ratio = double(BitmapHeight)/double(BitmapWidth); End2 = MakeLineAtAngle(Start, End, 90, INT32(Start.Distance(End) * Ratio)); } } } if (GradFill->IsKindOf(CC_RUNTIME_CLASS(AttrBitmapFill))) { // Get the Real Control Points // (The user only sees the virtual points) GetBitmapRealPoints(Start, End, End2, &Start, &End, &End2); } // Set the new fills points GradFill->SetStartPoint(&Start); GradFill->SetEndPoint(&End); if (GradFill->GetEndPoint2() != NULL) GradFill->SetEndPoint2(&End2); } else { // No current fill so use default colours *GradFill->GetStartColour() = DocColour(COLOUR_WHITE); *GradFill->GetEndColour() = DocColour(COLOUR_BLACK); } if (GradFill->GetAttributeType() == CC_RUNTIME_CLASS(AttrTranspFillGeometry)) { UINT32 Start = 0; UINT32 End = 255; GradFill->SetStartTransp(&Start); GradFill->SetEndTransp(&End); } SelRange *Selection = Camelot.FindSelection(); DocRect Bounds; if (Selection != NULL && Selection->Count()>0) Bounds = Selection->GetBoundingRect(); else Bounds = GetDragBlobRect(); // Give the attribute a bounding rect GradFill->SetBoundingRect(Bounds); GradFill->SelectBlob(FILLCONTROL_ENDPOINT); // And pass it to the attribute manager to sort the rest out AttributeManager::SendMessages = FALSE; AttributeManager::AttributeSelected(GradFill); AttributeManager::SendMessages = TRUE; IsOk = TRUE; } } EndOperation: // If something went wrong, then fail if (!IsOk) { delete GradFill; FailAndExecute(); } AttrFillGeometry::DraggedFill = NULL; // Turn the fill meshes back on BlobStyle MyBlobs; MyBlobs.Fill = TRUE; (Camelot.GetBlobManager())->AddInterest(MyBlobs); AttrFillGeometry::SelectionCount = AttrFillGeometry::CountSelectionControlPoints(); // We've probably changed a Colour BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::COLOURATTCHANGED)); // and the Attrib may have changed shape or summit. Who knows ? BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::NONCOLOURATTCHANGED)); // always call end End(); } /******************************************************************************************** > void OpCreateFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Rect - The rectangle that needs to be redrawn pSpread - the spread that it should be drawn on Purpose: Renders the EOR blobs during a drag. It can also get called from OnDraw() ********************************************************************************************/ void OpCreateFill::RenderDragBlobs(DocRect Rect, Spread* pSpread, BOOL bSolidDrag) { // If being called from DocView::RenderView, then the spread could be wrong - so // convert the rectangle if necessary. if (pSpread != StartSpread) { Rect.lo = MakeRelativeToSpread(StartSpread, pSpread, Rect.lo); Rect.hi = MakeRelativeToSpread(StartSpread, pSpread, Rect.hi); } RenderRegion* pRegion = DocView::RenderOnTop(&Rect, StartSpread, UnclippedEOR); while (pRegion) { GradFill->RenderFillBlobs(pRegion); // Get the Next render region pRegion = DocView::GetNextOnTop(&Rect); } // Bodge to stop fill meshes EOR each other out. AttrFillGeometry::LastRenderedMesh = NULL; } /******************************************************************************************** > DocRect OpCreateFill::GetDragBlobRect() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: The bounding rect of the NodeGradFill parallelogram Purpose: This function finds the bounding rect that the parallelogram of the NodeGradFill we are using for EOR renderings parallelogram fits in. ********************************************************************************************/ DocRect OpCreateFill::GetDragBlobRect() { return GradFill->GetBlobBoundingRect(); } /******************************************************************************************** > BOOL OpCreateFill::Init() Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Returns: TRUE if the operation was initialised sucessfully Purpose: Declare the operations OpDescriptor ********************************************************************************************/ BOOL OpCreateFill::Init() { return (RegisterOpDescriptor( 0, _R(IDS_CREATEFILLOP), CC_RUNTIME_CLASS(OpCreateFill), OPTOKEN_GRADFILL, OpCreateFill::GetState, 0, /* help ID */ 0, 0 /* bitmap ID */)); } /******************************************************************************************** > OpState OpCreateFill::GetState(String_256* Description, OpDescriptor*) Author: Will_Cowling (Xara Group Ltd) Created: 20/7/94 Inputs: Description - The Description of the operation Purpose: Finds out the operations current state for greying etc in menus ********************************************************************************************/ OpState OpCreateFill::GetState(String_256* Description, OpDescriptor*) { OpState Blobby; return Blobby; } /******************************************************************************************** > BOOL OpCreateFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers Mods) Author: Will_Cowling (Xara Group Ltd) Created: 29/1/95 Inputs: pSpread - pioter to spread mouse is over DocPos - position of mouse in doc Mods - click modifiers Outputs: pText - buffer for StatusLine text Returns: FALSE if fails Purpose: Gets the status text during the creation of a fill. ********************************************************************************************/ BOOL OpCreateFill::GetStatusLineText(String_256* pText, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods) { if (GradFill == NULL) { *pText=""; // We don't seem to be doing anything at the moment ! return TRUE; } pText->Load(_R(IDS_K_OPGRAD_CREATENEW)); String_256 FillName; if (FillName.Load(GradFill->GetAttrNameID())) { // Add the description of the attribute onto the base string *pText += FillName; return TRUE; } return FALSE; } #endif // STANDALONE /******************************************************************************************** These are some Global Functions, used by the Grad Fill stuff to render it's fill mesh bits. They are only tempory. ********************************************************************************************/ /******************************************************************************************** > void MakeMeshArrow(Path* ArrowPath, DocCoord &Start, DocCoord &End, DocCoord* LineEnd) Author: Will_Cowling (Xara Group Ltd) Created: 21/07/94 Inputs: - Returns: - Purpose: Makes an Arrow for the Fill Mesh. This is a Tempory Function, used until ArrowHeads work properly. ********************************************************************************************/ void MakeMeshArrow(Path* ArrowPath, DocCoord &Start, DocCoord &End, DocCoord* LineEnd) { #if !defined(EXCLUDE_FROM_RALPH) // First we Must caculate the angle of the arrow from the line angle const INT32 dx = Start.x - End.x; const INT32 dy = Start.y - End.y; if ( (dy==0) && (dx==0) ) { ArrowPath->InsertMoveTo(End); ArrowPath->InsertLineTo(End); ArrowPath->TryToClose(); *LineEnd = End; return; // draw nothing as too small } double ArrowAngle; if ( (dx>=0) && (dy>=0) ) ArrowAngle = PI - atan2( (double)dx, (double)dy ); else if ( (dx<0) && (dy>=0) ) ArrowAngle = -PI + atan2( (double)-dx, (double)dy ); else if ( (dx<0) && (dy<0) ) ArrowAngle = -atan2( (double)-dx, (double)-dy ); else if ( (dx>=0) && (dy<0) ) ArrowAngle = atan2( (double)dx, (double)-dy ); else { TRACE( _T("invalid angle on arrowhead\n")); // this should be impossible return; } ANGLE RotateAngle = 360 * (ArrowAngle / (2*PI)); // Convert from radians to degrees Matrix ArrowMatrix = Matrix(RotateAngle); // Rotate it ArrowMatrix *= Matrix(End); // Move it to the line point const UINT32 PointCount = 4; Coord ArrowPts[PointCount]; INT32 BlobSize = (Camelot.GetBlobManager())->GetBlobSize(); INT32 ArrowWidth = (BlobSize * 3)/2; INT32 ArrowHeight = (BlobSize * 3)/2; ArrowPts[0] = Coord( 0, 0); ArrowPts[1] = Coord( -ArrowWidth/2, -ArrowHeight); ArrowPts[2] = Coord( ArrowWidth/2, -ArrowHeight); ArrowPts[3] = Coord( 0, -ArrowHeight); ArrowMatrix.transform(ArrowPts, PointCount); ArrowPath->InsertMoveTo(DocCoord(ArrowPts[0].x, ArrowPts[0].y)); ArrowPath->InsertLineTo(DocCoord(ArrowPts[1].x, ArrowPts[1].y)); ArrowPath->InsertLineTo(DocCoord(ArrowPts[2].x, ArrowPts[2].y)); ArrowPath->InsertLineTo(DocCoord(ArrowPts[0].x, ArrowPts[0].y)); ArrowPath->TryToClose(); *LineEnd = DocCoord(ArrowPts[3].x, ArrowPts[3].y); #endif } /******************************************************************************************** > void MakeMeshDottedLine(Path* ArrowPath, DocCoord &Start, DocCoord &End) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Makes a Dotted Line for the Fill Mesh. This is a Tempory Function, used until Stroke Providers work properly. ********************************************************************************************/ void MakeMeshDottedLine(Path* ArrowPath, DocCoord &Start, DocCoord &End) { #if !defined(EXCLUDE_FROM_RALPH) ArrowPath->InsertMoveTo(Start); ArrowPath->InsertLineTo(End); #endif } /******************************************************************************************** > void MakeMeshSemiCircle(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord ArrowPoint) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Make a Semi-Circle for the Conical Fill Mesh. ********************************************************************************************/ void MakeMeshSemiCircle(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord ArrowPoint) { #if !defined(EXCLUDE_FROM_RALPH) End = MakeLineAtAngle(Start, End, 180); DocCoord End2 = MakeLineAtAngle(Start, End); ANGLE LineAngle = CalcLineAngle(Start, End); Matrix Rotate = Matrix(-LineAngle); End.translate(-Start.x, -Start.y); End2.translate(-Start.x, -Start.y); Rotate.transform(&End); Rotate.transform(&End2); End.translate(Start.x, Start.y); End2.translate(Start.x, Start.y); DocCoord Parallel[4]; INT32 dx = End2.x - Start.x; INT32 dy = Start.y - End.y; // Copy the rectangle into the parallelogram Parallel[0] = DocCoord(Start.x - dx, Start.y - dy); Parallel[1] = DocCoord(Start.x + dx, Start.y - dy); Parallel[2] = DocCoord(Start.x + dx, Start.y + dy); Parallel[3] = DocCoord(Start.x - dx, Start.y + dy); // Get an array to put the 12 different coords needed to specify an ellipse DocCoord NewCoords[12]; // Calculate the 3 coordinates along each side of the parallelogram CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]); CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]); CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]); CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]); Matrix Rotate2 = Matrix(LineAngle); for (INT32 i=0; i<12; i++) { NewCoords[i].translate(-Start.x, -Start.y); Rotate2.transform(&NewCoords[i]); NewCoords[i].translate(Start.x, Start.y); } // build a path EllipsePath->ClearPath(); EllipsePath->FindStartOfPath(); // Start at bottom left corner // EllipsePath->InsertMoveTo(NewCoords[0]); // EllipsePath->InsertCurveTo(NewCoords[1], NewCoords[2], NewCoords[3]); // EllipsePath->InsertCurveTo(NewCoords[4], NewCoords[5], NewCoords[6]); EllipsePath->InsertMoveTo(ArrowPoint); EllipsePath->InsertCurveTo(NewCoords[7], NewCoords[8], NewCoords[9]); EllipsePath->InsertCurveTo(NewCoords[10], NewCoords[11], NewCoords[0]); // Close the path properly // EllipsePath->CloseSubPath(); #endif } /******************************************************************************************** > DocRect GetMeshSemiCircleBounds(DocCoord Start, DocCoord End, DocCoord EndPoint) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Gets the Bounding rect of the Conical Fill Mesh. ********************************************************************************************/ DocRect GetMeshSemiCircleBounds(DocCoord Start, DocCoord End, DocCoord EndPoint) { #if !defined(EXCLUDE_FROM_RALPH) DocCoord End2 = MakeLineAtAngle(Start, End); ANGLE LineAngle = CalcLineAngle(Start, End); Matrix Rotate = Matrix(-LineAngle); End.translate(-Start.x, -Start.y); End2.translate(-Start.x, -Start.y); Rotate.transform(&End); Rotate.transform(&End2); End.translate(Start.x, Start.y); End2.translate(Start.x, Start.y); DocCoord Parallel[4]; INT32 dx = End2.x - Start.x; INT32 dy = Start.y - End.y; // Copy the rectangle into the parallelogram Parallel[0] = DocCoord(Start.x - dx, Start.y - dy); Parallel[1] = DocCoord(Start.x + dx, Start.y - dy); Parallel[2] = DocCoord(Start.x + dx, Start.y + dy); Parallel[3] = DocCoord(Start.x - dx, Start.y + dy); // Get an array to put the 12 different coords needed to specify an ellipse DocCoord NewCoords[12]; // Calculate the 3 coordinates along each side of the parallelogram CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]); CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]); CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]); CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]); Matrix Rotate2 = Matrix(LineAngle); for (INT32 i=0; i<12; i++) { NewCoords[i].translate(-Start.x, -Start.y); Rotate2.transform(&NewCoords[i]); NewCoords[i].translate(Start.x, Start.y); } DocRect Bounds = DocRect(EndPoint, EndPoint); Bounds.IncludePoint(NewCoords[6]); Bounds.IncludePoint(NewCoords[7]); Bounds.IncludePoint(NewCoords[8]); Bounds.IncludePoint(NewCoords[9]); Bounds.IncludePoint(NewCoords[10]); Bounds.IncludePoint(NewCoords[11]); return Bounds; #else return DocRect(0,0,0,0); #endif } /******************************************************************************************** > void MakeMeshEllipse(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord End2) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Makes an Elliptical Fill Mesh. ********************************************************************************************/ void MakeMeshEllipse(Path* EllipsePath, DocCoord Start, DocCoord End, DocCoord End2) { #if !defined(EXCLUDE_FROM_RALPH) if (End2 == DocCoord(0,0)) { End2 = MakeLineAtAngle(Start, End); } ANGLE LineAngle = CalcLineAngle(Start, End); Matrix Rotate = Matrix(-LineAngle); End.translate(-Start.x, -Start.y); End2.translate(-Start.x, -Start.y); Rotate.transform(&End); Rotate.transform(&End2); End.translate(Start.x, Start.y); End2.translate(Start.x, Start.y); DocCoord Parallel[4]; INT32 dx = End2.x - Start.x; INT32 dy = Start.y - End.y; // Copy the rectangle into the parallelogram Parallel[0] = DocCoord(Start.x - dx, Start.y - dy); Parallel[1] = DocCoord(Start.x + dx, Start.y - dy); Parallel[2] = DocCoord(Start.x + dx, Start.y + dy); Parallel[3] = DocCoord(Start.x - dx, Start.y + dy); // Get an array to put the 12 different coords needed to specify an ellipse DocCoord NewCoords[12]; // Calculate the 3 coordinates along each side of the parallelogram CalcMeshEllipseEdge(Parallel[0], Parallel[1], &NewCoords[11], &NewCoords[0], &NewCoords[1]); CalcMeshEllipseEdge(Parallel[1], Parallel[2], &NewCoords[2], &NewCoords[3], &NewCoords[4]); CalcMeshEllipseEdge(Parallel[2], Parallel[3], &NewCoords[5], &NewCoords[6], &NewCoords[7]); CalcMeshEllipseEdge(Parallel[3], Parallel[0], &NewCoords[8], &NewCoords[9], &NewCoords[10]); Matrix Rotate2 = Matrix(LineAngle); for (INT32 i=0; i<12; i++) { NewCoords[i].translate(-Start.x, -Start.y); Rotate2.transform(&NewCoords[i]); NewCoords[i].translate(Start.x, Start.y); } // build a path EllipsePath->ClearPath(); EllipsePath->FindStartOfPath(); // Start at bottom left corner EllipsePath->InsertMoveTo(NewCoords[0]); EllipsePath->InsertCurveTo(NewCoords[1], NewCoords[2], NewCoords[3]); EllipsePath->InsertCurveTo(NewCoords[4], NewCoords[5], NewCoords[6]); EllipsePath->InsertCurveTo(NewCoords[7], NewCoords[8], NewCoords[9]); EllipsePath->InsertCurveTo(NewCoords[10], NewCoords[11], NewCoords[0]); // Close the path properly EllipsePath->CloseSubPath(); #endif } /******************************************************************************************** > void CalcMeshEllipseEdge(const DocCoord& P0, const DocCoord& P1, DocCoord* NewCoordA, DocCoord* NewCoordB, DocCoord* NewCoordC) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Used when making an Elliptical Fill Mesh. ********************************************************************************************/ void CalcMeshEllipseEdge(const DocCoord& P0, const DocCoord& P1, DocCoord* NewCoordA, DocCoord* NewCoordB, DocCoord* NewCoordC) { #if !defined(EXCLUDE_FROM_RALPH) // Do the maths. This is described in the document camelot\docs\ellipse.doc // Find the length of the first side (pythag) INT32 dx = P1.x - P0.x; INT32 dy = P1.y - P0.y; //double SideLength = sqrt(dx*dx + dy*dy); //ENSURE(SideLength!=0.0, "CalcMeshEllipseEdge was thinking of doing a div 0!"); // This would be different if we were calculating a rounded rectangle //double ArcRadius = SideLength / 2.0; //double ControlDist = ArcRadius/(SideLength*2.2336); // This is equivalent to the above 2 lines, only less maths is needed // (ie none as the compiler should do it) double ControlDist = 1.0 / (2 * 2.2336); // Find the control point closest to P0 NewCoordA->x = (INT32) (P0.x + (dx * ControlDist)); NewCoordA->y = (INT32) (P0.y + (dy * ControlDist)); // Find the mid point of the side, for the middle control point NewCoordB->x = (INT32) (P0.x + (dx / 2)); NewCoordB->y = (INT32) (P0.y + (dy / 2)); // Find the Control point closest to p1 NewCoordC->x = (INT32) (P1.x - dx * ControlDist); NewCoordC->y = (INT32) (P1.y - dy * ControlDist); #endif } /******************************************************************************************** > DocRect GetMeshEllipseBounds(DocCoord Start, DocCoord End, DocCoord End2) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Gets the Bounding rect of the Radial Fill Mesh. ********************************************************************************************/ DocRect GetMeshEllipseBounds(DocCoord Start, DocCoord End, DocCoord End2) { #if !defined(EXCLUDE_FROM_RALPH) if (End2 == DocCoord(0,0)) { End2 = MakeLineAtAngle(Start, End); } ANGLE LineAngle = CalcLineAngle(Start, End); Matrix Rotate = Matrix(-LineAngle); End.translate(-Start.x, -Start.y); End2.translate(-Start.x, -Start.y); Rotate.transform(&End); Rotate.transform(&End2); End.translate(Start.x, Start.y); End2.translate(Start.x, Start.y); DocCoord Parallel[4]; INT32 dx = End2.x - Start.x; INT32 dy = Start.y - End.y; // Copy the rectangle into the parallelogram Parallel[0] = DocCoord(Start.x - dx, Start.y - dy); Parallel[1] = DocCoord(Start.x + dx, Start.y - dy); Parallel[2] = DocCoord(Start.x + dx, Start.y + dy); Parallel[3] = DocCoord(Start.x - dx, Start.y + dy); Matrix Rotate2 = Matrix(LineAngle); for (INT32 i=0; i<4; i++) { Parallel[i].translate(-Start.x, -Start.y); Rotate2.transform(&Parallel[i]); Parallel[i].translate(Start.x, Start.y); } DocRect Bounds = DocRect(Parallel[0], Parallel[0]); Bounds.IncludePoint(Parallel[1]); Bounds.IncludePoint(Parallel[2]); Bounds.IncludePoint(Parallel[3]); return Bounds; #else return DocRect(0,0,0,0); #endif } /******************************************************************************************** > void MakePerpendicularMeshLines(DocCoord Start, DocCoord End, DocRect Bounds, DocCoord* PointArray) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Makes lines perpendicular to a fill line. ********************************************************************************************/ void MakePerpendicularMeshLines(DocCoord Start, DocCoord End, DocRect Bounds, DocCoord* PointArray) { #if !defined(EXCLUDE_FROM_RALPH) INT32 Infinity = max(Bounds.Width(), Bounds.Height()); PointArray[0] = MakeLineAtAngle(Start, End, 90, Infinity); PointArray[1] = MakeLineAtAngle(Start, End, -90, Infinity); PointArray[2] = MakeLineAtAngle(End, Start, 90, Infinity); PointArray[3] = MakeLineAtAngle(End, Start, -90, Infinity); #endif } /******************************************************************************************** > DocCoord MakeLineAtAngle(DocCoord Start, DocCoord End, ANGLE Offset, INT32 Length) DocRect Bounds, DocCoord* PointArray) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Make a line at a givel angle to another line. ********************************************************************************************/ DocCoord MakeLineAtAngle(DocCoord Start, DocCoord End, ANGLE Offset, INT32 Length) { const INT32 dx = End.x - Start.x; const INT32 dy = End.y - Start.y; if ( (dy==0) && (dx==0) ) { return End; } DocCoord End2; if (Length == 0 && Offset == ANGLE(90)) { End2.x = Start.x - (End.y - Start.y); End2.y = Start.y + (End.x - Start.x); return End2; } else if (Length == 0 && Offset == ANGLE(-90)) { End2.x = Start.x + (End.y - Start.y); End2.y = Start.y - (End.x - Start.x); return End2; } else if (Length == 0 && (Offset == ANGLE(180) || Offset == ANGLE(-180))) { End2.x = Start.x - (End.x - Start.x); End2.y = Start.y - (End.y - Start.y); return End2; } double LineAngle=0; if ( (dx>=0) && (dy>=0) ) LineAngle = PI - atan2( (double)dx, (double)dy ); else if ( (dx<0) && (dy>=0) ) LineAngle = -PI + atan2( (double)-dx, (double)dy ); else if ( (dx<0) && (dy<0) ) LineAngle = -atan2( (double)-dx, (double)-dy ); else if ( (dx>=0) && (dy<0) ) LineAngle = atan2( (double)dx, (double)-dy ); ANGLE RotateAngle = 360 * (LineAngle / (2*PI)); // Convert from radians to degrees double Length1 = Length; double Length2 = Start.Distance(End); if ( INT32(Length2) == 0) return End; if ( INT32(Length1) == 0) Length1 = Length2; FIXED16 LineScale = FIXED16(Length1/Length2); Matrix Rotate1 = Matrix(-RotateAngle); Matrix Rotate2 = Matrix(RotateAngle + Offset); Matrix Scale = Matrix(LineScale, LineScale); End2 = End; End2.translate(-Start.x, -Start.y); Rotate1.transform(&End2); Scale.transform(&End2); Rotate2.transform(&End2); End2.translate(Start.x, Start.y); return End2; } /******************************************************************************************** > ANGLE CalcLineAngle(DocCoord Start, DocCoord End) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Calculates the Angle of a Line. 0 is Straight Down. ********************************************************************************************/ ANGLE CalcLineAngle(DocCoord Start, DocCoord End) { const INT32 dx = End.x - Start.x; const INT32 dy = End.y - Start.y; if ( (dy==0) && (dx==0) ) { return 0; } double LineAngle=0; if ( (dx>=0) && (dy>=0) ) LineAngle = PI - atan2( (double)dx, (double)dy ); else if ( (dx<0) && (dy>=0) ) LineAngle = -PI + atan2( (double)-dx, (double)dy ); else if ( (dx<0) && (dy<0) ) LineAngle = -atan2( (double)-dx, (double)-dy ); else if ( (dx>=0) && (dy<0) ) LineAngle = atan2( (double)dx, (double)-dy ); return 360 * (LineAngle / (2*PI)); // Convert from radians to degrees } /******************************************************************************************** > DocCoord CentreOf(DocRect Rect) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Calculates the centre of a rectangle. ********************************************************************************************/ DocCoord CentreOf(DocRect Rect) { return DocCoord(Rect.lo.x + (Rect.Width()/2), Rect.lo.y + (Rect.Height()/2)); } /******************************************************************************************** > void MakeMeshParallelagram(Path* ParaPath, DocCoord Start, DocCoord End, DocCoord End2) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Makes a Parallelagram for the Bitmap fill mesh. ********************************************************************************************/ void MakeMeshParallelagram(Path* ParaPath, DocCoord Start, DocCoord End, DocCoord End2) { #if !defined(EXCLUDE_FROM_RALPH) INT32 dx1 = (End2.x - Start.x); INT32 dy1 = (End2.y - Start.y); INT32 dx2 = (End.x - Start.x); INT32 dy2 = (Start.y - End.y); // build a path ParaPath->ClearPath(); ParaPath->FindStartOfPath(); ParaPath->InsertMoveTo(DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2)); ParaPath->InsertLineTo(DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2)); ParaPath->InsertLineTo(DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2)); ParaPath->InsertLineTo(DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2)); ParaPath->InsertLineTo(DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2)); #endif } /******************************************************************************************** > void GetMeshParallelagram(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3, DocCoord* P4) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Makes a Parallelagram for the Bitmap fill mesh. Not used any more. ********************************************************************************************/ void GetMeshParallelagram(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3, DocCoord* P4) { #if !defined(EXCLUDE_FROM_RALPH) INT32 dx1 = (End2.x - Start.x); INT32 dy1 = (End2.y - Start.y); INT32 dx2 = (End.x - Start.x); INT32 dy2 = (Start.y - End.y); *P1 = DocCoord(Start.x + dx1 + dx2, Start.y + dy1 - dy2); *P2 = DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2); *P3 = DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2); *P4 = DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2); #endif } /******************************************************************************************** > DocRect GetParallelagramBounds(DocCoord Start, DocCoord End, DocCoord End2) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Gets the Bounding rect of the Bitmap Fill Mesh. ********************************************************************************************/ DocRect GetParallelagramBounds(DocCoord Start, DocCoord End, DocCoord End2) { DocCoord P1(0,0); DocCoord P2; DocCoord P3; DocCoord P4; GetMeshParallelagram(Start, End, End2, &P1, &P2, &P3, &P4); DocRect Bounds = DocRect(P1, P1); Bounds.IncludePoint(P2); Bounds.IncludePoint(P3); Bounds.IncludePoint(P4); return Bounds; } /******************************************************************************************** > void GetBitmapVirtualPoints(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Calculates the Virtual points of a bitmap from the actual bitmap control points given. ********************************************************************************************/ void GetBitmapVirtualPoints(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3) { #if !defined(EXCLUDE_FROM_RALPH) double dx1 = (End2.x - Start.x); double dy1 = (End2.y - Start.y); double dx2 = (End.x - Start.x); double dy2 = (Start.y - End.y); if (P1 != NULL && (Start != DocCoord(0,0))) *P1 = DocCoord(INT32(double(Start.x) + dx1/2 + dx2/2), INT32(double(Start.y) + dy1/2 - dy2/2)); if (P2 != NULL && (End != DocCoord(0,0))) *P2 = DocCoord(INT32(double(Start.x) + dx1/2 + dx2), INT32(double(Start.y) + dy1/2 - dy2)); if (P3 != NULL && (End2 != DocCoord(0,0))) *P3 = DocCoord(INT32(double(Start.x) + dx1 + dx2/2), INT32(double(Start.y) + dy1 - dy2/2)); #endif } /******************************************************************************************** > void GetBitmapRealPoints(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3) Author: Will_Cowling (Xara Group Ltd) Created: 22/07/94 Inputs: - Returns: - Purpose: Calculates the Real Control points of a bitmap from the vitual points given. ********************************************************************************************/ void GetBitmapRealPoints(DocCoord Start, DocCoord End, DocCoord End2, DocCoord* P1, DocCoord* P2, DocCoord* P3) { #if !defined(EXCLUDE_FROM_RALPH) INT32 dx1 = (End2 == DocCoord(0,0)) ? 0 : (End2.x - Start.x); INT32 dy1 = (End2 == DocCoord(0,0)) ? 0 : (End2.y - Start.y); INT32 dx2 = (End.x - Start.x); INT32 dy2 = (Start.y - End.y); if (P1 != NULL && (Start != DocCoord(0,0))) *P1 = DocCoord(Start.x - dx1 - dx2, Start.y - dy1 + dy2); if (P2 != NULL && (End != DocCoord(0,0))) *P2 = DocCoord(Start.x - dx1 + dx2, Start.y - dy1 - dy2); if (P3 != NULL && (End2 != DocCoord(0,0))) *P3 = DocCoord(Start.x + dx1 - dx2, Start.y + dy1 + dy2); #endif } /******************************************************************************************** > void IncludeArrowHead(DocRect* Bounds, DocCoord Start, DocCoord End) Author: Will_Cowling (Xara Group Ltd) Created: 8/9/94 Inputs: - Returns: - Purpose: Includes the Arrow head in the bounds calculations. ********************************************************************************************/ void IncludeArrowHead(DocRect* Bounds, DocCoord Start, DocCoord End) { #if !defined(EXCLUDE_FROM_RALPH) // First we Must caculate the angle of the arrow from the line angle const INT32 dx = Start.x - End.x; const INT32 dy = Start.y - End.y; if ( (dy==0) && (dx==0) ) { return; // draw nothing as too small } double ArrowAngle; if ( (dx>=0) && (dy>=0) ) ArrowAngle = PI - atan2( (double)dx, (double)dy ); else if ( (dx<0) && (dy>=0) ) ArrowAngle = -PI + atan2( (double)-dx, (double)dy ); else if ( (dx<0) && (dy<0) ) ArrowAngle = -atan2( (double)-dx, (double)-dy ); else if ( (dx>=0) && (dy<0) ) ArrowAngle = atan2( (double)dx, (double)-dy ); else { TRACE( _T("invalid angle on arrowhead\n")); // this should be impossible return; } ANGLE RotateAngle = 360 * (ArrowAngle / (2*PI)); // Convert from radians to degrees Matrix ArrowMatrix = Matrix(RotateAngle); // Rotate it ArrowMatrix *= Matrix(End); // Move it to the line point const UINT32 PointCount = 3; Coord ArrowPts[PointCount]; INT32 BlobSize = (Camelot.GetBlobManager())->GetBlobSize(); INT32 ArrowWidth = (BlobSize * 3)/2; INT32 ArrowHeight = (BlobSize * 3)/2; ArrowPts[0] = Coord( 0, 0); ArrowPts[1] = Coord( -ArrowWidth/2, -ArrowHeight); ArrowPts[2] = Coord( ArrowWidth/2, -ArrowHeight); ArrowMatrix.transform(ArrowPts, PointCount); Bounds->IncludePoint(DocCoord(ArrowPts[0].x, ArrowPts[0].y)); Bounds->IncludePoint(DocCoord(ArrowPts[1].x, ArrowPts[1].y)); Bounds->IncludePoint(DocCoord(ArrowPts[2].x, ArrowPts[2].y)); #endif } /******************************************************************************************** > BOOL AreLinesPerpendicular(DocCoord* Start, DocCoord* End, DocCoord* End2) Author: Will_Cowling (Xara Group Ltd) Created: 16/4/95 Inputs: - Returns: - Purpose: Tests to see if two fill control lines are perpendicular or not. Uses the 'Similar Triangles' method, but allows a 5 millipoint error. ********************************************************************************************/ BOOL AreLinesPerpendicular(DocCoord* Start, DocCoord* End, DocCoord* End2) { INT32 dx1 = (*End).x - (*Start).x; INT32 dy1 = (*End).y - (*Start).y; INT32 dx2 = (*End2).x - (*Start).x; INT32 dy2 = (*End2).y - (*Start).y; if ( (ABS(dx1 - dy2)) > 5 ) // Allow a few millipoints error return FALSE; if ( (ABS(dy1 + dx2)) > 5 ) // Allow a few millipoints error return FALSE; return TRUE; } //////////////////////////////////////////////////////////// // RestoreFillRampAction class /******************************************************************************************** > RestoreFillRampAction::RestoreFillRampAction() Author: David_McClarnon (Xara Group Ltd) Created: 24/10/99 Inputs: - Outputs: - Returns: - Purpose: Constructor for the action Errors: - SeeAlso: - ********************************************************************************************/ RestoreFillRampAction::RestoreFillRampAction() { m_pAttr = NULL; } /******************************************************************************************** > static ActionCode Init( Operation* pOp, ActionList* pActionList, AttrFillGeometry* pFill, RestoreFillRampAction** NewAction, Author: David_McClarnon (Xara Group Ltd) Created: 10/6/99 Inputs: pOp = ptr to the operation to which this action belongs pActionList = ptr to action list to which this action should be added pFill = ptr to the fill geometry attribute to store its ramp Outputs: ppNewAction = ptr to a ptr to an action, allowing the function to return a pointer to the created action Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: Stores the fill ramp of the given attribute for restoring on undo Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode RestoreFillRampAction::Init( Operation* pOp, ActionList* pActionList, AttrFillGeometry* pFill, RestoreFillRampAction** NewAction) { UINT32 ActSize = sizeof(RestoreFillRampAction); ActionCode Ac = Action::Init(pOp,pActionList,ActSize, CC_RUNTIME_CLASS(RestoreFillRampAction), (Action**)NewAction); // Document * pDoc = Document::GetCurrent(); DocRect dr; if (Ac != AC_FAIL) { if (pFill->GetColourRamp() != NULL) { (*NewAction)->m_LastRamp = *pFill->GetColourRamp(); } // bugfix #6835 (Marc 22/09/04) - make sure we store the fill even if there is no ramp (*NewAction)->m_pAttr = pFill; } return Ac; } /******************************************************************************************** > ActionCode RestoreFillRampAction::Execute(); Author: Olivier_Gascoin (Xara Group Ltd) Created: 07/01/96 Inputs: - Outputs: - Returns: ActionCode, one of AC_OK, AC_NO_RECORD or AC_FAIL Purpose: Executes the action. This will reset the num blend steps in pThisNodeBlend to OldNumSteps, after creating another action to record the current num steps of pThisNodeBlend Errors: - SeeAlso: Action::Init() ********************************************************************************************/ ActionCode RestoreFillRampAction::Execute() { ActionCode Act; RestoreFillRampAction* pAction; Act = RestoreFillRampAction::Init( pOperation, pOppositeActLst, m_pAttr, &pAction ); if (Act != AC_FAIL) { Document * pDoc = Document::GetCurrent(); if (pDoc) { /* DocRect dr = pController->GetBoundingRect(); pDoc->ForceRedraw(pController->FindParentSpread(), dr, FALSE); */ // CGS - we need to check for a NULL colour ramp - cause we may have deleted it! ColourRamp* ColRamp = m_pAttr->GetColourRamp(); if (ColRamp) { *m_pAttr->GetColourRamp() = m_LastRamp; } else { // if it is NULL, then we need to recreate one BEFORE making the above call! ColRamp = new ColourRamp (); m_pAttr->SetColourRamp (ColRamp); *m_pAttr->GetColourRamp() = m_LastRamp; } } } return Act; } RestoreFillRampAction::~RestoreFillRampAction() { }