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