hittest.cpp

Go to the documentation of this file.
00001 // $Id: hittest.cpp 1282 2006-06-09 09:46:49Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // The hit-testing functions of Node, NodeRenderableInk etc.
00099 
00100 #include "camtypes.h"
00101 
00102 //#include "ccobject.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00103 //#include "node.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 //#include "ink.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00105 //#include "nodeattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 #include "layer.h"
00108 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "rndrgn.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "osrndrgn.h"
00112 #include "rendbits.h"
00113 #include "objreg.h"
00114 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "qualattr.h"
00117 #include "attrmap.h"
00118 #include "nodeshad.h"
00119 #include "nodecont.h"
00120 //#include "ndcchbmp.h"
00121 #include "grndclik.h"
00122 #include "nodeclip.h"   // for NodeClipView
00123 #include "ndclpcnt.h"   // for NodeClipViewController
00124 #include "brshattr.h"
00125 #include "strkattr.h"
00126 #include "lineattr.h"
00127 
00128 
00129 #pragma warning(disable: 4786)
00130 #include <list>
00131 #pragma warning(default: 4786)
00132 
00133 DECLARE_SOURCE("$Revision: 1282 $");
00134 
00135 // This definition allows sensible debug trace output of memory leaks...
00136 // Declare smart memory handling in Debug builds
00137 #define new CAM_DEBUG_NEW
00138 
00139 //  Karim 12/04/2000 -  uncomment this #define to enable rendering
00140 //                      of the hit-test bitmap on-screen.
00141 //#define DEBUG_CLICK_DETECT_BITMAP
00142 
00143 static AttrFlatColourFill DummyFill;    // Dummy fill to use in the place of fancy
00144                                         // fills for hit testing
00145 
00146 /*********************************************************************************************
00147     Preference: SmartClicks
00148     Section:    DebugFlags
00149     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00150     Range:      TRUE or FALSE
00151     Purpose:    If TRUE then click detection is done with rendering, else if FALSE it is
00152                 done with rectangle intersection. Will be made obsolete once rendering
00153                 methods are reliable. Defaults to TRUE.
00154 **********************************************************************************************/
00155 
00156 BOOL NodeRenderableInk::bUseSmartClicks = TRUE;
00157 
00158 CCAttrMap* NodeRenderableInk::pAttribMap;
00159 INT32 NodeRenderableInk::nFoundAttributes;
00160 INT32 NodeRenderableInk::nMaxAttributes;
00161 
00162 
00163 /*********************************************************************************************
00164     Preference: AverageHitColour
00165     Section:    ?
00166     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00167     Range:      TRUE or FALSE
00168     Purpose:    If TRUE then click detection is done with rendering, else if FALSE it is
00169                 done with rectangle intersection. Will be made obsolete once rendering
00170                 methods are reliable. Defaults to TRUE.
00171 **********************************************************************************************/
00172 
00173 BOOL NodeRenderableInk::AverageHitColour = FALSE;
00174 
00175 
00178 //
00179 // These are only going here until I can get a lock on noderend.cpp and put them in there.
00180 // 11 years later and it's still not in noderend.cpp (why break with tradition)
00181 //
00182 
00183 /********************************************************************************************
00184 >   virtual BOOL NodeRenderableInk::OnMouseMove(const DocCoord& dcPos, Spread* pSpread,
00185                                                 ClickModifiers mods)
00186 
00187     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00188     Created:    23/11/94
00189     Inputs:     dcPos           the current mouse position, in spread coordinates
00190                 pSpread         the spread containing the mouse position
00191                 mods            which keys/buttons are down, eg. Adjust (shift/right button)
00192     Outputs:    -
00193     Returns:    TRUE if the node did something with this call, FALSE if it didn't/won't
00194     Purpose:    Called by the selector tool for the selected object(s) when the mouse is
00195                 moved.
00196     Errors:     -
00197     SeeAlso:    NodeRenderableInk::OnClick; NodeRenderableInk::GetStatusInfo
00198 ********************************************************************************************/
00199 
00200 BOOL NodeRenderableInk::OnMouseMove(const DocCoord& dcPos, Spread* pSpread, ClickModifiers mods)
00201 {
00202 #ifdef _DEBUG
00203 //  if (IsUserName("JustinF"))
00204 //  {
00205 //      TRACE( _T("Call to base class NodeRenderableInk::OnMouseMove for a %s\n"), (LPCTSTR) Name());
00206 //  }
00207 #endif
00208     return FALSE;
00209 }
00210 
00211 
00212 
00213 /********************************************************************************************
00214 >   virtual BOOL NodeRenderableInk::GetStatusInfo(String_256** ppStatusText,
00215                                                   Cursor** ppStatusCursor)
00216 
00217     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00218     Created:    23/11/94
00219     Inputs:     -
00220     Outputs:    ppStatusText        pointer to a string pointer to set to an approrpiate
00221                                     status bar text message, or 0 if not relevant
00222                 ppStatusCursor      pointer to a cursor pointer to set to an apppropriate
00223                                     Cursor object, or 0 if not relevant
00224     Returns:    TRUE if the routine used any of the output parameters and thus has some
00225                 text/cursor to show, FALSE if it doesn't.
00226     Purpose:    Called by the selector tool if either a previous OnMouseMove or OnClick
00227                 member function call returns TRUE, indicating that the node has some
00228                 feedback to display when the mous is at the previously passed point.
00229     Errors:     -
00230     SeeAlso:    NodeRenderableInk::OnClick; NodeRenderableInk::OnMouseMove
00231 ********************************************************************************************/
00232 
00233 BOOL NodeRenderableInk::GetStatusInfo(String_256** ppStatusText, Cursor** ppStatusCursor)
00234 {
00235 #ifdef _DEBUG
00236 //  if (IsUserName("JustinF"))
00237 //  {
00238 //      TRACE( _T("Call to base class NodeRenderableInk::GetStatusInfo for a %s\n"), (LPCTSTR) Name());
00239 //  }
00240 #endif
00241     return FALSE;
00242 }
00243 
00246 
00247 
00248 
00249 /********************************************************************************************
00250 >   static NodeRenderableInk* NodeRenderableInk::FindSimpleAtPoint(Spread* pSpread,
00251                                                                    DocCoord dcPoint,
00252                                                                    Node* pHighNode = 0,
00253                                                                    Node** ppInterruptNode = 0)
00254 
00255     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00256     Created:    7/6/94
00257     Inputs:     pSpread     ---     pointer to the spread containing the given point
00258                 dcPoint     ---     the point the object to be searched for occupies
00259                 pHighNode           Node below which search should be carried out.
00260                 ppInterruptNode     0 if hit-detection shouldn't be interrupted
00261                                     Non-null otherwsie, then contains pointer to node
00262                                     at which hit-detection was interrupted on exit.
00263     Outputs:    -
00264     Returns:    A pointer to an ink-able object that lies at or is closest to the
00265                 given point, or 0 if there is none.  This object will be "simple",
00266                 ie. it will be a single "atomic" object such as a path, rectangle etc.
00267                 and not a "compound" object like a group.
00268     Purpose:    Searches the ink-able objects within the given spread for one that lies
00269                 at or near the given point.  The method used to render each candidate
00270                 object into a small monochrome bitmap, in the reverse order to normal
00271                 (so that the object normally rendered last is the first to be rendered
00272                 into the bitmap).  The bitmap is examined to see if the object affected
00273                 the centre or surrounding pixels.
00274 
00275                 One of the biggest time users in this routine is the scanning of the tree
00276                 to find the attributes applied to an object. Attempts are made to cut down
00277                 this scanning. A hash table of pointers to attributes is maintained.
00278                 Assume that the hash table is full of attributes used to render an object.
00279                 Then, as we scan backwards along a sibling list we simply remove attributes
00280                 from the hash table as we encounter them because they have moved out of scope.
00281                 When we go down a child link we scan along the sibling child list adding all
00282                 those attributes encountered to the hash table because they have just moved
00283                 into scope. Thus, at any time the hash table contains pointers to all the
00284                 attributes that apply to the node we are about to render WITH ONE EXCEPTION!
00285                 The reverse render-order tree-scan means that objects are encountered before
00286                 their child attributes. The FindAppliedAttribute routine overcomes this by
00287                 forcibly adding all child attributes to the map when it scans for applied
00288                 attributes.
00289 
00290     Errors:     -
00291     SeeAlso:    NodeRenderableInk::FindCompoundAtPoint;
00292                 NodeRenderableInk::OldFindFirstAtPoint;
00293                 Node::FindFirstHitTest;
00294                 Node::FindNextHitTest
00295 ********************************************************************************************/
00296 
00297 NodeRenderableInk* NodeRenderableInk::FindSimpleAtPoint(Spread* pSpread, 
00298                                                         DocCoord dcPoint, 
00299                                                         Node* pHighNode,
00300                                                         Node** ppInterruptNode
00301                                                         )
00302 {
00303 //  TRACE(_T("FindSimpleAtPoint(%d, %d)\n"), dcPoint.x, dcPoint.y);
00304 
00305     // Find the currently active DocView.
00306     DocView* pDocView = DocView::GetSelected();
00307     ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint");
00308 
00309     // Initialise the count and limit on the number of attributes encountered.
00310     List* pTypeList = ObjectRegistry::GetRequiredAttribs(0);     // <---
00311     ENSURE(pTypeList != 0 && !pTypeList->IsEmpty(),
00312             "Object Registry returned a dodgy list in NodeRenderableInk::FindSimpleAtPoint");
00313     nFoundAttributes = 0;
00314     nMaxAttributes = pTypeList->GetCount();
00315 
00316     // Clear interrupt node pointer
00317     if (ppInterruptNode)
00318         *ppInterruptNode = 0;
00319 
00320     // Initialise the hash-table used to look-up attributes encountered.
00321     // Make the hash table twice as big as the number of att types for efficient look-up.
00322     pAttribMap = new CCAttrMap(nMaxAttributes*2);
00323     ERROR2IF(pAttribMap==0,0,"FindSimpleAtPoint: couldn't create a CCAttrMap!");
00324 
00325     // Build a rectangle around the given point, based on the size of a blob.
00326     DocRect drClickRect;
00327     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint,
00328                                 BT_CLICKME, &drClickRect);
00329 
00330     // Get the extent of the current view and convert to spread coordinates.
00331     DocRect drViewRect = pDocView->GetDocViewRect(pSpread);
00332     pSpread->DocCoordToSpreadCoord(&drViewRect);
00333     
00334     // Create a special hit-detection render region to draw into, if possible.
00335     RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, CLICKBITMAP);
00336     if (pRegion==NULL)
00337     {
00338         delete pAttribMap;
00339         ERROR2(NULL,"FindSimpleAtPoint: couldn't create a RenderRegion!");
00340     }
00341 
00342     // Start with a (very) clean sheet.
00343     pRegion->SetClean(TRUE, TRUE);
00344 
00345     // These point to ink-nodes in the tree that have been rendered into our hit-test
00346     // render-region.
00347     NodeRenderableInk* pHitNode  = NULL;            // node that occupies dcPoint
00348     NodeRenderableInk* pNearNode = NULL;            // node that came closest to dcPoint
00349     NodeRenderableInk* pCompoundHitNode = NULL;     // compound node that was hit before trying children
00350     NodeRenderableInk* pCompoundNearNode = NULL;    // compound node that was near before trying children
00351 
00352     MonotonicTime Slice;                            // Sample time now
00353 
00354     BOOL bSkipChildren = FALSE;
00355 
00356     // Perform hit-testing for each render-region provided by the DocView.
00357     while (pRegion != NULL)
00358     {
00359         // Render the first and subsequent ink-nodes into the render-region, checking if any
00360         // draw into it.
00361         for (Node* pNode = FindFirstHitTest(pSpread, drClickRect, pAttribMap, TRUE, pHighNode);
00362              pNode != NULL;
00363              pNode = FindNextHitTest(pNode, drClickRect, pAttribMap, TRUE, bSkipChildren))
00364         {
00365             bSkipChildren = FALSE;
00366 
00367             if (pNode)
00368             {
00369                 if (pNode->IsAnObject())
00370                 {
00371                     // Does this renderable node intersect the click rectangle?  If not, then
00372                     // ignore it.
00373                     DocRect drBounds = ((NodeRenderableInk*) pNode)->GetBoundingRect(FALSE, TRUE);
00374                     if (!drBounds.IsIntersectedWith(drClickRect)) continue;
00375                     
00376                     // This node can be rendered.  Find all the outstanding attributes in the
00377                     // tree that affect the rendering of this node, and render them.
00378                     //
00379                     // Jason 15/1/97 - Save the current RR context so that we can restore the render 
00380                     // stack to a sensible state after rendering this object. This is safe because we
00381                     // always render all relevant attributes for every object we hit test.
00382                     // We restore immediately after rendering (the 3 RestoreContext() calls, below)
00383                     pRegion->SaveContext();
00384                     ((NodeRenderableInk*) pNode)->RenderAppliedAttributes(pRegion);
00385                     
00386                     // This ensures that the object is rendered according to the view Quality setting.
00387                     // When Quality attributes appear in the tree this may not be a good thing!!!
00388                     // It should really be called before RenderAppliedAttributes but at the moment that seems
00389                     // to always set Quality to Normal for some reason!!!
00390                     // Extract Quality from DocView object and set it up in the renderregion/monobitmap
00391                     
00392                     // Markn 26/9/95: Added the following 'if' statement to prevent the setting of the view
00393                     // quality overriding the quality set by the AttrQuality attribute in the guides layer.
00394                     if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer)
00395                     {
00396                         QualityAttribute *pQualAttr = new QualityAttribute(pDocView->RenderQuality);
00397                         pRegion->SetQuality(pQualAttr, TRUE);
00398                     }
00399                     
00400                     // Phil 18/03/2004
00401                     // Must render clip region attribute AFTER Quality attribute has been set how 
00402                     // the region will be rendered otherwise GDraw will fail in FillPath!
00403                     //
00404                     // Karim 13/4/2000 (lucky for some...)
00405                     // ok, BODGE CITY here. we're going to do a RenderAppliedClipAttributes(), which
00406                     // renders all the ClipRegionAttributes which would normally apply to this node.
00407                     // see the method header for more info.
00408                     ((NodeRenderableInk*) pNode)->RenderAppliedClipAttributes(pRegion);
00409 
00410                     do
00411                     {
00412                         // This 'do' loop will render the same node until the sub render state is 0
00413                         // This allows heavy-duty nodes (e.g. blends) to get the hit test bmp to be checked
00414                         // at any point during the rendering of the node.
00415                         
00416                         // Now the attributes are done, render the object itself.
00417                         pRegion->SetClean(FALSE, FALSE);    // Reset just the clean flag so we can tell whether
00418                                                             // compound nodes render anything
00419                         // ------------------------------
00420                         pNode->Render(pRegion);
00421                         // ------------------------------
00422                         
00423 #ifdef DEBUG_CLICK_DETECT_BITMAP
00424                         OSRenderBitmap::DebugMe(pRegion, 8);
00425 #endif
00426                         // Check if any of the pixels in the render-region were affected by the
00427                         // last rendering.
00428                         BitmapContents bcDrawCode = OSRenderBitmap::GetContents(pRegion, FALSE);
00429 
00430                         if (pNode->IsCompoundClass() && !pRegion->IsClean())
00431                         {
00432 #ifdef DEBUG_CLICK_DETECT_BITMAP
00433                         OSRenderBitmap::DebugMe(pRegion, 108);
00434 #endif
00435                             // Compound has rendered something...
00436                             //
00437                             // Clean out the bitmap since we know we are going to use it some more
00438                             pRegion->SetClean(FALSE, TRUE);
00439 
00440                             // Compound nodes may render themselves if they have effect
00441                             // attributes applied (so that effect transparency is
00442                             // properly taken into account during hit testing)
00443                             // If the compound node rendered the centre pixel then we must
00444                             // allow the children of the node to render themselves to get
00445                             // a true indication of the clicked on node
00446                             // If not, then we should not allow our children to render
00447                             // themselves at all
00448                             if (bcDrawCode != BITMAPCONTENTS_CENTRE && bcDrawCode != BITMAPCONTENTS_ANY)
00449                             {
00450                                 // Skip out of this node's subtree...
00451                                 bSkipChildren = TRUE;
00452                             }
00453                             else
00454                             // else just keep going (treat this as a "near" node)
00455                             {
00456 //                              if (pNearNode == NULL) pNearNode = (NodeRenderableInk*) pNode;
00457                                 if (bcDrawCode == BITMAPCONTENTS_CENTRE)
00458                                 {
00459                                     if (pCompoundHitNode == NULL) pCompoundHitNode = (NodeRenderableInk*)pNode;
00460                                 }
00461                                 else
00462                                 {
00463                                     if (pCompoundNearNode == NULL) pCompoundNearNode = (NodeRenderableInk*)pNode;
00464                                 }
00465                             }
00466                         }
00467                         else
00468                         {
00469                             if (bcDrawCode == BITMAPCONTENTS_CENTRE)
00470                             {
00471                                 // The centre pixel was affected.  Remember this node and prepare to
00472                                 // return result to caller.
00473                                 pHitNode = (NodeRenderableInk*) pNode;
00474                                 
00475                                 // And exit both loops. We have to restore the previous RR state first, though
00476                                 pRegion->RestoreContext();
00477                                 goto AfterSearch;
00478                             }
00479                             else if (bcDrawCode == BITMAPCONTENTS_ANY)
00480                             {
00481                                 // Some outer pixels were affected.  If we don't already have a
00482                                 // "closest" node then remember this one.  If we do already have a
00483                                 // "closest" node then the previous "closest" is "closer", because
00484                                 // it is on-top!
00485                                 if (pNearNode == NULL) pNearNode = (NodeRenderableInk*) pNode;
00486 
00487                                 // Don't break because although this stage (typically a stage of a Blend) has returned
00488                                 // "Near" the next one might be a direct hit!
00489                                 //break;    // break out of the do-while sub render state loop
00490                             }
00491                         }
00492                     
00493                     
00494                         // If the caller has allowed us to inetrrupt the hit-testing process
00495                         // then we will do so if the mouse has moved...
00496                         // Only do these tests every twentieth of a second because they could take some time
00497                         if (ppInterruptNode && Slice.Elapsed(50))
00498                         {
00499                             Slice.Sample();                             // Reset the slice timer
00500                             DocView* pDocView = DocView::GetCurrent();
00501                             if (pDocView->IsMouseMoved())               // Has the mouse moved?
00502                             {
00503                                 TRACEUSER( "Phil", _T("FindSimpleAtPoint Interrupt! %lx\n"), pNode);
00504                                 if (*ppInterruptNode == NULL)           // If so set the interrupt
00505                                     *ppInterruptNode = pNode;           // node pointer
00506                                 pNode = 0;                          // Clear pointer to "hit" node
00507                                 
00508                                 // And exit both loops. We have to restore the previous RR state first, though
00509                                 pRegion->RestoreContext();
00510                                 goto AfterSearch;                       // and stop the search
00511                             }
00512                         }
00513                         
00514                         // Render the same node until the sub render state is NULL
00515                     } while (pRegion->GetSubRenderState() != NULL);
00516 
00517                     // Restore the RenderRegion attr state to how it was before we rendered this node
00518                     pRegion->RestoreContext();
00519                 }
00520             }
00521 
00522         }
00523         // Advance onto the next render-region.
00524         pRegion = pDocView->GetNextOnTop(&drClickRect);
00525     }
00526 
00527 AfterSearch:
00528 
00529     // Discard remaining render regions (although there shouldn't be any).
00530     while (pRegion != NULL)
00531         pRegion = pDocView->GetNextOnTop(&drClickRect);
00532 
00533     // Deallocate the hash table.
00534     delete pAttribMap;
00535 
00536     // If we found a "hit" then return that node, otherwise return the closest
00537     // "near miss" (which will be null if no node was close).
00538     if (pHitNode == NULL)
00539     {
00540         if (pNearNode != NULL)
00541         {
00542             if (!pNearNode->IsKindOf(CC_RUNTIME_CLASS(NodeShadow)) &&
00543                 !pNearNode->IsKindOf(CC_RUNTIME_CLASS(NodeShadowController)))
00544                     pHitNode = pNearNode;
00545         }
00546         else
00547             if (pCompoundHitNode)
00548                 pHitNode = pCompoundHitNode;
00549             else if (pCompoundNearNode)
00550                 pHitNode = pCompoundNearNode;
00551 
00552     }
00553     else
00554     {
00555         // We might have set pNearNode from a compound render
00556         // but then allowed processing to continue
00557         // So if pNearNode is set but is not the parent of the hit node then
00558         // we should use pNearNode instead
00559         //
00560         // NOTE! We could avoid extra work by making FinNextHitTest stop as soon
00561         // as it leaves the subtree whose root is pNearNode.
00562         if (pCompoundHitNode && !pCompoundHitNode->IsNodeInSubtree(pHitNode))
00563             pHitNode = pCompoundHitNode;
00564     }
00565 
00566     // Check if this node wishes to be selected
00567     // and if not, find it's parent instead...
00568     while (pHitNode != NULL && !pHitNode->CanSelectAsSimple())  // While we have a node but it doesn't
00569     {                                                       // want to be selected...
00570         Node* ptemp = pHitNode->FindParent();               // Find parent of current node
00571         if (ptemp != NULL && ptemp->IsAnObject())           // If parent is ink
00572         {
00573             pHitNode = (NodeRenderableInk*) ptemp;          // Then reset current pointer
00574             if (!pHitNode->CanSelectAsCompoundParent())     // See if it is prepared to be selected
00575                 pHitNode = NULL;                            // If not then can't select anything!
00576         }
00577         else                                                // Else (parent is NOT ink)
00578             pHitNode = NULL;                                // Then can't select anything!
00579     }
00580 
00581     // Give the node a chance to alter it's "hit" status
00582     if (pHitNode)
00583         pHitNode = pHitNode->FindNodeAtPointHelper(pSpread, dcPoint);
00584 
00585     return pHitNode;
00586 }
00587 
00588 
00589 
00590 /********************************************************************************************
00591 >   static NodeRenderableInk* NodeRenderableInk::FindSimpleAtPointForColourPicker(
00592                                                                        Spread* pSpread, 
00593                                                                        DocCoord dcPoint, 
00594                                                                        Pixel32bpp& Pix,
00595                                                                        Node* pHighNode,
00596                                                                        Node** ppInterruptNode)
00597 
00598     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
00599     Created:    28/10/99
00600     Inputs:     pSpread     ---     pointer to the spread containing the given point
00601                 dcPoint     ---     the point the object to be searched for occupies
00602                 Pix         ---     reference to the structure that will receive pixel data
00603                                     (created internally by custom colour picker control)
00604                 pHighNode           Node below which search should be carried out.
00605                 ppInterruptNode     0 if hit-detection shouldn't be interrupted
00606                                     Non-null otherwsie, then contains pointer to node
00607                                     at which hit-detection was interrupted on exit.
00608 
00609                 NOTE:  uses same inputs as NodeRenderableInk::FindSimpleAtPoint so that we
00610                        may call NodeRenderableInk::FindSimpleAtPoint internally.
00611     Outputs:    -
00612     Returns:    A pointer to an ink-able object that lies at or is closest to the
00613                 given point, or 0 if there is none.  This object will be "simple",
00614                 ie. it will be a single "atomic" object such as a path, rectangle etc.
00615                 and not a "compound" object like a group.
00616                 Also returns (by reference) details concerning the pixel that the colour
00617                 picker control is over.
00618     Purpose:    Firstly, this function serves to find the 'ID' of the item that our colour
00619                 picker is over - which is used to update our mouse cursor.  Secondly, it
00620                 obtains the TRUE 32-BIT RGB index for the pixel that our mouse cursor is
00621                 over - enabling us to do colour manager/dialog type things.  The first part
00622                 of this routine simply calls FindSimpleAtPointFor - and the second part
00623                 actually does colour specific work.
00624 
00625     Errors:     -
00626     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint
00627 
00628 ********************************************************************************************/
00629 
00630 NodeRenderableInk* NodeRenderableInk::FindSimpleAtPointForColourPicker(Spread* pSpread, 
00631                                                                        DocCoord dcPoint, 
00632                                                                        Pixel32bpp& Pix,
00633                                                                        Node* pHighNode,
00634                                                                        Node** ppInterruptNode)
00635 {
00636     // Find the currently active DocView.
00637     DocView* pDocView = DocView::GetSelected();
00638     ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint");
00639 
00640     // Build a rectangle around the given point, based on the size of a blob.
00641     DocRect drClickRect;
00642     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint,
00643                                 BT_CLICKME, &drClickRect);
00644 
00645     // Get the extent of the current view and convert to spread coordinates.
00646     DocRect drViewRect = pDocView->GetDocViewRect(pSpread);
00647     pSpread->DocCoordToSpreadCoord(&drViewRect);
00648 
00649     // find 'ID' of item that mouse cursor is over (or not)
00650     NodeRenderableInk* pHitNode = FindSimpleAtPoint (pSpread, dcPoint, pHighNode, ppInterruptNode);
00651 
00652     BOOL bOldForce = pDocView->SetForceDefaultColourContexts();
00653 
00654     // Create a colour render region to draw into, if possible.
00655     RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, COLOURBITMAP);
00656     if (pRegion == 0)
00657     {
00658         pDocView->SetForceDefaultColourContexts(bOldForce);
00659         delete pAttribMap;
00660         ERROR2(0,"FindSimpleAtPoint: couldn't create a RenderRegion!");
00661     }
00662 
00663     // Start with a (very) clean sheet.
00664     pRegion->SetClean(TRUE, TRUE);
00665     pRegion->StopRender();
00666 
00667     while (pRegion != 0)
00668     {
00669         // we want to render everything using the quality the document is using ....
00670         
00671         if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer)
00672         {
00673             QualityAttribute *pQualAttr = new QualityAttribute(pDocView->RenderQuality);
00674             pRegion->SetQuality(pQualAttr, TRUE);
00675         }
00676 
00677         // render everything over pSpread into our renderregion ....
00678         pDocView->ContinueRenderView(pRegion, pSpread, TRUE, FALSE);
00679         
00680         // uncommenting the following line allows us to see what is going on
00681 //      OSRenderBitmap::DebugMe(pRegion,8);
00682         
00683         // need explicit cast to enable us to call ReadCentrePixel32bpp
00684         if (pRegion->IsKindOf(CC_RUNTIME_CLASS(GRenderClickColour)))
00685         {
00686             GRenderClickColour* actualRegion = (GRenderClickColour*) pRegion;
00687             if (NodeRenderableInk::AverageHitColour)
00688                 Pix = actualRegion->GetAveragePixel32BPP();
00689             else
00690                 Pix = actualRegion->GetCentrePixel32BPP();
00691         }
00692 
00693         pRegion->StartRender();
00694         pRegion = pDocView->GetNextOnTop(&drClickRect);
00695     }
00696 
00697     pDocView->SetForceDefaultColourContexts(bOldForce);
00698     return pHitNode;
00699 }
00700 
00701 
00702 
00703 /********************************************************************************************
00704 >   static NodeRenderableInk* NodeRenderableInk::FindColourForNodeRenderableAtPoint (Spread* pSpread,
00705                                                                           DocCoord dcPoint,
00706                                                                           Pixel32bpp& Pix,
00707                                                                           Node* pThisNode,
00708                                                                           AttrFillGeometry* pThisFill)
00709 {
00710     Author:     Chris_Snook (Xara Group Ltd) <camelotdev@xara.com>
00711     Created:    12/5/2000
00712     Inputs:     pSpread     ---     pointer to the spread containing the given point
00713                 dcPoint     ---     the point the object to be searched for occupies
00714                 Pix         ---     reference to the structure that will receive pixel data
00715                                     (created internally by custom colour picker control)
00716                 pThisNode           The node that this function is concerned with
00717                 pThisFill   ---     The fill that this function is concerned with
00718                                     ( parent (pThisFill) == pThisNode )
00719 
00720     Outputs:    -
00721     Returns:    0 (at present)
00722     Purpose:    The function obtains the TRUE 32-BIT RGB index for the pixel that our mouse
00723                 cursor is over for the supplied node.
00724 
00725     Errors:     -
00726     SeeAlso:    NodeRenderableInk::FindSimpleAtPointForColourPicker
00727 
00728 ********************************************************************************************/
00729 
00730 NodeRenderableInk* NodeRenderableInk::FindColourForNodeRenderableAtPoint (Spread* pSpread,
00731                                                                           DocCoord dcPoint,
00732                                                                           Pixel32bpp& Pix,
00733                                                                           Node* pThisNode,
00734                                                                           AttrFillGeometry* pThisFill)
00735 {
00736     PORTNOTETRACE("other","NodeRenderableInk::FindColourForNodeRenderableAtPoint - do nothing");
00737 #ifndef EXCLUDE_FROM_XARALX
00738     // Find the currently active DocView.
00739     DocView* pDocView = DocView::GetSelected();
00740     ENSURE(pDocView != 0, "Null current DocView in NodeRenderableInk::FindFirstAtPoint");
00741 
00742     // Build a rectangle around the given point, based on the size of a blob.
00743     DocRect drClickRect;
00744     OSRenderRegion::GetBlobRect(pDocView->GetViewScale(), dcPoint,
00745                                 BT_CLICKME, &drClickRect);
00746 
00747     // Get the extent of the current view and convert to spread coordinates.
00748     DocRect drViewRect = pDocView->GetDocViewRect(pSpread);
00749     pSpread->DocCoordToSpreadCoord(&drViewRect);
00750     
00751     // Create a colour render region to draw into, if possible.
00752     RenderRegion* pRegion = pDocView->RenderOnTop(&drClickRect, pSpread, COLOURBITMAP);
00753 
00754     if (pRegion==0)
00755     {
00756         delete pAttribMap;
00757         ERROR2(0,"FindSimpleAtPoint: couldn't create a RenderRegion!");
00758     }
00759 
00760     // Start with a (very) clean sheet.
00761     pRegion->SetClean(TRUE, TRUE);
00762 
00763     pRegion->StopRender ();
00764 
00765     while (pRegion != 0)
00766     {
00767         // we want to render everything using the quality the document is using ....
00768         
00769         if (pRegion->RRQuality.GetQuality() != Quality::QualityGuideLayer)
00770         {
00771             QualityAttribute* pQualAttr = new QualityAttribute(pDocView->RenderQuality);
00772             pRegion->SetQuality(pQualAttr, TRUE);
00773         }
00774 
00775         pRegion->SetFillGeometry ((ColourFillAttribute*) (pThisFill->GetAttributeValue ()), FALSE);
00776         pThisNode->Render (pRegion);
00777         
00778         // uncommenting the following line allows us to see what is going on
00779 //      OSRenderBitmap::DebugMe(pRegion,8);
00780         
00781         // need explicit cast to enable us to call ReadCentrePixel32bpp
00782         if (pRegion->IsKindOf(CC_RUNTIME_CLASS(GRenderClickColour)))
00783         {
00784             GRenderClickColour* actualRegion = (GRenderClickColour*) pRegion;
00785             Pix = actualRegion->GetCentrePixel32BPP();
00786         }
00787 
00788         pRegion->StartRender ();
00789         pRegion = pDocView->GetNextOnTop(&drClickRect);
00790     }
00791 #endif
00792     return 0;   // I suppose I should return something else here? //pThisNode);
00793 }
00794 
00795 /********************************************************************************************
00796 >   static NodeRenderableInk* NodeRenderableInk::FindCompoundFromSimple(NodeRenderableInk* pSimpleNode,
00797                                                                         Node* pSiblingNode = 0)
00798 
00799     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00800     Created:    13/10/94
00801     Inputs:     pSimpleNode         a simple node, eg. a renderable node such as a path,
00802                                     possibly returned by a previous call to FindSimpleAtPoint.
00803     Outputs:    -
00804     Returns:    A pointer to the "compound" node, eg. a group, that contains the given
00805                 simple node, or (pSimpleNode) if there isn't one.
00806     Purpose:    This function will search up the parent links from the given "simple" node
00807                 looking for a node whose parent is a layer.  This guarantees that the
00808                 returned node will be a genuinely selectable object, for example the
00809                 routine will return the group node if the given point is within a
00810                 grouped object.
00811     Errors:     -
00812     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint
00813 ********************************************************************************************/
00814 
00815 NodeRenderableInk* NodeRenderableInk::FindCompoundFromSimple(NodeRenderableInk* pSimpleNode,
00816                                                              Node* pSiblingNode)
00817 {
00818     // If necessary, search for a selectable parent node.
00819     if (pSimpleNode != 0)
00820     {
00821         for (;;)
00822         {
00823             // Find the node's parent, and if it is a layer then we have a node that can
00824             // be selected.
00825             Node* pParentNode = pSimpleNode->FindParent();
00826             Node* pSaveParentNode = 0;
00827 
00828             // DMc - new test to see which parent node wishes to be selected
00829             while (pParentNode != 0)
00830             {
00831                 if (pParentNode->IsRenderable()     && 
00832                     !pParentNode->IsNodeDocument()  &&
00833                     !pParentNode->IsLayer()         && 
00834                     !pParentNode->IsPaper()         && 
00835                     !pParentNode->IsSpread()        &&
00836                     !pParentNode->IsChapter())
00837                 {
00838                     if (pParentNode->PromoteHitTestOnChildrenToMe() ||
00839                         pParentNode->IsSelected())
00840                             pSaveParentNode = pParentNode;
00841                 }
00842 
00843                 pParentNode = pParentNode->FindParent();
00844             }
00845 
00846             pParentNode = pSaveParentNode;
00847             if (pParentNode == 0 || pParentNode->IsLayer()) break;
00848 
00849             // If the caller has specified that the node should be in the same level
00850             // as the specified sibling then check to see if the node we've got to
00851             // exists in that sibling list or not...
00852             if (pSiblingNode != 0)
00853             {
00854                 // Check all siblings. Start from first sibling in this list.
00855                 Node* pSibling = pSimpleNode->FindParent();
00856                 if (pSibling != 0) pSibling = pSibling->FindFirstChild();
00857 
00858                 // Loop around comparing members of the sibling list with the specified sibling...
00859                 while (pSibling != 0 && pSibling != pSiblingNode)
00860                     pSibling = pSibling->FindNext();
00861 
00862                 // If we found the specified sibling then stop the CompoundFromSimple search.
00863                 if (pSibling == pSiblingNode) break;
00864             }
00865 
00866             // If the parent doesn't want to be selected due to one of it's children being
00867             // clicked on then stop NOW!
00868             if (pParentNode->IsAnObject())
00869             {
00870                 if (((NodeRenderableInk*) pParentNode)->CanSelectAsCompoundParent())
00871                     pSimpleNode = (NodeRenderableInk*) pParentNode;     // Parent allowed to be selected
00872                 else
00873                     break;                                              // Parent not allowed to be selected
00874                                                                         // So return pSimpleNode now!
00875             }
00876             else
00877             {
00878                 // Hit a non-ink node so we must have emerged above the selection surface
00879                 // This shouldn't happen, so raise an error!
00880                 ERROR2(0, "Couldn't find a selectable compound node in FindCompoundFromSimple");
00881             }
00882         }
00883     }
00884 
00885     // Return the node found, or 0 if we couldn't find any.
00886     return pSimpleNode;
00887 }
00888 
00889 
00890 
00891 /********************************************************************************************
00892 >   static NodeRenderableInk* NodeRenderableInk::FindInnerCompound(Node* pLowNode, Node* pHighNode)
00893 
00894     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00895     Created:    2/12/94
00896     Inputs:     pLowNode        the node to start the search from (deep in the tree)
00897                 pHighNode       the node to finish searching at
00898     Outputs:    -
00899     Returns:    -
00900     Purpose:    
00901     Errors:     -
00902     SeeAlso:    -
00903 ********************************************************************************************/
00904 
00905 NodeRenderableInk* NodeRenderableInk::FindInnerCompound(Node* pLowNode, Node* pHighNode)
00906 {
00907     if (pLowNode==pHighNode)
00908         return 0;
00909     
00910     if (!pHighNode->AllowSelectInside())
00911         return 0;
00912 
00913     Node* pLastCompoundNode = pLowNode;
00914     if (pLowNode != 0)
00915     {
00916         for (;;)
00917         {
00918             pLowNode = pLowNode->FindParent();
00919             if (pLowNode == 0 || pLowNode == pHighNode || IS_A(pLowNode, Layer)) break;
00920             if (pLowNode->IsCompound()) pLastCompoundNode = pLowNode;
00921 //          if (IsUserName("JustinF")) TRACE( _T("\t\tLooking at a %s\n"), pLowNode->Name());
00922         }
00923     }
00924 
00925     return (NodeRenderableInk*) pLastCompoundNode;
00926 }
00927 
00928 
00929 
00930 
00931 /********************************************************************************************
00932 >   static NodeRenderableInk* NodeRenderableInk::FindCompoundAtPoint(Spread* pSpread,
00933                                                                      DocCoord dcPoint,
00934                                                                      Node* pHighNode = 0)
00935 
00936     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00937     Created:    7/6/94
00938     Inputs:     pSpread     ---     pointer to the spread containing the given point
00939                 dcPoint     ---     the point the object to be searched for occupies
00940                 pHighNode       Node below which search should be carried out.
00941     Outputs:    -
00942     Returns:    A pointer to a node at the given point, which may be a "compound" object,
00943                 eg. a group.
00944     Purpose:    This function is similar to NodeRenderableInk::FindSimpleAtPoint, except
00945                 that 
00946     Errors:     -
00947     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint
00948 ********************************************************************************************/
00949 
00950 NodeRenderableInk* NodeRenderableInk::FindCompoundAtPoint(Spread* pSpread, DocCoord dcPoint, Node* pHighNode)
00951 {
00952     return FindCompoundFromSimple(FindSimpleAtPoint(pSpread, dcPoint, pHighNode));
00953 }
00954 
00955 
00956 
00957 
00958 /********************************************************************************************
00959 >   virtual BOOL NodeRenderableInk::CanSelectAsCompoundParent()
00960 
00961     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00962     Created:    05/01/95
00963     Inputs:     -
00964     Outputs:    -
00965     Returns:    TRUE if the node will allow itself to be selected when one of it's children
00966                 has been "hit"
00967                 FALSE otherwise
00968     Purpose:    Ask a node whether or not it's prepared to become selected when one of it's
00969                 children is clicked on.
00970                 This function is called in the FindSimple/Compound routines when they are
00971                 following parent links up the tree from a node which has been "hit".
00972                 This virtual function should be overridden in derived classes to alter its
00973                 behaviour.
00974     Errors:     -
00975     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint
00976 ********************************************************************************************/
00977 
00978 BOOL NodeRenderableInk::CanSelectAsCompoundParent()
00979 {
00980     // Virtual base-class function returns TRUE always.
00981     // Override this function in your class.
00982     return  TRUE;
00983 }
00984 
00985 
00986 
00987 
00988 /********************************************************************************************
00989 >   virtual BOOL NodeRenderableInk::CanSelectAsSimple()
00990 
00991     Author:     Phil_Martin (Xara Group Ltd) <camelotdev@xara.com>
00992     Created:    05/01/95
00993     Inputs:     -
00994     Outputs:    -
00995     Returns:    TRUE if the node will allow itself to be selected when it has been "hit"
00996                 FALSE otherwise
00997     Purpose:    Ask a node whether or not it's prepared to become selected when one of it's
00998                 clicked on.
00999                 This function is called in the FindSimple routines when they have just detected
01000                 a "hit" on a node.
01001                 This virtual function should be overridden in derived classes to alter its
01002                 behaviour.
01003     Errors:     -
01004     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint; NodeRenderableInk::FindCompoundAtPoint
01005 ********************************************************************************************/
01006 
01007 BOOL NodeRenderableInk::CanSelectAsSimple()
01008 {
01009     // Virtual base-class function returns TRUE always.
01010     // Override this function in your class.
01011     return  TRUE;
01012 }
01013 
01014 
01015 
01016 
01017 
01018 
01019 
01020 
01021 
01022 
01023 
01024 /********************************************************************************************
01025 >   static Node* NodeRenderableInk::FindFirstHitTest(Spread* pStartSpread,
01026                                                      const DocRect& drClickRect,
01027                                                      CCAttrMap* pAttribMap,
01028                                                      BOOL bExcludeLayers,
01029                                                      Node* pHighNode = 0)
01030 
01031     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01032     Created:    7/6/94
01033     Inputs:     pStartSpread    --- the spread node to begin the hit-testing search
01034                                     from.  Only nodes contained within this spread will
01035                                     be returned.
01036                 drClickRect     --- the rectangle centred on the mouse click position
01037                 pAttribMap      --- Pointer to the map of attributes current at this point
01038                 bExcludeLayers  --- TRUE if the routine should not return objects
01039                                     contained within locked or invisible layers.
01040                 pHighNode       Node below which search should be carried out.
01041     Outputs:    -
01042     Returns:    A pointer to the next NodeRenderableInk to consider for hit-testing.  The
01043                 returned node intersects the given click rectangle.
01044     Purpose:    Walks the node tree in the reverse order to rendering, which is the order
01045                 that nodes should be considered for hit-testing.
01046     Errors:     ENSURE failure if the routine runs out of nodes to consider - this should
01047                 never happen - if it does then there is something wrong with the node tree,
01048                 for example it contains no spread nodes.
01049     SeeAlso:    Node::FindNextHitTest; NodeRenderableInk::FindSimpleAtPoint
01050 ********************************************************************************************/
01051 
01052 Node* NodeRenderableInk::FindFirstHitTest(Spread* pStartSpread,
01053                                           const DocRect& drClickRect,
01054                                           CCAttrMap* pAttribMap,
01055                                           BOOL bExcludeLayers,
01056                                           Node* pHighNode)
01057 {
01058     // Make sure that the node we start at is really a spread, as all NodeRenderableInks
01059     // are children of spread nodes.
01060     ENSURE(pStartSpread != NULL,
01061             "Node::FindFirstHitTest: pStartSpread parameter is NULL!");
01062     ENSURE(pStartSpread->IsSpread(),
01063             "Node::FindFirstHitTest: pStartSpread parameter is not a spread!");
01064 
01065     // Get the bounding box of the objects within the specified spread, converted to
01066     // spread coordinates.
01067     DocRect drSpreadBounds = pStartSpread->GetBoundingRect();
01068     pStartSpread->DocCoordToSpreadCoord(&drSpreadBounds);
01069 
01070     // If the bounding box doesn't intersect the click rectangle then the given spread
01071     // cannot hold any object that was clicked on.
01072     if (!drSpreadBounds.IsIntersectedWith(drClickRect))
01073         return NULL;
01074 
01075     // As the spread does contain the click rectangle, begin the traversal...
01076     // Call FindNextHitTest directly on the Spread so that the last item of the
01077     // Spread's child list can be considered correctly.
01078     Node* pFirstNode = FindNextHitTest((Node*)pStartSpread, drClickRect, pAttribMap, bExcludeLayers);
01079     if (pFirstNode == NULL)
01080         return NULL;
01081 
01082     // If there is an upper limit node then go through the tree until we get down to that
01083     // without returning the results for hit-testing.
01084     // (Or until there are no more nodes to consider!)
01085     if (pHighNode != NULL)
01086     {
01087         while (pFirstNode != NULL && pFirstNode != pHighNode)
01088         {
01089             pFirstNode = FindNextHitTest(pFirstNode, drClickRect, pAttribMap, bExcludeLayers);
01090         }
01091 
01092         // If we have stopped at the high node then return the next node below that in HitTest order
01093         // as the "first" node...  Note that we do not want to examine child nodes of the high node.
01094         if (pFirstNode != NULL)
01095         {
01096             pFirstNode = FindNextHitTest(pFirstNode, drClickRect, pAttribMap, bExcludeLayers, TRUE);
01097         }
01098     }
01099 
01100     return pFirstNode;
01101 }
01102 
01103 
01104 
01105 /********************************************************************************************
01106 >   Node* NodeRenderableInk::FindNextHitTest(Node* pNode,
01107                                              const DocRect& drClickRect,
01108                                              CCAttrMap* pAttribMap,
01109                                              BOOL bExcludeLayers,
01110                                              BOOL bSkipChildren = FALSE)
01111 
01112     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01113     Created:    7/6/94
01114     Inputs:     pNode           --- Pointer to previous node in reverse rendering order
01115                                     (usually a result of calling FindFirstHitText or 
01116                                      a previous call to FindNextHitTest)
01117                 drClickRect     --- the rectangle centred on the mouse click position
01118                 pAttribMap      --- Pointer to the map of attributes current at this point
01119                 bExcludeLayers  --- TRUE if the routine should not return objects
01120                                     contained within locked or invisible layers.
01121                 bSkipChildren   --- if TRUE then the routine will not examine the children
01122                                     of this Node.  By default this flag is FALSE.
01123     Outputs:    -
01124     Returns:    A pointer to the next node to consider for hit-testing.
01125     Purpose:    Walks the node tree in the reverse order to rendering, which is the order
01126                 that nodes should be considered for hit-testing.
01127     Errors:     ENSURE failure if the routine runs out of nodes to consider - this should
01128                 never happen - if it does then there is something wrong with the node tree,
01129                 for example it contains no spread nodes.
01130     SeeAlso:    Node::FindFirstHitTest; NodeRenderableInk::FindSimpleAtPoint;
01131                 Node::IsUnhittableLayer
01132 ********************************************************************************************/
01133 
01134 Node* NodeRenderableInk::FindNextHitTest(Node* pNode,
01135                                          const DocRect& drClickRect,
01136                                          CCAttrMap* pAttribMap,
01137                                          BOOL bExcludeLayers,
01138                                          BOOL bSkipChildren)
01139 {
01140     if (pNode == NULL)
01141         return NULL;
01142 
01143     // Storage for pointer to attribute types...
01144     CCRuntimeClass* pTypeInfo;
01145     void* pDummy;
01146 
01147     // Check if we should go down the child link of this node.
01148     // (If node is a spread then allow it's children to be scanned because it's bounds
01149     // are in a different coordinate system. This is OK because we know that the
01150     // only time this routine is called on a Spread is the first call made in FindFirstHitTest
01151     // which has already done a bounds test.)
01152     if (!bSkipChildren &&
01153         (HitTestChildren(pNode, drClickRect, bExcludeLayers) || 
01154          pNode->IsSpread()))
01155     {
01156         // Find its right-most sibling and continue walking from there.
01157         pNode = pNode->FindFirstChild();
01158         while (pNode->FindNext() != NULL)
01159         {
01160             // If this node is an attribute then it's more local than anything that might
01161             // be in the Attribute map (because we've just gone DOWN in the tree).
01162             // So, we'll bung it directly into the attribute map!
01163             if (pNode->IsAnAttribute() && !pNode->IsAClipViewAttr())
01164             {
01165                 pTypeInfo = ((NodeAttribute*)pNode)->GetAttributeType();
01166 
01167                 // Check if this type is already in the hash table.  If it isn't then increment
01168                 // the number of attribute types found
01169                 if (!pAttribMap->Lookup(pTypeInfo, pDummy))
01170                     nFoundAttributes++;
01171 
01172                 // Insert this local attribute into the map
01173                 pAttribMap->SetAt(pTypeInfo, pNode);
01174             }
01175 
01176             pNode = pNode->FindNext();
01177         }
01178     }
01179     else if (pNode->FindPrevious() != NULL)
01180     {                                             
01181         // Otherwise, just backtrack to the immediately-previous sibling, if possible.
01182         pNode = pNode->FindPrevious();
01183     }
01184     else
01185     {   
01186         // We have hit a node with no previous sibling or children.  Search up through
01187         // its parents until we find a parent with a previous sibling.  This previous
01188         // sibling is our next node.  However, if while searching up the parent links
01189         // we hit a spread then we know we have passed all NodeRenderableInks, and the
01190         // search can be terminated.
01191         do
01192         {
01193             if (pNode->IsSpread())      // Don't allow hit-testing to cross spreads
01194                 return NULL;
01195 
01196             pNode = pNode->FindParent();
01197         }
01198         while (pNode != NULL && pNode->FindPrevious() == NULL);
01199         if (pNode->IsSpread())          // Don't allow hit-testing to cross spreads
01200             return NULL;
01201 
01202         // If we broke out of the above loop because we found a parent with a previous
01203         // sibling then advance to that sibling.
01204         if (pNode != NULL && pNode->FindPrevious() != NULL)
01205             pNode = pNode->FindPrevious();
01206         if (pNode->IsSpread())          // Don't allow hit-testing to cross spreads
01207             return NULL;
01208     }
01209 
01210     // If the found node is an attribute then remove it from the attribute map because it
01211     // has just gone out of scope!
01212     if (pNode->IsAnAttribute() && !pNode->IsAClipViewAttr())
01213     {
01214         // The node is an attribute.  Make sure that it no longer occurs in the
01215         // attribute hash table, so that the next time RenderAppliedAttributes is
01216         // called it will look for this kind.
01217         if (nFoundAttributes > 0 && pAttribMap->RemoveKey(((NodeAttribute*) pNode)->GetAttributeType()))
01218             nFoundAttributes--;
01219     }
01220 
01221     
01222     // Return the node we've found.
01223     return pNode;
01224 }
01225 
01226 
01227 
01228 /********************************************************************************************
01229 >   BOOL NodeRenderableInk::HitTestChildren(Node* pNode, const DocRect& drClickRect, BOOL bExcludeLayers)
01230 
01231     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01232     Created:    20/6/94
01233     Inputs:     pNode           ---     Pointer to node to test child link of
01234                 drClickRect     ---     the rectangle enclosing a mouse-click, in spread
01235                                         coordinates.
01236                 bExcludeLayers  ---     whether locked or invisible layers should be
01237                                         excluded from hit-testing.
01238     Outputs:    -
01239     Returns:    TRUE if the caller should go down the child link of "this" node.
01240     Purpose:    Private helper function for Node::FindNextHitTest.  This function decides
01241                 whether FindNextHitTest should go down a child link, which it should if
01242                 (i) there is a child link; AND (ii) this node isn't a locked or invisible
01243                 layer if such things should be excluded; (iii) if this node has a bounding
01244                 rectangle it intersects the click rectangle.
01245     Errors:     -
01246     SeeAlso:    Node::FindNextHitTest
01247 ********************************************************************************************/
01248 
01249 BOOL NodeRenderableInk::HitTestChildren(Node* pNode, const DocRect& drClickRect, BOOL bExcludeLayers)
01250 {
01251     // If there's no child link to follow the decision is easy . . .
01252     if (pNode->FindFirstChild() == 0) return FALSE;
01253 
01254     // If the node is a locked or invisible layer and the caller wants such things
01255     // ignored then don't follow link down . . .
01256     if (bExcludeLayers && pNode->IsLayer())
01257     {
01258         Layer* pLayer = (Layer*) pNode;
01259         if (pLayer->IsLocked() || !pLayer->IsVisible()) return FALSE;
01260     }
01261 
01262     // If this node has a bounding rectangle then don't go down the link if the given
01263     // mouse-click rectangle doesn't intersect with the bounds . . .
01264     if (pNode->IsBounded())
01265     {
01266         DocRect drBounds = ((NodeRenderableBounded*) pNode)->GetBoundingRect();
01267         if (!drBounds.IsIntersectedWith(drClickRect)) return FALSE;
01268     }
01269     
01270     // If we pass all the above tests then by all means go down the child link.
01271     return TRUE;
01272 }
01273 
01274 
01275 
01276 /********************************************************************************************
01277 >   void NodeRenderableInk::ClearAttribMap()
01278 
01279     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01280     Created:    31/7/94
01281     Inputs:     -
01282     Outputs:    -
01283     Returns:    -
01284     Purpose:    Clears out all entries in the attribute map that is being accumulated as 
01285                 click-detection occurs. This has to be done when going down a child-link
01286                 because then you are entering a deeper scope where the attributes in the map
01287                 may not apply.
01288                 It is called from Node::FindNextHitTest
01289     Errors:     -
01290     SeeAlso:    Node::FindNextHitTest
01291 ********************************************************************************************/
01292 
01293 void NodeRenderableInk::ClearAttribMap()
01294 {
01295     pAttribMap->RemoveAll();
01296     nFoundAttributes = 0;
01297 }
01298 
01299 
01300 
01301 /********************************************************************************************
01302 >   void NodeRenderableInk::RenderAppliedAttributes(RenderRegion* pRegion) const
01303 
01304     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01305     Created:    16/6/94
01306     Inputs:     pRegion     ---     pointer to the render-reigion the function should
01307                                     render attributes into.
01308     Outputs:    -
01309     Returns:    -
01310     Purpose:    Searches for all the attributes which apply to "this" ink-node, rendering
01311                 them into the given render-region.  Private helper function for
01312                 NodeRenderableInk::FindSimpleAtPoint.
01313     Errors:     -
01314     SeeAlso:    NodeRenderableInk::FindSimpleAtPoint
01315 ********************************************************************************************/
01316 
01317 void NodeRenderableInk::RenderAppliedAttributes(RenderRegion* pRegion) const
01318 {
01319     // If we are missing some attributes then search until we find all of them.
01320     FindAppliedAttributes(pAttribMap, nMaxAttributes, &nFoundAttributes);
01321 
01322     // Make sure our dummy flat fill, that will be used in the
01323     // place of any 'fancy' fills, is set up.
01324     *(DummyFill.GetStartColour()) = DocColour(COLOUR_BLACK);
01325 
01326     // OK, we have found the full quota of attributes.  Now render them all.
01327     CCAttrMap::iterator pos = pAttribMap->GetStartPosition();
01328     while( pos != pAttribMap->GetEndPosition() )
01329     {
01330         CCRuntimeClass* pKey;
01331         void* pVal;
01332         pAttribMap->GetNextAssoc(pos, pKey, pVal);
01333 
01334         // Get the 'Family' of Attributes the fill belongs to
01335         CCRuntimeClass* AttrFamily = ((NodeAttribute*) pVal)->GetAttributeType();
01336         // Get the actual fill type
01337         CCRuntimeClass* AttrClass = ((NodeAttribute*) pVal)->GetRuntimeClass();
01338 
01339         // Check for a Transparency fill.
01340         // We can ignore them, as they do not effect hit testing.
01341         if ( AttrFamily != CC_RUNTIME_CLASS(AttrTranspFillGeometry) )
01342         {
01343             // Is this a fancy fill type ?
01344             if ( AttrFamily == CC_RUNTIME_CLASS(AttrFillGeometry) &&
01345                     AttrClass != CC_RUNTIME_CLASS(AttrFlatColourFill) )
01346             {
01347                 if (AttrClass == CC_RUNTIME_CLASS(AttrBitmapColourFill) &&
01348                     ((NodeAttribute*) pVal)->NeedsTransparency())
01349                 {
01350                     // It must be a masked bitmap, so we'll need to render it.
01351                     // The render code will check the 'VeryMono' flag, and render
01352                     // the bitmap non-masked pixels as black
01353                     ((NodeAttribute*) pVal)->Render(pRegion);
01354                 }
01355                 else
01356                 {
01357                     // Yes, so just render a black flat fill
01358                     DummyFill.Render(pRegion);
01359                 }
01360             }   
01361             else
01362             {
01363                 // It's only a flat fill, so we might as well render it.
01364                 // This will allow 'no colour' fills to work too.
01365                 ((NodeAttribute*) pVal)->Render(pRegion);
01366             }
01367         }
01368     }
01369 }
01370 
01371 
01372 
01373 /********************************************************************************************
01374 
01375 >   void NodeRenderableInk::RenderAppliedClipAttributes(RenderRegion* pRender)
01376 
01377     Author:     Karim_MacDonald (Xara Group Ltd) <camelotdev@xara.com>
01378     Created:    13/04/2000
01379     Inputs:     pRender     the render region to render into.
01380     Outputs:    The given render region will have its clipping region reduced to the clip
01381                 region which would apply to this node in the normal course of rendering.
01382 
01383     Purpose:    Helper method for RenderAppliedAttributes().
01384 
01385                 Render all ClipRegionAttributes which would be applied to this node in the
01386                 normal render-loop. This actually gets a little complicated, as these attrs
01387                 are rendered by NodeClipView nodes, so we need to go up the tree looking for
01388                 NodeClipViewController nodes, then getting their NodeClipView nodes to render
01389                 the clip-attribute.Things are complicated more by the fact that a ClipView
01390                 clips all nodes below a NCVC *except* the keyhole node of that NCVC, which
01391                 is *not* clipped.
01392 
01393     Notes:      This is a bit of a BODGE, but is required because ClipRegionAttributes are
01394                 not stored in the tree inside a NodeAttribute, and are therefore not picked
01395                 up on by the normal RenderAppliedAttributes() process.
01396 
01397     See also:   RenderAppliedAttributes().
01398 
01399 ********************************************************************************************/
01400 void NodeRenderableInk::RenderAppliedClipAttributes(RenderRegion* pRender) const
01401 {
01402     // 1.   pRefNode starts on this node. We look for a NCVC which clips pRefNode, add it to
01403     //      our list, and then that NCVC becomes the new pRefNode to work from.
01404     //
01405     // 2.   To find a NCVC which clips us, we go straight up the tree. If we find a NCVC,
01406     //      then we check whether its child which we came from is also one of its keyhole
01407     //      nodes. If so, we ignore it and continue. If not, then it must clip us, and
01408     //      therefore the NCVC becomes the new pRefNode.
01409     //
01410     // 3.   We repeat the process until we hit a layer node, which marks the top of this
01411     //      branch of the tree.
01412     //
01413     Node* pRefNode = (Node*) this;
01414     std::list<NodeClipViewController*> lNCVC;
01415     BOOL bFinished = FALSE;
01416     while (!bFinished)
01417     {
01418         // go up the tree until we reach the top or we reach a NCVC
01419         // for which we are not a keyhole node.
01420         Node* pChild = pRefNode;
01421         Node* pParent = pRefNode->FindParent();
01422         BOOL bFoundApplicableNCVC = pParent->IsANodeClipViewController() &&
01423                                 !((NodeClipViewController*)pParent)->OwnsAsKeyholeNode(pChild);
01424 
01425         while (pParent != NULL && !pParent->IsLayer() && !bFoundApplicableNCVC)
01426         {
01427             pChild  = pParent;
01428             pParent = pParent->FindParent();
01429             bFoundApplicableNCVC = pParent->IsANodeClipViewController() &&
01430                                 !((NodeClipViewController*)pParent)->OwnsAsKeyholeNode(pChild);
01431         }
01432 
01433         if (bFoundApplicableNCVC)
01434         {
01435             lNCVC.push_front((NodeClipViewController*) pParent);
01436             pRefNode = pParent;
01437         }
01438         else
01439         {
01440             bFinished = TRUE;
01441         }
01442     }
01443 
01444     // ok, now go through the list in reverse order, telling the NCV belonging to each NCVC
01445     // to render is ClipRegionAttribute.
01446     NodeClipView* pClipView         = 0;
01447     NodeClipViewController* pNCVC   = 0;
01448     while (!lNCVC.empty())
01449     {
01450         pNCVC = lNCVC.back();
01451         lNCVC.pop_back();
01452         pClipView = pNCVC->GetClipView();
01453         if (pClipView != 0)
01454         {
01455 // DEBUG:
01456 //          TRACEUSER( "Karim", _T("NRInk::RenderAppliedClipAttrs; NCVC: 0x%x\n"), (DWORD)pNCVC);
01457             pClipView->RenderClipAttr(pRender);
01458         }
01459     }
01460 // DEBUG:
01461 //  TRACEUSER( "Karim", _T("NRInk::RenderAppliedClipAttrs; this: 0x%x; %s\n"),
01462 //                                              (DWORD)this, this->Name());
01463 }
01464 
01465 
01466 
01467 /********************************************************************************************
01468 >   BOOL NodeRenderableInk::FindAppliedAttributes(CCAttrMap* pAttribMap,
01469                                                   INT32 nMax = 5000,
01470                                                   INT32* pnFound = 0,
01471                                                   BOOL ExcludeIndirectlyAppliedGLAs = FALSE,
01472                                                   BOOL bStrictEffectStatus = TRUE) const
01473 
01474     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
01475     Created:    13/6/94
01476     Inputs:     pAttribMap  Map table to fill in with attribute details. This table stores 
01477                 Key,Value tuples of the form (CCRuntimeClass*, NodeAttribute*). You can use
01478                 the normal CMap Lookup function to extract the pointer to an attribute from
01479                 the map, eg.
01480                     Lookup(CC_RUNTIME_CLASS(NodeAttrFill),pAttrib);
01481                 nMax        The maximum number of attribute types that can be found
01482                 pnFound     Pointer to the number already in the Map. This param can be used
01483                             in conjunction with nMax to make the scan stop as soon as the 
01484                             required number of attributes have been found. (Defaults cause 
01485                             whole tree scan.)
01486                 ExcludeIndirectlyAppliedGLAs -  ensures that this map won't include a pointer
01487                                                 to a geometry linked attribute which is not
01488                                                 directly linked to this nodes geometry (ie
01489                                                 exclude parent GLA's)
01490                 bStrictEffectStatus - TRUE when non-effect attrs are ignored in compound that
01491                                         allow effect attrs
01492                                     - FALSE when non-effect attrs are allowed throughout
01493     Outputs:    -
01494     Returns:    TRUE if attributes were successfully found
01495                 FALSE otherwise (if no attributes were found)
01496     Purpose:    Searches for all the attributes which apply to "this" ink-node. This routine
01497                 does NOT render them! Use RenderAppliedAttributes if you want that functionality.
01498     Errors:     -
01499     SeeAlso:    NodeRenderableInk::RenderAppliedAttributes
01500                 NodeAttribute::FindFirstAppliedAtt
01501                 NodeAttribute::FindPrevAppliedAtt
01502 ********************************************************************************************/
01503 
01504 BOOL NodeRenderableInk::FindAppliedAttributes(CCAttrMap* pAttribMap,
01505                                               INT32 nMax,
01506                                               INT32* pnFound, 
01507                                               BOOL ExcludeIndirectlyAppliedGLAs,
01508                                               BOOL bStrictEffectStatus) const
01509 {
01510     // Precondition checks
01511     ERROR2IF(pAttribMap == 0, FALSE, "FindAppliedAttributes passed null map");
01512 
01513     // Find the closest attribute applied to "this" node...
01514     NodeAttribute* pAttrib = NodeAttribute::FindFirstAppliedAttr((Node*)this);
01515     if (pAttrib == 0) return FALSE;
01516 
01517     // Get variables ready for the loop, OUTSIDE the loop!!!
01518     CCRuntimeClass* pTypeInfo;
01519     void* pDummy;
01520     INT32 nFound;
01521     if (pnFound)
01522         nFound = *pnFound;
01523     else
01524         nFound = 0;
01525 
01526 //  BOOL FoundClipAttr = FALSE;
01527 
01528     // If we are finding a completely new set of attrs then we should not overwrite any entries
01529     // into the map that have been made within this routine
01530     // (So that normal attrs don't overwrite effect attrs)
01531     BOOL bOverwriteExisting = (nFound!=0);
01532 
01533     // Repeat the following loop for all attributes we encounter in the tree.
01534     while (pAttrib)
01535     {
01536         // Get the run-time type of the attribute, which serves as a hash "key".
01537         pTypeInfo = pAttrib->GetAttributeType();
01538 
01539         // Check if this type is already in the hash table.  If it isn't then insert
01540         // its address with its run-time type as the key, and check if we need to look
01541         // for any more.
01542         BOOL bEffectStatusOK = FALSE;
01543         if (bStrictEffectStatus)
01544             bEffectStatusOK = (pAttrib->IsEffectAttribute() == this->IsValidEffectAttr(pAttrib));
01545         else
01546             bEffectStatusOK = !pAttrib->IsEffectAttribute();
01547 
01548         if (!pAttrib->IsAClipViewAttr() && (pAttrib->FindParent() != (Node*)this || bEffectStatusOK))
01549         {
01550             if (!pAttribMap->Lookup(pTypeInfo, pDummy))
01551             {
01552                 // This is a new attribute for the map
01553                 BOOL AddAttr = TRUE;
01554                 // NB keep default attrs in map so we don't have incomplete maps
01555                 if (ExcludeIndirectlyAppliedGLAs && !pAttrib->IsADefaultAttr() && pAttrib->IsLinkedToNodeGeometry())
01556                 {
01557                     if(!pAttrib->IsLinkedToThisNode((Node*)this))
01558                     {
01559                         AddAttr = FALSE;
01560                     }
01561                 }
01562                 
01563                 if(AddAttr)
01564                 {
01565                     pAttribMap->SetAt(pTypeInfo, pAttrib);
01566                     nFound++;
01567                     ERROR3IF(nFound>nMax,"Found more attribute types than are registered!");
01568                 }
01569             }
01570             else if (bOverwriteExisting)
01571             {
01572                 // An attribute of that type is already in the map
01573                 // But if this attribute is a child of the scanned node
01574                 // we should force it into the map because it's
01575                 // still in scope!
01576                 if (pAttrib->FindParent() == (Node*)this)
01577                 {
01578                     pAttribMap->SetAt(pTypeInfo,pAttrib);
01579                     // (Attrib type was already in map so no need to increment counter.)
01580                 }
01581             }
01582         }
01583 
01584         // After dealing with that attribute can we stop the scan?
01585         // We want to stop as soon as we can, namely when we have all the attribute
01586         // types in the map and when we're no longer scanning children of "this".
01587         if ( nFound >= nMax && (pAttrib->FindParent()!=(Node*)this) )
01588             break;
01589 
01590         // Go on to find the next attribute.
01591         pAttrib = NodeAttribute::FindPrevAppliedAttr(pAttrib);
01592     }
01593 
01594 #ifdef _DEBUG
01595 PORTNOTE("other","Removed some IsUserName protected debug")
01596 #if 0
01597     if (IsUserName("Phil") && nMax<5000 && nFound<nMax)
01598     {
01599         TRACE( _T("Not found all reqd atts: (found %d attributes, should be %d)\n"),
01600                 nFoundAttributes, nMaxAttributes);
01601         
01602         for (POSITION pos = pAttribMap->GetStartPosition(); pos != 0; )
01603         {
01604             void* pKey;
01605             void* pValue;
01606             pAttribMap->GetNextAssoc(pos, pKey, pValue);
01607             TRACE( _T("\tFound a %s at 0x%lX\n"),
01608                     ((CCRuntimeClass*) pKey)->m_lpszClassName,
01609                     (UINT32) pValue);
01610         }
01611 
01612         List* pTypeList = ObjectRegistry::GetRequiredAttribs(GetRuntimeClass());
01613         for (ListItem* pItem = pTypeList->GetHead();
01614              pItem != 0;
01615              pItem = pTypeList->GetNext(pItem))
01616         {
01617             CCRuntimeClass* pClass = ((NodeAttributeClassItem*) pItem)->AttributeClass;
01618             void* pDummy;
01619             if (!pAttribMap->Lookup(pClass, pDummy))
01620             {
01621                 TRACE( _T("\t\tCouldn't find a %s\n"), pClass->m_lpszClassName);
01622             }
01623         }
01624     }
01625 #endif
01626 #endif
01627     ERROR2IF(nMax<5000 && nFound<nMax,FALSE,"Failed to find all required attributes.");
01628 
01629     if (pnFound)
01630         *pnFound = nFound;
01631 
01632     return TRUE;
01633 }
01634 
01635 
01636 
01637 /***********************************************************************************************
01638 >   AttrBrushType* NodeRenderableInk::GetAppliedBrushAttribute()
01639     
01640     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01641     Created:    15-5-2000
01642     Inputs:           
01643     Outputs:    -
01644     Returns:    the attrbrushtype that applies to this inknode   
01645     Purpose:    as above
01646     
01647 ***********************************************************************************************/
01648 
01649 AttrBrushType* NodeRenderableInk::GetAppliedBrushAttribute()
01650 {
01651     AttrBrushType* pAttr = 0;
01652     
01653     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrBrushType), (NodeAttribute**)&pAttr);
01654     
01655     if (pAttr == NULL)
01656         return NULL;
01657 
01658     BrushHandle Handle = pAttr->GetBrushHandle();
01659     
01660     if (Handle == BrushHandle_NoBrush)
01661         return NULL;
01662 
01663     // double check to make sure it isn't referring to a deleted brush
01664     BrushDefinition* pDef = BrushComponent::FindBrushDefinition(Handle);
01665     if (pDef == NULL)
01666         return NULL;
01667 
01668     return pAttr;
01669 }
01670 
01671 
01672 /***********************************************************************************************
01673 >   AttrStrokeType* NodeRenderableInk::GetActiveStroke()
01674     
01675     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01676     Created:    15-5-2000
01677     Inputs:           
01678     Outputs:    -
01679     Returns:    pointer to the stroke attribute applied to us, so long as it is active
01680     Purpose:    We may have a stroke attribute applied to us that does not do anything under
01681                 certain circumstances (zero line width, no accompanying variable width attribute).
01682     
01683 ***********************************************************************************************/
01684 
01685 AttrStrokeType* NodeRenderableInk::GetActiveStroke()
01686 {
01687     // first up, if we have no stroke colour then any strokes we have aren't really active
01688     AttrStrokeColour* pStrokeColour = NULL;
01689     
01690     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeColour), (NodeAttribute**)&pStrokeColour);
01691 
01692     if (pStrokeColour != NULL)
01693     {
01694         // get the stroke colour
01695         StrokeColourAttribute* pVal = (StrokeColourAttribute*)pStrokeColour->GetAttributeValue();
01696         if (pVal != NULL)
01697             if (pVal->Colour.IsTransparent())
01698                 return NULL;
01699     }
01700     else
01701         return NULL;
01702     
01703     // now find out if we have both a stroke and variable width attribute
01704     AttrStrokeType* pStroke = NULL;
01705     AttrVariableWidth* pVarWidth = NULL;
01706     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrStrokeType), (NodeAttribute**)&pStroke);
01707     FindAppliedAttribute(CC_RUNTIME_CLASS(AttrVariableWidth), (NodeAttribute**)&pVarWidth);
01708                     
01709     if (pVarWidth != NULL && ((VariableWidthAttrValue*)pVarWidth->GetAttributeValue())->GetWidthFunction() != NULL)
01710     {
01711         if (pStroke != NULL && pStroke->HasPathProcessor())
01712             return pStroke;
01713     }
01714     return NULL;
01715 
01716 }
01717 
01718 /***********************************************************************************************
01719 >   virtual NodePath* NodeRenderableInk::GetVariableWidthStrokePath()
01720     
01721     Author:     Diccon_Yamanaka (Xara Group Ltd) <camelotdev@xara.com>
01722     Created:    15-5-2000
01723     Inputs:     -  
01724     Outputs:    -
01725     Returns:    a nodepath containing the path that is generated by any variable width strokes that
01726                 are applied to us, or NULL if there aren't any
01727     Purpose:    If we have a variable width stroke applied to us then this will get the path generated
01728                 by that stroke.  This base class version returns NULL, overridden versions must supply
01729                 their own outline path.
01730 
01731     See also:   NodePath::GetVariableWidthStrokePath, NodeRegularShape::GetVariableWidthStrokePath
01732                 
01733     
01734 ***********************************************************************************************/
01735 
01736 NodePath* NodeRenderableInk::GetVariableWidthStrokePath()
01737 {
01738     return NULL;
01739 }
01740 
01741 
01742 
01743 NodePath* NodeRenderableInk::GetSmoothVariableWidthStrokePath()
01744 {
01745     return NULL;
01746 }
01747 

Generated on Sat Nov 10 03:45:30 2007 for Camelot by  doxygen 1.4.4