// $Id: hittest.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 hit-testing functions of Node, NodeRenderableInk etc. #include "camtypes.h" //#include "ccobject.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "layer.h" //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "osrndrgn.h" #include "rendbits.h" #include "objreg.h" //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "qualattr.h" #include "attrmap.h" #include "nodeshad.h" #include "nodecont.h" //#include "ndcchbmp.h" #include "grndclik.h" #include "nodeclip.h" // for NodeClipView #include "ndclpcnt.h" // for NodeClipViewController #include "brshattr.h" #include "strkattr.h" #include "lineattr.h" #pragma warning(disable: 4786) #include #pragma warning(default: 4786) DECLARE_SOURCE("$Revision: 1282 $"); // This definition allows sensible debug trace output of memory leaks... // Declare smart memory handling in Debug builds #define new CAM_DEBUG_NEW // Karim 12/04/2000 - uncomment this #define to enable rendering // of the hit-test bitmap on-screen. //#define DEBUG_CLICK_DETECT_BITMAP static AttrFlatColourFill DummyFill; // Dummy fill to use in the place of fancy // fills for hit testing /********************************************************************************************* Preference: SmartClicks Section: DebugFlags Author: Andy_Pennell (Xara Group Ltd) Range: TRUE or FALSE Purpose: If TRUE then click detection is done with rendering, else if FALSE it is done with rectangle intersection. Will be made obsolete once rendering methods are reliable. Defaults to TRUE. **********************************************************************************************/ BOOL NodeRenderableInk::bUseSmartClicks = TRUE; CCAttrMap* NodeRenderableInk::pAttribMap; INT32 NodeRenderableInk::nFoundAttributes; INT32 NodeRenderableInk::nMaxAttributes; /********************************************************************************************* Preference: AverageHitColour Section: ? Author: Phil_Martin (Xara Group Ltd) Range: TRUE or FALSE Purpose: If TRUE then click detection is done with rendering, else if FALSE it is done with rectangle intersection. Will be made obsolete once rendering methods are reliable. Defaults to TRUE. **********************************************************************************************/ BOOL NodeRenderableInk::AverageHitColour = FALSE; ///////////////////////////////////////////////////////////////////////////// TEMP ///////// ///////////////////////////////////////////////////////////////////////////// TEMP ///////// // // These are only going here until I can get a lock on noderend.cpp and put them in there. // 11 years later and it's still not in noderend.cpp (why break with tradition) // /******************************************************************************************** > virtual BOOL NodeRenderableInk::OnMouseMove(const DocCoord& dcPos, Spread* pSpread, ClickModifiers mods) Author: Justin_Flude (Xara Group Ltd) Created: 23/11/94 Inputs: dcPos the current mouse position, in spread coordinates pSpread the spread containing the mouse position mods which keys/buttons are down, eg. Adjust (shift/right button) Outputs: - Returns: TRUE if the node did something with this call, FALSE if it didn't/won't Purpose: Called by the selector tool for the selected object(s) when the mouse is moved. Errors: - SeeAlso: NodeRenderableInk::OnClick; NodeRenderableInk::GetStatusInfo ********************************************************************************************/ BOOL NodeRenderableInk::OnMouseMove(const DocCoord& dcPos, Spread* pSpread, ClickModifiers mods) { #ifdef _DEBUG // if (IsUserName("JustinF")) // { // TRACE( _T("Call to base class NodeRenderableInk::OnMouseMove for a %s\n"), (LPCTSTR) Name()); // } #endif return FALSE; } /******************************************************************************************** > virtual BOOL NodeRenderableInk::GetStatusInfo(String_256** ppStatusText, Cursor** ppStatusCursor) Author: Justin_Flude (Xara Group Ltd) Created: 23/11/94 Inputs: - Outputs: ppStatusText pointer to a string pointer to set to an approrpiate status bar text message, or 0 if not relevant ppStatusCursor pointer to a cursor pointer to set to an apppropriate Cursor object, or 0 if not relevant Returns: TRUE if the routine used any of the output parameters and thus has some text/cursor to show, FALSE if it doesn't. Purpose: Called by the selector tool if either a previous OnMouseMove or OnClick member function call returns TRUE, indicating that the node has some feedback to display when the mous is at the previously passed point. Errors: - SeeAlso: NodeRenderableInk::OnClick; NodeRenderableInk::OnMouseMove ********************************************************************************************/ BOOL NodeRenderableInk::GetStatusInfo(String_256** ppStatusText, Cursor** ppStatusCursor) { #ifdef _DEBUG // if (IsUserName("JustinF")) // { // TRACE( _T("Call to base class NodeRenderableInk::GetStatusInfo for a %s\n"), (LPCTSTR) Name()); // } #endif return FALSE; } ///////////////////////////////////////////////////////////////////////////// TEMP ///////// ///////////////////////////////////////////////////////////////////////////// TEMP ///////// /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindSimpleAtPoint(Spread* pSpread, DocCoord dcPoint, Node* pHighNode = 0, Node** ppInterruptNode = 0) Author: Justin_Flude (Xara Group Ltd) Created: 7/6/94 Inputs: pSpread --- pointer to the spread containing the given point dcPoint --- the point the object to be searched for occupies pHighNode Node below which search should be carried out. ppInterruptNode 0 if hit-detection shouldn't be interrupted Non-null otherwsie, then contains pointer to node at which hit-detection was interrupted on exit. Outputs: - Returns: A pointer to an ink-able object that lies at or is closest to the given point, or 0 if there is none. This object will be "simple", ie. it will be a single "atomic" object such as a path, rectangle etc. and not a "compound" object like a group. Purpose: Searches the ink-able objects within the given spread for one that lies at or near the given point. The method used to render each candidate object into a small monochrome bitmap, in the reverse order to normal (so that the object normally rendered last is the first to be rendered into the bitmap). The bitmap is examined to see if the object affected the centre or surrounding pixels. One of the biggest time users in this routine is the scanning of the tree to find the attributes applied to an object. Attempts are made to cut down this scanning. A hash table of pointers to attributes is maintained. Assume that the hash table is full of attributes used to render an object. Then, as we scan backwards along a sibling list we simply remove attributes from the hash table as we encounter them because they have moved out of scope. When we go down a child link we scan along the sibling child list adding all those attributes encountered to the hash table because they have just moved into scope. Thus, at any time the hash table contains pointers to all the attributes that apply to the node we are about to render WITH ONE EXCEPTION! The reverse render-order tree-scan means that objects are encountered before their child attributes. The FindAppliedAttribute routine overcomes this by forcibly adding all child attributes to the map when it scans for applied attributes. Errors: - SeeAlso: NodeRenderableInk::FindCompoundAtPoint; NodeRenderableInk::OldFindFirstAtPoint; Node::FindFirstHitTest; Node::FindNextHitTest ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindSimpleAtPoint(Spread* pSpread, DocCoord dcPoint, Node* pHighNode, Node** ppInterruptNode ) { // TRACE(_T("FindSimpleAtPoint(%d, %d)\n"), dcPoint.x, dcPoint.y); // Find the currently active DocView. DocView* pDocView = DocView::GetSelected(); ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint"); // Initialise the count and limit on the number of attributes encountered. List* pTypeList = ObjectRegistry::GetRequiredAttribs(0); // <--- ENSURE(pTypeList != 0 && !pTypeList->IsEmpty(), "Object Registry returned a dodgy list in NodeRenderableInk::FindSimpleAtPoint"); nFoundAttributes = 0; nMaxAttributes = pTypeList->GetCount(); // Clear interrupt node pointer if (ppInterruptNode) *ppInterruptNode = 0; // Initialise the hash-table used to look-up attributes encountered. // Make the hash table twice as big as the number of att types for efficient look-up. pAttribMap = new CCAttrMap(nMaxAttributes*2); ERROR2IF(pAttribMap==0,0,"FindSimpleAtPoint: couldn't create a CCAttrMap!"); // Build a rectangle around the given point, based on the size of a blob. DocRect drClickRect; OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint, BT_CLICKME, &drClickRect); // Get the extent of the current view and convert to spread coordinates. DocRect drViewRect = pDocView->GetDocViewRect(pSpread); pSpread->DocCoordToSpreadCoord(&drViewRect); // Create a special hit-detection render region to draw into, if possible. RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, CLICKBITMAP); if (pRegion==NULL) { delete pAttribMap; ERROR2(NULL,"FindSimpleAtPoint: couldn't create a RenderRegion!"); } // Start with a (very) clean sheet. pRegion->SetClean(TRUE, TRUE); // These point to ink-nodes in the tree that have been rendered into our hit-test // render-region. NodeRenderableInk* pHitNode = NULL; // node that occupies dcPoint NodeRenderableInk* pNearNode = NULL; // node that came closest to dcPoint NodeRenderableInk* pCompoundHitNode = NULL; // compound node that was hit before trying children NodeRenderableInk* pCompoundNearNode = NULL; // compound node that was near before trying children MonotonicTime Slice; // Sample time now BOOL bSkipChildren = FALSE; // Perform hit-testing for each render-region provided by the DocView. while (pRegion != NULL) { // Render the first and subsequent ink-nodes into the render-region, checking if any // draw into it. for (Node* pNode = FindFirstHitTest(pSpread, drClickRect, pAttribMap, TRUE, pHighNode); pNode != NULL; pNode = FindNextHitTest(pNode, drClickRect, pAttribMap, TRUE, bSkipChildren)) { bSkipChildren = FALSE; if (pNode) { if (pNode->IsAnObject()) { // Does this renderable node intersect the click rectangle? If not, then // ignore it. DocRect drBounds = ((NodeRenderableInk*) pNode)->GetBoundingRect(FALSE, TRUE); if (!drBounds.IsIntersectedWith(drClickRect)) continue; // This node can be rendered. Find all the outstanding attributes in the // tree that affect the rendering of this node, and render them. // // Jason 15/1/97 - Save the current RR context so that we can restore the render // stack to a sensible state after rendering this object. This is safe because we // always render all relevant attributes for every object we hit test. // We restore immediately after rendering (the 3 RestoreContext() calls, below) pRegion->SaveContext(); ((NodeRenderableInk*) pNode)->RenderAppliedAttributes(pRegion); // This ensures that the object is rendered according to the view Quality setting. // When Quality attributes appear in the tree this may not be a good thing!!! // It should really be called before RenderAppliedAttributes but at the moment that seems // to always set Quality to Normal for some reason!!! // Extract Quality from DocView object and set it up in the renderregion/monobitmap // Markn 26/9/95: Added the following 'if' statement to prevent the setting of the view // quality overriding the quality set by the AttrQuality attribute in the guides layer. if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer) { QualityAttribute *pQualAttr = new QualityAttribute(pDocView->RenderQuality); pRegion->SetQuality(pQualAttr, TRUE); } // Phil 18/03/2004 // Must render clip region attribute AFTER Quality attribute has been set how // the region will be rendered otherwise GDraw will fail in FillPath! // // Karim 13/4/2000 (lucky for some...) // ok, BODGE CITY here. we're going to do a RenderAppliedClipAttributes(), which // renders all the ClipRegionAttributes which would normally apply to this node. // see the method header for more info. ((NodeRenderableInk*) pNode)->RenderAppliedClipAttributes(pRegion); do { // This 'do' loop will render the same node until the sub render state is 0 // This allows heavy-duty nodes (e.g. blends) to get the hit test bmp to be checked // at any point during the rendering of the node. // Now the attributes are done, render the object itself. pRegion->SetClean(FALSE, FALSE); // Reset just the clean flag so we can tell whether // compound nodes render anything // ------------------------------ pNode->Render(pRegion); // ------------------------------ #ifdef DEBUG_CLICK_DETECT_BITMAP OSRenderBitmap::DebugMe(pRegion, 8); #endif // Check if any of the pixels in the render-region were affected by the // last rendering. BitmapContents bcDrawCode = OSRenderBitmap::GetContents(pRegion, FALSE); if (pNode->IsCompoundClass() && !pRegion->IsClean()) { #ifdef DEBUG_CLICK_DETECT_BITMAP OSRenderBitmap::DebugMe(pRegion, 108); #endif // Compound has rendered something... // // Clean out the bitmap since we know we are going to use it some more pRegion->SetClean(FALSE, TRUE); // Compound nodes may render themselves if they have effect // attributes applied (so that effect transparency is // properly taken into account during hit testing) // If the compound node rendered the centre pixel then we must // allow the children of the node to render themselves to get // a true indication of the clicked on node // If not, then we should not allow our children to render // themselves at all if (bcDrawCode != BITMAPCONTENTS_CENTRE && bcDrawCode != BITMAPCONTENTS_ANY) { // Skip out of this node's subtree... bSkipChildren = TRUE; } else // else just keep going (treat this as a "near" node) { // if (pNearNode == NULL) pNearNode = (NodeRenderableInk*) pNode; if (bcDrawCode == BITMAPCONTENTS_CENTRE) { if (pCompoundHitNode == NULL) pCompoundHitNode = (NodeRenderableInk*)pNode; } else { if (pCompoundNearNode == NULL) pCompoundNearNode = (NodeRenderableInk*)pNode; } } } else { if (bcDrawCode == BITMAPCONTENTS_CENTRE) { // The centre pixel was affected. Remember this node and prepare to // return result to caller. pHitNode = (NodeRenderableInk*) pNode; // And exit both loops. We have to restore the previous RR state first, though pRegion->RestoreContext(); goto AfterSearch; } else if (bcDrawCode == BITMAPCONTENTS_ANY) { // Some outer pixels were affected. If we don't already have a // "closest" node then remember this one. If we do already have a // "closest" node then the previous "closest" is "closer", because // it is on-top! if (pNearNode == NULL) pNearNode = (NodeRenderableInk*) pNode; // Don't break because although this stage (typically a stage of a Blend) has returned // "Near" the next one might be a direct hit! //break; // break out of the do-while sub render state loop } } // If the caller has allowed us to inetrrupt the hit-testing process // then we will do so if the mouse has moved... // Only do these tests every twentieth of a second because they could take some time if (ppInterruptNode && Slice.Elapsed(50)) { Slice.Sample(); // Reset the slice timer DocView* pDocView = DocView::GetCurrent(); if (pDocView->IsMouseMoved()) // Has the mouse moved? { TRACEUSER( "Phil", _T("FindSimpleAtPoint Interrupt! %lx\n"), pNode); if (*ppInterruptNode == NULL) // If so set the interrupt *ppInterruptNode = pNode; // node pointer pNode = 0; // Clear pointer to "hit" node // And exit both loops. We have to restore the previous RR state first, though pRegion->RestoreContext(); goto AfterSearch; // and stop the search } } // Render the same node until the sub render state is NULL } while (pRegion->GetSubRenderState() != NULL); // Restore the RenderRegion attr state to how it was before we rendered this node pRegion->RestoreContext(); } } } // Advance onto the next render-region. pRegion = pDocView->GetNextOnTop(&drClickRect); } AfterSearch: // Discard remaining render regions (although there shouldn't be any). while (pRegion != NULL) pRegion = pDocView->GetNextOnTop(&drClickRect); // Deallocate the hash table. delete pAttribMap; // If we found a "hit" then return that node, otherwise return the closest // "near miss" (which will be null if no node was close). if (pHitNode == NULL) { if (pNearNode != NULL) { if (!pNearNode->IsKindOf(CC_RUNTIME_CLASS(NodeShadow)) && !pNearNode->IsKindOf(CC_RUNTIME_CLASS(NodeShadowController))) pHitNode = pNearNode; } else if (pCompoundHitNode) pHitNode = pCompoundHitNode; else if (pCompoundNearNode) pHitNode = pCompoundNearNode; } else { // We might have set pNearNode from a compound render // but then allowed processing to continue // So if pNearNode is set but is not the parent of the hit node then // we should use pNearNode instead // // NOTE! We could avoid extra work by making FinNextHitTest stop as soon // as it leaves the subtree whose root is pNearNode. if (pCompoundHitNode && !pCompoundHitNode->IsNodeInSubtree(pHitNode)) pHitNode = pCompoundHitNode; } // Check if this node wishes to be selected // and if not, find it's parent instead... while (pHitNode != NULL && !pHitNode->CanSelectAsSimple()) // While we have a node but it doesn't { // want to be selected... Node* ptemp = pHitNode->FindParent(); // Find parent of current node if (ptemp != NULL && ptemp->IsAnObject()) // If parent is ink { pHitNode = (NodeRenderableInk*) ptemp; // Then reset current pointer if (!pHitNode->CanSelectAsCompoundParent()) // See if it is prepared to be selected pHitNode = NULL; // If not then can't select anything! } else // Else (parent is NOT ink) pHitNode = NULL; // Then can't select anything! } // Give the node a chance to alter it's "hit" status if (pHitNode) pHitNode = pHitNode->FindNodeAtPointHelper(pSpread, dcPoint); return pHitNode; } /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindSimpleAtPointForColourPicker( Spread* pSpread, DocCoord dcPoint, Pixel32bpp& Pix, Node* pHighNode, Node** ppInterruptNode) Author: Chris_Snook (Xara Group Ltd) Created: 28/10/99 Inputs: pSpread --- pointer to the spread containing the given point dcPoint --- the point the object to be searched for occupies Pix --- reference to the structure that will receive pixel data (created internally by custom colour picker control) pHighNode Node below which search should be carried out. ppInterruptNode 0 if hit-detection shouldn't be interrupted Non-null otherwsie, then contains pointer to node at which hit-detection was interrupted on exit. NOTE: uses same inputs as NodeRenderableInk::FindSimpleAtPoint so that we may call NodeRenderableInk::FindSimpleAtPoint internally. Outputs: - Returns: A pointer to an ink-able object that lies at or is closest to the given point, or 0 if there is none. This object will be "simple", ie. it will be a single "atomic" object such as a path, rectangle etc. and not a "compound" object like a group. Also returns (by reference) details concerning the pixel that the colour picker control is over. Purpose: Firstly, this function serves to find the 'ID' of the item that our colour picker is over - which is used to update our mouse cursor. Secondly, it obtains the TRUE 32-BIT RGB index for the pixel that our mouse cursor is over - enabling us to do colour manager/dialog type things. The first part of this routine simply calls FindSimpleAtPointFor - and the second part actually does colour specific work. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindSimpleAtPointForColourPicker(Spread* pSpread, DocCoord dcPoint, Pixel32bpp& Pix, Node* pHighNode, Node** ppInterruptNode) { // Find the currently active DocView. DocView* pDocView = DocView::GetSelected(); ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint"); // Build a rectangle around the given point, based on the size of a blob. DocRect drClickRect; OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint, BT_CLICKME, &drClickRect); // Get the extent of the current view and convert to spread coordinates. DocRect drViewRect = pDocView->GetDocViewRect(pSpread); pSpread->DocCoordToSpreadCoord(&drViewRect); // find 'ID' of item that mouse cursor is over (or not) NodeRenderableInk* pHitNode = FindSimpleAtPoint (pSpread, dcPoint, pHighNode, ppInterruptNode); BOOL bOldForce = pDocView->SetForceDefaultColourContexts(); // Create a colour render region to draw into, if possible. RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, COLOURBITMAP); if (pRegion == 0) { pDocView->SetForceDefaultColourContexts(bOldForce); delete pAttribMap; ERROR2(0,"FindSimpleAtPoint: couldn't create a RenderRegion!"); } // Start with a (very) clean sheet. pRegion->SetClean(TRUE, TRUE); pRegion->StopRender(); while (pRegion != 0) { // we want to render everything using the quality the document is using .... if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer) { QualityAttribute *pQualAttr = new QualityAttribute(pDocView->RenderQuality); pRegion->SetQuality(pQualAttr, TRUE); } // render everything over pSpread into our renderregion .... pDocView->ContinueRenderView(pRegion, pSpread, TRUE, FALSE); // uncommenting the following line allows us to see what is going on // OSRenderBitmap::DebugMe(pRegion,8); // need explicit cast to enable us to call ReadCentrePixel32bpp if (pRegion->IsKindOf(CC_RUNTIME_CLASS(GRenderClickColour))) { GRenderClickColour* actualRegion = (GRenderClickColour*) pRegion; if (NodeRenderableInk::AverageHitColour) Pix = actualRegion->GetAveragePixel32BPP(); else Pix = actualRegion->GetCentrePixel32BPP(); } pRegion->StartRender(); pRegion = pDocView->GetNextOnTop(&drClickRect); } pDocView->SetForceDefaultColourContexts(bOldForce); return pHitNode; } /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindColourForNodeRenderableAtPoint (Spread* pSpread, DocCoord dcPoint, Pixel32bpp& Pix, Node* pThisNode, AttrFillGeometry* pThisFill) { Author: Chris_Snook (Xara Group Ltd) Created: 12/5/2000 Inputs: pSpread --- pointer to the spread containing the given point dcPoint --- the point the object to be searched for occupies Pix --- reference to the structure that will receive pixel data (created internally by custom colour picker control) pThisNode The node that this function is concerned with pThisFill --- The fill that this function is concerned with ( parent (pThisFill) == pThisNode ) Outputs: - Returns: 0 (at present) Purpose: The function obtains the TRUE 32-BIT RGB index for the pixel that our mouse cursor is over for the supplied node. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPointForColourPicker ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindColourForNodeRenderableAtPoint (Spread* pSpread, DocCoord dcPoint, Pixel32bpp& Pix, Node* pThisNode, AttrFillGeometry* pThisFill) { PORTNOTETRACE("other","NodeRenderableInk::FindColourForNodeRenderableAtPoint - do nothing"); #ifndef EXCLUDE_FROM_XARALX // Find the currently active DocView. DocView* pDocView = DocView::GetSelected(); ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint"); // Build a rectangle around the given point, based on the size of a blob. DocRect drClickRect; OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint, BT_CLICKME, &drClickRect); // Get the extent of the current view and convert to spread coordinates. DocRect drViewRect = pDocView->GetDocViewRect(pSpread); pSpread->DocCoordToSpreadCoord(&drViewRect); // Create a colour render region to draw into, if possible. RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, COLOURBITMAP); if (pRegion==0) { delete pAttribMap; ERROR2(0,"FindSimpleAtPoint: couldn't create a RenderRegion!"); } // Start with a (very) clean sheet. pRegion->SetClean(TRUE, TRUE); pRegion->StopRender (); while (pRegion != 0) { // we want to render everything using the quality the document is using .... if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer) { QualityAttribute* pQualAttr = new QualityAttribute(pDocView->RenderQuality); pRegion->SetQuality(pQualAttr, TRUE); } pRegion->SetFillGeometry ((ColourFillAttribute*) (pThisFill->GetAttributeValue ()), FALSE); pThisNode->Render (pRegion); // uncommenting the following line allows us to see what is going on // OSRenderBitmap::DebugMe(pRegion,8); // need explicit cast to enable us to call ReadCentrePixel32bpp if (pRegion->IsKindOf(CC_RUNTIME_CLASS(GRenderClickColour))) { GRenderClickColour* actualRegion = (GRenderClickColour*) pRegion; Pix = actualRegion->GetCentrePixel32BPP(); } pRegion->StartRender (); pRegion = pDocView->GetNextOnTop(&drClickRect); } #endif return 0; // I suppose I should return something else here? //pThisNode); } /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindCompoundFromSimple(NodeRenderableInk* pSimpleNode, Node* pSiblingNode = 0) Author: Justin_Flude (Xara Group Ltd) Created: 13/10/94 Inputs: pSimpleNode a simple node, eg. a renderable node such as a path, possibly returned by a previous call to FindSimpleAtPoint. Outputs: - Returns: A pointer to the "compound" node, eg. a group, that contains the given simple node, or (pSimpleNode) if there isn't one. Purpose: This function will search up the parent links from the given "simple" node looking for a node whose parent is a layer. This guarantees that the returned node will be a genuinely selectable object, for example the routine will return the group node if the given point is within a grouped object. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindCompoundFromSimple(NodeRenderableInk* pSimpleNode, Node* pSiblingNode) { // If necessary, search for a selectable parent node. if (pSimpleNode != 0) { for (;;) { // Find the node's parent, and if it is a layer then we have a node that can // be selected. Node* pParentNode = pSimpleNode->FindParent(); Node* pSaveParentNode = 0; // DMc - new test to see which parent node wishes to be selected while (pParentNode != 0) { if (pParentNode->IsRenderable() && !pParentNode->IsNodeDocument() && !pParentNode->IsLayer() && !pParentNode->IsPaper() && !pParentNode->IsSpread() && !pParentNode->IsChapter()) { if (pParentNode->PromoteHitTestOnChildrenToMe() || pParentNode->IsSelected()) pSaveParentNode = pParentNode; } pParentNode = pParentNode->FindParent(); } pParentNode = pSaveParentNode; if (pParentNode == 0 || pParentNode->IsLayer()) break; // If the caller has specified that the node should be in the same level // as the specified sibling then check to see if the node we've got to // exists in that sibling list or not... if (pSiblingNode != 0) { // Check all siblings. Start from first sibling in this list. Node* pSibling = pSimpleNode->FindParent(); if (pSibling != 0) pSibling = pSibling->FindFirstChild(); // Loop around comparing members of the sibling list with the specified sibling... while (pSibling != 0 && pSibling != pSiblingNode) pSibling = pSibling->FindNext(); // If we found the specified sibling then stop the CompoundFromSimple search. if (pSibling == pSiblingNode) break; } // If the parent doesn't want to be selected due to one of it's children being // clicked on then stop NOW! if (pParentNode->IsAnObject()) { if (((NodeRenderableInk*) pParentNode)->CanSelectAsCompoundParent()) pSimpleNode = (NodeRenderableInk*) pParentNode; // Parent allowed to be selected else break; // Parent not allowed to be selected // So return pSimpleNode now! } else { // Hit a non-ink node so we must have emerged above the selection surface // This shouldn't happen, so raise an error! ERROR2(0, "Couldn't find a selectable compound node in FindCompoundFromSimple"); } } } // Return the node found, or 0 if we couldn't find any. return pSimpleNode; } /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindInnerCompound(Node* pLowNode, Node* pHighNode) Author: Justin_Flude (Xara Group Ltd) Created: 2/12/94 Inputs: pLowNode the node to start the search from (deep in the tree) pHighNode the node to finish searching at Outputs: - Returns: - Purpose: Errors: - SeeAlso: - ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindInnerCompound(Node* pLowNode, Node* pHighNode) { if (pLowNode==pHighNode) return 0; if (!pHighNode->AllowSelectInside()) return 0; Node* pLastCompoundNode = pLowNode; if (pLowNode != 0) { for (;;) { pLowNode = pLowNode->FindParent(); if (pLowNode == 0 || pLowNode == pHighNode || IS_A(pLowNode, Layer)) break; if (pLowNode->IsCompound()) pLastCompoundNode = pLowNode; // if (IsUserName("JustinF")) TRACE( _T("\t\tLooking at a %s\n"), pLowNode->Name()); } } return (NodeRenderableInk*) pLastCompoundNode; } /******************************************************************************************** > static NodeRenderableInk* NodeRenderableInk::FindCompoundAtPoint(Spread* pSpread, DocCoord dcPoint, Node* pHighNode = 0) Author: Justin_Flude (Xara Group Ltd) Created: 7/6/94 Inputs: pSpread --- pointer to the spread containing the given point dcPoint --- the point the object to be searched for occupies pHighNode Node below which search should be carried out. Outputs: - Returns: A pointer to a node at the given point, which may be a "compound" object, eg. a group. Purpose: This function is similar to NodeRenderableInk::FindSimpleAtPoint, except that Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint ********************************************************************************************/ NodeRenderableInk* NodeRenderableInk::FindCompoundAtPoint(Spread* pSpread, DocCoord dcPoint, Node* pHighNode) { return FindCompoundFromSimple(FindSimpleAtPoint(pSpread, dcPoint, pHighNode)); } /******************************************************************************************** > virtual BOOL NodeRenderableInk::CanSelectAsCompoundParent() Author: Phil_Martin (Xara Group Ltd) Created: 05/01/95 Inputs: - Outputs: - Returns: TRUE if the node will allow itself to be selected when one of it's children has been "hit" FALSE otherwise Purpose: Ask a node whether or not it's prepared to become selected when one of it's children is clicked on. This function is called in the FindSimple/Compound routines when they are following parent links up the tree from a node which has been "hit". This virtual function should be overridden in derived classes to alter its behaviour. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint ********************************************************************************************/ BOOL NodeRenderableInk::CanSelectAsCompoundParent() { // Virtual base-class function returns TRUE always. // Override this function in your class. return TRUE; } /******************************************************************************************** > virtual BOOL NodeRenderableInk::CanSelectAsSimple() Author: Phil_Martin (Xara Group Ltd) Created: 05/01/95 Inputs: - Outputs: - Returns: TRUE if the node will allow itself to be selected when it has been "hit" FALSE otherwise Purpose: Ask a node whether or not it's prepared to become selected when one of it's clicked on. This function is called in the FindSimple routines when they have just detected a "hit" on a node. This virtual function should be overridden in derived classes to alter its behaviour. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint ********************************************************************************************/ BOOL NodeRenderableInk::CanSelectAsSimple() { // Virtual base-class function returns TRUE always. // Override this function in your class. return TRUE; } /******************************************************************************************** > static Node* NodeRenderableInk::FindFirstHitTest(Spread* pStartSpread, const DocRect& drClickRect, CCAttrMap* pAttribMap, BOOL bExcludeLayers, Node* pHighNode = 0) Author: Justin_Flude (Xara Group Ltd) Created: 7/6/94 Inputs: pStartSpread --- the spread node to begin the hit-testing search from. Only nodes contained within this spread will be returned. drClickRect --- the rectangle centred on the mouse click position pAttribMap --- Pointer to the map of attributes current at this point bExcludeLayers --- TRUE if the routine should not return objects contained within locked or invisible layers. pHighNode Node below which search should be carried out. Outputs: - Returns: A pointer to the next NodeRenderableInk to consider for hit-testing. The returned node intersects the given click rectangle. Purpose: Walks the node tree in the reverse order to rendering, which is the order that nodes should be considered for hit-testing. Errors: ENSURE failure if the routine runs out of nodes to consider - this should never happen - if it does then there is something wrong with the node tree, for example it contains no spread nodes. SeeAlso: Node::FindNextHitTest; NodeRenderableInk::FindSimpleAtPoint ********************************************************************************************/ Node* NodeRenderableInk::FindFirstHitTest(Spread* pStartSpread, const DocRect& drClickRect, CCAttrMap* pAttribMap, BOOL bExcludeLayers, Node* pHighNode) { // Make sure that the node we start at is really a spread, as all NodeRenderableInks // are children of spread nodes. ENSURE(pStartSpread != NULL, "Node::FindFirstHitTest: pStartSpread parameter is NULL!"); ENSURE(pStartSpread->IsSpread(), "Node::FindFirstHitTest: pStartSpread parameter is not a spread!"); // Get the bounding box of the objects within the specified spread, converted to // spread coordinates. DocRect drSpreadBounds = pStartSpread->GetBoundingRect(); pStartSpread->DocCoordToSpreadCoord(&drSpreadBounds); // If the bounding box doesn't intersect the click rectangle then the given spread // cannot hold any object that was clicked on. if (!drSpreadBounds.IsIntersectedWith(drClickRect)) return NULL; // As the spread does contain the click rectangle, begin the traversal... // Call FindNextHitTest directly on the Spread so that the last item of the // Spread's child list can be considered correctly. Node* pFirstNode = FindNextHitTest((Node*)pStartSpread, drClickRect, pAttribMap, bExcludeLayers); if (pFirstNode == NULL) return NULL; // If there is an upper limit node then go through the tree until we get down to that // without returning the results for hit-testing. // (Or until there are no more nodes to consider!) if (pHighNode != NULL) { while (pFirstNode != NULL && pFirstNode != pHighNode) { pFirstNode = FindNextHitTest(pFirstNode, drClickRect, pAttribMap, bExcludeLayers); } // If we have stopped at the high node then return the next node below that in HitTest order // as the "first" node... Note that we do not want to examine child nodes of the high node. if (pFirstNode != NULL) { pFirstNode = FindNextHitTest(pFirstNode, drClickRect, pAttribMap, bExcludeLayers, TRUE); } } return pFirstNode; } /******************************************************************************************** > Node* NodeRenderableInk::FindNextHitTest(Node* pNode, const DocRect& drClickRect, CCAttrMap* pAttribMap, BOOL bExcludeLayers, BOOL bSkipChildren = FALSE) Author: Justin_Flude (Xara Group Ltd) Created: 7/6/94 Inputs: pNode --- Pointer to previous node in reverse rendering order (usually a result of calling FindFirstHitText or a previous call to FindNextHitTest) drClickRect --- the rectangle centred on the mouse click position pAttribMap --- Pointer to the map of attributes current at this point bExcludeLayers --- TRUE if the routine should not return objects contained within locked or invisible layers. bSkipChildren --- if TRUE then the routine will not examine the children of this Node. By default this flag is FALSE. Outputs: - Returns: A pointer to the next node to consider for hit-testing. Purpose: Walks the node tree in the reverse order to rendering, which is the order that nodes should be considered for hit-testing. Errors: ENSURE failure if the routine runs out of nodes to consider - this should never happen - if it does then there is something wrong with the node tree, for example it contains no spread nodes. SeeAlso: Node::FindFirstHitTest; NodeRenderableInk::FindSimpleAtPoint; Node::IsUnhittableLayer ********************************************************************************************/ Node* NodeRenderableInk::FindNextHitTest(Node* pNode, const DocRect& drClickRect, CCAttrMap* pAttribMap, BOOL bExcludeLayers, BOOL bSkipChildren) { if (pNode == NULL) return NULL; // Storage for pointer to attribute types... CCRuntimeClass* pTypeInfo; void* pDummy; // Check if we should go down the child link of this node. // (If node is a spread then allow it's children to be scanned because it's bounds // are in a different coordinate system. This is OK because we know that the // only time this routine is called on a Spread is the first call made in FindFirstHitTest // which has already done a bounds test.) if (!bSkipChildren && (HitTestChildren(pNode, drClickRect, bExcludeLayers) || pNode->IsSpread())) { // Find its right-most sibling and continue walking from there. pNode = pNode->FindFirstChild(); while (pNode->FindNext() != NULL) { // If this node is an attribute then it's more local than anything that might // be in the Attribute map (because we've just gone DOWN in the tree). // So, we'll bung it directly into the attribute map! if (pNode->IsAnAttribute() && !pNode->IsAClipViewAttr()) { pTypeInfo = ((NodeAttribute*)pNode)->GetAttributeType(); // Check if this type is already in the hash table. If it isn't then increment // the number of attribute types found if (!pAttribMap->Lookup(pTypeInfo, pDummy)) nFoundAttributes++; // Insert this local attribute into the map pAttribMap->SetAt(pTypeInfo, pNode); } pNode = pNode->FindNext(); } } else if (pNode->FindPrevious() != NULL) { // Otherwise, just backtrack to the immediately-previous sibling, if possible. pNode = pNode->FindPrevious(); } else { // We have hit a node with no previous sibling or children. Search up through // its parents until we find a parent with a previous sibling. This previous // sibling is our next node. However, if while searching up the parent links // we hit a spread then we know we have passed all NodeRenderableInks, and the // search can be terminated. do { if (pNode->IsSpread()) // Don't allow hit-testing to cross spreads return NULL; pNode = pNode->FindParent(); } while (pNode != NULL && pNode->FindPrevious() == NULL); if (pNode->IsSpread()) // Don't allow hit-testing to cross spreads return NULL; // If we broke out of the above loop because we found a parent with a previous // sibling then advance to that sibling. if (pNode != NULL && pNode->FindPrevious() != NULL) pNode = pNode->FindPrevious(); if (pNode->IsSpread()) // Don't allow hit-testing to cross spreads return NULL; } // If the found node is an attribute then remove it from the attribute map because it // has just gone out of scope! if (pNode->IsAnAttribute() && !pNode->IsAClipViewAttr()) { // The node is an attribute. Make sure that it no longer occurs in the // attribute hash table, so that the next time RenderAppliedAttributes is // called it will look for this kind. if (nFoundAttributes > 0 && pAttribMap->RemoveKey(((NodeAttribute*) pNode)->GetAttributeType())) nFoundAttributes--; } // Return the node we've found. return pNode; } /******************************************************************************************** > BOOL NodeRenderableInk::HitTestChildren(Node* pNode, const DocRect& drClickRect, BOOL bExcludeLayers) Author: Justin_Flude (Xara Group Ltd) Created: 20/6/94 Inputs: pNode --- Pointer to node to test child link of drClickRect --- the rectangle enclosing a mouse-click, in spread coordinates. bExcludeLayers --- whether locked or invisible layers should be excluded from hit-testing. Outputs: - Returns: TRUE if the caller should go down the child link of "this" node. Purpose: Private helper function for Node::FindNextHitTest. This function decides whether FindNextHitTest should go down a child link, which it should if (i) there is a child link; AND (ii) this node isn't a locked or invisible layer if such things should be excluded; (iii) if this node has a bounding rectangle it intersects the click rectangle. Errors: - SeeAlso: Node::FindNextHitTest ********************************************************************************************/ BOOL NodeRenderableInk::HitTestChildren(Node* pNode, const DocRect& drClickRect, BOOL bExcludeLayers) { // If there's no child link to follow the decision is easy . . . if (pNode->FindFirstChild() == 0) return FALSE; // If the node is a locked or invisible layer and the caller wants such things // ignored then don't follow link down . . . if (bExcludeLayers && pNode->IsLayer()) { Layer* pLayer = (Layer*) pNode; if (pLayer->IsLocked() || !pLayer->IsVisible()) return FALSE; } // If this node has a bounding rectangle then don't go down the link if the given // mouse-click rectangle doesn't intersect with the bounds . . . if (pNode->IsBounded()) { DocRect drBounds = ((NodeRenderableBounded*) pNode)->GetBoundingRect(); if (!drBounds.IsIntersectedWith(drClickRect)) return FALSE; } // If we pass all the above tests then by all means go down the child link. return TRUE; } /******************************************************************************************** > void NodeRenderableInk::ClearAttribMap() Author: Justin_Flude (Xara Group Ltd) Created: 31/7/94 Inputs: - Outputs: - Returns: - Purpose: Clears out all entries in the attribute map that is being accumulated as click-detection occurs. This has to be done when going down a child-link because then you are entering a deeper scope where the attributes in the map may not apply. It is called from Node::FindNextHitTest Errors: - SeeAlso: Node::FindNextHitTest ********************************************************************************************/ void NodeRenderableInk::ClearAttribMap() { pAttribMap->RemoveAll(); nFoundAttributes = 0; } /******************************************************************************************** > void NodeRenderableInk::RenderAppliedAttributes(RenderRegion* pRegion) const Author: Justin_Flude (Xara Group Ltd) Created: 16/6/94 Inputs: pRegion --- pointer to the render-reigion the function should render attributes into. Outputs: - Returns: - Purpose: Searches for all the attributes which apply to "this" ink-node, rendering them into the given render-region. Private helper function for NodeRenderableInk::FindSimpleAtPoint. Errors: - SeeAlso: NodeRenderableInk::FindSimpleAtPoint ********************************************************************************************/ void NodeRenderableInk::RenderAppliedAttributes(RenderRegion* pRegion) const { // If we are missing some attributes then search until we find all of them. FindAppliedAttributes(pAttribMap, nMaxAttributes, &nFoundAttributes); // Make sure our dummy flat fill, that will be used in the // place of any 'fancy' fills, is set up. *(DummyFill.GetStartColour()) = DocColour(COLOUR_BLACK); // OK, we have found the full quota of attributes. Now render them all. CCAttrMap::iterator pos = pAttribMap->GetStartPosition(); while( pos != pAttribMap->GetEndPosition() ) { CCRuntimeClass* pKey; void* pVal; pAttribMap->GetNextAssoc(pos, pKey, pVal); // Get the 'Family' of Attributes the fill belongs to CCRuntimeClass* AttrFamily = ((NodeAttribute*) pVal)->GetAttributeType(); // Get the actual fill type CCRuntimeClass* AttrClass = ((NodeAttribute*) pVal)->GetRuntimeClass(); // Check for a Transparency fill. // We can ignore them, as they do not effect hit testing. if ( AttrFamily != CC_RUNTIME_CLASS(AttrTranspFillGeometry) ) { // Is this a fancy fill type ? if ( AttrFamily == CC_RUNTIME_CLASS(AttrFillGeometry) && AttrClass != CC_RUNTIME_CLASS(AttrFlatColourFill) ) { if (AttrClass == CC_RUNTIME_CLASS(AttrBitmapColourFill) && ((NodeAttribute*) pVal)->NeedsTransparency()) { // It must be a masked bitmap, so we'll need to render it. // The render code will check the 'VeryMono' flag, and render // the bitmap non-masked pixels as black ((NodeAttribute*) pVal)->Render(pRegion); } else { // Yes, so just render a black flat fill DummyFill.Render(pRegion); } } else { // It's only a flat fill, so we might as well render it. // This will allow 'no colour' fills to work too. ((NodeAttribute*) pVal)->Render(pRegion); } } } } /******************************************************************************************** > void NodeRenderableInk::RenderAppliedClipAttributes(RenderRegion* pRender) Author: Karim_MacDonald (Xara Group Ltd) Created: 13/04/2000 Inputs: pRender the render region to render into. Outputs: The given render region will have its clipping region reduced to the clip region which would apply to this node in the normal course of rendering. Purpose: Helper method for RenderAppliedAttributes(). Render all ClipRegionAttributes which would be applied to this node in the normal render-loop. This actually gets a little complicated, as these attrs are rendered by NodeClipView nodes, so we need to go up the tree looking for NodeClipViewController nodes, then getting their NodeClipView nodes to render the clip-attribute.Things are complicated more by the fact that a ClipView clips all nodes below a NCVC *except* the keyhole node of that NCVC, which is *not* clipped. Notes: This is a bit of a BODGE, but is required because ClipRegionAttributes are not stored in the tree inside a NodeAttribute, and are therefore not picked up on by the normal RenderAppliedAttributes() process. See also: RenderAppliedAttributes(). ********************************************************************************************/ void NodeRenderableInk::RenderAppliedClipAttributes(RenderRegion* pRender) const { // 1. pRefNode starts on this node. We look for a NCVC which clips pRefNode, add it to // our list, and then that NCVC becomes the new pRefNode to work from. // // 2. To find a NCVC which clips us, we go straight up the tree. If we find a NCVC, // then we check whether its child which we came from is also one of its keyhole // nodes. If so, we ignore it and continue. If not, then it must clip us, and // therefore the NCVC becomes the new pRefNode. // // 3. We repeat the process until we hit a layer node, which marks the top of this // branch of the tree. // Node* pRefNode = (Node*) this; std::list lNCVC; BOOL bFinished = FALSE; while (!bFinished) { // go up the tree until we reach the top or we reach a NCVC // for which we are not a keyhole node. Node* pChild = pRefNode; Node* pParent = pRefNode->FindParent(); BOOL bFoundApplicableNCVC = pParent->IsANodeClipViewController() && !((NodeClipViewController*)pParent)->OwnsAsKeyholeNode(pChild); while (pParent != NULL && !pParent->IsLayer() && !bFoundApplicableNCVC) { pChild = pParent; pParent = pParent->FindParent(); bFoundApplicableNCVC = pParent->IsANodeClipViewController() && !((NodeClipViewController*)pParent)->OwnsAsKeyholeNode(pChild); } if (bFoundApplicableNCVC) { lNCVC.push_front((NodeClipViewController*) pParent); pRefNode = pParent; } else { bFinished = TRUE; } } // ok, now go through the list in reverse order, telling the NCV belonging to each NCVC // to render is ClipRegionAttribute. NodeClipView* pClipView = 0; NodeClipViewController* pNCVC = 0; while (!lNCVC.empty()) { pNCVC = lNCVC.back(); lNCVC.pop_back(); pClipView = pNCVC->GetClipView(); if (pClipView != 0) { // DEBUG: // TRACEUSER( "Karim", _T("NRInk::RenderAppliedClipAttrs; NCVC: 0x%x\n"), (DWORD)pNCVC); pClipView->RenderClipAttr(pRender); } } // DEBUG: // TRACEUSER( "Karim", _T("NRInk::RenderAppliedClipAttrs; this: 0x%x; %s\n"), // (DWORD)this, this->Name()); } /******************************************************************************************** > BOOL NodeRenderableInk::FindAppliedAttributes(CCAttrMap* pAttribMap, INT32 nMax = 5000, INT32* pnFound = 0, BOOL ExcludeIndirectlyAppliedGLAs = FALSE, BOOL bStrictEffectStatus = TRUE) const Author: Justin_Flude (Xara Group Ltd) Created: 13/6/94 Inputs: pAttribMap Map table to fill in with attribute details. This table stores Key,Value tuples of the form (CCRuntimeClass*, NodeAttribute*). You can use the normal CMap Lookup function to extract the pointer to an attribute from the map, eg. Lookup(CC_RUNTIME_CLASS(NodeAttrFill),pAttrib); nMax The maximum number of attribute types that can be found pnFound Pointer to the number already in the Map. This param can be used in conjunction with nMax to make the scan stop as soon as the required number of attributes have been found. (Defaults cause whole tree scan.) ExcludeIndirectlyAppliedGLAs - ensures that this map won't include a pointer to a geometry linked attribute which is not directly linked to this nodes geometry (ie exclude parent GLA's) bStrictEffectStatus - TRUE when non-effect attrs are ignored in compound that allow effect attrs - FALSE when non-effect attrs are allowed throughout Outputs: - Returns: TRUE if attributes were successfully found FALSE otherwise (if no attributes were found) Purpose: Searches for all the attributes which apply to "this" ink-node. This routine does NOT render them! Use RenderAppliedAttributes if you want that functionality. Errors: - SeeAlso: NodeRenderableInk::RenderAppliedAttributes NodeAttribute::FindFirstAppliedAtt NodeAttribute::FindPrevAppliedAtt ********************************************************************************************/ BOOL NodeRenderableInk::FindAppliedAttributes(CCAttrMap* pAttribMap, INT32 nMax, INT32* pnFound, BOOL ExcludeIndirectlyAppliedGLAs, BOOL bStrictEffectStatus) const { // Precondition checks ERROR2IF(pAttribMap == 0, FALSE, "FindAppliedAttributes passed null map"); // Find the closest attribute applied to "this" node... NodeAttribute* pAttrib = NodeAttribute::FindFirstAppliedAttr((Node*)this); if (pAttrib == 0) return FALSE; // Get variables ready for the loop, OUTSIDE the loop!!! CCRuntimeClass* pTypeInfo; void* pDummy; INT32 nFound; if (pnFound) nFound = *pnFound; else nFound = 0; // BOOL FoundClipAttr = FALSE; // If we are finding a completely new set of attrs then we should not overwrite any entries // into the map that have been made within this routine // (So that normal attrs don't overwrite effect attrs) BOOL bOverwriteExisting = (nFound!=0); // Repeat the following loop for all attributes we encounter in the tree. while (pAttrib) { // Get the run-time type of the attribute, which serves as a hash "key". pTypeInfo = pAttrib->GetAttributeType(); // Check if this type is already in the hash table. If it isn't then insert // its address with its run-time type as the key, and check if we need to look // for any more. BOOL bEffectStatusOK = FALSE; if (bStrictEffectStatus) bEffectStatusOK = (pAttrib->IsEffectAttribute() == this->IsValidEffectAttr(pAttrib)); else bEffectStatusOK = !pAttrib->IsEffectAttribute(); if (!pAttrib->IsAClipViewAttr() && (pAttrib->FindParent() != (Node*)this || bEffectStatusOK)) { if (!pAttribMap->Lookup(pTypeInfo, pDummy)) { // This is a new attribute for the map BOOL AddAttr = TRUE; // NB keep default attrs in map so we don't have incomplete maps if (ExcludeIndirectlyAppliedGLAs && !pAttrib->IsADefaultAttr() && pAttrib->IsLinkedToNodeGeometry()) { if(!pAttrib->IsLinkedToThisNode((Node*)this)) { AddAttr = FALSE; } } if(AddAttr) { pAttribMap->SetAt(pTypeInfo, pAttrib); nFound++; ERROR3IF(nFound>nMax,"Found more attribute types than are registered!"); } } else if (bOverwriteExisting) { // An attribute of that type is already in the map // But if this attribute is a child of the scanned node // we should force it into the map because it's // still in scope! if (pAttrib->FindParent() == (Node*)this) { pAttribMap->SetAt(pTypeInfo,pAttrib); // (Attrib type was already in map so no need to increment counter.) } } } // After dealing with that attribute can we stop the scan? // We want to stop as soon as we can, namely when we have all the attribute // types in the map and when we're no longer scanning children of "this". if ( nFound >= nMax && (pAttrib->FindParent()!=(Node*)this) ) break; // Go on to find the next attribute. pAttrib = NodeAttribute::FindPrevAppliedAttr(pAttrib); } #ifdef _DEBUG PORTNOTE("other","Removed some IsUserName protected debug") #if 0 if (IsUserName("Phil") && nMax<5000 && nFoundGetStartPosition(); pos != 0; ) { void* pKey; void* pValue; pAttribMap->GetNextAssoc(pos, pKey, pValue); TRACE( _T("\tFound a %s at 0x%lX\n"), ((CCRuntimeClass*) pKey)->m_lpszClassName, (UINT32) pValue); } List* pTypeList = ObjectRegistry::GetRequiredAttribs(GetRuntimeClass()); for (ListItem* pItem = pTypeList->GetHead(); pItem != 0; pItem = pTypeList->GetNext(pItem)) { CCRuntimeClass* pClass = ((NodeAttributeClassItem*) pItem)->AttributeClass; void* pDummy; if (!pAttribMap->Lookup(pClass, pDummy)) { TRACE( _T("\t\tCouldn't find a %s\n"), pClass->m_lpszClassName); } } } #endif #endif ERROR2IF(nMax<5000 && nFound AttrBrushType* NodeRenderableInk::GetAppliedBrushAttribute() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15-5-2000 Inputs: Outputs: - Returns: the attrbrushtype that applies to this inknode Purpose: as above ***********************************************************************************************/ AttrBrushType* NodeRenderableInk::GetAppliedBrushAttribute() { AttrBrushType* pAttr = 0; FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), (NodeAttribute**)&pAttr); if (pAttr == NULL) return NULL; BrushHandle Handle = pAttr->GetBrushHandle(); if (Handle == BrushHandle_NoBrush) return NULL; // double check to make sure it isn't referring to a deleted brush BrushDefinition* pDef = BrushComponent::FindBrushDefinition(Handle); if (pDef == NULL) return NULL; return pAttr; } /*********************************************************************************************** > AttrStrokeType* NodeRenderableInk::GetActiveStroke() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15-5-2000 Inputs: Outputs: - Returns: pointer to the stroke attribute applied to us, so long as it is active Purpose: We may have a stroke attribute applied to us that does not do anything under certain circumstances (zero line width, no accompanying variable width attribute). ***********************************************************************************************/ AttrStrokeType* NodeRenderableInk::GetActiveStroke() { // first up, if we have no stroke colour then any strokes we have aren't really active AttrStrokeColour* pStrokeColour = NULL; FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), (NodeAttribute**)&pStrokeColour); if (pStrokeColour != NULL) { // get the stroke colour StrokeColourAttribute* pVal = (StrokeColourAttribute*)pStrokeColour->GetAttributeValue(); if (pVal != NULL) if (pVal->Colour.IsTransparent()) return NULL; } else return NULL; // now find out if we have both a stroke and variable width attribute AttrStrokeType* pStroke = NULL; AttrVariableWidth* pVarWidth = NULL; FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke); FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth); if (pVarWidth != NULL && ((VariableWidthAttrValue*)pVarWidth->GetAttributeValue())->GetWidthFunction() != NULL) { if (pStroke != NULL && pStroke->HasPathProcessor()) return pStroke; } return NULL; } /*********************************************************************************************** > virtual NodePath* NodeRenderableInk::GetVariableWidthStrokePath() Author: Diccon_Yamanaka (Xara Group Ltd) Created: 15-5-2000 Inputs: - Outputs: - Returns: a nodepath containing the path that is generated by any variable width strokes that are applied to us, or NULL if there aren't any Purpose: If we have a variable width stroke applied to us then this will get the path generated by that stroke. This base class version returns NULL, overridden versions must supply their own outline path. See also: NodePath::GetVariableWidthStrokePath, NodeRegularShape::GetVariableWidthStrokePath ***********************************************************************************************/ NodePath* NodeRenderableInk::GetVariableWidthStrokePath() { return NULL; } NodePath* NodeRenderableInk::GetSmoothVariableWidthStrokePath() { return NULL; }