// $Id: shadtool.cpp 1386 2006-06-28 17:49:55Z 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============================ */ // Implementation of the Shadow Tool /* */ #include "camtypes.h" #include "shadtool.h" // Shadow Tool header #include "nodecont.h" //#include "oilkeys.h" #include "keypress.h" #include "vkextra.h" #ifdef BUILDSHADOWS // code headers //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "blobs.h" #include "csrstack.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "oilfiles.h" #include "opshadow.h" #include "shadinfo.h" #include "toollist.h" #include "layer.h" #include "layermsg.h" #include "nodeshad.h" // resource headers //#include "markn.h" //#include "shadbar.h" //#include "shadres.h" //#include "viewrc.h" #include "opbevel.h" //#include "biasres.h" // bias profile header #include "objchge.h" #include "bubbleid.h" //#include "mario.h" #include "nodetxts.h" #include "nodeblnd.h" #include "nodebldr.h" #include "slicehelper.h" #include "opliveeffects.h" #include "liveeffectstool.h" #include "ophist.h" #include "shadinfo.h" DECLARE_SOURCE( "$Revision: 1386 $" ); CC_IMPLEMENT_MEMDUMP(SoftShadowTool,Tool_v1) CC_IMPLEMENT_DYNCREATE(OpDragCreateShadow2, SelOperation) CC_IMPLEMENT_DYNCREATE(SaveShadowSettingsAction, Action) // Must come after the last CC_IMPLEMENT.. macro #define new CAM_DEBUG_NEW // default shadow colour is black. #define DEFAULT_SHADOW_COLOUR COLOUR_BLACK // These are still char* while we wait for resource technology to be developed for modules TCHAR* SoftShadowTool::FamilyName = _T("Shadow Tools"); TCHAR* SoftShadowTool::ToolName = _T("Shadow Tool"); TCHAR* SoftShadowTool::Purpose = _T("Shadow manipulation"); TCHAR* SoftShadowTool::Author = _T("DavidMc"); // Init those other useful static vars BOOL SoftShadowTool::CurrentTool = FALSE; SoftShadowInfoBarOp* SoftShadowTool::pSoftShadowInfoBarOp = NULL; UINT32 SoftShadowTool::StatusID = _R(IDS_SOFTSHADOWSTATUS_FINDSTART); /******************************************************************************************** > SoftShadowTool::SoftShadowTool() Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Purpose: Default Constructor. Other initialisation is done in SoftShadowTool::Init which is called by the Tool Manager SeeAlso: SoftShadowTool::Init ********************************************************************************************/ SoftShadowTool::SoftShadowTool() { pcCurrentCursor = NULL; m_bBlobRendering = TRUE; pLastBlobSpread = NULL; m_iEditStackPos = STACKPOS_TOP; m_pEditRange = NULL; } /******************************************************************************************** > SoftShadowTool::~SoftShadowTool() Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Purpose: Destructor (Virtual). Does nothing. ********************************************************************************************/ SoftShadowTool::~SoftShadowTool() { if (m_pEditRange) { delete m_pEditRange; m_pEditRange = NULL; } } /******************************************************************************************** > BOOL SoftShadowTool::Init( INT32 Pass ) Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Returns: FALSE if it does not want to be created, TRUE otherwise Purpose: Used to check if the Tool was properly constructed SeeAlso: SoftShadowTool::SoftShadowTool ********************************************************************************************/ BOOL SoftShadowTool::Init() { // Declare all your ops here and only succeed if all declarations succeed BOOL ok = TRUE; if (ok) ok = ( OpApplyShadow::Declare() && OpChangeShadowType::Declare() && OpRemoveShadow::Declare() && OpShadowPosition::Declare() && OpShadowPenumbra::Declare() && OpShadowAngle::Declare() && OpShadowHeight::Declare() && OpShadowScale::Declare() && OpShadowHeightAndAngle::Declare() && OpSaveShadowData::Declare() && OpGlowWidth::Declare() && OpChangeShadowProfile::Declare() && OpChangeShadowDarkness::Declare() && OpDragCreateShadow2::Declare()); // This section reads in the infobar definition and creates an instance of // SoftShadowInfoBarOp. Also pSoftShadowInfoBarOp, the ptr to the tool's infobar, is set up // after the infobar is successfully read and created. if (ok) { pSoftShadowInfoBarOp = new SoftShadowInfoBarOp; ERROR2IF(pSoftShadowInfoBarOp==NULL, FALSE, "Can't create Shadow tool Infobar"); if (pSoftShadowInfoBarOp) pSoftShadowInfoBarOp->pSoftShadowTool = this; /* CCResTextFile file; // Resource File SoftShadowInfoBarOpCreate BarCreate; // Object that creates BlankInfoBarOp objects ok = file.open(_R(IDM_SOFTSHADOW_BAR), _R(IDT_INFO_BAR_RES)); // Open resource if (ok) ok = DialogBarOp::ReadBarsFromFile(file,BarCreate); // Read and create info bar if (ok) file.close(); // Close resource ERROR3IF(!ok,"Unable to load shadbar.ini from resource\n"); if (ok) { // Info bar now exists. Now get a pointer to it String_32 str = String_32(_R(IDS_SHADOWTOOL_INFOBARNAME)); DialogBarOp* pDialogBarOp = DialogBarOp::FindDialogBarOp(str); ok = (pDialogBarOp != NULL); if (ok) ok = pDialogBarOp->IsKindOf(CC_RUNTIME_CLASS(SoftShadowInfoBarOp)); if (ok) { pSoftShadowInfoBarOp = (SoftShadowInfoBarOp*)pDialogBarOp; pSoftShadowInfoBarOp->pSoftShadowTool = this; } } ERROR3IF(!ok,"Error finding the shadow tool info bar"); */ } return (ok); } /******************************************************************************************** > void SoftShadowTool::Describe(void *InfoPtr) Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: InfoPtr - A pointer to a tool info block. It is passed cast to void* as the version of the tool is unknown at this point. Later versions of the Tool class may have more items in this block, that this tool will not use Outputs: InfoPtr - The structure pointed to by InfoPtr will have had all the info that this version of the Tool knows about Purpose: Allows the tool manager to extract information about the tool ********************************************************************************************/ void SoftShadowTool::Describe(void *InfoPtr) { // Cast structure into the latest one we understand. ToolInfo_v1 *Info = (ToolInfo_v1 *) InfoPtr; Info->InfoVersion = 1; Info->InterfaceVersion = GetToolInterfaceVersion(); // You should always have this line. // These are all arbitrary at present. Info->Version = 1; Info->ID = GetID(); Info->TextID = _R(IDS_SOFTSHADOW_TOOL); Info->Family = FamilyName; Info->Name = ToolName; Info->Purpose = Purpose; Info->Author = Author; Info->BubbleID = _R(IDBBL_SOFTSHADOW_TOOLBOX); } /******************************************************************************************** > virtual void SoftShadowTool::SelectChange(BOOL isSelected) Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: isSelected - TRUE = tool has been selected - FALSE = tool has been deselected Outputs: - Returns: - Purpose: Starts up and closes down the blank tool Errors: Debug warning if creating the cursor fails. SeeAlso: - ********************************************************************************************/ void SoftShadowTool::SelectChange(BOOL isSelected) { if (isSelected) { if (!CreateCursors()) return; CurrentCursorID = CursorStack::GPush(pcNormalSoftShadowCursor, FALSE); // Push cursor but don't display now pcCurrentCursor = pcNormalSoftShadowCursor; // This tool is now the current one CurrentTool = TRUE; SetShadowEditRangeFromSelection(); // Create and display the tool's info bar pSoftShadowInfoBarOp->Create(); if (pSoftShadowInfoBarOp->pSoftShadowInfoBar) { // Update the InfoBar pSoftShadowInfoBarOp->UpdateInfoBar(); } // Say which blobs should be displayed BlobManager* BlobMgr = GetApplication()->GetBlobManager(); if (BlobMgr != NULL) { // Decide which blobs we will display and tell the blob manager BlobStyle MyBlobs; MyBlobs.Tiny = TRUE; BlobMgr->ToolInterest(MyBlobs); } RenderToolBlobs(Document::GetSelectedSpread(), NULL); } else { // Deselection - destroy the tool's cursors, if they exist. if (pcCurrentCursor != NULL) { CursorStack::GPop(CurrentCursorID); pcCurrentCursor = NULL; CurrentCursorID = 0; } DestroyCursors(); pSoftShadowInfoBarOp->CloseProfileDialog (pSoftShadowInfoBarOp->BiasGainGadget_m); // Remove the info bar from view by deleting the actual underlying window pSoftShadowInfoBarOp->Delete(); // ensure any tool object blobs are removed. BlobManager* BlobMgr = GetApplication()->GetBlobManager(); if (BlobMgr != NULL) { BlobStyle bsRemoves; bsRemoves.ToolObject = TRUE; BlobMgr->RemoveInterest(bsRemoves); } // No longer the current tool CurrentTool = FALSE; RenderToolBlobs(Document::GetSelectedSpread(), NULL); } } /******************************************************************************************** > BOOL SoftShadowTool::CreateCursors() Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: - Outputs: - Returns: TRUE if all the shadow tool cursors have been successfully created Purpose: Creates all the shadow tool cursors SeeAlso: - ********************************************************************************************/ BOOL SoftShadowTool::CreateCursors() { // This tool has just been selected. Create the cursors. pcNormalSoftShadowCursor = new Cursor(this, _R(IDC_SOFTSHADOWTOOLCURSOR)); pcBlobSoftShadowCursor = new Cursor(this, _R(IDC_SOFTSHADOWBLOBCURSOR)); if ( pcNormalSoftShadowCursor==NULL || !pcNormalSoftShadowCursor->IsValid() || pcBlobSoftShadowCursor==NULL || !pcBlobSoftShadowCursor->IsValid()) { DestroyCursors(); return FALSE; } else return TRUE; } /******************************************************************************************** > void SoftShadowTool::DestroyCursors() Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: - Outputs: - Returns: - Purpose: Destroys all the Shadow Tool cursors SeeAlso: - ********************************************************************************************/ void SoftShadowTool::DestroyCursors() { if (pcNormalSoftShadowCursor != NULL) delete pcNormalSoftShadowCursor; if (pcBlobSoftShadowCursor != NULL) delete pcBlobSoftShadowCursor ; } /******************************************************************************************** > void SoftShadowTool::OnClick( DocCoord PointerPos, ClickType Click, ClickModifiers ClickMods, Spread* pSpread ) Author: Karim_MacDonald (Xara Group Ltd) Created: 15/06/2000 Inputs: PointerPos DocCoord of the point where the mouse button was clicked. Click The type of click that was detected. ClickMods Any modifiers on the click, eg CTRL/SHIFT held down. pSpread Spread in which the click happened. Returns: Purpose: The Shadow tool's mouse-click handler. SeeAlso: Tool::MouseClick; ClickType; ClickModifiers ********************************************************************************************/ void SoftShadowTool::OnClick( DocCoord PointerPos, ClickType cType, ClickModifiers cMods, Spread* pSpread ) { if (cMods.Menu) return; // Don't do anything if the user clicked the Menu button switch (cType) { // if a drag is performed, then we have an Op especially for this contingency :o) case CLICKTYPE_DRAG: { OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(OPTOKEN_DRAGCREATESHADOW); DragCreateShadowParam DragParam(this, pSoftShadowInfoBarOp, PointerPos, cMods); if (pOpDesc != NULL) pOpDesc->Invoke(&DragParam); } break; default: { // call the base class .... DragTool::OnClick (PointerPos, cType, cMods, pSpread); } break; } } /******************************************************************************************** > void SoftShadowTool::OnMouseMove( DocCoord PointerPos,Spread* pSpread, ClickModifiers ClickMod ) Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: PointerPos - The DocCoord of the point where the mouse has moved to pSpread - The spread in which the move occurred ClickMods - The state of the various modifiers at the time of the mouse move Returns: TRUE if it handled the Click, FALSE otherwise Purpose: To handle a Mouse Move event for the Blank Tool. This method lets the tool provide user feedback by changing status line text and the mouse cursor, depending on where the mouse is and what modifiers the user is pressing at the mo'. SeeAlso: Tool::MouseClick; ClickType; ClickModifiers ********************************************************************************************/ void SoftShadowTool::OnMouseMove(DocCoord PointerPos, Spread* pSpread, ClickModifiers ClickMods) { String_256 strFeedback; BOOL bShadowingOn = FigureUserFeedback(&strFeedback, pSpread, PointerPos, ClickMods); // we've found something we can drag a shadow from. if (bShadowingOn) { if (pcCurrentCursor != pcBlobSoftShadowCursor) { CursorStack::GSetTop(pcBlobSoftShadowCursor, CurrentCursorID); pcCurrentCursor = pcBlobSoftShadowCursor; } } // no dragging here, sonny. else { if (pcCurrentCursor != pcNormalSoftShadowCursor) { CursorStack::GSetTop(pcNormalSoftShadowCursor, CurrentCursorID); pcCurrentCursor = pcNormalSoftShadowCursor; } } // make sure the status line is up to date. GetApplication()->UpdateStatusBarText(&strFeedback); } /******************************************************************************************** > virtual BOOL SoftShadowTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods) Author: Olivier_Gascoin (Xara Group Ltd) Created: 29/10/96 Inputs: ptest - pointer to a string pSpread points to a spread DocPos points to a point in a document ClickMods are the current click modifiers Outputs: Updates the string in ptext Returns: TRUE if the string was updates (FALSE if not updated) Purpose: Returns the current status line help string SeeAlso: SoftShadowTool::GetCurrentStatusText, Tool_v1::GetStatusLineText ********************************************************************************************/ BOOL SoftShadowTool::GetStatusLineText(String_256* ptext, Spread* pSpread, DocCoord DocPos, ClickModifiers ClickMods) { // We can call the underlying help function to get a string and return the result. String_256 strFeedback; FigureUserFeedback(&strFeedback, pSpread, DocPos, ClickMods); *ptext += strFeedback; return TRUE; } /******************************************************************************************** > BOOL SoftShadowTool::FigureUserFeedback(String_256* pText, Spread* pSpread, const DocCoord& dcPos, ClickModifiers cMods) Author: Karim_MacDonald (Xara Group Ltd) Created: 31 August 2000 Returns: TRUE if the mouse is in a position where the shadow tool can be used, FALSE if the mouse is in a position where the tool cannot be used. Purpose: Determines our status line feedback text, dependent on the mouse position, click modifiers etc. ********************************************************************************************/ BOOL SoftShadowTool::FigureUserFeedback(String_256* pText, Spread* pSpread, const DocCoord& dcPos, ClickModifiers cMods) { NodeRenderableInk* pTargetInk = NodeRenderableInk::FindSimpleAtPoint(pSpread, dcPos); if (pTargetInk == NULL) { // There's nothing under the pointer! // "Move pointer over object to shadow" pText->Empty(); pText->Load(_R(IDS_SHADOWSTATUS_MOVEPOINTER)); return FALSE; } // Find out whether the clicked item relates to a shadow in the postprocessor stack // Range* pTargetRange = NULL; // INT32 iPos = STACKPOS_TOP; // BOOL bTopLevel = FALSE; // if we're over an existing shadow, tell the user what result dragging will have. // "Drag to position wall shadows" // "Drag to change angle and height of floor shadows" // "Drag left/right or up/down to change width of glow shadows" if (pTargetInk->IsAShadow()) { NodeShadowController* pControl = (NodeShadowController*)((NodeShadow*)pTargetInk)->GetParentController(); if (pControl != NULL) { ShadowType sType = pControl->GetShadowType(); UINT32 StatusID = 0; StatusID = (sType == SHADOWTYPE_WALL) ? _R(IDS_SHADOWSTATUS_DRAGWALLSHADOWS) : (sType == SHADOWTYPE_FLOOR) ? _R(IDS_SHADOWSTATUS_DRAGFLOORSHADOWS) : (sType == SHADOWTYPE_GLOW) ? _R(IDS_SHADOWSTATUS_DRAGGLOWSHADOWS) : 0; pText->Empty(); if (StatusID != 0) pText->Load(StatusID); else ERROR3("Unrecognised shadow type!"); } else { ERROR3("NodeShadow found without a NodeShadowController!"); } return TRUE; } pText->Empty(); pText->Load(_R(IDS_SHADOWSTATUS_DRAGTOSHADOW)); return TRUE; } /******************************************************************************************** > ListRange* SoftShadowTool::GetShadowEditRange(NodeShadowController** ppMasterShadow = NULL) Author: Phil_Martin (Xara Group Ltd) Created: 13/05/2005 Inputs: - Outputs: - Returns: Pointer to a range of shadow nodes to operate on (owned by this function, no need to delete) NULL if no current range is editable Purpose: Find the editable shadow nodes in the current selection Errors: - SeeAlso: - ********************************************************************************************/ ListRange* SoftShadowTool::GetShadowEditRange(NodeShadowController** ppMasterShadow) { // See whether we have a specific list of nodes... // if (m_pEditRange && m_pEditRange->MatchesSelectionEffectLevel(m_iEditStackPos)) if (m_pEditRange==NULL) SetShadowEditRangeFromSelection(); if (m_pEditRange) { if (ppMasterShadow) { if (!m_pEditRange->IsEmpty()) { ERROR3IF(!m_pEditRange->FindFirst()->IsAShadowController(), "Stored ShadowEditRange contains non-shadow"); *ppMasterShadow = (NodeShadowController*)m_pEditRange->FindFirst(); } else *ppMasterShadow = NULL; } return m_pEditRange; } /* // See whether the user has selected shadow nodes himself... if (SetShadowEditRangeFromSelection()) { // We found that all nodes in the sel range are shadows if (ppMasterShadow) { ERROR3IF(!m_pEditRange->FindFirst()->IsAShadowController(), "Stored ShadowEditRange contains non-shadow"); *ppMasterShadow = (NodeShadowController*)m_pEditRange->FindFirst(); } return m_pEditRange; } */ ERROR3("I don't think we should ever get here!"); // Stored edit context doesn't match the current selection // so we must find the top surface of shadows in the effects stack above the selection SelRange* pSelRange = GetApplication()->FindSelection(); return pSelRange->GetTopClassRange(CC_RUNTIME_CLASS(NodeShadowController), TRUE, FALSE, (Node**)ppMasterShadow, NULL); } /******************************************************************************************** > void SoftShadowTool::SetShadowEditRange(ListRange* pNewRange, INT32 iNewPos, BOOL bConsistent = FALSE) Author: Phil_Martin (Xara Group Ltd) Created: 12/05/2005 Inputs: pNewRange - pointer to new range to edit iStackPos - value of stack pos Outputs: - Returns: - Purpose: Find the editable shadow nodes in the current selection Errors: - SeeAlso: - ********************************************************************************************/ void SoftShadowTool::SetShadowEditRange(ListRange* pNewRange, INT32 iNewPos, BOOL bConsistent) { if (m_pEditRange && m_pEditRange != pNewRange) { delete m_pEditRange; m_pEditRange = NULL; } m_iEditStackPos = iNewPos; if (pNewRange) m_pEditRange = new ListRange(pNewRange); else m_pEditRange = NULL; // Since the user usually indicates a shadow range by selecting shadow nodes // we must do the same thing for consistency /* if (m_pEditRange) { NodeRenderableInk::DeselectAll(); Node* pNode = m_pEditRange->FindFirst(); while (pNode) { if (pNode->IsAShadowController()) { NodeShadowController* pController = (NodeShadowController*) pNode; pController->GetShadow()->Select(FALSE); } pNode = m_pEditRange->FindNext(pNode); } } // Make sure InfoBar is updated to reflect the new stored edit range BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); */ } /******************************************************************************************** > void SoftShadowTool::SelectShadowRange(ListRange* pNewRange) Author: Phil_Martin (Xara Group Ltd) Created: 11/07/2005 Inputs: pNewRange - pointer to new range to edit Outputs: - Returns: - Purpose: Find the editable shadow nodes in the current selection Errors: - SeeAlso: - ********************************************************************************************/ void SoftShadowTool::SelectShadowRange(ListRange* pNewRange) { ERROR3IF(pNewRange==NULL, "SelectShadowRange not given range pointer"); // Make a local copy because the passed-in list may automatically die // when the selection changes ListRange myRange(pNewRange); NodeRenderableInk::DeselectAll(TRUE, TRUE); Node* pNode = myRange.FindFirst(); while (pNode) { if (pNode->IsAShadowController()) { NodeShadowController* pController = (NodeShadowController*)pNode; if (pController->GetShadow()) pController->GetShadow()->Select(TRUE); } pNode = myRange.FindNext(pNode); } // Make sure InfoBar is updated to reflect the new stored edit range BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); } /******************************************************************************************** > BOOL SoftShadowTool::SetShadowEditRangeFromSelection(BOOL bBroadcast = FALSE) Author: Phil_Martin (Xara Group Ltd) Created: 18/05/2005 Inputs: - Outputs: - Returns: - Purpose: Scan the selection to see whether it contains just shadow nodes and whether they map to a level in the effects stack If so, set the edit range from that info Errors: - SeeAlso: - ********************************************************************************************/ BOOL SoftShadowTool::SetShadowEditRangeFromSelection(BOOL bBroadcast) { SelRange mySelRange = *GetApplication()->FindSelection(); mySelRange.SetPromoteToParent(FALSE); LiveEffectsTool* pLETool = NULL; INT32 iPos = 0; EffectsStack* pStack = mySelRange.GetEffectsStack(); Node* pNode = mySelRange.FindFirst(); // if (pNode==NULL) // return FALSE; ListRange* pShadowRange = new ListRange; while (pNode) { if (pNode->IsAShadowController() && !pShadowRange->Contains(pNode)) pShadowRange->AddNode(pNode); else if (pNode->IsAShadow()) { NodeShadow* pShadow = (NodeShadow*)pNode; ERROR3IF(pShadow->GetParentController()==NULL, "Shadow has no controller!"); if (!pShadowRange->Contains(pShadow->GetParentController())) pShadowRange->AddNode(pShadow->GetParentController()); } else { Node* pParent = pNode->FindParent(); NodeShadowController* pTopController = NULL; while (pParent && (pParent->IsEffect() || pParent->IsController())) { if (pParent->IsAShadowController()) pTopController = (NodeShadowController*)pParent; pParent = pParent->FindParent(); } if (pTopController && !pShadowRange->Contains(pTopController)) pShadowRange->AddNode(pTopController); } pNode = mySelRange.FindNext(pNode); } // Let's set up the range we've just made for editing! if (m_pEditRange) { delete m_pEditRange; m_pEditRange = NULL; } // Here we have a listrange containing only shadow controller nodes while (iPos < (INT32)pStack->GetCount()) { if (pShadowRange->MatchesSelectionEffectLevel(iPos)) break; iPos++; } if (iPos >= (INT32)pStack->GetCount()) iPos = STACKPOS_INVALID; // Tell the effects tool about this consistent range of effects m_iEditStackPos = iPos; m_pEditRange = pShadowRange; // Assign ownership of this list to the class pLETool = (LiveEffectsTool*)Tool::FindTool(TOOLID_LIVEEFFECT); if (pLETool) { pLETool->SetCurrentEffectID(POSTPRO_ID_SHADOW); pLETool->SetCurrentStackPos(iPos); } if (bBroadcast) { // Make sure InfoBar is updated to reflect the new stored edit range BROADCAST_TO_ALL(SelChangingMsg(SelChangingMsg::EFFECTSTACKCHANGED)); } return TRUE; //NoSet: // if (pShadowRange) // delete pShadowRange; // // return FALSE; } /******************************************************************************************** > NodeShadowController* SoftShadowTool::GetShadowEditField(ShadowMaskBits flags, String_256* pStrInfo = NULL) Author: Phil_Martin (Xara Group Ltd) Created: 16/05/2005 Inputs: flags - bitfield describing which fields to search for Outputs: - Returns: Pointer to a sample node NULL if no common nodes could be found Purpose: Find the editable shadow nodes with common values in the current selection Errors: - SeeAlso: - ********************************************************************************************/ NodeShadowController* SoftShadowTool::GetShadowEditField(ShadowMaskBits flags, String_256* pStrInfo) { BOOL bNone = TRUE; BOOL bConsistent = FALSE; BOOL bMany = FALSE; NodeShadowController* pMaster = NULL; pMaster = GetShadowEditField(flags, &bNone, &bConsistent, &bMany); // There are no fields of the required type if (bNone && pStrInfo!=NULL) pStrInfo->Load(_R(IDS_SHADOWSTRING_NONE)); // There are many fields of the required type until we prove otherwise if (bMany && pStrInfo!=NULL) pStrInfo->Load(_R(IDS_SHADOWSTRING_MANY)); // Don't confuse the caller by returning None or Many when there is a consistent value if (bConsistent && pStrInfo!=NULL) pStrInfo->Empty(); return pMaster; } /******************************************************************************************** > NodeShadowController* SoftShadowTool::GetShadowEditField(ShadowMaskBits flags, BOOL* pbNone, BOOL* pbConsistent, BOOL* pbMany) Author: Phil_Martin (Xara Group Ltd) Created: 16/05/2005 Inputs: flags - bitfield describing which fields to search for Outputs: - Returns: Pointer to a sample node NULL if no common nodes could be found Purpose: Find the editable shadow nodes with common values in the current selection Errors: - SeeAlso: - ********************************************************************************************/ NodeShadowController* SoftShadowTool::GetShadowEditField(ShadowMaskBits flags, BOOL* pbNone, BOOL* pbConsistent, BOOL* pbMany) { ERROR3IF(pbNone==NULL, "!1"); ERROR3IF(pbConsistent==NULL, "!2"); ERROR3IF(pbMany==NULL, "!3"); *pbNone = TRUE; *pbConsistent = FALSE; *pbMany = FALSE; Range* pRange = GetShadowEditRange(); if (pRange==NULL || pRange->IsEmpty()) return NULL; *pbNone = FALSE; NodeShadowController* pMaster = NULL; Node* pNode = pRange->FindFirst(); ShadowFieldMask mask(flags); while (pNode) { ENSURE(pNode->IsAShadowController(), "Expected a NodeShadowController in GetShadowField"); if (pMaster==NULL) pMaster = (NodeShadowController*)pNode; else { *pbMany = TRUE; if (!pMaster->CompareState((NodeShadowController*)pNode, mask)) return NULL; } pNode = pRange->FindNext(pNode); } *pbConsistent = TRUE; return pMaster; } /******************************************************************************************** > virtual BOOL SelectorTool::OnKeyPress(KeyPress* pKey) Author: Phil_Martin (Xara Group Ltd) Created: 23/11/2004 Inputs: pKey pointer to a key-press object representing a keystroke. Outputs: - Returns: TRUE if the key-event is handled, FALSE if not. Purpose: Called when a key is pressed of released. If the key is a "click modifier" such as the ALT or CTRL key then the cursor is changed to indicate whatever the tool can do with that modifier. Errors: - SeeAlso: - ********************************************************************************************/ BOOL SoftShadowTool::OnKeyPress(KeyPress* pKey) { // Find the current state of the "click" keyboard modifiers... ClickModifiers ClickMods = ClickModifiers::GetClickModifiers(); switch (pKey->GetVirtKey()) { case CAMKEY(1): // toggle tool blobs if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->HandleShadowTypeChange(SHADOWTYPE_NONE); return TRUE; } break; case CAMKEY(2): // toggle edit blobs if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->HandleShadowTypeChange(SHADOWTYPE_WALL); return TRUE; } break; case CAMKEY(3): // toggle fill blobs if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->HandleShadowTypeChange(SHADOWTYPE_FLOOR); return TRUE; } break; case CAMKEY(4): // toggle bounds/rotate blobs if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->HandleShadowTypeChange(SHADOWTYPE_GLOW); return TRUE; } break; case CAMKEY(5): // toggle bounds/rotate blobs if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->HandleShadowTypeChange(SHADOWTYPE_FEATHER); return TRUE; } break; case CAMKEY(A): // select all shadows only if (pKey->IsPress() && !pKey->IsModified()) { pSoftShadowInfoBarOp->SelectShadowsAlone(); return TRUE; } break; } return FALSE; } //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- /******************************************************************************************** > OpDragCreateShadow2::OpDragCreateShadow2() Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Purpose: Constructor. ********************************************************************************************/ OpDragCreateShadow2::OpDragCreateShadow2() { m_pInfoBar = NULL; m_pTool = NULL; m_plInitialShadowSettings = NULL; m_pOwnedRange = NULL; } /******************************************************************************************** > OpDragCreateShadow2::~OpDragCreateShadow2() Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Purpose: Destructor. Free any resources used during this drag. ********************************************************************************************/ OpDragCreateShadow2::~OpDragCreateShadow2() { m_pInfoBar = NULL; m_pTool = NULL; if (m_plInitialShadowSettings != NULL) { m_plInitialShadowSettings->DeleteAll(); delete m_plInitialShadowSettings; m_plInitialShadowSettings = NULL; } if (m_pOwnedRange) { delete m_pOwnedRange; m_pOwnedRange = NULL; } } /******************************************************************************************** > virtual void OpDragCreateShadow2::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam) Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Inputs: pOpDesc ptr to the OpDescriptor which invoked this operation. pOpParam parameter structure required by the operation to perform. Purpose: Called to start this drag operation. Description of behaviour for the various dragging scenarios: Selected Shadowed Dragged Result -------- -------- ------- ------ No No Shape Select shape, then apply & drag shadow. Yes Shape Select shape, then drag shadow. Shadow Select shadow, then drag it. Yes No Shape Apply and } drag shadow, and copy its Yes Shape } settings to all similar Shadow } shadows in the selection. Notes: Apart from initially biding by the selection rules for groups, it doesn't matter whether the target object is grouped or not - we act on the selection. The node on which the drag started determines the behaviour of the drag. If it has no shadow, then a default shadow will be applied to it. Selected nodes without shadows will have the control shadow applied to them. Selected nodes with shadows will only mirror the control shadow if they have the same type of shadow. ********************************************************************************************/ void OpDragCreateShadow2::DoWithParam(OpDescriptor* pOpDesc, OpParam* pOpParam) { // sometimes we work on the selection, and sometimes we change the selection and // _then_ work on it; either way, we gotta be a selop... // ps don't know what the BOOLs do - they're one of the few things left from Dave's code. DoStartSelOp(FALSE, FALSE, FALSE, FALSE); // initialise our internal state, partly from the passed-in parameter. DragCreateShadowParam* pParam = (DragCreateShadowParam*)pOpParam; m_pInfoBar = pParam->m_pInfoBar; m_pTool = pParam->m_pTool; m_InitialPointerPos = pParam->m_PointerPos; m_pMasterShadow = NULL; if (m_plInitialShadowSettings != NULL) { m_plInitialShadowSettings->DeleteAll(); delete m_plInitialShadowSettings; m_plInitialShadowSettings = NULL; } // ----------------------------------------------------- // get the node under the pointer, and use it to determine our target node for the drag. // if there is none, then exit. NodeRenderableInk* pTargetInk = NodeRenderableInk::FindSimpleAtPoint(Document::GetSelectedSpread(), m_InitialPointerPos); if (pTargetInk == NULL) { NodeRenderableInk::DeselectAll(TRUE, TRUE); End(); return; } NodeRenderableInk* pTargetCompound = NodeRenderableInk::FindCompoundFromSimple(pTargetInk); // Normal click logic NodeCompound* pController = pTargetInk->GetParentController(); BOOL bControllerOfSelected = FALSE; if (pController) bControllerOfSelected = pController->IsParentOfSelected(); // By default assume that the selected shadows, as decribed by m_pEditRange will be dragged // If the clicked node is selected // Or is the shadow of a selected node // Or is the inknode of a selected shadow // Then just use the selection as is // Otherwise, call Select the clicked object and call SetEditRangeFromSelection // INT32 iPos = STACKPOS_INVALID; // BOOL bTopLevel = FALSE; ListRange* pShadowRange = NULL; // ListRange* pTempRange = NULL; // Get the master shadow from the current edit range m_pTool->GetShadowEditRange(&m_pMasterShadow); // If the clicked node was a shadow but is not in the selection if (pTargetInk->IsAShadow()) { Node* pShadowedInk = NULL; if (pController && pController->IsAShadowController()) { pShadowedInk = ((NodeShadowController*)pController)->GetInkNodeFromController(); } if (pTargetInk->IsSelected() || pShadowedInk->IsSelected()) { // Shadow is in selection } else { // Shadow is not in selection so alter the selection to refer to the // clicked shadow NodeRenderableInk::DeselectAll(TRUE, TRUE); pTargetInk->Select(TRUE); m_pTool->SetShadowEditRangeFromSelection(); m_pTool->GetShadowEditRange(&m_pMasterShadow); } } else { if (pTargetCompound->IsSelected()) { // Clicked node is in selection } else { // Clicked node is not in selection NodeRenderableInk::DeselectAll(TRUE, TRUE); pTargetCompound->Select(TRUE); m_pTool->SetShadowEditRangeFromSelection(); m_pTool->GetShadowEditRange(&m_pMasterShadow); } } // Make sure you call GetShadowEditRange from this point onwards to get the stored COPY of pShadowRange! // after all the malarky above, we'll see if we can get the target's shadow controller. // if we can't, or if the Shift key was pressed, then we'll make a new one. if (m_pMasterShadow == NULL || pParam->m_ClickMods.Adjust) { // Doesn't yet have a shadow so we can create one for the whole selection NodeShadowParam NSParam(NodeShadowController::DefaultType); Range TempRange(*GetApplication()->FindSelection()); TempRange.SetPromoteToParent(TRUE); pShadowRange = OpApplyShadow::DoApplyShadow(this, &TempRange, &NSParam); m_pOwnedRange = pShadowRange; m_pTool->SetShadowEditRange(pShadowRange, STACKPOS_TOP, TRUE); // Make sure you call GetShadowEditRange from this point onwards to get the stored COPY of pShadowRange! // quit gracefully if we can't create it. if (pShadowRange == NULL) { FailAndExecute(); End(); return; } m_pMasterShadow = (NodeShadowController*)pShadowRange->FindFirst(); if (m_pMasterShadow == NULL) { FailAndExecute(); End(); return; } SliceHelper::AddNamesToController(this, m_pMasterShadow); } else { // Find out whether we have a consistent level or not // (And preserve current shadow settings while we're looking) pShadowRange = m_pTool->GetShadowEditRange(); Node* pNode = pShadowRange->FindFirst(); // NodeShadowController* pNewController = NULL; BOOL bOK; while (pNode) { if (pNode->IsAShadowController()) { bOK = m_pMasterShadow->CompareState((NodeShadowController*)pNode) && SaveSettings((NodeShadowController*)pNode); if (!bOK) { FailAndExecute(); End(); return; } } else { FailAndExecute(); End(); return; } pNode = pShadowRange->FindNext(pNode); } } // at this point in the proceedings, we should definitely have a master shadow controller. // if we don't, then something somewhere has gone fruity :o) if (m_pMasterShadow == NULL) { ERROR3("OpDragCreateShadow2::DoWithParam; Selection problem"); FailAndExecute(); End(); return; } // Conditionally issue redraws if there were other effects above us in the stack pShadowRange = m_pTool->GetShadowEditRange(); Node* pNode = pShadowRange->FindFirst(); // NodeShadowController* pNewController = NULL; BOOL bOK; bOK = DoInvalidateNodesRegions(*pShadowRange, TRUE, FALSE, FALSE, FALSE); // Don't recache if (!bOK) { FailAndExecute(); End(); return; } while (pNode) { if (pNode->IsAShadowController()) { NodeShadowController* pController = (NodeShadowController*)pNode; // pController->ReleaseCached(TRUE, FALSE, FALSE, TRUE); pController->ReleaseCached(TRUE, FALSE, FALSE, FALSE); } pNode = pShadowRange->FindNext(pNode); } // record initial information from the master shadow. m_InitialGlowWidth = m_pMasterShadow->GetGlowWidth(); m_dcInitialWallOffset = m_pMasterShadow->GetWallShadowOffset(); m_dcInitialFloorShadowVector = m_pMasterShadow->GetFloorShadowVector(); m_InitialFeatherWidth = m_pMasterShadow->GetFeatherWidth(); // tell the info-bar to update itself for whatever objects are now selected. if (m_pInfoBar != NULL) m_pInfoBar->UpdateAllGadgets(); // Prevent re-cacheing during this drag Operation::SetQuickRender(TRUE); // ok, stop faffing around and get on with the drag. StartDrag(DRAGTYPE_AUTOSCROLL); } /******************************************************************************************** > static NodeRenderableInk* OpDragCreateShadow2::GetSelectedShadowFromNode(NodeRenderableInk* pClickedNode) Author: Phil_Martin (Xara Group Ltd) Created: 18/11/2004 Inputs: pClickedInk - ptr to a simple node which the user has clicked on Outputs: ppLevelRange - output ptr to range of NodeShadowControllers Returns: NodeRenderableInk* - ptr to a user-selectable node in the postprocessor stack above the selection, if present NULL otherwise Purpose: Determine whether the clicked item is at an editable position in the postprocessor stack ********************************************************************************************/ NodeRenderableInk* OpDragCreateShadow2::GetSelectedShadowFromNode(NodeRenderableInk* pClickedNode) { Node* pUpNode = pClickedNode->FindParent(); while (pUpNode && (pUpNode->IsAShadowController() || pUpNode->IsNodeHidden())) { if (pUpNode->IsAShadowController()) { NodeShadowController* pController = (NodeShadowController*)pUpNode; NodeShadow* pShadow = pController->GetShadow(); if (pShadow && pShadow->IsSelected()) { return pShadow; } } pUpNode = pUpNode->FindParent(); } return NULL; } /******************************************************************************************** > virtual void OpDragCreateShadow2::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Inputs: PointerPos where it's at, man ;) ClickMods what's happenin', man ;) pSpread the spread where the action is, dude ;) Purpose: Called by Camelot's drag code whenever the pointer moves as a shadow is being dragged. Updates the master shadow according to the position of the mouse pointer, and mirrors the master shadow's settings in all other shadows of the same type, which were selected when the drag was started. Errors: See also: ********************************************************************************************/ void OpDragCreateShadow2::DragPointerMove( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSolidDrag) { // ok, decide how the drag is gonna change our master shadow controller, // then apply its settings to all other shadows in our shadow-list. // the shadow settings are calculated relative to this rectangle. DocRect drBaseRect = m_pMasterShadow->GetInsideBoundingRect(); // two useful offset vectors which we use to determine the shadow settings. DocCoord dcTotalOffset = PointerPos - m_InitialPointerPos; // we have different behaviours, depending on the type of shadow we are dealing with. switch (m_pMasterShadow->GetShadowType()) { // wall shadows are simply offset by the pointer. case SHADOWTYPE_WALL: { DocCoord dcControlOffset = m_dcInitialWallOffset + dcTotalOffset; // set the same shadow settings in all wall-shadow members of the shadow list. ChangeAllShadows(&dcControlOffset, NULL, 0, SHADOWTYPE_WALL); // update the info-bar with these settings as well: // 1. to feedback the wall-shadow position in the UI, and // 2. to keep a record of the most recent wall-shadow settings. m_pInfoBar->UpdateGadgetPositionShadowX(); m_pInfoBar->UpdateGadgetPositionShadowY(); } break; // floor shadows are positioned so as to maintain two constant values: // 1. the ratio of the shadow's height to the pointer's height above the shadow's base (alpha). // 2. the horizontal distance from the pointer to the centre of the shadow (offx / alpha) // // in this model, the floor shadow is a parallelogram with a fixed base. // it has a defining line from the centre of its base to the centre of its top, // and the control pointer is glued to one of the sides. case SHADOWTYPE_FLOOR: { DocCoord BaseCentre((drBaseRect.hi.x + drBaseRect.lo.x) / 2, drBaseRect.lo.y); DocCoord InitVector = m_InitialPointerPos - BaseCentre; // we don't like a zero initial pointer height. if (InitVector.y > 0) { DocCoord NewVector = PointerPos - BaseCentre; const double alpha = (double)m_dcInitialFloorShadowVector.y / (double)InitVector.y; const double offx = (double)m_dcInitialFloorShadowVector.x - ((double)InitVector.x * alpha); DocCoord dcControlVector((MILLIPOINT)(0.5 + (double)NewVector.x * alpha + offx), (MILLIPOINT)(0.5 + (double)NewVector.y * alpha) ); // set the same shadow settings in all floor-shadow members of the shadow list. ChangeAllShadows(NULL, &dcControlVector, 0, SHADOWTYPE_FLOOR); } } break; // it's very hard to accurately feed back glow-width changes, as the outline of the // shadowed node would need to be taken into account. therefore, we use a clever & quick // approximation from David's old code - add up the x- and y- offsets from where the // drag started, taking into account our position relative to the centre of the shadowed // node. the upshot of this is that vertical or horizontal mouse movements directly // relate to how wide the glow is. case SHADOWTYPE_GLOW: { if (m_InitialPointerPos.x < drBaseRect.Centre().x) dcTotalOffset.x = -dcTotalOffset.x; if (m_InitialPointerPos.y < drBaseRect.Centre().y) dcTotalOffset.y = -dcTotalOffset.y; MILLIPOINT GlowWidth = m_InitialGlowWidth + (dcTotalOffset.x + dcTotalOffset.y); if (GlowWidth < 0) GlowWidth = 0; // set the same shadow settings in all glow-shadow members of the shadow list. ChangeAllShadows(NULL, NULL, GlowWidth, SHADOWTYPE_GLOW); } break; case SHADOWTYPE_FEATHER: { if (m_InitialPointerPos.x < drBaseRect.Centre().x) dcTotalOffset.x = -dcTotalOffset.x; if (m_InitialPointerPos.y < drBaseRect.Centre().y) dcTotalOffset.y = -dcTotalOffset.y; MILLIPOINT FeatherWidth = m_InitialFeatherWidth + (dcTotalOffset.x + dcTotalOffset.y); if (FeatherWidth < 0) FeatherWidth = 0; // set the same shadow settings in all glow-shadow members of the shadow list. ChangeAllShadows(NULL, NULL, FeatherWidth, SHADOWTYPE_FEATHER); } break; default: ERROR3("OpDragCreateShadow2::DragPointerMove; found an unrecognised shadow type!"); return; } } /******************************************************************************************** > virtual void OpDragCreateShadow2::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSuccess, BOOL bSolidDrag) Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Inputs: PointerPos where the pointer is when the drag ends. ClickMods modifiers (eg CTRL/SHIFT) in use when the drag ends. pSpread the spread where the drag is taking place. bSuccess whether the drag finished successfully, or aborted half way through, eg if the user pressed Escape. Purpose: Lets us tidy ourselves up, refresh the view etc. after the drag has finished. Used by the shadowing code to save or restore the old shadow settings from before the drag. Errors: See also: ********************************************************************************************/ void OpDragCreateShadow2::DragFinished( DocCoord PointerPos, ClickModifiers ClickMods, Spread* pSpread, BOOL bSuccess, BOOL bSolidDrag) { // tell the drag framework that we're finished. EndDrag(); // Allow re-cacheing after this drag Operation::SetQuickRender(FALSE); // if we've succeeded so far, then attempt to create a new action in which the original // shadow settings are saved. if there are no saved shadow settings, then we obviously // don't need to worry about this. // also, note that the process of saving settings means that we relinquish control of our // list of initial settings to the action. to make sure we don't try to access this // list again, we set our pointer to it to NULL. if (bSuccess) { if (m_plInitialShadowSettings != NULL && !m_plInitialShadowSettings->IsEmpty() ) { ActionCode ac = SaveShadowSettingsAction::Init( this, GetUndoActionList(), m_plInitialShadowSettings ); if (ac == AC_FAIL) bSuccess = FALSE; else m_plInitialShadowSettings = NULL; } } if (m_pOwnedRange) { delete m_pOwnedRange; m_pOwnedRange = NULL; } // if something went wrong, eg the user cancelled the drag, then fail, // and reset all shadows to their initial settings before the drag. if (!bSuccess) { // Force Redraw all the affected regions ListRange* pShadowRange = m_pTool->GetShadowEditRange(); Node* pNode = pShadowRange->FindFirst(); DocRect drInvalidRegion; while (pNode) { ENSURE(pNode->IsAShadowController(), "Unexpected node type in ChangeAllShadows"); NodeShadowController* pShadowController = (NodeShadowController*)pNode; NodeShadow* pNodeShadow = pShadowController->GetShadow(); drInvalidRegion = drInvalidRegion.Union(pShadowController->GetEffectStackBounds()); pShadowController->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only // record the current bounds covered by the shadows. drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); pNode = pShadowRange->FindNext(pNode); } // redraw the invalidated screen areas. Document* pDoc = Document::GetCurrent(); Spread* pSpread = pDoc->GetSelectedSpread(); if (pDoc != NULL && pSpread != NULL) { pDoc->ForceRedraw(pSpread, drInvalidRegion, FALSE, NULL, FALSE); // Don't automatically flush out subtree caches } RestoreSavedSettings(); FailAndExecute(); } else { // Conditionally issue redraws if there were other effects above us in the stack EffectsStack* pStack = GetApplication()->FindSelection()->GetEffectsStack(); // From cache ENSURE(pStack, "Something's gone seriously wrong"); if (!pStack->NodeInTopLevel(m_pMasterShadow)) { ListRange* pShadowRange = m_pTool->GetShadowEditRange(); DoInvalidateNodesRegions(*pShadowRange, TRUE); // Argh! this function wants an actual Range! } } // tell the Op system that we're finished. End(); } /******************************************************************************************** > static BOOL OpDragCreateShadow2::Declare() Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Returns: TRUE if all went ok, FALSE otherwise. Purpose: Adds ourself to the list of all known operations. See also: SoftShadowTool::Init(), where this function is called from. ********************************************************************************************/ BOOL OpDragCreateShadow2::Declare() { return (RegisterOpDescriptor( 0, _R(IDS_DRAGCREATESHADOWOP), CC_RUNTIME_CLASS(OpDragCreateShadow2), OPTOKEN_DRAGCREATESHADOW, OpDragCreateShadow2::GetState)); } /******************************************************************************************** > static OpState OpDragCreateShadow2::GetState( String_256* pstrDescription, OpDescriptor* pOpDesc ) Author: Karim_MacDonald (Xara Group Ltd) Created: 14/06/2000 Returns: The state of the operation. Purpose: Get the state of this operation, ie whether or not it can be invoked. We currently always give an all-clear - this function is really a lot more use for Ops which live in menus. ********************************************************************************************/ OpState OpDragCreateShadow2::GetState(String_256* pstrDescription, OpDescriptor* pOpDesc) { OpState DefaultOpSt; return DefaultOpSt; } /******************************************************************************************** > void OpDragCreateShadow2::ChangeAllShadows( DocCoord* pdcWallOffset, DocCoord* pdcFloorVector, MILLIPOINT GlowWidth, ShadowType ChangeType ) Author: Karim_MacDonald (Xara Group Ltd) Created: 16/06/2000 Inputs: pdcWallOffset the offset to reset wall shadows with. pdcFloorVector the vector to reset floor shadows with. GlowWidth the width to reset glow shadows with. ChangeType which Outputs: Shadows of type ChangeType in m_ShadowList have their parameters reset accordingly. Purpose: Change all shadows in our list, of type ChangeType, in a way specific to the type: SHADOWTYPE_WALL Wall shadow is offset. SHADOWTYPE_FLOOR Floor shadow vector is reset. SHADOWTYPE_GLOW Glow shadow width is reset. Each shadow is regenerated, and the document is refreshed accordingly. Errors: ERROR3 in DEBUG if an invalid ShadowType, or if the specific input parameter relating to the ShadowType is invalid. NB Does *NOT* check for errors in release! ********************************************************************************************/ void OpDragCreateShadow2::ChangeAllShadows( DocCoord* pdcWallOffset, DocCoord* pdcFloorVector, MILLIPOINT GlowWidth, ShadowType ChangeType ) { // input validation, only in _DEBUG. // Declare this outside the DEBUG bit so it can be used below... MILLIPOINT FeatherWidth = GlowWidth; BOOL fInvalidParams = FALSE; double dFloorShadowAngle = 0; double dFloorShadowHeight = 0; switch (ChangeType) { case SHADOWTYPE_WALL: // wall shadows - must have a valid wall offset. fInvalidParams = (pdcWallOffset == NULL); break; case SHADOWTYPE_FLOOR: // floor shadows - must have a valid floor control vector. fInvalidParams = (pdcFloorVector == NULL); if (pdcFloorVector) { // Compute common angle and height for all selected floor shadows INT32 BaseHeight = m_pMasterShadow->GetInsideBoundingRect().Height(); if (BaseHeight > 0) dFloorShadowHeight = (double)pdcFloorVector->y / (double)BaseHeight; else dFloorShadowHeight = 1.0; if (dFloorShadowHeight > MAXSHADOWHEIGHT) dFloorShadowHeight = MAXSHADOWHEIGHT; else if (dFloorShadowHeight < 0.01) dFloorShadowHeight = 0.01; // the angle of the floor shadow is the clockwise rotation of the control vector // from the vertical, in degrees. // Phil says: ARGH! Should be anti-clockwise from due east! What's wrong with the mathematical convention? dFloorShadowAngle = pdcFloorVector->AngleFromVertical(); if (dFloorShadowAngle < -MAXSHADOWANGLE) dFloorShadowAngle = -MAXSHADOWANGLE; else if (dFloorShadowAngle > MAXSHADOWANGLE) dFloorShadowAngle = MAXSHADOWANGLE; } break; case SHADOWTYPE_GLOW: // glow shadows - must have a non-negative glow-width. fInvalidParams = (GlowWidth < 0); break; case SHADOWTYPE_FEATHER: // glow shadows - must have a non-negative glow-width. fInvalidParams = (FeatherWidth < 0); break; default: // unrecognised ShadowType. fInvalidParams = TRUE; break; } ERROR3IF(fInvalidParams, "OpDragCreateShadow2::ChangeAllShadows; Invalid input params!"); // the region to redraw onscreen. DocRect drInvalidRegion; // initialisation - get the bounds of the first shadow on our list. NodeShadowController* pShadowController = NULL; NodeShadow* pNodeShadow = NULL; ENSURE(!drInvalidRegion.IsValid() || drInvalidRegion.IsEmpty(), "bounding rect should be invalid before loop!"); // apply the new settings to every shadow we have which matches ChangeType. ListRange* pShadowRange = m_pTool->GetShadowEditRange(); Node* pNode = pShadowRange->FindFirst(); while (pNode) { ENSURE(pNode->IsAShadowController(), "Unexpected node type in ChangeAllShadows"); pShadowController = (NodeShadowController*)pNode; pNodeShadow = pShadowController->GetShadow(); drInvalidRegion = drInvalidRegion.Union(pShadowController->GetEffectStackBounds()); pShadowController->ReleaseCached(TRUE, FALSE, FALSE, TRUE); // Parents and derived data only if (pShadowController->GetShadowType() == ChangeType) { // record the bounds covered by the shadows before the change. drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); // make the change. switch (ChangeType) { case SHADOWTYPE_WALL: { pShadowController->SetWallShadowOffset(*pdcWallOffset, TRUE); } break; case SHADOWTYPE_FLOOR: { // pShadowController->SetFloorShadowVector(*pdcFloorVector); pShadowController->SetFloorShadowAngle(dFloorShadowAngle); pShadowController->SetFloorShadowHeight(dFloorShadowHeight); pShadowController->RegenerateNode(NULL); } break; case SHADOWTYPE_GLOW: { pShadowController->SetGlowWidth(GlowWidth); pShadowController->RegenerateNode(NULL); } break; case SHADOWTYPE_FEATHER: { pShadowController->SetFeatherWidth(FeatherWidth); pShadowController->RegenerateNode(NULL); } break; default: // Do nothing break; } // record the current bounds covered by the shadows. drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); } // Karim 16/11/2000 // Feathers BODGE - search up the tree for any GeometryLinkedAttrs attached to // ourself or our parents, and tell them that their linked node geometry has changed. { for (Node* pParent = pShadowController; pParent != NULL && pParent->IsAnObject(); pParent = pParent->FindParent() ) { for (NodeAttribute* pGLA = pParent->FindFirstGeometryLinkedAttr(); pGLA != NULL; pGLA = pGLA->FindNextGeometryLinkedAttr() ) { pGLA->LinkedNodeGeometryHasChanged(this); } } } pNode = pShadowRange->FindNext(pNode); } // redraw the invalidated screen areas. Document* pDoc = Document::GetCurrent(); Spread* pSpread = pDoc->GetSelectedSpread(); if (pDoc != NULL && pSpread != NULL) { pDoc->ForceRedraw(pSpread, drInvalidRegion, FALSE, NULL, FALSE); // Don't automatically flush out subtree caches GetApplication()->ServiceRendering(); } } /******************************************************************************************** > BOOL OpDragCreateShadow2::SaveSettings(Range* pShadowRange) Author: Phli Created: 25/11/2004 Inputs: pShadow the shadow whose settings we will save. Outputs: m_plInitialShadowSettings is created if necessary, and updated with the saved settings. Returns: TRUE if successful, FALSE otherwise. Purpose: Record the given shadow's settings. Errors: ERROR3 if pShadow is NULL. See also: RestoreSavedSettings(). ********************************************************************************************/ BOOL OpDragCreateShadow2::SaveSettings(Range* pShadowRange) { ERROR3IF(pShadowRange == NULL, "OpDragCreateShadow2::SaveSettings; NULL input params!"); NodeShadowController* pController = (NodeShadowController*)pShadowRange->FindFirst(); while (pController) { BOOL bOK = SaveSettings(pController); if (!bOK) return FALSE; pController = (NodeShadowController*)pShadowRange->FindNext(pController); } return TRUE; } /******************************************************************************************** > BOOL OpDragCreateShadow2::SaveSettings(NodeShadowController* pShadow) Author: Karim_MacDonald (Xara Group Ltd) Created: 17/06/2000 Inputs: pShadow the shadow whose settings we will save. Outputs: m_plInitialShadowSettings is created if necessary, and updated with the saved settings. Returns: TRUE if successful, FALSE otherwise. Purpose: Record the given shadow's settings. Errors: ERROR3 if pShadow is NULL. See also: RestoreSavedSettings(). ********************************************************************************************/ BOOL OpDragCreateShadow2::SaveSettings(NodeShadowController* pShadow) { ERROR3IF(pShadow == NULL, "OpDragCreateShadow2::SaveSettings; NULL input params!"); if (m_plInitialShadowSettings == NULL) { ALLOC_WITH_FAIL(m_plInitialShadowSettings, new List, this); if (m_plInitialShadowSettings == NULL) return FALSE; } ShadowSettingsItem* pShadowSettings = NULL; ALLOC_WITH_FAIL(pShadowSettings, new ShadowSettingsItem(pShadow), this); if (pShadowSettings == NULL) return FALSE; switch (pShadow->GetShadowType()) { case SHADOWTYPE_WALL: pShadowSettings->dcWallOffset = pShadow->GetWallShadowOffset(); break; case SHADOWTYPE_FLOOR: pShadowSettings->dcFloorVector = pShadow->GetFloorShadowVector(); break; case SHADOWTYPE_GLOW: pShadowSettings->GlowWidth = pShadow->GetGlowWidth(); break; case SHADOWTYPE_FEATHER: pShadowSettings->FeatherWidth = pShadow->GetFeatherWidth(); break; default: ERROR3("OpDragCreateShadow2::SaveSettings; unrecognised shadow type!"); break; } m_plInitialShadowSettings->AddTail(pShadowSettings); return TRUE; } /******************************************************************************************** > void OpDragCreateShadow2::RestoreSavedSettings() Author: Karim_MacDonald (Xara Group Ltd) Created: 17/06/2000 Outputs: Nothing happens to m_plInitialShadowSettings. Purpose: For all shadows in our saved-settings list, restore their settings to those stored with them in the list. Note that this routine also causes the shadows to regenerate, and causes redraws for the invalidated areas of the screen. Errors: See also: ********************************************************************************************/ void OpDragCreateShadow2::RestoreSavedSettings() { // first of all, check whether we actually have any shadow settings to restore. if (m_plInitialShadowSettings == NULL || m_plInitialShadowSettings->IsEmpty()) return; // the region to redraw onscreen. DocRect drInvalidRegion; // initialisation - get the bounds of the first shadow on our list. ShadowSettingsItem* pShadowSettings = (ShadowSettingsItem*)m_plInitialShadowSettings->GetHead(); NodeShadowController* pShadowController = pShadowSettings->pShadow; NodeShadow* pNodeShadow = pShadowController->GetShadow(); drInvalidRegion = pNodeShadow->GetBlobBoundingRect(); // skip through our list and reset all shadows on it. while (pShadowSettings != NULL) { pShadowController = pShadowSettings->pShadow; pNodeShadow = pShadowController->GetShadow(); drInvalidRegion = drInvalidRegion.Union(pShadowController->GetEffectStackBounds()); pShadowController->ReleaseCached(); pNodeShadow->ReleaseCached(); // record the bounds covered by the shadows before the change. drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); // change the shadows appropriately. switch (pShadowController->GetShadowType()) { case SHADOWTYPE_WALL: pShadowController->SetWallShadowOffset(pShadowSettings->dcWallOffset, TRUE); break; case SHADOWTYPE_FLOOR: pShadowController->SetFloorShadowVector(pShadowSettings->dcFloorVector); pShadowController->RegenerateNode(this); break; case SHADOWTYPE_GLOW: pShadowController->SetGlowWidth(pShadowSettings->GlowWidth); pShadowController->RegenerateNode(this); break; case SHADOWTYPE_FEATHER: pShadowController->SetFeatherWidth(pShadowSettings->FeatherWidth); pShadowController->RegenerateNode(this); break; default: ERROR3("OpDragCreateShadow2::RestoreSavedSettings; unrecognised shadow type!"); break; } // record the new bounds of the shadows. drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); pShadowSettings = (ShadowSettingsItem*)m_plInitialShadowSettings->GetNext(pShadowSettings); } // now redraw the invalid screen-region. Document* pDoc = Document::GetCurrent(); Spread* pSpread = pDoc->GetSelectedSpread(); if (pDoc != NULL && pSpread != NULL) { pDoc->ForceRedraw(pSpread, drInvalidRegion); GetApplication()->ServiceRendering(); } } //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- /******************************************************************************************** > SaveShadowSettingsAction::SaveShadowSettingsAction() Author: Karim_MacDonald (Xara Group Ltd) Created: 20/06/2000 Purpose: Constructor. ********************************************************************************************/ SaveShadowSettingsAction::SaveShadowSettingsAction() { m_plShadowSettings = NULL; } /******************************************************************************************** > SaveShadowSettingsAction::~SaveShadowSettingsAction() Author: Karim_MacDonald (Xara Group Ltd) Created: 20/06/2000 Purpose: Destructor - free used resources. ********************************************************************************************/ SaveShadowSettingsAction::~SaveShadowSettingsAction() { if (m_plShadowSettings != NULL) { m_plShadowSettings->DeleteAll(); delete m_plShadowSettings; m_plShadowSettings = NULL; } } /******************************************************************************************** > ActionCode SaveShadowSettingsAction::Init( UndoableOperation* pUndoOp, Author: Karim_MacDonald (Xara Group Ltd) Created: 20/06/2000 Inputs: pUndoOp ptr to the Op which invokes this action. pActionList ptr to the action list which this action should live on. plShadowSettings ptr to a list of saved shadows & their settings. Returns: AC_OK if we're successful. AC_NORECORD if things went wrong but we can continue, just without undo. AC_FAIL if things went so wrong that the op will have to rewind itself. Purpose: Doh! Just realised that this action's name is a complete misnomer! It actually restores saved shadow settings, given a pointer to a list of ShadowSettingsItem's, saving the settings before the restore into a new a new action on the opposite undo/redo list. ********************************************************************************************/ ActionCode SaveShadowSettingsAction::Init( UndoableOperation* pUndoOp, ActionList* pActionList, List* plShadowSettings ) { // call the base class to create and put the action onto the action list. ActionCode ac = AC_FAIL; UINT32 ActSize = sizeof(SaveShadowSettingsAction); SaveShadowSettingsAction* pNewAction = NULL; ac = Action::Init( pUndoOp, pActionList, ActSize, CC_RUNTIME_CLASS(SaveShadowSettingsAction), (Action**)&pNewAction ); // ok, the action now exists. // initialise it with the passed-in list of saved shadow settings. if (ac != AC_FAIL && pNewAction != NULL) { pNewAction->SetShadowList(plShadowSettings); } return ac; } /******************************************************************************************** > ActionCode SaveShadowSettingsAction::Execute() Author: Karim_MacDonald (Xara Group Ltd) Created: 23/06/2000 Inputs: Outputs: Returns: Purpose: The nitty gritty bit, which is called on undo/redo. Errors: See also: ********************************************************************************************/ ActionCode SaveShadowSettingsAction::Execute() { List* plCurrentSettings = NULL; ALLOC_WITH_FAIL(plCurrentSettings, new List, pOperation); if (plCurrentSettings == NULL) return AC_FAIL; // first loop - save the current settings for each shadow we'll be changing. // ShadowSettingsItem* pOldSettings = NULL; ShadowSettingsItem* pNewSettings = (ShadowSettingsItem*)m_plShadowSettings->GetHead(); while (pNewSettings != NULL) { NodeShadowController* pShadow = pNewSettings->pShadow; pOldSettings = NULL; ALLOC_WITH_FAIL(pOldSettings, new ShadowSettingsItem(pShadow), pOperation); if (pOldSettings == NULL) { plCurrentSettings->DeleteAll(); return AC_FAIL; } switch (pShadow->GetShadowType()) { case SHADOWTYPE_WALL: pOldSettings->dcWallOffset = pShadow->GetWallShadowOffset(); break; case SHADOWTYPE_FLOOR: pOldSettings->dcFloorVector = pShadow->GetFloorShadowVector(); break; case SHADOWTYPE_GLOW: pOldSettings->GlowWidth = pShadow->GetGlowWidth(); break; case SHADOWTYPE_FEATHER: pOldSettings->FeatherWidth = pShadow->GetFeatherWidth(); break; default: ERROR3("SaveShadowSettingsAction::Execute; unrecognised shadow type!"); break; } plCurrentSettings->AddTail(pOldSettings); pNewSettings = (ShadowSettingsItem*)m_plShadowSettings->GetNext(pNewSettings); } // second loop - reset each shadow with the settings stored for it in the list. // DocRect drInvalidRegion; pNewSettings = (ShadowSettingsItem*)m_plShadowSettings->GetHead(); NodeShadowController* pShadow = pNewSettings->pShadow; NodeShadow* pNodeShadow = pShadow->GetShadow(); drInvalidRegion = pNodeShadow->GetBlobBoundingRect(); while (pNewSettings != NULL) { pShadow = pNewSettings->pShadow; pNodeShadow = pShadow->GetShadow(); drInvalidRegion = drInvalidRegion.Union(pShadow->GetEffectStackBounds()); pShadow->ReleaseCached(); pNodeShadow->ReleaseCached(); drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); switch (pShadow->GetShadowType()) { case SHADOWTYPE_WALL: pShadow->SetWallShadowOffset(pNewSettings->dcWallOffset, TRUE); break; case SHADOWTYPE_FLOOR: pShadow->SetFloorShadowVector(pNewSettings->dcFloorVector); pShadow->RegenerateNode((UndoableOperation*)pOperation); break; case SHADOWTYPE_GLOW: pShadow->SetGlowWidth(pNewSettings->GlowWidth); pShadow->RegenerateNode((UndoableOperation*)pOperation); break; case SHADOWTYPE_FEATHER: pShadow->SetFeatherWidth(pNewSettings->FeatherWidth); pShadow->RegenerateNode((UndoableOperation*)pOperation); break; default: ERROR3("SaveShadowSettingsAction::Execute; unrecognised shadow type!"); break; } drInvalidRegion = drInvalidRegion.Union(pNodeShadow->GetBlobBoundingRect()); pNewSettings = (ShadowSettingsItem*)m_plShadowSettings->GetNext(pNewSettings); } // update the document. Document* pDoc = Document::GetCurrent(); Spread* pSpread = pDoc->GetSelectedSpread(); if (pDoc != NULL && pSpread != NULL) { pDoc->ForceRedraw(pSpread, drInvalidRegion); GetApplication()->ServiceRendering(); } // empty our current list of settings and use our new one to create a new action. m_plShadowSettings->DeleteAll(); delete m_plShadowSettings; m_plShadowSettings = NULL; return SaveShadowSettingsAction::Init( (UndoableOperation*)pOperation, pOppositeActLst, plCurrentSettings ); } #endif // BUILDSHADOWS