sgtree.cpp

Go to the documentation of this file.
00001 // $Id: sgtree.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 
00099 // SGTree.cpp - super gallery DisplayTree classes
00100 
00101 
00102 #include "camtypes.h"
00103 
00104 #include "dlgmgr.h"
00105 //#include "document.h" // For Document->GetTitle() - in camtypes.h [AUTOMATICALLY REMOVED]
00106 #include "dragmgr.h"    // Drag manager (DragManagerOp::StartDrag, RedrawStarting etc)
00107 //#include "galres.h"       // Gallery bitmap resources
00108 //#include "galstr.h"       // Gallery string resources
00109 //#include "scroller.h" // For scroll bar width
00110 //#include "sgallery.h" // SuperGallery definitions - in camtypes.h [AUTOMATICALLY REMOVED]
00111 #include "sgdrag.h"     // Scroll bar drag target/info
00112 //#include "sgtree.h"       // This file's associated header - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "sglib.h"      // For virtualising static switch
00114 //#include "sglcart.h"
00115 
00116 #include "ccdc.h"       // For render-into-dialogue support
00117 //#include "sglfills.h"
00118 #include "dlgcol.h"
00119 //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00120 //#include "grnddib.h"
00121 
00122 //#include "richard3.h" // For _R(IDS_GALLERY_PREPARE_FOR_UNFOLD)
00123 #include "progress.h"
00124 // Webster stuff
00125 //#include "webster.h"
00126 //#include "inetop.h"
00127 
00128 //#include "bars.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00129 
00130 // Implement the dynamic class bits...
00131 CC_IMPLEMENT_DYNAMIC(SGDisplayNode,  CCObject)
00132     CC_IMPLEMENT_DYNAMIC(SGDisplayRoot,  SGDisplayNode)
00133         CC_IMPLEMENT_DYNAMIC(SGDisplayRootScroll,  SGDisplayRoot)
00134     CC_IMPLEMENT_DYNAMIC(SGDisplayGroup, SGDisplayNode)
00135     CC_IMPLEMENT_DYNAMIC(SGDisplayItem,  SGDisplayNode)
00136 
00137 
00138 // This line mustn't go before any CC_IMPLEMENT_... macros
00139 #define new CAM_DEBUG_NEW
00140 
00141 
00142 static INT32 ScrollBarWidth     = 10;       // Width (PIXELS) of an SGDisplayRootScroll
00143                                             // provided gallery list scrollbar. MUST be even!
00144 
00145 
00146 SGDisplayNode *SGDisplayNode::CurrentBGRenderNode = NULL;
00147 BOOL SGDisplayNode::BGRenderClaimed= FALSE;
00148 BOOL SGDisplayNode::BkgEraseMode = TRUE;
00149 
00150 
00151 /***********************************************************************************************
00152 
00153 >   SGDisplayNode::SGDisplayNode()
00154 
00155     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00156     Created:    20/10/94
00157     Purpose:    SGDisplayNode constructor
00158     SeeAlso:    SuperGallery; SGDisplayGroup; SGDisplayItem
00159 
00160 ***********************************************************************************************/
00161 
00162 SGDisplayNode::SGDisplayNode()
00163 {
00164     Parent = Next = Previous = NULL;
00165 
00166     Flags.Invisible = Flags.ReadOnly = Flags.Modified = FALSE;
00167     Flags.CanSelect = Flags.Selected = FALSE;
00168     Flags.Folded = Flags.RedrawPending = FALSE;
00169     Flags.Virtualised = FALSE;
00170 
00171     Flags.HandleEventCount = 0;
00172 
00173     Flags.Reserved = 0;
00174 
00175     FormatRect = DocRect(0,0,0,0);
00176 }
00177 
00178 
00179 
00180 /***********************************************************************************************
00181 
00182 >   SGDisplayNode::~SGDisplayNode()
00183 
00184     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00185     Created:    20/10/94
00186     Purpose:    SGDisplayNode destructor
00187     SeeAlso:    SuperGallery; SGDisplayGroup; SGDisplayItem
00188 
00189 ***********************************************************************************************/
00190 
00191 SGDisplayNode::~SGDisplayNode()
00192 {
00193     ERROR3IF(Flags.HandleEventCount > 0, "AWOOGA! SGDisplayNode deleted while in its own HandleEvent method - Alert Jason!");
00194     ERROR3IF(Flags.HandleEventCount != 0, "Deleted SGDisplayNode had a corrupted HandleEventCount");
00195 
00196     if (Parent != NULL || GetChild() != NULL || Next != NULL || Previous != NULL)
00197     {
00198         ERROR3("Destructing SGDisplayNode which is still linked into a tree! I'll try to delink it first\n");
00199         RemoveFromTree();
00200     }
00201 }
00202 
00203 
00204 
00205 /***********************************************************************************************
00206 
00207 >   virtual SGDisplayNode *SGDisplayNode::GetChild(void) const
00208 
00209     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00210     Created:    20/10/94 (Made virtual on 13/5/95)
00211 
00212     Returns:    A pointer to the first child of this SGDisplayNode object, or NULL
00213 
00214     Purpose:    Finds the child of this DisplayTree Node.
00215                 Returns NULL if you have reached the boundary of the tree
00216 
00217     Notes:      This base-class method returns NULL - base nodes and items do not have
00218                 children (to reduce memory usage). SGDisplayRoot and SGDisplayGroup override
00219                 this method to provide child pointers.
00220 
00221     SeeAlso:    SGDisplayNode::SetChild; SuperGallery; SGDisplayNode::GetParent;
00222                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
00223 
00224 ***********************************************************************************************/
00225 
00226 SGDisplayNode *SGDisplayNode::GetChild(void) const
00227 {
00228     return NULL;
00229 }
00230 
00231 
00232 
00233 /***********************************************************************************************
00234 
00235 >   void SGDisplayNode::SetChild(SGDisplayNode *NewChild)
00236 
00237     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00238     Created:    20/10/94
00239 
00240     Inputs:     A pointer to the new first-child of this SGDisplayNode object
00241 
00242     Purpose:    Sets the child of this DisplayTree Node.
00243 
00244     Notes:      This base-class method gives an ERROR3 - base nodes and items do not have
00245                 children (to reduce memory usage). SGDisplayRoot and SGDisplayGroup override
00246                 this method to provide child pointers.
00247 
00248     SeeAlso:    SuperGallery; SGDisplayNode::GetParent;
00249                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
00250 
00251 ***********************************************************************************************/
00252 
00253 void SGDisplayNode::SetChild(SGDisplayNode *NewChild)
00254 {
00255     ERROR3("This is a childless SGDisplayNode - You cannot set it's child!");
00256 }
00257 
00258 
00259 
00260 /***********************************************************************************************
00261 
00262 >   virtual void SGDisplayNode::InsertInternal(SGDisplayNode *NodeToInsert,
00263                                                 SGDisplayNode *PrevNode, SGDisplayNode *NextNode)
00264 
00265     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00266     Created:    20/10/94
00267 
00268     Inputs:     NodeToInsert - the node to insert
00269                 PrevNode - NULL, or the node to insert after
00270                 NextNode  - NULL, or the node to insert before
00271 
00272     Purpose:    Inserts the given node/subtree into this subtree, between PrevNode and
00273                 NextNode. One of these two nodes may be NULL if you are trying to insert
00274                 at the head/tail of a sibling list.
00275                 
00276     Errors:     ERROR3s will be reported in debug builds for NULL NodeToInsert, or if
00277                 NodeToInsert is linked into another tree (has non-null parent/next/previous
00278                 pointers). It is perfectly legal for it to have a child subtree, though.
00279 
00280                 Errors will also occur if BOTH Next/PrevNode are NULL, or if PrevNode is
00281                 not currently the Previous Node of NextNode. (i.e. you must insert between
00282                 two valid adjacent nodes, or at the head/tail of the sibling list)
00283 
00284     SeeAlso:    SuperGallery; SGDisplayNode::AddItem;
00285                 SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
00286 
00287 ***********************************************************************************************/
00288 
00289 void SGDisplayNode::InsertInternal(SGDisplayNode *NodeToInsert,
00290                                     SGDisplayNode *PrevNode, SGDisplayNode *NextNode)
00291 {
00292     if (NodeToInsert == NULL)
00293     {
00294         ERROR3("Attempt to insert a NULL node into a tree was ignored");
00295         return;
00296     }
00297 
00298     // The node cannot be inserted after/before *itself*!
00299     ERROR3IF(NodeToInsert == PrevNode || NodeToInsert == NextNode,
00300             "Illegal attempt to link a node before/after ITSELF!");
00301 
00302     // The node cannot have parent, next, prev, but can have children
00303     ERROR3IF(NodeToInsert->Parent != NULL ||
00304              NodeToInsert->Next != NULL   || NodeToInsert->Previous != NULL,
00305              "Illegal attempt to link an already-linked node into a tree");
00306 
00307     ERROR3IF(PrevNode == NULL && NextNode == NULL,
00308              "SGDisplayNode::InsertInternal - can't insert between TWO NULL nodes!");
00309 
00310     ERROR3IF((PrevNode != NULL && PrevNode->Next != NextNode) ||
00311              (NextNode != NULL && NextNode->Previous != PrevNode),
00312              "SGDisplayNode::InsertInternal - Prev/Next nodes are not adjacent!");
00313 
00314     NodeToInsert->Previous = PrevNode;
00315     if (PrevNode != NULL)
00316     {
00317         // Linking in the middle of the sibling list
00318         PrevNode->Next = NodeToInsert;
00319         NodeToInsert->Parent = PrevNode->Parent;
00320     }
00321     else
00322     {
00323         // Must be trying to insert at the head of the sibling list, so becomes 1st child
00324         if (NextNode != NULL)
00325         {
00326             ERROR3IF(NextNode->Parent->GetChild() != NextNode,
00327                         "Attempt to Insert node as first child has gone awry!");
00328         
00329             NextNode->Parent->SetChild(NodeToInsert);
00330         }
00331     }
00332 
00333     NodeToInsert->Next = NextNode;
00334     if (NextNode != NULL)
00335     {
00336         NextNode->Previous = NodeToInsert;
00337         NodeToInsert->Parent = NextNode->Parent;
00338     }
00339 
00340     // Because we have recreated part of the tree, we must inform the gallery that the
00341     // cached format is incorrect and needs to be recalculated.
00342     SuperGallery *ParentGallery = GetParentGallery();
00343     if (ParentGallery != NULL)
00344         ParentGallery->InvalidateCachedFormat();
00345 }
00346 
00347 
00348 
00349 /***********************************************************************************************
00350 
00351 >   virtual void SGDisplayNode::AddItem(SGDisplayNode *NodeToInsert,
00352                                         SGSortKey *SortInfo = NULL)
00353 
00354     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00355     Created:    20/10/94
00356 
00357     Inputs:     NodeToInsert - the node/subtree to be inserted
00358                 SortInfo - NULL, or an array of MaxSGSortKeys sort key structures which
00359                 describe how the item should be inserted. [NOTE that this parameter is 
00360                 only used for insertion of SGDisplayItem and derived classes]
00361 
00362     Purpose:    Inserts the given node/subtree into this subtree. If SortInfo == NULL or
00363                 NodeToInsert is not an SGDisplayItem, it is added as the last child of
00364                 this node. Otherwise, it is inserted into the subtree of SGDisplayItems
00365                 at the point 'specified' by SortInfo (By asking each DisplayItem in turn
00366                 to compare itself to the one being added, until one is found which is
00367                 considered "greater than" this one according to the sort mode)
00368 
00369     Errors:     ERROR3s will be reported in debug builds for NULL NodeToInsert, or if
00370                 NodeToInsert is linked into another tree (has non-null parent/next/previous
00371                 pointers). It is perfectly legal for it to have a child subtree, though.
00372 
00373     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
00374 
00375 ***********************************************************************************************/
00376 
00377 void SGDisplayNode::AddItem(SGDisplayNode *NodeToInsert, SGSortKey *SortInfo)
00378 {
00379     if (NodeToInsert == NULL)
00380     {
00381         ERROR3("Attempt to add a NULL node to a tree was ignored");
00382         return;
00383     }
00384 
00385     // The node cannot have parent, next, prev, but can have children
00386     ERROR3IF(NodeToInsert->Parent != NULL ||
00387              NodeToInsert->Next != NULL   || NodeToInsert->Previous != NULL,
00388              "Illegal attempt to link an already-linked node into a tree");
00389 
00390     if (GetChild() == NULL) // We have no children, so there is only one place for this node!
00391     {
00392         SetChild(NodeToInsert);                     // Add it as our first child and return
00393         NodeToInsert->Parent = this;
00394 
00395         // Because we have recreated part of the tree, we must inform the gallery that the
00396         // cached format is incorrect and needs to be recalculated.
00397         SuperGallery *ParentGallery = GetParentGallery();
00398         if (ParentGallery != NULL)
00399             ParentGallery->InvalidateCachedFormat();
00400         return;
00401     }
00402 
00403     SGDisplayNode *Ptr  = GetChild();
00404     SGDisplayNode *Last = NULL;
00405 
00406     if (SortInfo == NULL || SortInfo[0].SortKey == 0 ||
00407             !NodeToInsert->IsKindOf(CC_RUNTIME_CLASS(SGDisplayItem)) ||
00408             !IsKindOf(CC_RUNTIME_CLASS(SGDisplayGroup)))
00409     {
00410         // There is no requested sort mode, or it is sort-by-none, or the node being inserted
00411         // is not a DisplayItem, or I am not a DisplayGroup, so I just add the item to the end
00412         // of my child list
00413 
00414         while (Ptr != NULL)     // Find the end of the sibling list
00415         {
00416             Last = Ptr;
00417             Ptr  = Ptr->Next;
00418         }
00419 
00420         // This ENSURE should never occur, as we checked for no-children above
00421         ERROR3IF(Last == NULL, "Something screwy has happened in SGDisplayNode:AddItem!");
00422         InsertInternal(NodeToInsert, Last, NULL);   // Insert it as the last child
00423     }
00424     else
00425     {
00426         // While searching givvus a parent! (bodge so libraries can sort whilst adding)
00427         NodeToInsert->Parent = this;
00428 
00429         // We can add with sorting, so add the item at an appropriate position based upon
00430         // the provided sort mode
00431         INT32 Result;
00432         while (Ptr != NULL)     // Search the sibling list for the first appropriate insertion point
00433         {
00434             Last = Ptr;
00435 
00436             // Compare using sort key 1
00437             Result = Ptr->CompareTo(NodeToInsert, SortInfo[0].SortKey);
00438             if (SortInfo[0].Reversed)
00439                 Result = -Result;
00440 
00441             // If they are equal, and we have multi-key sort, use key 2
00442             if (Result == 0 && SortInfo[1].SortKey != 0)
00443             {
00444                 Result = Ptr->CompareTo(NodeToInsert, SortInfo[1].SortKey);
00445                 if (SortInfo[1].Reversed)
00446                     Result = -Result;
00447             }
00448         
00449             // We have compared - if the current item is "greater" than the one being
00450             // inserted, then we can stop and insert it before that item
00451             if (Result > 0)
00452                 break;
00453 
00454             Ptr = Ptr->Next;
00455         }
00456 
00457         // Delink the parent since it shouldn't really be connected yet
00458         NodeToInsert->Parent = NULL;
00459         
00460         if (Ptr == NULL)
00461         {
00462             // We must have run off the end of the list - insert at the end
00463             ERROR3IF(Last == NULL, "Something screwy has happened in SGDisplayNode:AddItem!");
00464             InsertInternal(NodeToInsert, Last, NULL);
00465         }
00466         else
00467         {
00468             // We found an item to insert before, so insert before it
00469             InsertInternal(NodeToInsert, Ptr->GetPrevious(), Ptr);
00470         }
00471     }
00472 }
00473 
00474 
00475 
00476 /***********************************************************************************************
00477 
00478 >   virtual void SGDisplayNode::InsertAfter(SGDisplayNode *NodeToInsert)
00479 
00480     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00481     Created:    20/10/94
00482 
00483     Inputs:     NodeToInsert - the node to, ... erm... insert.
00484 
00485     Purpose:    Inserts the given node into the DisplayTree as the next (right) sibling
00486                 of this node.
00487 
00488     Notes:      The derived SGDisplayRoot node overrides this with a call to AddItem()
00489 
00490     Errors:     ERROR3 and quiet exit if NodeToInsert == NULL
00491 
00492     SeeAlso:    SuperGallery; SGDisplayNode::InsertBefore; SGDisplayNode::AddItem
00493 
00494 ***********************************************************************************************/
00495 
00496 void SGDisplayNode::InsertAfter(SGDisplayNode *NodeToInsert)
00497 {
00498     // Set the parent to modified to signify that one of it's children has been
00499     SGDisplayNode *Parent = GetParent();
00500     if(Parent != NULL)
00501         Parent->Flags.Modified = TRUE;
00502 
00503     InsertInternal(NodeToInsert, this, Next);
00504 }
00505 
00506 
00507 
00508 /***********************************************************************************************
00509 
00510 >   virtual void SGDisplayNode::InsertBefore(SGDisplayNode *NodeToInsert)
00511 
00512     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00513     Created:    20/10/94
00514 
00515     Inputs:     NodeToInsert - the node to, ... erm... insert.
00516 
00517     Purpose:    Inserts the given node into the DisplayTree as the previous (left) sibling
00518                 of this node.
00519 
00520     Notes:      The derived SGDisplayRoot node overrides this with a call to AddItem()
00521 
00522     Errors:     ERROR3 and quiet exit if NodeToInsert == NULL
00523 
00524     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::AddItem
00525 
00526 ***********************************************************************************************/
00527 
00528 void SGDisplayNode::InsertBefore(SGDisplayNode *NodeToInsert)
00529 {
00530     // Set the parent to modified to signify that one of it's children has been
00531     SGDisplayNode *Parent = GetParent();
00532     if(Parent != NULL)
00533         Parent->Flags.Modified = TRUE;
00534 
00535     InsertInternal(NodeToInsert, Previous, this);
00536 }
00537 
00538 
00539 
00540 /***********************************************************************************************
00541 
00542 >   virtual void SGDisplayNode::MoveAfter(SGDisplayNode *NodeToMove)
00543 
00544     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00545     Created:    14/3/95
00546 
00547     Inputs:     NodeToMove - the node to move
00548 
00549     Purpose:    MOVES the given node (to a different position in the DisplayTree) as the
00550                 previous (left) sibling of this node. If the node is not linked into
00551                 a tree, it is effectively just inserted.
00552 
00553     Notes:      This base class method simply delinks the item and relinks it elsewhere
00554                 in the display tree. However, derived classes will override this method
00555                 so that moving display items can have a further effect of also rearranging
00556                 the displayed "real" items. Before/After moving the real item, the
00557                 derived class can then call this baseclass method to complete the action.
00558         
00559                 Take care when moving items between groups (e.g. if an item is "moved"
00560                 from one docuemnt to another, it could be a bad thing, so be very
00561                 careful in derived classes to take appropriate action)
00562 
00563                 Any attempt to move an item after *itself* is queitly ignored
00564 
00565     Errors:     ERROR3 and quiet exit if NodeToMove == NULL
00566 
00567     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::AddItem
00568 
00569 ***********************************************************************************************/
00570 
00571 void SGDisplayNode::MoveAfter(SGDisplayNode *NodeToMove)
00572 {
00573     ERROR3IF(NodeToMove == NULL, "Illegal NULL param");
00574 
00575     if (NodeToMove == this)
00576         return;
00577 
00578     NodeToMove->RemoveFromTree();
00579     InsertAfter(NodeToMove);
00580 }
00581 
00582 
00583 
00584 /***********************************************************************************************
00585 
00586 >   virtual void SGDisplayNode::MoveBefore(SGDisplayNode *NodeToMove)
00587 
00588     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00589     Created:    14/3/95
00590 
00591     Inputs:     NodeToMove - the node to move
00592 
00593     Purpose:    MOVES the given node (to a different position in the DisplayTree) as the
00594                 previous (left) sibling of this node. If the node is not linked into
00595                 a tree, it is effectively just inserted.
00596 
00597     Notes:      This base class method simply delinks the item and relinks it elsewhere
00598                 in the display tree. However, derived classes will override this method
00599                 so that moving display items can have a further effect of also rearranging
00600                 the displayed "real" items. Before/After moving the real item, the
00601                 derived class can then call this baseclass method to complete the action.
00602         
00603                 Take care when moving items between groups (e.g. if an item is "moved"
00604                 from one docuemnt to another, it could be a bad thing, so be very
00605                 careful in derived classes to take appropriate action)
00606 
00607                 Any attempt to move an item before *itself* is queitly ignored
00608 
00609     Errors:     ERROR3 and quiet exit if NodeToMove == NULL
00610 
00611     SeeAlso:    SuperGallery; SGDisplayNode::InsertBefore; SGDisplayNode::AddItem
00612 
00613 ***********************************************************************************************/
00614 
00615 void SGDisplayNode::MoveBefore(SGDisplayNode *NodeToMove)
00616 {
00617     ERROR3IF(NodeToMove == NULL, "Illegal NULL param");
00618 
00619     if (NodeToMove == this)
00620         return;
00621 
00622     NodeToMove->RemoveFromTree();
00623     InsertBefore(NodeToMove);
00624 }
00625 
00626 
00627 
00628 /***********************************************************************************************
00629 
00630 >   virtual void SGDisplayNode::RemoveFromTree(void)
00631 
00632     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00633     Created:    20/10/94
00634 
00635     Purpose:    De-links this node/subtree from the DisplayTree. This DOES NOT DELETE the
00636                 node, just unlinks it in preparation for being deleted.
00637 
00638     Notes:      This does NOT delink children of this node from this node. To do that, you
00639                 must call RemoveFromTree from each child node in turn. To delete a subtree
00640                 you are better off calling DestroySubtree
00641 
00642     Errors:     ERROR3s will be reported in debug builds if certain corrupted tree
00643                 structures are detected, indicating tree generation/maintenance code 
00644                 has gone wrong
00645 
00646     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter;
00647                 SGDisplayNode::InsertBefore; SGDisplayNode::AddItem;
00648                 SGDisplayNode::DestroySubtree
00649 
00650 ***********************************************************************************************/
00651 
00652 void SGDisplayNode::RemoveFromTree(void)
00653 {
00654     // Because we have changed part of the tree, we must inform the gallery that the
00655     // cached format is incorrect and needs to be recalculated. We must also make sure
00656     // that this item is not pending a background redraw.
00657     SuperGallery *ParentGallery = GetParentGallery();
00658     if (ParentGallery != NULL)
00659     {
00660         // Ensure that the ParentGallery doesn't dereference this pointer on the next background
00661         // rendering pass (if we happen to be removed just after redrawing in the BG)
00662         if (this == ParentGallery->GetLastBackgroundNode())
00663             ParentGallery->SetLastBackgroundNode(NULL);
00664 
00665         if (Flags.RedrawPending)
00666             ParentGallery->DecrementPendingRedraws();
00667 
00668         ParentGallery->InvalidateCachedFormat();
00669     }
00670 
00671     Flags.RedrawPending = FALSE;        // We are NOT pending a redraw!
00672 
00673     if (Next == NULL)
00674     {
00675         // If the next sibling ptr is NULL, then we are at the end of a group or the list
00676         // so we 'touch' the previous node (or parent) FormatRect so that the next reformat
00677         // realises that a change has occurred around here and redraws where we were.
00678         SGDisplayNode *ToTouch = Previous;
00679         if (ToTouch == NULL)
00680             ToTouch = Parent;
00681 
00682         if (ToTouch != NULL)
00683             ToTouch->FormatRect.MakeEmpty();
00684     }
00685 
00686     // Delink our Parent from ourself
00687     if (Parent != NULL)
00688     {
00689         if (Parent->GetChild() == this)     // If we are first child, make Next child the first
00690         {
00691             // I must be the first child, so should not have a Previous node
00692             ERROR3IF(Previous != NULL, "Tree linking failure detected in SGDisplayNode::RemoveFromTree");
00693             Parent->SetChild(Next);
00694         }
00695         else
00696         {
00697             // I am not the parent's first child, so I must have a Previous node
00698             ERROR3IF(Previous == NULL, "Tree linking failure detected in SGDisplayNode::RemoveFromTree");
00699         }
00700     }
00701 
00702 
00703     // Delink our Previous/Next siblings (if any) from ourself
00704     if (Previous != NULL)
00705         Previous->Next = Next;
00706 
00707     if (Next != NULL)
00708         Next->Previous = Previous;
00709 
00710 
00711     // And finally, destroy our own backward links to parent, next, previous.
00712     // Done last to avoid 'losing' pointers before we have finished using them for delinking!
00713     Parent   = NULL;
00714     Previous = NULL;
00715     Next     = NULL;
00716 }
00717 
00718 
00719 
00720 /***********************************************************************************************
00721 
00722 >   virtual void SGDisplayNode::DestroySubtree(BOOL IncludingThisNode = TRUE)
00723 
00724     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00725     Created:    21/10/94
00726 
00727     Inputs:     IncludingThisNode - TRUE (The default) to delete this node (the root of
00728                 the subtree) as well as all its children.
00729                 FALSE to delete its children only (This leaves this node untouched, but
00730                 vapes all child nodes)
00731 
00732     Purpose:    DESTROYS the subtree starting at (and including, if IncludingThisNode is
00733                 TRUE) this node.
00734                 This does a depth-first recursive scan of the subtree, delinking each item,
00735                 and then CALLING EACH ITEMS DESTRUCTOR.
00736 
00737     Notes:      If you destroy at the root node, the entire tree is destroyed. The root node
00738                 will be deleted, but note that the reference(s) to the root node (e.g. in
00739                 the parent SuperGallery) will NOT be de-linked, so be *very* careful!
00740 
00741                 However, if the root node is a derived SGDisplayRoot node, it will refuse
00742                 to delete itself in this case, 
00743 
00744     Errors:     May be generated by the RemoveFromTree and destructor calls if the subtree
00745                 is in some way corrupt - see these calls for details.
00746 
00747                 An ERROR3 may be caused by the destructor if you are trying to delete
00748                 a tree item from within that item's event handler!
00749 
00750     SeeAlso:    SuperGallery; SGDisplayNode::RemoveFromTree; SGDisplayNode::~SGDisplayNode
00751 
00752 ***********************************************************************************************/
00753 
00754 void SGDisplayNode::DestroySubtree(BOOL IncludingThisNode)
00755 {
00756     while (GetChild() != NULL)          // Recurse depth-first down the subtree, destroying it
00757         GetChild()->DestroySubtree();   // Destroy child. Child now points at the next child
00758 
00759     if (IncludingThisNode)          // If this node is included in the destruction...
00760     {
00761         RemoveFromTree();           // Delink ourself from the tree
00762         delete this;                // and invoke our own destructor
00763     }
00764 }
00765 
00766 
00767 
00768 /***********************************************************************************************
00769 
00770 >   virtual SuperGallery *SGDisplayNode::GetParentGallery(void) const
00771 
00772     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00773     Created:    17/1/95
00774 
00775     Returns:    NULL if a cataclysmic event occurs
00776                 Otherwise,a pointer to the SuperGallery that 'owns' the tree this node is in
00777 
00778     Purpose:    Recursively scans up the tree asking each parent node in turn for the parent
00779                 gallery. It is expected that a node (eg SGDisplayRoot or SGDisplayGroup)
00780                 will be found somewhere on this path which will know who our parent gallery
00781                 is. (If not, there is a serious tree problem!)
00782 
00783                 Nodes which directly know their parent gallery must override this method
00784                 to ensure that they return the correct result instead of asking their parent!
00785 
00786     SeeAlso:    SGDisplayRoot::GetParentGallery; SGDisplayGroup::GetParentGallery
00787 
00788 ***********************************************************************************************/
00789 
00790 SuperGallery *SGDisplayNode::GetParentGallery() const
00791 {
00792     if (GetParent() != NULL) return GetParent()->GetParentGallery();
00793 
00794     return NULL;
00795 }
00796 
00797 
00798 
00799 /***********************************************************************************************
00800 
00801 >   virtual BOOL SGDisplayNode::GiveEventToMyChildren(SGEventType EventType,
00802                                                         void *EventInfo,
00803                                                         SGMiscInfo *MiscInfo)
00804 
00805     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00806     Created:    20/10/94
00807 
00808     Inputs:     EventType - An enumerated value describing what type of event is to be processed
00809 
00810                 EventInfo - A structure describing the event (may be NULL). The exact thing
00811                             pointed at by this pointer depends upon the event type:
00812 
00813                             MonoOn
00814                             Event               Thing EventInfo points at
00815                             SGEVENT_FORMAT      (SGFormatInfo *)
00816                             SGEVENT_REDRAW      (SGRedrawInfo *)
00817                             SGEVENT_MOUSECLICK  (SGMouseInfo *)
00818                             MonoOff
00819                             
00820                             Use the SGDisplayNode::Get[Format]Info() inlines to retrieve
00821                             this information for you (it does helpful ERROR3 checking for you)
00822 
00823                 MiscInfo - A structure containing any other relevant information.
00824                 This will always be non-NULL, and contain valid information.
00825 
00826     Outputs:    FormatInfo is updated as appropriate
00827 
00828     Returns:    TRUE if the event was handled successfully
00829                 FALSE if it was not
00830 
00831     Purpose:    Causes the entire subtree below this node to check their formatting, and
00832                 handle the given event. Once a node returns TRUE from its HandleEvent method,
00833                 the event will NOT be passed on.
00834 
00835     Notes:      This is used by derived classes to save them the work of
00836                 having to scan the tree in their own event code - each node just
00837                 redraws itself, and then passes the event along by calling this
00838                 function.
00839 
00840                 If this node is folded, then it is treated as having no children (as they
00841                 do not appear in the displayed list.
00842 
00843                 The traversal loops in this code can handle any nodes being deleted around
00844                 them, except for the "current node". However, in debug builds the "current
00845                 node" should refuse to be deleted while it is handling an event.
00846                 The traversal loops increment the "HandleEventCount" during calls to all the
00847                 node HandleEvent methods in order to be able to detect this situation (It's
00848                 done here to save doing it in all derived HandleEvent methods).
00849 
00850     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayRoot::HandleEvent;
00851                 SGDisplayGroup::HandleEvent; SGDisplayItem::HandleEvent
00852 
00853 ***********************************************************************************************/
00854 
00855 BOOL SGDisplayNode::GiveEventToMyChildren(SGEventType EventType, void *EventInfo,
00856                                              SGMiscInfo *MiscInfo)
00857 {
00858     BOOL Handled = FALSE;
00859     SGDisplayNode *Ptr = GetChild();
00860 //  SGDisplayNode *NextPtr = NULL;
00861 
00862     if (EventType == SGEVENT_FORMAT)
00863     {
00864         // We're formatting, so update FormatInfo as we go
00865         SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
00866 
00867         NewLine(FormatInfo, MiscInfo);      // When going down the tree, go to a new line
00868 
00869         if (Flags.Folded || Ptr == NULL)    // If folded, or no children, return
00870             return(FALSE);
00871 
00872         if (!Flags.Invisible)
00873             FormatInfo->IndentLevel++;      // Increment the indent level
00874 
00875         while (Ptr != NULL && !Handled)
00876         {
00877             // Call each child's handler in turn. We can cope with any child being deleted except
00878             // for the current one. To detect this, we increment HandleEventCOunt across the
00879             // call, which will trigger ERROR3's in the destructor if we attempt suicide!
00880             Ptr->Flags.HandleEventCount++;
00881             ERROR3IF(Ptr->Flags.HandleEventCount > 100,
00882                         "Rampant recursion or node corruption in GiveEventToMyChildren");
00883             if (Ptr->HandleEvent(EventType, EventInfo, MiscInfo))
00884                 Handled = TRUE;
00885             Ptr->Flags.HandleEventCount--;
00886 
00887             Ptr = Ptr->Next;
00888         }
00889 
00890         if (!Flags.Invisible)
00891             FormatInfo->IndentLevel--;      // Restore the indent level
00892 
00893         NewLine(FormatInfo, MiscInfo);      // When ascending back up the tree, go to a new line
00894 
00895         FormatInfo->LineHeight = UpTreeGap;
00896         NewLine(FormatInfo, MiscInfo);      // ... and add a small gap
00897     }
00898     else
00899     {
00900         if (Ptr == NULL)        // We have no children, so return
00901             return(FALSE);
00902 
00903         if (EventType != SGEVENT_BGFLUSH && Flags.Folded)
00904         {
00905             // If folded, return (but not if this is a FLUSH, which MUST go to ALL)
00906             return(FALSE);
00907         }
00908 
00909         while (Ptr != NULL && !Handled)
00910         {
00911             // Call each child's handler in turn. We can cope with any child being deleted except
00912             // for the current one. To detect this, we increment HandleEventCOunt across the
00913             // call, which will trigger ERROR3's in the destructor if we attempt suicide!
00914             Ptr->Flags.HandleEventCount++;
00915             ERROR3IF(Ptr->Flags.HandleEventCount > 100,
00916                         "Rampant recursion or node corruption in GiveEventToMyChildren");
00917             if (Ptr->HandleEvent(EventType, EventInfo, MiscInfo))
00918                 Handled = TRUE;
00919             Ptr->Flags.HandleEventCount--;
00920 
00921             Ptr = Ptr->Next;
00922         }
00923     }
00924 
00925     return(Handled);
00926 }
00927 
00928 
00929 
00930 /***********************************************************************************************
00931 
00932 >   virtual void SGDisplayNode::NewLine(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo)
00933 
00934     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00935     Created:    24/10/94
00936 
00937     Inputs:     FormatInfo - A structure containing all relevant information for items to
00938                 calculate their formatted positions in the display list
00939 
00940     Outputs:    FormatInfo is updated as appropriate
00941 
00942     Purpose:    Resets the formatting info structure to default values for the start of
00943                 the next 'line'.
00944 
00945     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayRoot::HandleEvent;
00946                 SGDisplayGroup::HandleEvent; SGDisplayItem::HandleEvent
00947 
00948 ***********************************************************************************************/
00949 
00950 void SGDisplayNode::NewLine(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo)
00951 {
00952     if (FormatInfo->LineHeight > 0)
00953     {
00954         FormatInfo->LineHeight = GridLock(MiscInfo, FormatInfo->LineHeight) +
00955                                     GridLock(MiscInfo, InterLineGap);
00956         FormatInfo->LinePos -= FormatInfo->LineHeight;
00957     }
00958     
00959     FormatInfo->LineHeight = 0;
00960     FormatInfo->AvailableWidth = MiscInfo->MaxWidth - (FormatInfo->IndentLevel * IndentWidth);
00961     FormatInfo->AvailableWidth = GridLock(MiscInfo, FormatInfo->AvailableWidth);
00962 }
00963 
00964 
00965 
00966 /***********************************************************************************************
00967 
00968 >   void SGDisplayNode::GridLockRect(SGMiscInfo *MiscInfo, DocRect *Rect)
00969 
00970     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
00971     Created:    28/10/94
00972 
00973     Inputs:     FormatInfo - A structure containing all relevant information for items to
00974                 calculate their formatted positions in the display list. NOTE this is also
00975                 an output!
00976 
00977                 Rect - A rectangle which needs to be locked to the pixel grid
00978 
00979     Outputs:    FormatInfo  - updated as appropriate
00980                 Rect        - modified as necessary to lock its points to the pixel grid
00981 
00982     Purpose:    Given a rectangle and the normal FormatInfo, this ensures that all points
00983                 of the rectangle are snapped onto a grid of the destination device pixels.
00984                 This ensures that aliasing effects, due to rounding errors when mapping
00985                 to the output pixel coordinates, do not occur.
00986 
00987     SeeAlso:    SGDisplayNode::GridLock; SGDisplayNode::DevicePixels
00988 
00989 ***********************************************************************************************/
00990 
00991 void SGDisplayNode::GridLockRect(SGMiscInfo *MiscInfo, DocRect *Rect)
00992 {
00993     Rect->lo.x = GridLock(MiscInfo, Rect->lo.x);
00994     Rect->lo.y = GridLock(MiscInfo, Rect->lo.y);
00995     Rect->hi.x = GridLock(MiscInfo, Rect->hi.x);
00996     Rect->hi.y = GridLock(MiscInfo, Rect->hi.y);
00997 }
00998 
00999 
01000 
01001 /***********************************************************************************************
01002 
01003 >   virtual void SGDisplayNode::CalculateFormatRect(SGFormatInfo *FormatInfo,
01004                                                     SGMiscInfo *MiscInfo,
01005                                                     INT32 ItemWidth, INT32 ItemHeight)
01006 
01007     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01008     Created:    24/10/94
01009 
01010     Inputs:     FormatInfo - A structure containing all relevant information for items to
01011                 calculate their formatted positions in the display list. NOTE this is also
01012                 an output! (As passed into HandleEvent for SGEVENT_FORMAT events)
01013 
01014                 MiscInfo - Miscellaneous information needed for formatting; as passed
01015                 into all HandleEvent calls
01016 
01017                 ItemWidth - The width of this item in millipoints, or 0 if this item
01018                 is 'infinite' width (fills the entire line). If there is not enough space left
01019                 on this line for the item, a new line is started.
01020 
01021                 ItemHeight - The height of this item in millipoints. Sibling items are
01022                 currently expected to have equal heights; although line formatting will cope
01023                 with different heights, redraw may miss strips below items for the time
01024                 being.
01025 
01026     Outputs:    FormatInfo  - updated as appropriate
01027                 Member variable FormatRect now contains the format rectangle
01028 
01029     Returns:    TRUE if the resulting rectangle overlaps RedrawInfo->Bounds (i.e. if the
01030                 node would need to redraw itself if handling a redraw request)
01031                 else FALSE if the node need not be redrawn
01032 
01033     Purpose:    Given current formatting information, generates the rectangle within which
01034                 this node and its subtree should be redrawn.
01035 
01036     Notes:      This relies upon the cached formatting information in this node being
01037                 correct - this function can only be called once per node during a pass
01038                 through the tree... the second call would give a different result.
01039 
01040                 If you do not use this function to do all the formatting work for you, then
01041                 you must remember to either updaet the 'FormatRect' member variable, or
01042                 override the 'GetFormatRect' member function to supply this information
01043                 to the caller properly.
01044 
01045     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayRoot::HandleEvent;
01046                 SGDisplayGroup::HandleEvent; SGDisplayItem::HandleEvent
01047 
01048 ***********************************************************************************************/
01049 
01050 void SGDisplayNode::CalculateFormatRect(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo,
01051                                         INT32 ItemWidth, INT32 ItemHeight)
01052 {
01053     ERROR3IF(FormatInfo == NULL || MiscInfo == NULL,
01054              "NULL parameter(s) passed to SGDisplayNode::CalculateFormatRect");
01055 
01056     if (Previous == NULL                        ||  // Is first child node, so go to a new line
01057         FormatInfo->AvailableWidth <= 0         ||  // No room left at all on this line
01058         FormatInfo->AvailableWidth < ItemWidth  ||  // Not enough room on the line for this item
01059         ItemWidth == 0)                             // This item is infinitely wide
01060     {
01061             NewLine(FormatInfo, MiscInfo);
01062     }
01063 
01064     // If items do not fill the entire width, add a 2-pixel gap at the right edge, so that
01065     // adjacent items have a small gap between them. (see below)
01066     if (ItemWidth != 0)
01067         ItemWidth += MiscInfo->PixelSize * 2;
01068 
01069     ItemWidth  = GridLock(MiscInfo, ItemWidth);
01070     ItemHeight = GridLock(MiscInfo, ItemHeight);
01071 
01072     // We must now have enough room for this item across the current line, so plonk it in!
01073     // Update the height of the current line
01074     if (FormatInfo->LineHeight < ItemHeight)
01075     {
01076         // if (LineHeight != 0) AWOOGA! Line height has increased! Must go back and fill in
01077         // the background colour below the previous items on this line, to ensure the entire
01078         // line is fully redrawn.
01079 
01080         ERROR3IF(FormatInfo->LineHeight != 0,
01081             "Sibling Display Item heights are not equal! Jason must upgrade the redraw code");
01082 
01083         FormatInfo->LineHeight = ItemHeight;
01084     }
01085 
01086     // Generate the position rectangle. Note that this may be wider than MaxWidth - it is the
01087     // actual rectangle the item has (so scaling into the returned rect will always work,
01088     // even if part of the rectangle is clipped out of view). However, if the item was
01089     // requested as infinite width, it is returned as MaxWidth.
01090     INT32 ActualWidth = (ItemWidth == 0) ? FormatInfo->AvailableWidth : ItemWidth;
01091 
01092     // Ensure it is clipped within the available window space
01093     if (ActualWidth > FormatInfo->AvailableWidth)
01094         ActualWidth = FormatInfo->AvailableWidth;
01095     
01096     DocRect OldFormatRect(FormatRect);
01097     FormatRect.lo.x = MiscInfo->MaxWidth - FormatInfo->AvailableWidth;
01098     FormatRect.hi.x = FormatRect.lo.x + ActualWidth;
01099     FormatRect.lo.y = FormatInfo->LinePos - FormatInfo->LineHeight;
01100     FormatRect.hi.y = FormatInfo->LinePos;
01101 
01102     // If items do not fill the entire width, add a 2-pixel gap at the right edge, so that
01103     // adjacent items have a small gap between them. (see above)
01104     if (ActualWidth < FormatInfo->AvailableWidth)
01105         FormatRect.hi.x -= MiscInfo->PixelSize * 2;
01106 
01107     GridLockRect(MiscInfo, &FormatRect);    // And ensure it is locked onto the grid
01108 
01109     // If this node is no longer in the same position as it was last time we formatted,
01110     // then accumulate its Y bounds into the 'InvalidBounds' rect, to allow the gallery
01111     // to redraw only those regions of the list which are invalid
01112     if (FormatInfo->AccumulateBounds)
01113     {
01114         if (OldFormatRect != FormatRect)
01115         {
01116             FormatInfo->LastInvalidNode = this;     // We are the last node found to have invalid bounds
01117 
01118             if (FormatInfo->InvalidBounds.hi.y > 0) // If we haven't found an invalid item before
01119                 FormatInfo->InvalidBounds.hi.y = FormatRect.hi.y;
01120 
01121             if (FormatInfo->InvalidBounds.lo.y > FormatRect.lo.y)
01122                 FormatInfo->InvalidBounds.lo.y = FormatRect.lo.y;
01123         }
01124         else
01125         {
01126             if (FormatInfo->LastInvalidNode != NULL)
01127             {
01128                 // The immediately previous node had invalid bounds. If we lie below the current
01129                 // invalid bounds, then we extend them to touch the top of us, to include any gap
01130                 // between them and us.
01131 
01132                 ERROR3IF(FormatInfo->InvalidBounds.hi.y > 0,
01133                             "Gallery display formatting error - LastInvalidNode should be NULL "
01134                             "if there haven't been any invalid nodes yet");
01135 
01136                 if (FormatInfo->InvalidBounds.lo.y > FormatRect.hi.y)
01137                     FormatInfo->InvalidBounds.lo.y = FormatRect.hi.y;
01138 
01139                 FormatInfo->LastInvalidNode = NULL;     // Reset LastInvalid node so that the next node doesn't extend!
01140             }
01141         }
01142     }
01143 
01144 //  if (Flags.Invisible)
01145 //  {
01146 //      FormatRect.hi.x = FormatRect.lo.x;
01147 //      FormatRect.hi.y = FormatRect.lo.y;
01148 //  }
01149 
01150     // Update the free width on the end of this line. Note that this may now be 0 or -ve,
01151     // but this will be sorted out on the next call to this method, in the clauses above.
01152 //  if (!Flags.Invisible)
01153 //  {
01154         FormatInfo->AvailableWidth -= ActualWidth;
01155         GridLock(MiscInfo, FormatInfo->AvailableWidth);
01156 //  }
01157 }
01158 
01159 
01160 
01161 /***********************************************************************************************
01162 
01163 >   virtual BOOL SGDisplayNode::SetFoldedState(BOOL NewState, BOOL ForceRedraw = TRUE)
01164 
01165     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01166     Created:    15/4/95
01167 
01168     Inputs:     NewState - TRUE to fold, FALSE to unfold
01169 
01170     Returns:    TRUE if the new state is different from the old state (if anything has changed)
01171 
01172     Purpose:    Folds or unfolds a display node
01173 
01174     Notes:      This base-class implementation gives an ERROR3 - use folding only on groups
01175 
01176 ***********************************************************************************************/
01177 
01178 BOOL SGDisplayNode::SetFoldedState(BOOL NewState, BOOL ForceRedraw)
01179 {
01180     ERROR3("Folding can only be applied to groups!");
01181     return(FALSE);
01182 }
01183 
01184 
01185 
01186 /***********************************************************************************************
01187 
01188 >   void SGDisplayNode::SetSelected(BOOL IsSelected = TRUE)
01189 
01190     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01191     Created:    28/10/94
01192 
01193     Inputs:     IsSelected - TRUE to select, FALSE to deselect this item
01194 
01195     Purpose:    Sets the selection state of a SuperGallery Display Item tree node.
01196                 Note that this does not cause a redraw of the gallery list box or anything.
01197                 After setting the state(s) of item(s) you must therefore redraw them.
01198 
01199     Notes:      An ENSURE will be generated by any node which cannot be selected.
01200                 (Derived classes wishing to allow selection state changes should set
01201                 Flags.CanSelect)
01202 
01203     SeeAlso:    SGDisplayItem::SetSelected; SGDisplayNode::IsSelected
01204 
01205 ***********************************************************************************************/
01206 
01207 void SGDisplayNode::SetSelected(BOOL IsSelected)
01208 {
01209     ERROR3IF(!Flags.CanSelect, "Base class SGDisplayNode::SetSelected called! I'm not a selectable ITEM!");
01210 
01211     if (Flags.CanSelect && Flags.Selected != (UINT32)IsSelected)
01212     {
01213         Flags.Selected = (UINT32) IsSelected;
01214         ForceRedrawOfMyself();
01215     }
01216 }
01217 
01218 
01219 
01220 /***********************************************************************************************
01221 
01222 >   virtual BOOL SGDisplayNode::HandleEvent(SGEventType EventType, void *EventInfo,
01223                                             SGMiscInfo *MiscInfo);
01224 
01225     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01226     Created:    1/4/95  (was pure virtual before then)
01227 
01228     Inputs:     EventType - Indicates the event type to be handled (see SGEventType)
01229 
01230                 EventInfo - NULL, or points at a structure containing useful info
01231                 for processing this specific event. (See SGEventInfo)
01232 
01233                 MiscInfo - Always supplied. Points at an SGMiscInfo struct which
01234                 contains miscellanous useful information.
01235 
01236     Purpose:    Handles a generic display tree event. Events of interest trigger
01237                 specific actions. 
01238 
01239                 Generally overridden to provide redraw, mouse click, etc functionality.
01240                 Overridden methos should call the base class for any event types
01241                 which are unknown or which they do not specifically want to handle.
01242 
01243                 For a description of how to use this, see the documentation, or
01244                 take a look at other SGDisplay* types and galleries for example code.
01245 
01246                 The base class doesn't do much, but does handle background redraw, and
01247                 will pass all unhandled events on to its children (if any)
01248 
01249     Documentation:  docs\howtouse\sgallery.doc
01250 
01251 ***********************************************************************************************/
01252 
01253 BOOL SGDisplayNode::HandleEvent(SGEventType EventType, void *EventInfo, SGMiscInfo *MiscInfo)
01254 {
01255     switch(EventType)
01256     {
01257 // Now handled by recursive DoBGRedrawPass method
01258 //      case SGEVENT_BGREDRAW:
01259 //          // Redraw in the background if necessary. This will return TRUE if it claims
01260 //          // the event (the first one that successfully background renders will claim
01261 //          // the event)
01262 //          if (DoBGRedraw(MiscInfo))
01263 //              return(TRUE);
01264 //          break;
01265 
01266         case SGEVENT_BGFLUSH:
01267             DeregisterForBGRedraw();
01268             break;              // And drop through to flush all children
01269         default:
01270             break;
01271     }
01272 
01273     // And pass all events on to my children
01274     return(GiveEventToMyChildren(EventType, EventInfo, MiscInfo));
01275 }
01276 
01277 
01278 
01279 /***********************************************************************************************
01280 
01281 >   virtual void SGDisplayNode::DragWasReallyAClick(SGMouseInfo *MouseInfo,
01282                                                     SGMiscInfo *MiscInfo)
01283 
01284     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01285     Created:    15/3/95
01286 
01287     Inputs:     MouseInfo - The mouse info passed to the original click handler
01288                 MiscInfo - The misc info passed to the original click handler
01289 
01290     Purpose:    Handles a mouse click event. This is a callback function - drags of
01291                 items from galleries will call this function back if the drag turns
01292                 out to just be a click.
01293 
01294     Notes:      The base class method takes no action whatsoever. Derived classes
01295                 should override this method to do something useful.
01296 
01297                 For a description of how to use this, see the documentation, or
01298                 take a look at other galleries for example code.
01299 
01300     Documentation:  docs\howtouse\sgallery.doc
01301 
01302     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayNode::DefaultDragHandler
01303 
01304 ***********************************************************************************************/
01305 
01306 void SGDisplayNode::DragWasReallyAClick(SGMouseInfo *MouseInfo, SGMiscInfo *MiscInfo)
01307 {
01308     // The base class does nothing
01309 }
01310 
01311 
01312 
01313 /***********************************************************************************************
01314 
01315 >   virtual void SGDisplayNode::GetFormatRect(DocRect *FormatRect)
01316 
01317     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01318     Created:    16/1/95
01319 
01320     Outputs:    FormatRect - Returned containing the format rectangle where this item last
01321                 positioned itself - make sure a formatting event has gone around before
01322                 calling this method, so that you get a meaningful result
01323 
01324     Purpose:    Determines where this item wants to redraw itself within the logical window
01325                 DocCoord coordinates. If this is not a visible node type, or if someone
01326                 neglected to cache the value in overridden methods, returns (0,0,0,0)
01327 
01328 ***********************************************************************************************/
01329 
01330 void SGDisplayNode::GetFormatRect(DocRect *ResultFormatRect)
01331 {
01332     if (ResultFormatRect != NULL)
01333         *ResultFormatRect = FormatRect;
01334 }
01335 
01336 
01337 
01338 /***********************************************************************************************
01339 
01340 >   BOOL SGDisplayNode::IMustRedraw(SGRedrawInfo *RedrawInfo)
01341 
01342     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01343     Created:    31/10/94
01344 
01345     Inputs:     RedrawInfo - The redraw information indicating the redraw Bounds
01346 
01347     Returns:    TRUE if this item should redraw itself. FALSE if it need not bother
01348 
01349     Purpose:    To determine if a given item needs to be redrawn. This is done by 
01350                 determining if its bounding rectangle ('FormatRect') overlaps the
01351                 bounding rectangle of the area to be redrawn as specified by
01352                 RedrawInfo->Bounds.
01353     
01354     Notes:      Obviously, if FormatRect has not been correctly calculated/cached,
01355                 this method will give spurious results!
01356 
01357     SeeAlso:    SGDisplayNode::CalculateFormatRect
01358 
01359 ***********************************************************************************************/
01360 
01361 BOOL SGDisplayNode::IMustRedraw(SGRedrawInfo *RedrawInfo)
01362 {
01363     // No redraw info?!
01364     if (RedrawInfo == NULL)
01365         return(TRUE);
01366 
01367     // Determine if the rect overlaps RedrawInfo->Bounds, returning TRUE if it does
01368     return (FormatRect.lo.y <= RedrawInfo->Bounds.hi.y &&
01369             FormatRect.hi.y >= RedrawInfo->Bounds.lo.y &&
01370             FormatRect.lo.x <= RedrawInfo->Bounds.hi.x &&
01371             FormatRect.hi.x >= RedrawInfo->Bounds.lo.x);
01372 }
01373 
01374 
01375 
01376 /***********************************************************************************************
01377 
01378 >   virtual void SGDisplayNode::ForceRedrawOfMyself(void)
01379 
01380     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01381     Created:    17/1/95
01382 
01383     Purpose:    Uses the cached FormatRect to force-redraw the appropriate part of the 
01384                 SuperGallery display window to cause myself (only) to be redrawn.
01385 
01386     Notes:      Before redrawing, this scans up the tree to see if any parent is Folded.
01387                 If this is the case, it returns without doing anything, as this item must
01388                 therefore be hidden in the fold.
01389                 By default, the background is erased. If bEraseBkg is FALSE, the backgound 
01390                 will not be altered before redrawing. (Adrian 10/05/97) 
01391 
01392 
01393     SeeAlso:    SuperGallery::ForceRedrawOfArea
01394 
01395 ***********************************************************************************************/
01396 
01397 void SGDisplayNode::ForceRedrawOfMyself(BOOL bEraseBkg)
01398 {
01399 //  if (!Flags.Invisible)
01400 //  {
01401         SuperGallery *ParentGallery = GetParentGallery();
01402         if (ParentGallery != NULL)
01403         {
01404             // Before redrawing, search up the tree for any nodes which are folded - if there
01405             // are any, then I cannot be visible, so should not redraw!
01406 
01407             SGDisplayNode *Ptr = GetParent();
01408             while (Ptr != NULL)
01409             {
01410                 if (Ptr->Flags.Folded)
01411                     return;
01412 
01413                 Ptr = Ptr->GetParent();
01414             }
01415 
01416             // Not hidden in a fold, so redraw my rectangle and set the redraw mode
01417             BkgEraseMode = bEraseBkg ? TRUE : FALSE;
01418             ParentGallery->ForceRedrawOfArea(&FormatRect);
01419         }
01420 //  }
01421 }
01422 
01423 
01424 
01425 /***********************************************************************************************
01426 
01427 >   virtual void SGDisplayNode::ForceRedrawOfMyselfAndChildren(void)
01428 
01429     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01430     Created:    17/1/95
01431 
01432     Purpose:    Uses the cached FormatRect to force-redraw the appropriate part of the 
01433                 SuperGallery display window to cause myself to be redrawn - derived
01434                 classes may override this to also redraw their children (SGDisplayNode)
01435 
01436 ***********************************************************************************************/
01437 
01438 void SGDisplayNode::ForceRedrawOfMyselfAndChildren(void)
01439 {
01440 //  if (!Flags.Invisible)
01441 //  {
01442         ForceRedrawOfMyself();
01443 //  }
01444 }
01445 
01446 
01447 
01448 /***********************************************************************************************
01449 
01450 >   virtual void SGDisplayNode::RegisterForBGRedraw(void);
01451 
01452     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01453     Created:    1/4/95
01454 
01455     Purpose:    Called by derived classes to register themselves for background redraw.
01456 
01457                 Must be called every time you want a background redraw to occur. May be
01458                 called multiple times before the redraw occurs (it ignores repeated
01459                 calls)
01460 
01461                 DO NOT call this method directly - see ShouldIDrawForeground()
01462 
01463     Notes:      DO NOT TOUCH the Flags.RedrawPending member variable directly!
01464 
01465     SeeAlso:    SGDisplayNode::DoBGRedraw; SGDisplayNode::ShouldIDrawForeground
01466 
01467 ***********************************************************************************************/
01468 
01469 void SGDisplayNode::RegisterForBGRedraw(void)
01470 {
01471     if (!Flags.RedrawPending)
01472     {
01473         SuperGallery *ParentGallery = GetParentGallery();
01474 
01475         if (ParentGallery != NULL)
01476         {
01477             Flags.RedrawPending = TRUE;
01478             ParentGallery->IncrementPendingRedraws();
01479         }
01480     }
01481 }
01482 
01483 
01484 
01485 /***********************************************************************************************
01486 
01487 >   virtual BOOL SGDisplayNode::DoBGRedraw(SGMiscInfo *MiscInfo);
01488 
01489     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01490     Created:    1/4/95
01491 
01492     Inputs:     MiscInfo - the usual
01493 
01494     Returns:    TRUE to claim the redraw event, FALSE to not claim it
01495 
01496     Purpose:    Forces an immediate redraw of a given node, if it is pending for
01497                 background redraw.
01498 
01499                 This is used by the base-class system to render items waiting for
01500                 BG redraw, but can also be used by derived classes to force a given
01501                 item to be visible immediately. e.g. This might be done if your item is
01502                 clicked while waiting to be redrawn.
01503     
01504                 DO NOT call this method directly - see ShouldIDrawForeground()
01505 
01506     Notes:      DO NOT TOUCH the Flags.RedrawPending member variable directly!
01507 
01508     SeeAlso:    SGDisplayNode::RegisterForBGRedraw; SGDisplayNode::ShouldIDrawForeground
01509 
01510 ***********************************************************************************************/
01511 
01512 BOOL SGDisplayNode::DoBGRedraw(SGMiscInfo *MiscInfo)
01513 {
01514     BGRenderClaimed = FALSE;            
01515 
01516     if (Flags.RedrawPending /*&& !Flags.Invisible*/)
01517     {
01518         SuperGallery *ParentGallery = GetParentGallery();
01519 
01520         if (ParentGallery != NULL)
01521         {
01522             // Interlock the redraw with the drag manager to make sure the screen isn't
01523             // screwed up by us redrawing over a solid drag
01524             DocRect KernelRect(FormatRect);
01525             BOOL NeedInterlock = ParentGallery->ConvertFromVirtualCoords(MiscInfo, &KernelRect);
01526 
01527             if (NeedInterlock)
01528             {
01529                 DragManagerOp::RedrawStarting(ParentGallery->WindowID,
01530                                             ParentGallery->GetListGadgetID(),
01531                                             &KernelRect);
01532             }
01533 
01534             CurrentBGRenderNode = this;
01535             ForceRedrawOfMyself();              // Invalidate myself
01536             ParentGallery->PaintListNow();      // And immediately redraw
01537             CurrentBGRenderNode = NULL;
01538 
01539             if (NeedInterlock)
01540                 DragManagerOp::RedrawFinished();
01541 
01542         }
01543     }
01544 
01545     return(BGRenderClaimed);
01546 }
01547 
01548 
01549 
01550 /***********************************************************************************************
01551 
01552 >   BOOL SGDisplayNode::ShouldIDrawForeground(BOOL ForceForeground)
01553 
01554     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01555     Created:    6/5/95
01556 
01557     Inputs:     ForceForeground - TRUE if you want to force foreground redrawing (e.g. if you
01558                 know your thumbnail is cached you are better off just drawing it immediately),
01559                 or FALSE to allow the operation to be backgrounded.
01560 
01561     Returns:    TRUE if you should draw in the foreground (i.e. draw the item to completion),
01562                 FALSE if you should draw the background stuff (i.e. draw a grey box instead
01563                 of a thumbnail) - if FALSE, you'll be called back to complete the redraw later.
01564 
01565     Purpose:    Call this method in derived class redraw methods. Your code should go like
01566                 this:
01567                 MonoOn
01568                     if (ShouldIDrawForeground(ByGollyIveGotAThumbnailCachedAlready)
01569                         DrawTheItemFully();
01570                     else
01571                         DrawAGreyBox();
01572                 MonoOff
01573 
01574                 This system is full automatic, and is all you have to do to get BG redraw
01575                 to work. Note that foreground redraw will be expected when (a) ForceForeground
01576                 is TRUE, (b) the item is selected, or (c) we are doing the second BG redraw pass.
01577                 Background redraw will generally be used in all other circumstances.
01578 
01579     SeeAlso:    SGDisplayNode::RegisterForBGRedraw
01580 
01581 ***********************************************************************************************/
01582 
01583 BOOL SGDisplayNode::ShouldIDrawForeground(BOOL ForceForeground)
01584 {
01585     BOOL FG = FALSE;
01586 
01587     if (ForceForeground || Flags.Selected)
01588         FG = TRUE;
01589     else
01590     {
01591         if (CurrentBGRenderNode == this)
01592             FG = TRUE;
01593     }
01594 
01595     // And make sure our current idea of the state is up to date
01596     if (FG)
01597     {
01598         DeregisterForBGRedraw();
01599         BGRenderClaimed = TRUE;     // Flag the fact that someone has FG rendered
01600     }
01601     else
01602         RegisterForBGRedraw();
01603 
01604     return(FG);
01605 }
01606 
01607 
01608 
01609 /***********************************************************************************************
01610 
01611 >   virtual void SGDisplayNode::DeregisterForBGRedraw(void);
01612 
01613     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01614     Created:    1/4/95
01615 
01616     Purpose:    Ensures that this node is not pending background redraw (resets it)
01617                 This is called in response to SGEVENT_BGFLUSH, when flushing BG redraws
01618     
01619                 DO NOT call this method directly - see ShouldIDrawForeground()
01620 
01621     Notes:      DO NOT TOUCH the Flags.RedrawPending member variable directly!
01622 
01623     SeeAlso:    SGDisplayNode::RegisterForBGRedraw; SGDisplayNode::ShouldIDrawForeground
01624 
01625 ***********************************************************************************************/
01626 
01627 void SGDisplayNode::DeregisterForBGRedraw(void)
01628 {
01629     if (Flags.RedrawPending /*&& !Flags.Invisible*/)
01630     {
01631         SuperGallery *ParentGallery = GetParentGallery();
01632 
01633         if (ParentGallery != NULL)
01634             ParentGallery->DecrementPendingRedraws();
01635 
01636         Flags.RedrawPending = FALSE;        // And finally, turn off pending flag
01637     }
01638 }
01639 
01640 
01641 
01642 /***********************************************************************************************
01643 
01644 >   SGDisplayNode *SGDisplayNode::DoBGRedrawPass(SGMiscInfo *MiscInfo)
01645 
01646     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01647     Created:    16/5/95
01648 
01649     Inputs:     MiscInfo - The usual gallery MiscInfo struct
01650 
01651     Returns:    NULL, or a pointer to the last rendered node.
01652 
01653     Purpose:    Applies a background rendering pass to the display tree. This will scan the
01654                 tree from this node onwards looking for a node to background render.
01655 
01656                 Once a node has been background rendered, this method will return the node
01657                 which was rendered - SuperGalleries store this in LastBackgroundNode, and will
01658                 call this method again, using that pointer as a point to start scanning from.
01659 
01660                 This makes scanning for background rendering nodes generally very much more
01661                 efficient than searching the entire tree each time, as usually bg render nodes
01662                 occur in sequential runs in the tree.
01663 
01664     Notes:      The SGDisplayNode RemoveFromTree method ensures that the parent gallery does
01665                 not use this pointer after the node is deleted, by calling
01666                 SuperGallery::SetLastBackgroundNode(NULL) if necessary - if the system is
01667                 changed, make sure that it is updated to ensure that the pointer is not called
01668                 upon after the item it references has been deleted.
01669 
01670                 ONLY LEAF NODES are guaranteed to background render with this scheme
01671 
01672     SeeAlso:    SGDisplayNode::RegisterForBGRedraw; SGDisplayNode::ShouldIDrawForeground;
01673                 SuperGallery::SetLastBackgroundNode; SGDisplayNode::RemoveFromTree
01674 
01675 ***********************************************************************************************/
01676 
01677 SGDisplayNode *SGDisplayNode::DoBGRedrawPass(SGMiscInfo *MiscInfo)
01678 {
01679     // If I'm not a leaf node, scan down the tree until the first leaf child is found
01680     if (GetChild() != NULL)
01681         return(GetChild()->DoBGRedrawPass(MiscInfo));
01682 
01683     // This is a bit nasty... The trouble is, if a group is virtualised, GetChild() will
01684     // return NULL and we won't go any further. In such situations we now execute this
01685     // bit of code here which will pass the rendering onto the next group...
01686     if (this->IsKindOf(CC_RUNTIME_CLASS(SGDisplayGroup)))
01687     {
01688         if(GetNext() != NULL)
01689             return(GetNext()->DoBGRedrawPass(MiscInfo));
01690     }
01691 
01692     // Search (including ourself in the search) for the next node which needs to render
01693     // If we find one, we render it and return immediately, returning the pointer to that
01694     // node as the place to start the next BG rendering pass.
01695     SGDisplayNode *Ptr = this;
01696     while (Ptr != NULL)
01697     {
01698         if (Ptr->DoBGRedraw(MiscInfo))
01699             return(Ptr);
01700 
01701         Ptr = Ptr->GetNext();
01702     }
01703 
01704     // We failed to find a sibling that needs to redraw, so go skip on to the next group
01705     // To save the stack, we search until we find a likely candidate (a group with kids)
01706     Ptr = Parent;
01707     while (Ptr != NULL)
01708     {
01709         // Go on to the next sibling of our parent
01710         Ptr = Ptr->GetNext();
01711 
01712         if (Ptr != NULL && (Ptr->Flags.RedrawPending || Ptr->GetChild() != NULL))
01713             return(Ptr->DoBGRedrawPass(MiscInfo));
01714     }
01715 
01716     // We didn't find anything to draw. On the next pass, we'll start from the root of
01717     // the tree again (we pass back NULL, and the SuperGallery starts from scratch)
01718     return(NULL);
01719 }
01720 
01721 
01722 
01723 /***********************************************************************************************
01724 
01725 >   virtual SGDisplayNode *SGDisplayNode::FindSubtree(SuperGallery *ParentGal,
01726                                                     Document *ParentDoc, Library *ParentLib);
01727 
01728     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01729     Created:    26/10/94
01730 
01731     Inputs:     ParentGal - The parent gallery of the node to find
01732                 ParentDoc - The parent document of the node to find
01733                 ParentLib - The parent library of the node to find
01734 
01735     Returns:    NULL, or the found node
01736 
01737     Purpose:    Searches this node and its subtree for any SGDisplayGroup nodes which
01738                 have parent pointers which exactly match the input parameters. This is used
01739                 to find the subtree for a given document or library.
01740 
01741     SeeAlso:    SGDisplayGroup::SGDisplayGroup
01742 
01743 ***********************************************************************************************/
01744 
01745 SGDisplayGroup *SGDisplayNode::FindSubtree(SuperGallery *ParentGal,
01746                                             Document *ParentDoc, Library *ParentLib)
01747 {
01748     SGDisplayNode  *Ptr = GetChild();
01749     SGDisplayGroup *Result;
01750 
01751     while (Ptr != NULL)
01752     {
01753         if (Ptr->IsKindOf(CC_RUNTIME_CLASS(SGDisplayGroup)))    // Search immediate children
01754         {
01755             Result = (SGDisplayGroup *) Ptr;
01756             if (Result->GetParentGallery()  == ParentGal &&
01757                 Result->GetParentDocument() == ParentDoc &&
01758                 Result->GetParentLibrary()  == ParentLib)
01759                 return(Result);
01760         }
01761 
01762                                                                 // Recurse down subtrees
01763         Result = Ptr->FindSubtree(ParentGal, ParentDoc, ParentLib);
01764         if (Result != NULL)
01765             return(Result);
01766 
01767         Ptr = Ptr->GetNext();                                   // No luck - try the next child
01768     }
01769 
01770     return(NULL);
01771 }
01772 
01773 
01774 
01775 /***********************************************************************************************
01776 
01777 >   virtual INT32 SGDisplayNode::CompareTo(SGDisplayNode *Other, INT32 SortKey)
01778 
01779     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01780     Created:    26/10/94
01781 
01782     Inputs:     Other - the node to compare this node to
01783                 SortKey - An integer identifying how to compare the items
01784                     0 = No sorting (always returns 0)
01785                     1 = Sort-by-name
01786                     Other values will return 0, unless the derived class overrides this
01787                     method in order to provide other sort modes.
01788 
01789     Returns:    negative (I am lesser), 0 (we are equal), or positive (I am greater)
01790 
01791     Purpose:    Compares this node to the 'other' node, to determine their relative positions
01792                 in the display tree. Returns a value which usually indicates that the other
01793                 node should be inserted before (-1, or 0) or after (+1) this item.
01794 
01795     SeeAlso:    SGDisplayNode::AddItem
01796 
01797 ***********************************************************************************************/
01798 
01799 INT32 SGDisplayNode::CompareTo(SGDisplayNode *Other, INT32 SortKey)
01800 {
01801     ERROR3IF(Other == NULL, "Illegal NULL parameter");
01802 
01803     switch (SortKey)
01804     {
01805         case SGSORTKEY_BYNAME:
01806             {
01807                 String_256 MyName;
01808                 String_256 ItsName;
01809 
01810                 GetNameText(&MyName);
01811                 Other->GetNameText(&ItsName);
01812 
01813                 return(MyName.CompareTo(ItsName));
01814             }
01815             break;
01816     }
01817 
01818     // No sorting (SGSORTKEY_NONE - 0), or 
01819     return(0);
01820 }
01821 
01822 
01823 
01824 /********************************************************************************************
01825 
01826 >   virtual void SGDisplayNode::GetNameText(String_256 *Result)
01827 
01828     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01829     Created:    9/3/95
01830 
01831     Outputs:    On exit, the string pointed at by Result will contain either a blank
01832                 string, or the name text associated with this item (if any)
01833 
01834     Purpose:    To determine a name string for this node. Generally, this is used for
01835                 a simple mechanism which searches for display items whose names match
01836                 given search parameters in some way. It is also used in libraries to
01837                 provide default redraw methods.
01838 
01839     Notes:      The base class returns a blank string.
01840                 If you can provide a better name string, then override the base class
01841                 method to do so.
01842 
01843     SeeAlso:    SGDisplayNode::GetFullInfoText
01844 
01845 ********************************************************************************************/
01846 
01847 void SGDisplayNode::GetNameText(String_256 *Result)
01848 {
01849     ERROR3IF(Result == NULL, "Illegal NULL param");
01850     if(Result == NULL) return;
01851 
01852     *Result = TEXT("");
01853 }
01854 
01855 
01856 
01857 /********************************************************************************************
01858 
01859 >   virtual void SGDisplayNode::GetFullInfoText(String_256 *Result)
01860 
01861     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01862     Created:    9/3/95
01863 
01864     Outputs:    On exit, the string pointed at by Result will contain either a blank
01865                 string, or the full-information text associated with this item (if any)
01866                 (NOTE that the FullInfo string does NOT include the name!)
01867 
01868     Purpose:    To determine a full-info string for this node. Generally, this is used for
01869                 a simple mechanism which searches for display items whose info matches
01870                 given search parameters in some way. It is also used in libraries to
01871                 provide default redraw methods.
01872 
01873     Notes:      The base class returns a blank string
01874 
01875                 If you can provide a better full-info string, then override the base class
01876                 method to do so.
01877 
01878     SeeAlso:    SGDisplayNode::GetNameText
01879 
01880 ********************************************************************************************/
01881 
01882 void SGDisplayNode::GetFullInfoText(String_256 *Result)
01883 {
01884     ERROR3IF(Result == NULL, "Illegal NULL param");
01885     if(Result == NULL) return;
01886 
01887     *Result = TEXT("");
01888 }
01889 
01890 
01891 /********************************************************************************************
01892 
01893 >   virtual void SGDisplayNode::GetKeyWords(String_256 *Result)
01894 
01895     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
01896     Created:    30/3/95
01897 
01898     Outputs:    On exit, the string pointed at by Result will contain either a blank
01899                 string, or a list of | seperated keywords associated with the item
01900                 
01901     Purpose:    To determine the keywords for this node. Generally, this is used for
01902                 a simple searching mechanism.
01903                 
01904     Notes:      The base class returns a blank string.
01905                 If you can provide a better name string, then override the base class
01906                 method to do so.
01907 
01908     SeeAlso:    SGDisplayNode::GetFullInfoText
01909 
01910 ********************************************************************************************/
01911 
01912 void SGDisplayNode::GetKeyWords(String_256 *Result)
01913 {
01914     ERROR3IF(Result == NULL, "Illegal NULL param");
01915     if(Result == NULL) return;
01916 
01917     *Result = TEXT("");
01918 }
01919 
01920 
01921 
01922 /********************************************************************************************
01923 
01924 >   virtual BOOL SGDisplayNode::GetBubbleHelp(DocCoord *MousePos, String_256 *Result)
01925 
01926     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01927     Created:    16/4/95
01928 
01929     Inputs:     MousePos - The current mouse position. This will generally be expected
01930                 to lie inside this item's FormatRect. With it, this item can provide
01931                 help on specific areas of an item.
01932 
01933     Outputs:    On exit, if the return value is TRUE, the string pointed at by Result
01934                 will contain a bubble help string for this item
01935 
01936     Returns:    TRUE if it filled in the string, FALSE if it did not
01937                 
01938     Purpose:    Called by the parent gallery when bubble help is needed. The parent
01939                 gallery will do a hit test to determine which node contains the pointer,
01940                 and will then ask that node to supply bubble/status-line help.
01941                 
01942     Notes:      The base class returns FALSE (i.e. provides no help)
01943                 If you can provide help, then override the base class method to do so.
01944 
01945     SeeAlso:    SGDisplayNode::GetStatusLineHelp
01946 
01947 ********************************************************************************************/
01948 
01949 BOOL SGDisplayNode::GetBubbleHelp(DocCoord *MousePos, String_256 *Result)
01950 {
01951     ERROR3IF(MousePos == NULL || Result == NULL, "Invalid NULL params");
01952     return(FALSE);
01953 }
01954 
01955 
01956     
01957 /********************************************************************************************
01958 
01959 >   virtual BOOL SGDisplayNode::GetStatusLineHelp(DocCoord *MousePos, String_256 *Result)
01960 
01961     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01962     Created:    16/4/95
01963 
01964     Inputs:     MousePos - The current mouse position. This will generally be expected
01965                 to lie inside this item's FormatRect. With it, this item can provide
01966                 help on specific areas of an item.
01967 
01968     Outputs:    On exit, if the return value is TRUE, the string pointed at by Result
01969                 will contain a status line help string for this item
01970 
01971     Returns:    TRUE if it filled in the string, FALSE if it did not
01972                 
01973     Purpose:    Called by the parent gallery when status line help is needed. The parent
01974                 gallery will do a hit test to determine which node contains the pointer,
01975                 and will then ask that node to supply bubble/status-line help.
01976                 
01977     Notes:      The base class returns FALSE (i.e. provides no help)
01978                 If you can provide help, then override the base class method to do so.
01979 
01980     SeeAlso:    SGDisplayNode::GetBubbleHelp
01981 
01982 ********************************************************************************************/
01983 
01984 BOOL SGDisplayNode::GetStatusLineHelp(DocCoord *MousePos, String_256 *Result)
01985 {
01986     ERROR3IF(MousePos == NULL || Result == NULL, "Invalid NULL params");
01987     return(FALSE);
01988 }
01989 
01990 
01991 
01992 /********************************************************************************************
01993 
01994 >   virtual void SGDisplayNode::SelectItems(BOOL SelectThem, BOOL Exclusive = FALSE,
01995                         Document *ParentDocument = NULL, Library *ParentLibrary = NULL)
01996 
01997     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
01998     Created:    20/1/95
01999 
02000     Inputs:     SelectThem - TRUE to select the given items, FALSE to deselect them
02001 
02002                 Exclusive  - TRUE to apply this action to all items *outside* the given
02003                 range, FALSE to apply it to all items *inside* the range.
02004 
02005                 Document - NULL, or the document which defines the range of items to affect
02006 
02007                 Library - NULL, or the library which defines the range of items to affect
02008 
02009     Purpose:    To select/deselect groups of display items in this Gallery display.
02010                 All items whose state changes will force redraw themselves
02011 
02012                 If selecting items, all groups will be deselected
02013 
02014                 Do not call this method - use the SuperGallery version
02015 
02016     Notes:      To select all items in a range, and deselect all items outside the range,
02017                 you need to use 2 calls to this method.
02018 
02019                 To select/deselect all items in the display, pass FALSE, NULL, NULL to
02020                 the last 3 parameters. (If Doc/Lib are both NULL, 'Exclusive' has no effect)
02021 
02022                 This base-class method does a simple thing: If this node is selectable
02023                 (not invisible) then it sets its own selection state according to the
02024                 'SelectThem' flag, and then passes the request on to its children.
02025                 Any node type which understands how to cope with the last 3 parameters MUST
02026                 override this method to provide the appropriate handling.
02027                 (This default handling suffices, however, for root and leaf nodes)
02028 
02029     SeeAlso:    SuperGallery::SelectItems; SGDisplayGroup::SelectItems
02030 
02031 ********************************************************************************************/
02032 
02033 void SGDisplayNode::SelectItems(BOOL SelectThem, BOOL Exclusive,
02034                                 Document *ParentDocument, Library *ParentLibrary)
02035 {
02036     // First, if I'm a selectable node, {de}select myself as appropriate to the request
02037     if (!Flags.Invisible && Flags.CanSelect)
02038         SetSelected(SelectThem);
02039 
02040     // Now, pass the selection request on to my children
02041     SGDisplayNode *Ptr = GetChild();
02042 
02043     while (Ptr != NULL)
02044     {
02045         Ptr->SelectItems(SelectThem, Exclusive, ParentDocument, ParentLibrary);
02046         Ptr = Ptr->GetNext();
02047     }
02048 }
02049 
02050 
02051 
02052 /********************************************************************************************
02053 
02054 >   virtual void SGDisplayNode::SelectGroups(BOOL SelectThem, BOOL Exclusive = FALSE,
02055                         Document *ParentDocument = NULL, Library *ParentLibrary = NULL)
02056 
02057     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02058     Created:    14/5/95
02059 
02060     Inputs:     SelectThem - TRUE to select the given groups, FALSE to deselect them
02061 
02062                 Exclusive  - TRUE to apply this action to all groups *outside* the given
02063                 range, FALSE to apply it to all groups *inside* the range.
02064 
02065                 Document - NULL, or the document which defines the range of groups to affect
02066 
02067                 Library - NULL, or the library which defines the range of groups to affect
02068 
02069     Purpose:    To select/deselect sets of display groups in this Gallery display.
02070                 All groups whose state changes will force redraw themselves
02071 
02072                 If selecting, all items in the tree will be deselected
02073 
02074                 Do not call this method - use the SuperGallery version
02075 
02076     Notes:      To select all groups in a range, and deselect all groups outside the range,
02077                 you need to use 2 calls to this method.
02078 
02079                 To select/deselect all groups in the display, pass FALSE, NULL, NULL to
02080                 the last 3 parameters. (If Doc/Lib are both NULL, 'Exclusive' has no effect)
02081 
02082     SeeAlso:    SuperGallery::SelectGroups; SGDisplayGroup::SelectGroups
02083 
02084 ********************************************************************************************/
02085 
02086 void SGDisplayNode::SelectGroups(BOOL SelectThem, BOOL Exclusive,
02087                                 Document *ParentDocument, Library *ParentLibrary)
02088 {
02089     // First, if selecting, then we need to ensure that all base-class nodes are deselected.
02090     // The group class overrides this behaviour to correctly select groups.
02091     if (SelectThem && !Flags.Invisible && Flags.CanSelect)
02092         SetSelected(FALSE);
02093 
02094     // Now, pass the selection request on to my children
02095     SGDisplayNode *Ptr = GetChild();
02096     while (Ptr != NULL)
02097     {
02098         Ptr->SelectGroups(SelectThem, Exclusive, ParentDocument, ParentLibrary);
02099         Ptr = Ptr->GetNext();
02100     }
02101 }
02102 
02103 /********************************************************************************************
02104 
02105 >   virtual void SGDisplayNode::SelectRangeGroups(SGDisplayGroup *PrimeNode, SGDisplayGroup *AnchorNode);
02106 
02107     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02108     Created:    10/2/95
02109 
02110     Inputs:     PrimeNode - The group which MUST be selected. May NOT be NULL.
02111 
02112                 AnchorNode - The other group, specifying a range of sibling groups to
02113                 be selected. May be NULL, in which case only PrimeNode is selected
02114 
02115     Purpose:    Selects the PrimeNode, and if possible, all sibling items between it and
02116                 the Anchor node. If Anchor == NULL or is not found, only PrimeNode is
02117                 selected. Does not deselect any items - you should call SelectItems first
02118                 to clear the seln.
02119 
02120 ********************************************************************************************/
02121 
02122 void SGDisplayNode::SelectRangeGroups(SGDisplayGroup *PrimeNode, SGDisplayGroup *AnchorNode)
02123 {
02124     ERROR3IF(PrimeNode == NULL, "SGDisplayNode::SelectRangeGroup - PrimeNode must be non-NULL");
02125 
02126     BOOL AnchorIsHere = FALSE;
02127     SGDisplayGroup *First = PrimeNode;
02128 
02129     SuperGallery *ParentGallery = GetParentGallery();
02130     ERROR3IF(ParentGallery == NULL, "NULL Parent gallery?!");
02131 
02132     if (AnchorNode != NULL)
02133     {
02134         SGDisplayNode *Ptr = PrimeNode->GetParent();
02135         ERROR3IF(Ptr == NULL, "SGDisplayNode::SelectRangeGroup - Tree linkage corrupt, or PrimeNode is the root!");     
02136 
02137         // Find which of the two nodes comes first  
02138         Ptr = Ptr->GetChild();
02139         while (Ptr != NULL && Ptr != PrimeNode)
02140         {
02141             if (Ptr == AnchorNode)
02142             {
02143                 First = (SGDisplayGroup *)AnchorNode;       // Ooops - the Anchor node occurs first!
02144                 AnchorIsHere = TRUE;    // And remember we've found the Anchor
02145                 break;
02146             }
02147             
02148             Ptr = Ptr->GetNext();
02149         }
02150 
02151         // Continue scanning until we can be sure that the Anchor Node is in the sibling list
02152         while (Ptr != NULL && !AnchorIsHere)
02153         {
02154             AnchorIsHere = (Ptr == AnchorNode);
02155             Ptr = Ptr->GetNext();
02156         }
02157     }
02158 
02159     if (!AnchorIsHere)      // Only one item in the range!
02160     {
02161         PrimeNode->SetSelected(TRUE);
02162         PrimeNode->ForceRedrawOfMyself();
02163 
02164         // And inform the parent gallery of the selection change
02165         ParentGallery->SetLastSelectedNode(PrimeNode);
02166         ParentGallery->SelectionHasChanged();
02167         return;
02168     }
02169 
02170     // We have a valid range. Scan from First to Last, selecting everything in our path
02171     SGDisplayGroup *Last = (First == PrimeNode) ? AnchorNode : PrimeNode;
02172     while (First != NULL && First != Last)
02173     {
02174         First->SetSelected(TRUE);
02175         First->ForceRedrawOfMyself();
02176         
02177         First = (SGDisplayGroup *) First->GetNext();
02178     }
02179 
02180     // And set the last one
02181     Last->SetSelected(TRUE);
02182     Last->ForceRedrawOfMyself();
02183 
02184     // And inform the parent gallery of the selection change
02185     ParentGallery->SetLastSelectedNode(Last);
02186     ParentGallery->SelectionHasChanged();
02187 }
02188 
02189 /********************************************************************************************
02190 
02191 >   virtual void SGDisplayNode::SelectRangeItems(SGDisplayItem *PrimeNode, SGDisplayItem *AnchorNode);
02192 
02193     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02194     Created:    10/2/95
02195 
02196     Inputs:     PrimeNode - The item which MUST be selected. May NOT be NULL.
02197 
02198                 AnchorNode - The other item, specifying a range of sibling items to
02199                 be selected. May be NULL, in which case only PrimeNode is selected
02200 
02201     Purpose:    Selects the PrimeNode, and if possible, all sibling items between it and
02202                 the Anchor node. If Anchor == NULL or is not found, only PrimeNode is
02203                 selected. Does not deselect any items - you should call SelectItems first
02204                 to clear the seln.
02205 
02206 ********************************************************************************************/
02207 
02208 void SGDisplayNode::SelectRangeItems(SGDisplayItem *PrimeNode, SGDisplayItem *AnchorNode)
02209 {
02210     ERROR3IF(PrimeNode == NULL, "SGDisplayItem::SelectRangeItem - PrimeNode must be non-NULL");
02211 
02212     BOOL AnchorIsHere = FALSE;
02213     SGDisplayItem *First = PrimeNode;
02214 
02215     SuperGallery *ParentGallery = GetParentGallery();
02216     ERROR3IF(ParentGallery == NULL, "NULL Parent gallery?!");
02217 
02218     if (AnchorNode != NULL)
02219     {
02220         SGDisplayNode *Ptr = PrimeNode->GetParent();
02221         ERROR3IF(Ptr == NULL, "SGDisplayItem::SelectRangeItem - Tree linkage corrupt, or PrimeNode is the root!");      
02222 
02223         // Find which of the two nodes comes first  
02224         Ptr = Ptr->GetChild();
02225         while (Ptr != NULL && Ptr != PrimeNode)
02226         {
02227             if (Ptr == AnchorNode)
02228             {
02229                 First = (SGDisplayItem *)AnchorNode;        // Ooops - the Anchor node occurs first!
02230                 AnchorIsHere = TRUE;    // And remember we've found the Anchor
02231                 break;
02232             }
02233             
02234             Ptr = Ptr->GetNext();
02235         }
02236 
02237         // Continue scanning until we can be sure that the Anchor Node is in the sibling list
02238         while (Ptr != NULL && !AnchorIsHere)
02239         {
02240             AnchorIsHere = (Ptr == AnchorNode);
02241             Ptr = Ptr->GetNext();
02242         }
02243     }
02244 
02245     if (!AnchorIsHere)      // Only one item in the range!
02246     {
02247         PrimeNode->SetSelected(TRUE);
02248         PrimeNode->ForceRedrawOfMyself();
02249 
02250         // And inform the parent gallery of the selection change
02251         ParentGallery->SetLastSelectedNode(PrimeNode);
02252         ParentGallery->SelectionHasChanged();
02253         return;
02254     }
02255 
02256     // We have a valid range. Scan from First to Last, selecting everything in our path
02257     SGDisplayItem *Last = (First == PrimeNode) ? AnchorNode : PrimeNode;
02258     while (First != NULL && First != Last)
02259     {
02260         First->SetSelected(TRUE);
02261         First->ForceRedrawOfMyself();
02262         
02263         First = (SGDisplayItem *) First->GetNext();
02264     }
02265 
02266     // And set the last one
02267     Last->SetSelected(TRUE);
02268     Last->ForceRedrawOfMyself();
02269 
02270     // And inform the parent gallery of the selection change
02271     ParentGallery->SetLastSelectedNode(Last);
02272     ParentGallery->SelectionHasChanged();
02273 }
02274 
02275 
02276 /***********************************************************************************************
02277 
02278 >   void SGDisplayNode::DrawPlinth(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo,
02279                                     DialogColourInfo *RedrawColours,
02280                                     DocRect *ButtonRect,
02281                                     BOOL Indented = FALSE,
02282                                     UINT32 GlyphResourceID = 0)
02283                                     
02284     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02285     Created:    24/1/95
02286 
02287     Inputs:     RedrawInfo - The redraw info, as normal with all this redraw stuff
02288                 MiscInfo - The MiscInfo, as normal for a SG Event
02289                 RedrawColours - The object what knows about them thar plotting colours
02290                 ButtonRect - The rectangle inside which to draw the plinth
02291                 Indented - TRUE for indented button, FALSE for raised button
02292                 GlyphResourceID - 0 (for no glyph, in which case the plinth 'face' is flat-
02293                 filled with the appropriate colour), else the resource ID of a bitmap
02294                 to be drawn in the center of the plinth/button.
02295 
02296     Purpose:    Draws a plinth (2 white lines and 2 dark grey lines) around the inside edge
02297                 of the given rectangle, in order to give a Windows 95 like button plinth.
02298                 It can include a button glyph bitmap. It's static so anybody can
02299                 call it if they so desire.
02300     
02301     Notes:      It would be advantageous if the provided button rectangle is aligned
02302                 to the device pixel grid if you want its appearance to be correct
02303 
02304                 The area within the plinth is filled with colour, so all pixels within
02305                 the given rectangle are guaranteed to be painted - i.e. you do not need
02306                 to clear the background region before calling this routine (indeed you
02307                 should not, if you wish to avoid flicker)
02308 
02309 ***********************************************************************************************/
02310 
02311 void SGDisplayNode::DrawPlinth(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo,
02312                                      DialogColourInfo *RedrawColours,
02313                                      DocRect *ButtonRect, BOOL Indented,
02314                                      UINT32 GlyphResourceID)
02315 {
02316     ERROR3IF(RedrawInfo == NULL || RedrawColours == NULL || ButtonRect == NULL,
02317             "SGDisplayNode::DrawPlinth - NULL parameters aer illegal");
02318 //  if (!Flags.Invisible)
02319 //  {
02320         RenderRegion *pRender = RedrawInfo->Renderer;
02321         pRender->SaveContext();
02322     
02323         pRender->SetLineWidth(0);
02324     
02325         // Fill inside the plinth with Button Face grey
02326         DocRect InsideRect(*ButtonRect);
02327         InsideRect.Inflate(-MiscInfo->PixelSize);
02328 
02329         // Fill inside the plinth with button-face (grey), and optionally with the glyph bitmap
02330         // (We always fill behind the bitmap first in case the bitmap isn't big enough to
02331         // fill the entire area)
02332         DocColour trans(COLOUR_TRANS);
02333         pRender->SetLineColour(trans);
02334         pRender->SetFillColour(RedrawColours->ButtonFace());
02335         pRender->DrawRect(&InsideRect);
02336 
02337         if (GlyphResourceID != 0)
02338         {
02339             // Quick v1.5 release bodge to center the up & down scroll arrow glyphs in the plinth
02340             if (GlyphResourceID == _R(IDB_GALLERY_SCROLLUP) || GlyphResourceID == _R(IDB_GALLERY_SCROLLDOWN))
02341             {
02342                 DocRect CenterRect(InsideRect);
02343                 CenterRect.Inflate(((7*MiscInfo->PixelSize) - CenterRect.Width())/2);   // Shrink to 7 pixels, centered
02344                 DrawBitmap(pRender, &CenterRect, GlyphResourceID);
02345             }
02346             else
02347                 DrawBitmap(pRender, &InsideRect, GlyphResourceID);
02348         }
02349 
02350         // Draw the plinth border
02351         DocColour LineCol;
02352 
02353         if (Indented)
02354             LineCol = RedrawColours->ButtonShadow();
02355         else
02356             LineCol = RedrawColours->ButtonHighlight();
02357 
02358         pRender->SetFillColour(LineCol);
02359 
02360         DocRect TempRect(*ButtonRect);                  // Left
02361         TempRect.hi.x = TempRect.lo.x + MiscInfo->PixelSize;
02362         pRender->DrawRect(&TempRect);
02363 
02364         TempRect = *ButtonRect;                         // Top
02365         TempRect.lo.y = TempRect.hi.y - MiscInfo->PixelSize;
02366         pRender->DrawRect(&TempRect);
02367 
02368         if (Indented)
02369             LineCol = RedrawColours->ButtonHighlight();
02370         else
02371             LineCol = RedrawColours->ButtonShadow();
02372     
02373         pRender->SetFillColour(LineCol);
02374         TempRect = *ButtonRect;                         // Right
02375         TempRect.lo.x = TempRect.hi.x - MiscInfo->PixelSize;
02376         pRender->DrawRect(&TempRect);
02377     
02378         TempRect = *ButtonRect;                         // Bottom
02379         TempRect.hi.y = TempRect.lo.y + MiscInfo->PixelSize;
02380         pRender->DrawRect(&TempRect);
02381     
02382         pRender->RestoreContext();
02383 //  }
02384 }
02385 
02386 
02387 
02388 /***********************************************************************************************
02389 
02390 >   void SGDisplayNode::DrawSelectionOutline(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo,
02391                                             DocRect *BoundsRect, INT32 Width = 0)
02392 
02393     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02394     Created:    9/3/95
02395 
02396     Inputs:     RedrawInfo - The info passed in to HandleEvent for redraws
02397                 MiscInfo   - Standard miscinfo struct
02398                 BoundsRect - The rectangle to draw the border into
02399                 Width - 0 for a default (2-pixel-wide) border, else the width of the
02400                 lines, in pixels. The outside of the frame will always touch the edge
02401                 of the given rectangle, so essentially, this is used as a 'defalate'
02402                 value to find the inside edge of the lines.
02403 
02404     Purpose:    Draws a black 2-pixel-thick frame-rectangle just inside the given Rect.
02405                 This is the normal selection-rectangle outline which should go outside
02406                 an icon or thumbnail when an item is selected.
02407 
02408     Notes:      If you wish to draw inside the rectangle, use
02409                     TheRect.Inflate(DevicePixels(MiscInfo, 2));
02410 
02411                 This draws 4 filled rectangles around the border of the rectangle. The
02412                 area inside the rectangle is left unpainted - this will reduce flicker
02413                 on large items as compared to blatting a single rectangle behind the
02414                 icon/thumbnail.
02415 
02416 ***********************************************************************************************/
02417 
02418 void SGDisplayNode::DrawSelectionOutline(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo,
02419                                             DocRect *BoundsRect, INT32 Width)
02420 {
02421     ERROR3IF(RedrawInfo == NULL || MiscInfo == NULL || BoundsRect == NULL,
02422             "SGDisplayNode::DrawSelectionOutline - NULL parameters aer illegal");
02423 
02424 //  if (!Flags.Invisible)
02425 //  {
02426         RenderRegion *pRender = RedrawInfo->Renderer;   
02427 
02428         if (Width == 0)         // Default value of zero means a 2-pixel border
02429             Width = DevicePixels(MiscInfo, 2);
02430 
02431         pRender->SaveContext();
02432     
02433         pRender->SetLineWidth(0);
02434         DocColour trans(COLOUR_TRANS);
02435         pRender->SetLineColour(trans);
02436         DocColour black(COLOUR_BLACK);
02437         pRender->SetFillColour(black);
02438 
02439         DocRect TempRect(*BoundsRect);                  // Left
02440         TempRect.hi.x = TempRect.lo.x + Width;
02441         pRender->DrawRect(&TempRect);
02442 
02443         TempRect = *BoundsRect;                         // Top
02444         TempRect.lo.y = TempRect.hi.y - Width;
02445         pRender->DrawRect(&TempRect);
02446 
02447         TempRect = *BoundsRect;                         // Right
02448         TempRect.lo.x = TempRect.hi.x - Width;
02449         pRender->DrawRect(&TempRect);
02450 
02451         TempRect = *BoundsRect;                         // Bottom
02452         TempRect.hi.y = TempRect.lo.y + Width;
02453         pRender->DrawRect(&TempRect);
02454 
02455         pRender->RestoreContext();
02456 //  }
02457 }
02458 
02459 
02460 
02461 /***********************************************************************************************
02462 
02463 >   void SGDisplayNode::DrawBitmap(RenderRegion *Renderer, DocRect *BoundsRect, UINT32 ResID)
02464 
02465     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02466     Created:    6/2/95
02467 
02468     Inputs:     Renderer - The render region to render with
02469                 BoundsRect - The rectangle to draw the bitmap into
02470                 ResID - A non-zero bitmap-resource identifier (the bitmap to draw)
02471 
02472     Purpose:    Draws the given bitmap to screen in the given DocRect of the supergallery
02473                 display list. (Used to be necessary before RR:DrawBitmap came along)
02474 
02475     SeeAlso:    RenderRegion::DrawBitmap
02476 
02477 ***********************************************************************************************/
02478 
02479 void SGDisplayNode::DrawBitmap(RenderRegion *Renderer, DocRect *BoundsRect, UINT32 ResID)
02480 {
02481     ERROR3IF(Renderer == NULL || BoundsRect == NULL || ResID == 0,
02482             "SGDisplayNode::DrawBitmap - NULL Parameter(s) are illegal");
02483 
02484 //  if (!Flags.Invisible)
02485 //  {
02486         Renderer->DrawBitmap(BoundsRect->lo, ResID);
02487 //  }
02488 }
02489 
02490 
02491 
02492 /***********************************************************************************************
02493 
02494 >   virtual void SGDisplayNode::StartRendering(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo)
02495 
02496     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02497     Created:    1/2/95
02498 
02499     Inputs:     RedrawInfo - The RedrawInfo passed to your HandleEvent function
02500                              for SGEVENT_REDRAWs
02501                 MiscInfo -   The MiscInfo passed in to your HandleEvent function
02502 
02503     Purpose:    MUST be called by all derived node types when they are about to start
02504                 rendering. This allows us to do things like using GRenderRegions to small
02505                 bitmaps (a region for each item rather than one region for the whole window)
02506                 and other fabby things.
02507 
02508     SeeAlso:    SGDisplayNode::StopRendering
02509 
02510 ***********************************************************************************************/
02511 
02512 void SGDisplayNode::StartRendering(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo)
02513 {
02514     // Does nothing, for now
02515 }
02516 
02517 
02518 
02519 /***********************************************************************************************
02520 
02521 >   virtual void SGDisplayNode::StopRendering(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo)
02522 
02523     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02524     Created:    1/2/95
02525 
02526     Inputs:     RedrawInfo - The RedrawInfo passed to your HandleEvent function
02527                              for SGEVENT_REDRAWs
02528                 MiscInfo -   The MiscInfo passed in to your HandleEvent function
02529 
02530     Purpose:    MUST be called by all derived node types when they have finished
02531                 rendering. This allows us to do things like using GRenderRegions to small
02532                 bitmaps (a region for each item rather than one region for the whole window)
02533                 and other fabby things.
02534 
02535     SeeAlso:    SGDisplayNode::StopRendering
02536 
02537 ***********************************************************************************************/
02538 
02539 void SGDisplayNode::StopRendering(SGRedrawInfo *RedrawInfo, SGMiscInfo *MiscInfo)
02540 {
02541     // Does nothing, for now
02542 }
02543 
02544 
02545 /***********************************************************************************************
02546 
02547 >   virtual BOOL SGDisplayNode::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
02548 
02549     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02550     Created:    16/4/95
02551 
02552     Inputs:     Mouse - Information on the mouse state for this click
02553                 MiscInfo - the normal info as passed to event handlers
02554 
02555     Returns:    TRUE if the click caused any action to be taken (selection state to change)
02556                 FALSE if the click was ignored for whatever reason
02557 
02558     Purpose:    Provides part 1 of the default selection model for clicks on gallery display
02559                 nodes. Should be called by all derived gallery DisplayItems to handle clicks
02560                 upon them, when multiple-selection support is desired.
02561 
02562                 You should call this method immediately prior to starting a drag as a result
02563                 of a click event. Note that it is paired with DefaultClickHandler (which
02564                 should be called when the drag you start turns out to be a click, if you
02565                 want multiple-selection capability).
02566 
02567                 See the SGDisplayColour (kernel\sgcolour.cpp) for an example of use
02568 
02569     SeeAlso:    SGDisplayItem::DefaultClickHandler; SGDisplayColour::HandleEvent
02570 
02571 ***********************************************************************************************/
02572 
02573 BOOL SGDisplayNode::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
02574 {
02575     if (Mouse->Adjust)      // Drags cannot be started with adjust
02576         return(FALSE);
02577 
02578     SuperGallery *ParentGallery = GetParentGallery();
02579 
02580     if (Mouse->Extend)
02581     {
02582         BOOL Handled = TRUE;
02583 
02584         // This was a click to extend the selection to the clicked item
02585 #if FALSE
02586 /* I was led to believe that this was how shift-select worked, but it isn't (thank God)
02587    so I have removed this case again - Jason
02588 
02589         if (IsSelected())
02590         {
02591             // If this item is selected, then shift-clicking it acts to leave it
02592             // selected, and deselect every other item.
02593             
02594             ParentGallery->SelectItems(FALSE);  // Deselect everything else
02595             ParentGallery->SelectGroups(FALSE); // Deselect all groups
02596 
02597             // Repaint the list box now. This is because if there is a large
02598             // distance between the old selection and the new one, we get a huge
02599             // redraw cliprect, so get a (slow) complete redraw, instead of two
02600             // small redraws. It is thus better to break the redraw into 2 steps
02601             // so that we are more likely to get 2 fast redraws than one slow one.
02602             ParentGallery->PaintListNow();
02603         
02604             // And select myself, with immediate redraw
02605             SetSelected(TRUE);
02606             ParentGallery->PaintListNow();
02607 
02608             // Update the ParentGallery to know that we are the new multi-selection anchor
02609             ParentGallery->SetLastSelectedNode(this);
02610 
02611             // And inform the parent gallery that the selection may have changed
02612             ParentGallery->SelectionHasChanged();
02613 
02614             return(TRUE);
02615         }
02616         else
02617 */
02618 #endif
02619         {
02620             if (ParentGallery->GetSelectedItemCount() == 0 && ParentGallery->GetSelectedGroupCount() == 0)
02621                 Handled = FALSE;    // No selection - treat extend-click as a normal click
02622             else
02623             {
02624                 ParentGallery->SelectItems(FALSE);  // Deselect all items
02625                 ParentGallery->SelectGroups(FALSE); // Deselect all groups
02626 
02627                 ParentGallery->SelectRange(this, ParentGallery->GetLastSelectedNode());
02628             }
02629         }
02630 
02631         if (Handled)
02632             return(TRUE);
02633     }
02634     
02635     
02636     if (!Flags.Selected)
02637     {
02638         if (!Mouse->Adjust)
02639         {
02640             // If it's not an adjust-click, deselect all other items and groups
02641             ParentGallery->SelectItems(FALSE);
02642             ParentGallery->SelectGroups(FALSE);
02643 
02644             // Repaint the list box now. This is because if there is a large
02645             // distance between the old selection and the new one, we get a huge
02646             // redraw cliprect, so get a (slow) complete redraw, instead of two
02647             // small redraws. It is thus better to break the redraw into 2 steps
02648             // so that we are more likely to get 2 fast redraws than one slow one.
02649             ParentGallery->PaintListNow();
02650         }
02651         else
02652         {
02653             // Mutual exclusion stuff... If we're a group, deselect all items, etc...
02654             if(this->IS_KIND_OF(SGDisplayGroup))
02655                 ParentGallery->SelectItems(FALSE);
02656             else if(this->IS_KIND_OF(SGDisplayItem))
02657                 ParentGallery->SelectGroups(FALSE);
02658             else
02659                 ERROR3("What are we if we're not a group or an item ?");                
02660         }
02661     
02662         // And select myself, and do an immediate redraw
02663         SetSelected(TRUE);
02664         ParentGallery->PaintListNow();
02665 
02666         // Update the ParentGallery to know that we are the new multi-selection anchor
02667         ParentGallery->SetLastSelectedNode(this);
02668 
02669         // And inform the parent gallery that the selection may have changed
02670         ParentGallery->SelectionHasChanged();
02671 
02672         return(TRUE);
02673     }
02674 
02675     return(FALSE);
02676 }
02677 
02678 
02679 
02680 /***********************************************************************************************
02681 
02682 >   virtual BOOL SGDisplayNode::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
02683                                                     BOOL AfterDrag = FALSE,
02684                                                     BOOL AdjustDoubleClick = TRUE)
02685 
02686     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02687     Created:    9/2/94
02688 
02689     Inputs:     Mouse - Information on the mouse state for this click
02690                 MiscInfo - the normal info as passed to event handlers
02691 
02692                 AfterDrag - TRUE if this is being called when a drag turns into a click, and
02693                 you called DefaultPreDragHandler before the drag started
02694 
02695                 AdjustDoubleClick - TRUE to do normal adjust-double-click handling (which
02696                 closes the gallery after applying the item)
02697                 FALSE to not close the gallery (used by the colour gallery to stop the
02698                 gallery closing when applying with adjust as it applies line colour, so
02699                 overrides the default behaviour)
02700 
02701     Returns:    TRUE if the click caused any action to be taken (selection state to change)
02702                 FALSE if the click was ignored for whatever reason
02703 
02704     Purpose:    Provides the default selection model for clicks on gallery display nodes.
02705                 Should be called by all derived gallery DisplayItems to handle clicks
02706                 upon them, when multiple-selection support is desired.
02707 
02708     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
02709 
02710 ***********************************************************************************************/
02711 
02712 BOOL SGDisplayNode::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
02713                                         BOOL AfterDrag, BOOL AdjustDoubleClick)
02714 {
02715     TRACEUSER( "Matt", _T("SGDisplyaNode::DefaultClickHandler called\n"));
02716     SuperGallery *ParentGallery = GetParentGallery();
02717 
02718     if (!AfterDrag)
02719     {
02720         // This was not called after having previously called DefaultPreDragHandler, 
02721         // so we have to do both halves of the processing. If PreDrag deals with it,
02722         // we don't need to do anything further.
02723         if (DefaultPreDragHandler(Mouse, MiscInfo))
02724             return(TRUE);
02725     }
02726 
02727     if (Mouse->DoubleClick && !Flags.Invisible)
02728     {
02729         // On double-click, this item becomes the only selected item, and
02730         // it is applied (if that action is supported by the parent gallery)
02731 
02732         ParentGallery->SelectItems(FALSE);  // Deselect everything else
02733         ParentGallery->SelectGroups(FALSE); // Deselect all groups
02734 
02735         // Repaint the list box now. This is because if there is a large
02736         // distance between the old selection and the new one, we get a huge
02737         // redraw cliprect, so get a (slow) complete redraw, instead of two
02738         // small redraws. It is thus better to break the redraw into 2 steps
02739         // so that we are more likely to get 2 fast redraws than one slow one.
02740         ParentGallery->PaintListNow();
02741     
02742         // And select myself, and do an immediate redraw
02743         SetSelected(TRUE);
02744         ParentGallery->PaintListNow();
02745 
02746         // Update the ParentGallery to know that we are the new multi-selection anchor
02747         ParentGallery->SetLastSelectedNode(this);
02748 
02749         // And inform the parent gallery that the selection may have changed
02750         ParentGallery->SelectionHasChanged();
02751     
02752         if (Mouse->Adjust)
02753         {
02754             BOOL ActionApplied = ParentGallery->ApplyAction(SGACTION_APPLYADJUST);
02755             if (!ActionApplied)
02756                 ActionApplied = ParentGallery->ApplyAction(SGACTION_APPLY);
02757 
02758             if (ActionApplied && AdjustDoubleClick)
02759             {
02760                 // Adjust/Ctrl double click of an item. This applies the item and then
02761                 // auto closes the gallery (just like RISC OS and Win95)
02762                 ParentGallery->SetVisibility(FALSE);
02763 
02764                 DialogBarOp::SetSystemStateChanged();   // Ensure toolbar button pops out again
02765             }
02766         }
02767 PORTNOTE("galleries", "Disabled clipart gallery")
02768 #ifndef EXCLUDE_FROM_XARALX
02769         else if (ParentGallery->IsKindOf(CC_RUNTIME_CLASS(LibFillsSGallery)))
02770                          LibClipartSGallery::ImportClipart(TRUE, (LibraryGallery*) ParentGallery);
02771 #endif
02772         else
02773             ParentGallery->ApplyAction(SGACTION_APPLY);
02774 
02775         return(TRUE);
02776     }
02777 
02778     // If this was an adjust click, it should toggle the selection state
02779     if (Mouse->Adjust)
02780     {
02781         BOOL AreWeSelected = IsSelected();
02782 
02783         // Mutual exclusion stuff... If we're a group, deselect all items, etc...
02784         if(this->IS_KIND_OF(SGDisplayGroup))
02785             ParentGallery->SelectItems(FALSE);
02786         else if(this->IS_KIND_OF(SGDisplayItem))
02787             ParentGallery->SelectGroups(FALSE);
02788         else
02789             ERROR3("What are we if we're not a group or an item ?");                
02790 
02791         // Invert my selection state, and do an immediate redraw
02792         SetSelected(!AreWeSelected);
02793         ParentGallery->PaintListNow();
02794 
02795         // Update the ParentGallery to know that we are the new multi-selection anchor
02796         ParentGallery->SetLastSelectedNode(this);
02797 
02798         // And inform the parent gallery that the selection may have changed
02799         ParentGallery->SelectionHasChanged();
02800 
02801         return(TRUE);
02802     }
02803     return(FALSE);
02804 }
02805 
02806 
02807 /***********************************************************************************************
02808 
02809 >   CWindowID SGDisplayNode::GetListWindow(void)
02810 
02811     Author:     Gerry_Iles (Xara Group Ltd) <camelotdev@xara.com>
02812     Created:    18/05/2006
02813 
02814     Purpose:    Returns the window id of the list box window of the parent gallery
02815 
02816     SeeAlso:    -
02817 
02818 ***********************************************************************************************/
02819 
02820 CWindowID SGDisplayNode::GetListWindow(void)
02821 {
02822     return(DialogManager::GetGadget(GetParentGallery()->GetReadWriteWindowID(), GetParentGallery()->GetListGadgetID()));
02823 }
02824 
02825 
02826 
02827 /***********************************************************************************************
02828 
02829 >   virtual void SGDisplayNode::DumpSubtree(INT32 TreeLevel = 1)
02830 
02831     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02832     Created:    26/10/94
02833     Purpose:    Dumps TRACE output to the debugger showing the layout of the tree
02834                 THIS FUNCTION IS ONLY PRESENT IN DEBUG BUILDS (#ifdef _DEBUG)
02835                 It will also only produce debug output if your UserName is "Jason"
02836 
02837 ***********************************************************************************************/
02838 
02839 #ifdef _DEBUG
02840 void SGDisplayNode::DumpSubtree(INT32 TreeLevel)
02841 {
02842     PORTNOTETRACE("galleries", "Disabled debug dump");
02843 #ifndef EXCLUDE_FROM_XARALX
02844     if (IsUserName("Matt"/*Jason*/))
02845     {
02846         // First, dump out myself...
02847         TCHAR Temp[200];
02848         TCHAR Msg[200];
02849         INT32 i;
02850         for (i = 0; i < (TreeLevel * 2) && i < 50; i++)
02851            Msg[i] = ' ';
02852 
02853         Msg[i] = '\0';
02854 
02855         wsprintf(Temp, "%s (%x)\n", GetRuntimeClass()->m_lpszClassName, (INT32)this);
02856         camStrcat(Msg, Temp);
02857         OutputDebugString(Msg);
02858 
02859 
02860         // Now recursively dump all my children...
02861         SGDisplayNode *Ptr = GetChild();
02862         while (Ptr != NULL)
02863         {
02864             Ptr->DumpSubtree(TreeLevel+1);
02865             Ptr = Ptr->Next;
02866         }
02867     }
02868 #endif
02869 }
02870 #endif
02871 
02872 
02873 
02874 
02875 
02876 
02877 
02878 
02879 
02880 
02881 
02882 
02883 /***********************************************************************************************
02884 
02885 >   SGDisplayRoot::SGDisplayRoot()
02886 
02887     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02888     Created:    26/10/94
02889 
02890     Purpose:    SGDisplayRoot constructor (makes root nodes invisible by default)
02891 
02892     SeeAlso:    SGDisplayNode::SGDisplayNode
02893 
02894 ***********************************************************************************************/
02895 
02896 SGDisplayRoot::SGDisplayRoot()
02897 {
02898     Child = NULL;
02899 
02900     Flags.Invisible = TRUE;         // The root node is always invisible
02901     ParentGallery = NULL;
02902     ScrollExtent = 0;
02903 
02904     ERROR3("SGDisplayRoot constructed in an illegal manner");
02905 }
02906 
02907 
02908 
02909 /***********************************************************************************************
02910 
02911 >   SGDisplayRoot::SGDisplayRoot(SuperGallery *ParentGal)
02912 
02913     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02914     Created:    26/10/94
02915 
02916     Inputs:     ParentGal - A pointer to the parent gallery of this node/tree
02917 
02918     Purpose:    SGDisplayRoot constructor (makes root nodes invisible by default)
02919 
02920     SeeAlso:    SGDisplayNode::SGDisplayNode
02921 
02922 ***********************************************************************************************/
02923 
02924 SGDisplayRoot::SGDisplayRoot(SuperGallery *ParentGal)
02925 {
02926     Child = NULL;
02927 
02928     Flags.Invisible = TRUE;         // The root node is always invisible
02929     ParentGallery = ParentGal;
02930     ScrollExtent = 0;
02931 }
02932 
02933 
02934 
02935 /***********************************************************************************************
02936 
02937 >   virtual SGDisplayNode *SGDisplayRoot::GetChild(void) const
02938 
02939     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02940     Created:    20/10/94 (Made virtual 13/5/95)
02941     Returns:    A pointer to the first child of this SGDisplayNode object, or NULL
02942 
02943     Purpose:    Finds the child of this DisplayTree Node.
02944                 Returns NULL if you have reached the boundary of the tree
02945 
02946     SeeAlso:    SuperGallery; SGDisplayNode::GetParent;
02947                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
02948 
02949 ***********************************************************************************************/
02950 
02951 SGDisplayNode *SGDisplayRoot::GetChild(void) const
02952 {
02953     return(Child);
02954 }
02955 
02956 
02957 
02958 /***********************************************************************************************
02959 
02960 >   void SGDisplayRoot::SetChild(SGDisplayNode *NewChild)
02961 
02962     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02963     Created:    20/10/94
02964 
02965     Inputs:     A pointer to the new first-child of this SGDisplayNode object
02966 
02967     Purpose:    Sets the child of this DisplayTree Node. Overrides to base class method
02968                 to allow the child to be set.
02969 
02970     SeeAlso:    SuperGallery; SGDisplayNode::GetParent;
02971                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
02972 
02973 ***********************************************************************************************/
02974 
02975 void SGDisplayRoot::SetChild(SGDisplayNode *NewChild)
02976 {
02977     Child = NewChild;
02978 }
02979 
02980 
02981 
02982 /***********************************************************************************************
02983 
02984 >   virtual SuperGallery *SGDisplayRoot::GetParentGallery(void) const
02985 
02986     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
02987     Created:    28/10/94
02988 
02989     Returns:    The parent SuperGallery, or NULL
02990 
02991     Purpose:    Returns the SuperGallery which 'owns' this node (and its subtree)
02992 
02993     SeeAlso:    SGDisplayNode::GetParentGallery
02994 
02995 ***********************************************************************************************/
02996 
02997 SuperGallery *SGDisplayRoot::GetParentGallery(void) const
02998 {
02999     ERROR3IF(ParentGallery == NULL, "I have no parent gallery! This is not right!");
03000 
03001     return(ParentGallery);
03002 }
03003 
03004 
03005 
03006 /***********************************************************************************************
03007 
03008 >   virtual void SGDisplayRoot::InsertAfter(SGDisplayNode *NodeToInsert)
03009 
03010     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03011     Created:    21/10/94
03012 
03013     Inputs:     NodeToInsert - the new node to insert into the tree
03014 
03015     Purpose:    OVERRIDES SGDisplayNode::InsertAfter
03016 
03017                 Insertions at the root node are always converted to AddItem() calls,
03018                 to insert the item as the last child of the root node. The root cannot
03019                 have siblings!
03020 
03021     SeeAlso:    SGDisplayNode::AddItem
03022 
03023 ***********************************************************************************************/
03024 
03025 void SGDisplayRoot::InsertAfter(SGDisplayNode *NodeToInsert)
03026 {
03027     AddItem(NodeToInsert);
03028 }
03029 
03030 
03031 
03032 /***********************************************************************************************
03033 
03034 >   virtual void SGDisplayRoot::InsertBefore(SGDisplayNode *NodeToInsert)
03035 
03036     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03037     Created:    21/10/94
03038 
03039     Inputs:     NodeToInsert - the new node to insert into the tree
03040 
03041     Purpose:    OVERRIDES SGDisplayNode::InsertBefore
03042 
03043                 Insertions at the root node are always converted to AddItem() calls,
03044                 to insert the item as the last child of the root node. The root cannot
03045                 have siblings!
03046 
03047     SeeAlso:    SGDisplayNode::AddItem
03048 
03049 ***********************************************************************************************/
03050 
03051 void SGDisplayRoot::InsertBefore(SGDisplayNode *NodeToInsert)
03052 {
03053     AddItem(NodeToInsert);
03054 }
03055 
03056 
03057 
03058 /***********************************************************************************************
03059 
03060 >   virtual void SGDisplayRoot::RemoveFromTree(void)
03061 
03062     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03063     Created:    21/10/94
03064 
03065     Purpose:    OVERRIDES SGDisplayNode::RemoveFromTree
03066                 The root node cannot be removed from the tree - it returns quietly, taking
03067                 no action (this allows the generic treatment of DestroyTree to work without
03068                 having to take special action for delinking the root node)
03069 
03070     SeeAlso:    SGDisplayNode::RemoveFromTree; SGDisplayNode::DestroySubtree
03071 
03072 ***********************************************************************************************/
03073 
03074 void SGDisplayRoot::RemoveFromTree(void)
03075 {
03076     // do nothing
03077 }
03078 
03079 
03080 
03081 /***********************************************************************************************
03082 
03083 >   virtual void SGDisplayRoot::DestroySubtree(BOOL IncludingThisNode = TRUE)
03084 
03085     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03086     Created:    21/10/94
03087 
03088     Inputs:     IncludingThisNode - TRUE (The default) to delete this node (the root of
03089                 the subtree) as well as all its children.
03090                 FALSE to delete its children only (This leaves this node untouched, but
03091                 vapes all child nodes)
03092                 NOTE - This parameter is ignored, and always treated as FALSE
03093 
03094     Purpose:    DESTROYS the subtree starting at this root node.
03095                 This does a depth-first recursive scan of the subtree, delinking each item,
03096                 and then CALLING EACH ITEMS DESTRUCTOR.
03097 
03098                 IT DOES NOT destruct the root node, however (i.e. it destroys the tree down
03099                 to the point where only the root is left alive).
03100 
03101     Errors:     May be generated by the RemoveFromTree and destructor calls if the subtree
03102                 is in some way corrupt - see these calls for details.
03103 
03104     SeeAlso:    SuperGallery; SGDisplayNode::RemoveFromTree; SGDisplayNode::DestroySubtree
03105 
03106 ***********************************************************************************************/
03107 
03108 void SGDisplayRoot::DestroySubtree(BOOL IncludingThisNode)
03109 {
03110     // Destroy all our children, but DO NOT delete 'this' node!
03111     SGDisplayNode::DestroySubtree(FALSE);
03112 }
03113 
03114 
03115 
03116 /***********************************************************************************************
03117 
03118 >   virtual void SGDisplayRoot::InitFormatInfo(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo)
03119 
03120     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03121     Created:    24/10/94
03122 
03123     Inputs:     FormatInfo  - Points at the format information to be initialised
03124                 MiscInfo    - Points at a structure containing standard 'MiscInfo'
03125 
03126     Outputs:    FormatInfo is initialised as appropriate
03127                 MiscInfo->MaxWidth is gridlocked to the pixel grid
03128 
03129     Purpose:    Resets the formatting info structure to default values for the start of a
03130                 formatting pass. Must be called before SGDisplayRoot::Redraw
03131 
03132     SeeAlso:    SGDisplayRoot::Redraw
03133 
03134 ***********************************************************************************************/
03135 
03136 void SGDisplayRoot::InitFormatInfo(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo)
03137 {
03138     MiscInfo->MaxWidth = GridLock(MiscInfo, MiscInfo->MaxWidth); // Maximum width of formatting lines
03139 
03140     FormatInfo->LinePos = 0;                                // Start at the top of the list
03141     FormatInfo->LineHeight = 0;                             // The current line is 0 height so far
03142     FormatInfo->AvailableWidth = MiscInfo->MaxWidth;        // The full line is left to allocate
03143     FormatInfo->IndentLevel = 0;                            // Initially we are not indented
03144 
03145     FormatInfo->AccumulateBounds = FALSE;                   // By default, don't accumulate bounds
03146     FormatInfo->LastInvalidNode  = NULL;                    // For the group dragging redraw fix
03147     FormatInfo->InvalidBounds = DocRect(0, 1, MiscInfo->MaxWidth, 2);
03148 }
03149 
03150 
03151 
03152 /***********************************************************************************************
03153 
03154 >   virtual BOOL SGDisplayRoot::HandleEvent(SGEventType EventType, void *EventInfo,
03155                                             SGMiscInfo *MiscInfo)
03156 
03157     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03158     Created:    20/10/94
03159 
03160     Inputs:     See SGDisplayNode::HandleEvent
03161 
03162     Returns:    TRUE if the event was handled successfully
03163                 FALSE if it was not
03164 
03165     Purpose:    Handles a SuperGallery DisplayTree event
03166 
03167     Notes:      This overrides the pure virtual SGDisplayNode::HandleEvent method
03168 
03169                 A node need not handle a specific event - if it does not handle it, it
03170                 should return FALSE.
03171 
03172                 Redraw and Formatting handlers should never return TRUE, as this will
03173                 prevent the event from continuing through the tree.
03174 
03175                 Non-leaf-nodes must call SGDisplayNode::GiveEventToMyChildren in order
03176                 to pass the event down the tree.
03177 
03178     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayGroup::HandleEvent;
03179                 SGDisplayItem::HandleEvent; SGDisplayRoot::InitFormatInfo;
03180                 SGDisplayRoot::CalculateListExtent;
03181                 SGDisplayNode::GiveEventToMyChildren
03182 
03183 ***********************************************************************************************/
03184 
03185 BOOL SGDisplayRoot::HandleEvent(SGEventType EventType, void *EventInfo,
03186                                 SGMiscInfo *MiscInfo)
03187 {
03188     switch (EventType)
03189     {
03190         case SGEVENT_FORMAT:
03191             // Do nothing, but ensure we don't use the 'default:' case.
03192             break;
03193 
03194         case SGEVENT_REDRAW:
03195             {
03196                 SGRedrawInfo *RedrawInfo = GetRedrawInfo(EventType, EventInfo);
03197 
03198                 // This line is to stop the white flicker when backgrounding the items
03199                 // Hmmm... There are a few problems which need sorting out though !
03200                 if (BkgEraseMode == TRUE)
03201                 {
03202                     StartRendering(RedrawInfo, MiscInfo);
03203 
03204                     RenderRegion *pRender = RedrawInfo->Renderer;
03205 
03206                     // Set default background colour
03207                     pRender->SetFillColour(RedrawInfo->Background);
03208                     pRender->SetLineColour(COLOUR_TRANS);   //RedrawInfo->Background);
03209 
03210                     // Work out height first to avoid ENSURE in DocRect ctor
03211                     INT32 height=ScrollExtent;
03212                     // Extend the rect if necessary to ensure entire window bg is filled
03213                     if (ScrollExtent < MiscInfo->WindowHeight)
03214                         height = MiscInfo->WindowHeight;
03215 
03216                     if ((height>=0) && (MiscInfo->MaxWidth>=0))
03217                     {
03218                         // Fill entire display window background with bg colour
03219                         DocRect WindowBG(0, -height, MiscInfo->MaxWidth, 0);
03220                         pRender->DrawRect(&WindowBG);
03221                     }
03222 
03223                     StopRendering(RedrawInfo, MiscInfo);
03224                 }
03225                 else
03226                     BkgEraseMode = TRUE;
03227 
03228                 if (GetChild() == NULL) // We have no children (empty tree)
03229                 {
03230                     // Redraw ourself as 'No Items' indicator
03231                     // ERROR3("SGDisplayRoot::Redraw - I have no children to draw");
03232                     return(TRUE);   // Might as well return TRUE - there is nobody else!
03233                 }
03234             }
03235             break;
03236 
03237 
03238         default:
03239             return(SGDisplayNode::HandleEvent(EventType, EventInfo, MiscInfo));
03240     }
03241 
03242     // We have children, so recursively call them all with this event
03243     BOOL result = GiveEventToMyChildren(EventType, EventInfo, MiscInfo);
03244 
03245     // If we've just reformatted the tree, remember the new list extent
03246     if (EventType == SGEVENT_FORMAT)
03247     {
03248         SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
03249 
03250         ScrollExtent = (ABS(FormatInfo->LinePos));
03251         SetScrollOffset(GetScrollOffset(), MiscInfo);   // Ensure not scrolled off end!
03252 
03253         if (FormatInfo->LastInvalidNode != NULL)
03254         {
03255             // The immediately previous (final) node had invalid bounds.
03256             // We'd better include the blank part of the list below it in the
03257             // invalid bounds, to ensure we don't leave little bits of stuff past the end
03258             // of the list if it has got smaller.
03259 
03260             ERROR3IF(FormatInfo->InvalidBounds.hi.y > 0,
03261                         "Gallery display formatting error - LastInvalidNode should be NULL "
03262                         "if there haven't been any invalid nodes yet");
03263 
03264             FormatInfo->InvalidBounds.lo.y = FormatInfo->LinePos - MiscInfo->WindowHeight;
03265         }
03266     }
03267 
03268     return(result);
03269 }
03270 
03271 
03272 
03273 /***********************************************************************************************
03274 
03275 >   virtual INT32 SGDisplayRoot::CalculateListExtent(SGFormatInfo *FormatInfo,
03276                                                         SGMiscInfo *MiscInfo)
03277 
03278     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03279     Created:    26/10/94
03280 
03281     Inputs:     FormatInfo - should have been initialised with InitFormatInfo
03282                 MiscInfo - Miscellanous info needed for formatting
03283 
03284     Outputs:    FormatInfo is updated as appropriate
03285 
03286     Returns:    The needed (window scroll) extent to contain the entire list if it were to
03287                 be redrawn right now. NOTE that this is a positive value; if you wish to
03288                 use this to find the bottom of the virtual space coordinates, you must
03289                 negate it (as Virtual space runs from 0 to -Extent)
03290 
03291     Purpose:    Causes the entire subtree from this node to format themselves, and
03292                 returns the 'height' of the entire list display, in millipoints
03293 
03294     SeeAlso:    SGDisplayRoot::InitFormatInfo; SGDisplayRoot::GetCachedListExtent
03295 
03296 ***********************************************************************************************/
03297 
03298 INT32 SGDisplayRoot::CalculateListExtent(SGFormatInfo *FormatInfo, SGMiscInfo *MiscInfo)
03299 {
03300     if (GetChild() == NULL) 
03301         return(0);
03302 
03303     // Ask ourself to handle a FORMAT event. This recursively formats the tree, and returns
03304     // with FormatInfo indicating the state at the end of formatting (FormatInfo->LinePos
03305     // is at the end position of the list, and hence gives us the extent)
03306     HandleEvent(SGEVENT_FORMAT, FormatInfo, MiscInfo);
03307     return(ScrollExtent);
03308 }
03309 
03310 
03311 
03312 /***********************************************************************************************
03313 
03314 >   virtual INT32 SGDisplayRoot::GetCachedListExtent(void)
03315 
03316     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03317     Created:    1/3/95
03318 
03319     Returns:    The currently cached value for the scroll extent
03320 
03321     Purpose:    Returns the 'height' of the entire list display, in millipoints.
03322                 This return value is only valid after the tree has been formatted (including
03323                 a previous call to CalculateListExtent), so long as the cached tree format
03324                 is still valid. Use with care.
03325 
03326     SeeAlso:    SGDisplayRoot::CalculateListExtent
03327 
03328 ***********************************************************************************************/
03329 
03330 INT32 SGDisplayRoot::GetCachedListExtent(void)
03331 {
03332     return(ScrollExtent);
03333 }
03334 
03335 
03336 
03337 /***********************************************************************************************
03338 
03339 >   virtual void SGDisplayRoot::SetScrollOffset(INT32 NewOffset, SGMiscInfo *MiscInfo)
03340 
03341     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03342     Created:    21/1/95
03343 
03344     Inputs:     NewOffset - The new scroll offset.
03345                 This is a number between 0 and LengthOfList, in MILLIPOINTs
03346 
03347                 MiscInfo - the usual
03348 
03349     Purpose:    Changes the scroll offset, and if necessary, redraws the scroll bar
03350 
03351                 In the base class, no scrollbar is supplied; this method does nothing
03352 
03353     SeeAlso:    SGDisplayRoot::GetScrollOffset; SGDisplayRootScroll::SetScrollOffset
03354 
03355 ***********************************************************************************************/
03356 
03357 void SGDisplayRoot::SetScrollOffset(INT32 NewOffset, SGMiscInfo *MiscInfo)
03358 {
03359     // Base class does nothing
03360 }
03361 
03362 
03363 
03364 /***********************************************************************************************
03365 
03366 >   virtual INT32 SGDisplayRoot::GetScrollOffset(void)
03367 
03368     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03369     Created:    21/1/95
03370 
03371     Returns:    In the base class, always returns 0
03372 
03373     Returns:    The current Scroll offset. This is a positive MLLIPOINT value. Note that
03374                 virtual coordinates go from 0 to -Extent, so the point of VirtualSpace which
03375                 maps to the top left displayed pixel on screen is at (-ScrollOffset).
03376 
03377                 In the base class, no scrollbar is supplied; this method does nothing
03378 
03379     SeeAlso:    SGDisplayRoot::SetScrollOffset; SGDisplayRootScroll::GetScrollOffset
03380 
03381 ***********************************************************************************************/
03382 
03383 INT32 SGDisplayRoot::GetScrollOffset(void)
03384 {
03385     // Base class does nothing - There is no scroll offset
03386     return(0);
03387 }
03388 
03389 
03390 
03391 /***********************************************************************************************
03392 
03393 >   void SGDisplayRoot::RedrawScrollBar(SGMiscInfo *MiscInfo)
03394 
03395     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03396     Created:    3/2/95
03397 
03398     Inputs:     MiscInfo - The normal MiscInfo for SGDisplayNode events
03399 
03400     Purpose:    Causes the scroll bar, if any, to be redrawn.
03401                 In the base class, this method does nothing
03402 
03403     SeeAlso:    SGDisplayRootScroll::RedrawScrollBar
03404 
03405 ***********************************************************************************************/
03406 
03407 void SGDisplayRoot::RedrawScrollBar(SGMiscInfo *MiscInfo)
03408 {
03409     // The base class has no scrollbar, so does nothing
03410 }
03411 
03412 
03413 
03414 /***********************************************************************************************
03415 
03416 >   virtual SGDisplayItem *SGDisplayRoot::FindNextSelectedItem(
03417                                                 SGDisplayNode *CurrentItem = NULL,
03418                                                 BOOL *SkipGroup = NULL)
03419 
03420     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03421     Created:    10/2/95
03422 
03423     Inputs:     CurrentItem - NULL to find the first selected item, else
03424                 An SGDisplayItem (node) indicating the 'position' to search from for the next
03425                 selected item
03426     
03427     Outputs:    if Non-NULL, SkipGroup will be filled in with TRUE if the scan has just
03428                 skipped onto a new document/library group within the tree, or FALSE if the
03429                 scan remains in the same group as the last selected node was in. If CurrentItem
03430                 was NULL on entry, this returned value will be TRUE.
03431 
03432     Returns:    NULL (if no selection), or
03433                 A pointer to the first selected leaf of this tree which occurs after
03434                 the 'CurrentItem'. If CurrentItem is NULL, then the first selected
03435                 leaf node is returned.
03436 
03437     Purpose:    Scanning the selection within a Display Tree - this will scan over
03438                 multiple documents/libraries without blinking an eyelid.
03439 
03440                 Use to scan through multiple-group selections, or to find the
03441                 first selected item without having to know which document/library group
03442                 it belongs to.
03443 
03444     Notes:      This method relies heavily on the flat (root/group/item) structure of
03445                 current display trees - it will fail horribly if this simple layout is
03446                 changed.
03447 
03448                 Warning: It may be possible for groups AND items to be selected at one
03449                 time. This essentially filters Groups out of the selection scan.
03450 
03451     SeeAlso:    SGDisplayNode::FindSubtree; SGDisplayGroup::FindNextSelectedItem;
03452                 SGDisplayRoot::GetSelectedItemCount; SGDisplayGroup::GetSelectedItemCount;
03453                 SGDisplayRoot::FindNextSelectedGroup
03454 
03455 ***********************************************************************************************/
03456 
03457 SGDisplayItem *SGDisplayRoot::FindNextSelectedItem(SGDisplayNode *CurrentItem,
03458                                                     BOOL *SkipGroup)
03459 {
03460     BOOL ReturnSkip = (CurrentItem == NULL);
03461     SGDisplayItem *ReturnItem = NULL;
03462 
03463     // Find the current group node - my first child, or the the current item's parent.
03464     SGDisplayGroup *CurrentGroup;
03465     if (CurrentItem != NULL)
03466         CurrentGroup = (SGDisplayGroup *) CurrentItem->GetParent();
03467     else
03468         CurrentGroup = (SGDisplayGroup *) GetChild();
03469 
03470     ERROR3IF(CurrentGroup != NULL && !CurrentGroup->IsKindOf(CC_RUNTIME_CLASS(SGDisplayGroup)),
03471                 "The gallery displaytree seems to have mutated- "
03472                 "where have my lovely groups gone?");
03473 
03474     // Search each sibling group in turn until we find another selected leaf item
03475     while (CurrentGroup != NULL && ReturnItem == NULL)
03476     {
03477         ReturnItem = CurrentGroup->FindNextSelectedItem((SGDisplayItem *)CurrentItem);
03478         if (ReturnItem == NULL)
03479         {
03480             // No more selected items in this group - skip onto the next group, and flag
03481             // the fact that we had to skip to a different group
03482             ReturnSkip   = TRUE;
03483             CurrentGroup = (SGDisplayGroup *) CurrentGroup->GetNext();
03484             CurrentItem  = NULL;
03485         }
03486     }
03487 
03488     // Return the SkipGroup output
03489     if (SkipGroup != NULL)
03490         *SkipGroup = ReturnSkip;
03491 
03492     
03493     // And return NULL or the next selected node
03494     return(ReturnItem);
03495 }
03496 
03497 
03498 
03499 /***********************************************************************************************
03500 
03501 >   virtual INT32 SGDisplayRoot::GetSelectedItemCount(void)
03502 
03503     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03504     Created:    10/2/95
03505     
03506     Returns:    The number of items in this display tree which are selected (may return 0)
03507 
03508     Purpose:    To determine how many eaf items of this tree are selected
03509 
03510     Notes:      This scans the selection (is not cached), so calling it multiple times
03511                 should be avoided where possible
03512 
03513                 This method relies heavily on the flat (root/group/item) structure of
03514                 current display trees - it will fail horribly if this simple layout is
03515                 changed.
03516 
03517     SeeAlso:    SGDisplayRoot::FindNextSelectedItem; SGDisplayGroup::GetSelectedItemCount;
03518                 SGDisplayRoot::GetSelectedGroupCount
03519 
03520 ***********************************************************************************************/
03521 
03522 INT32 SGDisplayRoot::GetSelectedItemCount(void)
03523 {
03524     INT32 Count = 0;
03525 
03526     SGDisplayGroup *Ptr = (SGDisplayGroup *) GetChild();
03527     while (Ptr != NULL)
03528     {
03529         Count += Ptr->GetSelectedItemCount();
03530         Ptr = (SGDisplayGroup *) Ptr->GetNext();
03531     }
03532 
03533     return(Count);
03534 }
03535 
03536 
03537 /********************************************************************************************
03538 
03539 >   virtual void SGDisplayRoot::SelectRange(SGDisplayNode *PrimeNode, SGDisplayNode *AnchorNode);
03540 
03541     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
03542     Created:    26/5/95
03543 
03544     Inputs:     PrimeNode - The node which MUST be selected. May NOT be NULL.
03545 
03546                 AnchorNode - The other node, specifying a range of sibling nodes to
03547                 be selected. May be NULL, in which case only PrimeNode is selected
03548 
03549     Purpose:    Selects the PrimeNode, and if possible, all sibling nodes between it and
03550                 the Anchor node. If Anchor == NULL or is not found, only PrimeNode is
03551                 selected. Does not deselect any nodes - you should call SelectItems first
03552                 to clear the seln.
03553 
03554     Note:       The code to do the selection is now sub-contracted out to SelectRangeGroups
03555                 and SelectRangeItems, depending on the type of the two given nodes.
03556 
03557                 If prime node is an item and the other a group, nothing will be selected...
03558                 If prime node is an item and the null, the item will be selected...
03559 
03560 ********************************************************************************************/
03561 
03562 void SGDisplayRoot::SelectRange(SGDisplayNode *PrimeNode, SGDisplayNode *AnchorNode)
03563 {
03564     ERROR3IF(PrimeNode == NULL, "SGDisplayRoot::SelectRange - PrimeNode must be non-NULL");
03565 
03566     if (AnchorNode == NULL || PrimeNode->GetParent() != AnchorNode->GetParent())
03567     {
03568         // There is no anchor, or the prime/anchor nodes are not siblings, so
03569         // we cannot select a range - select PrimeNode, deselect all others
03570         ParentGallery->SelectItems(FALSE);
03571         ParentGallery->SelectGroups(FALSE);
03572 
03573         // Paint the list now to reduce the chance of large redraws if the
03574         // old/new selections are far apart.
03575         ParentGallery->PaintListNow();
03576 
03577         PrimeNode->SetSelected(TRUE);
03578         ParentGallery->PaintListNow();
03579     }
03580     else if (PrimeNode->IS_KIND_OF(SGDisplayItem) && (AnchorNode == NULL || AnchorNode->IS_KIND_OF(SGDisplayItem)))
03581     {
03582         // We are selecting a range of items so deselect all groups first
03583         ParentGallery->SelectGroups(FALSE); // Deselect all groups
03584         SelectRangeItems((SGDisplayItem *)PrimeNode, (SGDisplayItem *)AnchorNode);
03585     }
03586     else if (PrimeNode->IS_KIND_OF(SGDisplayGroup) && (AnchorNode == NULL || AnchorNode->IS_KIND_OF(SGDisplayGroup)))
03587     {
03588         // We are selecting a range of groups so deselect all items first
03589         ParentGallery->SelectItems(FALSE);  // Deselect all items
03590         SelectRangeGroups((SGDisplayGroup *)PrimeNode, (SGDisplayGroup *)AnchorNode);
03591     }
03592 
03593     // And ensure the gallery updates itself appropriately
03594     ParentGallery->SelectionHasChanged();
03595 }
03596 
03597 
03598 
03599 /***********************************************************************************************
03600 
03601 >   virtual SGDisplayGroup *SGDisplayRoot::FindNextSelectedGroup(
03602                                             SGDisplayNode *CurrentGroup = NULL)
03603 
03604     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03605     Created:    10/2/95
03606 
03607     Inputs:     CurrentGroup - NULL to find the first selected Group, else
03608                 An SGDisplayGroup indicating the 'position' to search from for the next
03609                 selected Group
03610 
03611     Returns:    NULL (if no selection), or
03612                 A pointer to the first selected leaf of this tree which occurs after
03613                 the 'CurrentGroup'. If CurrentGroup is NULL, then the first selected
03614                 leaf node is returned.
03615 
03616     Purpose:    Scanning the group-selection within a Display Tree.
03617 
03618     Notes:      This method relies heavily on the flat (root/group/Group) structure of
03619                 current display trees - it will fail horribly if this simple layout is
03620                 changed.
03621 
03622                 Warning: It may be possible for groups AND items to be selected at one
03623                 time. This essentially filters Items out of the selection scan.
03624 
03625     SeeAlso:    SGDisplayRoot::FindNextSelectedItem; SGDisplayRoot::GetSelectedGroupCount
03626 
03627 ***********************************************************************************************/
03628 
03629 SGDisplayGroup *SGDisplayRoot::FindNextSelectedGroup(SGDisplayNode *CurrentGroup)
03630 {
03631     // Find the current group node - my first child, or the the current Group's parent.
03632     if (CurrentGroup != NULL)
03633         CurrentGroup = ((SGDisplayGroup *) CurrentGroup)->GetNext();
03634     else
03635         CurrentGroup = (SGDisplayGroup *) GetChild();
03636 
03637     ERROR3IF(CurrentGroup != NULL && !CurrentGroup->IsKindOf(CC_RUNTIME_CLASS(SGDisplayGroup)),
03638                 "The gallery displaytree seems to have mutated- "
03639                 "where have my lovely groups gone?");
03640 
03641     // Search each sibling group in turn until we find another selected leaf Group
03642     while (CurrentGroup != NULL)
03643     {
03644         if (CurrentGroup->IsSelected())     // Found a selected group - return it
03645             return((SGDisplayGroup *)CurrentGroup);
03646 
03647         CurrentGroup = ((SGDisplayGroup *) CurrentGroup)->GetNext();
03648     }
03649 
03650     // No more selected groups
03651     return(NULL);
03652 }
03653 
03654 
03655 
03656 /***********************************************************************************************
03657 
03658 >   virtual INT32 SGDisplayRoot::GetSelectedGroupCount(void)
03659 
03660     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03661     Created:    10/2/95
03662     
03663     Returns:    The number of Groups in this display tree which are selected (may return 0)
03664 
03665     Purpose:    To determine how many eaf Groups of this tree are selected
03666 
03667     Notes:      This scans the selection (is not cached), so calling it multiple times
03668                 should be avoided where possible
03669 
03670                 This method relies heavily on the flat (root/group/Group) structure of
03671                 current display trees - it will fail horribly if this simple layout is
03672                 changed.
03673 
03674     SeeAlso:    SGDisplayRoot::FindNextSelectedGroup; SGDisplayGroup::GetSelectedGroupCount
03675 
03676 ***********************************************************************************************/
03677 
03678 INT32 SGDisplayRoot::GetSelectedGroupCount(void)
03679 {
03680     INT32 Count = 0;
03681 
03682     SGDisplayGroup *Ptr = (SGDisplayGroup *) GetChild();
03683     while (Ptr != NULL)
03684     {
03685         if (Ptr->IsSelected())
03686             Count++;
03687 
03688         Ptr = (SGDisplayGroup *) Ptr->GetNext();
03689     }
03690 
03691     return(Count);
03692 }
03693 
03694 
03695 
03696 
03697 
03698 /***********************************************************************************************
03699 
03700 >   static SGDisplayNode *SGDisplayRoot::FindNextItemInTree(SGDisplayNode *StartItem)
03701 
03702     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
03703     Created:    15/04/96
03704     
03705     Inputs:     StartItem   - Item from which to start looking for next item
03706     
03707     Returns:    The next item, or NULL if no further items found
03708 
03709     Purpose:    Given an item in the tree, find the next one. 
03710 
03711     Notes:      This function will skip virtualised groups, so if you need to do something
03712                 globally, don't call this...
03713 
03714     SeeAlso:    SGDisplayRoot::GetSelectedItemCount;
03715 
03716 ***********************************************************************************************/
03717 
03718 SGDisplayNode *SGDisplayRoot::FindNextItemInTree(SGDisplayNode *StartItem)
03719 {
03720     ERROR3IF(StartItem == NULL, "SGDisplayRoot::FindNextItemInTree given a NULL StartItem - bad");
03721     if(StartItem == NULL) return NULL;
03722 
03723     SGDisplayNode *Item = StartItem;
03724 
03725     // If this is the last item in the group, skip to the next group
03726     if (Item->GetNext() == NULL)
03727     {
03728         ERROR3IF(Item->GetParent() == NULL, "Tree linkage corruption");
03729 
03730         // Check if we're the last group
03731         if (Item->GetParent()->GetNext() != NULL)
03732         {
03733             SGDisplayNode *TmpItem = Item->GetParent()->GetNext();
03734 
03735             // Jump over any virtualised groups
03736             while(TmpItem != NULL && TmpItem->GetChild() == NULL)
03737                 TmpItem = TmpItem->GetNext();
03738 
03739             // Check if gone over last group
03740             if(TmpItem == NULL)
03741                 Item = NULL;
03742             else
03743                 Item = TmpItem->GetChild();
03744         }
03745         else
03746             Item = NULL;
03747     }
03748     else
03749         Item = Item->GetNext();     // Get next sibling item
03750 
03751     return Item;
03752 }
03753 
03754 
03755 
03756 
03757 
03758 
03759 
03760 
03761 
03762 
03763 
03764 /***********************************************************************************************
03765 
03766 >   SGDisplayRootScroll::SGDisplayRootScroll(SuperGallery *ParentGal)
03767 
03768     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03769     Created:    26/10/94
03770 
03771     Inputs:     ParentGal - A pointer to the parent gallery of this node/tree
03772 
03773     Purpose:    SGDisplayRootScroll constructor (makes root nodes invisible by default)
03774 
03775     SeeAlso:    SGDisplayNode::SGDisplayNode
03776 
03777 ***********************************************************************************************/
03778 
03779 SGDisplayRootScroll::SGDisplayRootScroll(SuperGallery *ParentGal) : SGDisplayRoot(ParentGal)
03780 {
03781     ScrollOffset = 0;
03782     IndentedButton = IBUTTON_NONE;
03783 
03784     // Read the scroll bar width - use half of the normal scroll bar width (and is an even number)
03785 PORTNOTE("galleries", "hard wired scrollbar width");
03786     ScrollBarWidth = 12;
03787 //  ScrollBarWidth = (CScroller::GetScrollerSize() / 2) & 0xFE;
03788     
03789     if (ScrollBarWidth < 10)    // But make sure it lies within a sensible range
03790         ScrollBarWidth = 10;
03791     if (ScrollBarWidth > 100)
03792         ScrollBarWidth = 100;
03793 }
03794 
03795 
03796 
03797 /***********************************************************************************************
03798 
03799 >   virtual void SGDisplayRootScroll::SetScrollOffset(INT32 NewOffset, SGMiscInfo *MiscInfo)
03800 
03801     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03802     Created:    21/1/95
03803 
03804     Inputs:     NewOffset - The new scroll offset.
03805                 This is a number between 0 and LengthOfList, in MILLIPOINTs
03806 
03807                 MiscInfo - the usual
03808 
03809     Purpose:    Changes the scroll offset, and if necessary, redraws the scroll bar
03810 
03811     Notes:      To ensure that the current scroll offset is within bounds, call
03812                 SetScrollOffset(GetScrollOffset(), MiscInfo);
03813 
03814     SeeAlso:    SGDisplayRootScroll::GetScrollOffset; SGDisplayRoot::SetScrollOffset
03815 
03816 ***********************************************************************************************/
03817 
03818 void SGDisplayRootScroll::SetScrollOffset(INT32 NewOffset, SGMiscInfo *MiscInfo)
03819 {
03820     // Ensure we don't scroll out of bounds
03821     if (NewOffset > ScrollExtent - MiscInfo->WindowHeight)
03822         NewOffset = ScrollExtent - MiscInfo->WindowHeight;
03823 
03824     if (NewOffset < 0)
03825         NewOffset = 0;
03826 
03827     // Make sure the scroll offset is aligned tidily with the output pixel grid
03828     GridLock(MiscInfo, NewOffset);
03829 
03830     // And if it has changed, set the new scroll offset, and scroll the window
03831     if (NewOffset != ScrollOffset)
03832     {
03833         DocCoord ScrollBy(0, NewOffset - ScrollOffset);
03834 
03835         if (!GetParentGallery()->AreYouRedrawingNow())
03836         {
03837             // A normal scroll - we are not inside the redraw code, so we can safely
03838             // assume that the screen display is up to date (and therefore can be scrolled
03839             // with 'ScrollArea') and that it is safe to invoke redraw.
03840 
03841             DocRect ScrollRect(0, 0, MiscInfo->MaxWidth - DevicePixels(MiscInfo, ScrollBarWidth),
03842                                 MiscInfo->WindowHeight);
03843 
03844             // Scroll the window. During the scroll, we make sure that the drag manager
03845             // has removed any eor or solid drag blobs from screen, so we don't copy
03846             // them as part of the scroll!
03847             DragManagerOp::RedrawStarting(GetParentGallery()->WindowID,
03848                                             GetParentGallery()->GetListGadgetID());
03849 
03850                 // And scroll
03851                 GetParentGallery()->ScrollArea(&ScrollRect, &ScrollBy);
03852 
03853                 // Set the new scroll offset, using the gridlocked value returned from ScrollArea
03854                 ScrollOffset += ScrollBy.y;
03855 
03856                 // Redraw immediately. If we scroll only a little bit, and do not redraw
03857                 // immediately, we get a small horizontal strip, plus the vertical scroll bar
03858                 // region, and so the redraw bounds is the entire window, so we redraw heaps
03859                 // more than actually changes, thus making scrolling dead slow.
03860                 GetParentGallery()->PaintListNow();
03861 
03862                 // And redraw the proportional scrollbar
03863                 DocRect ScrollBarRect(MiscInfo->MaxWidth - DevicePixels(MiscInfo, ScrollBarWidth),
03864                                         -ScrollExtent,
03865                                         MiscInfo->MaxWidth, 0);
03866                 GetParentGallery()->ForceRedrawOfArea(&ScrollBarRect);
03867 
03868                 // And finally, force another immediate repaint of the list gadget
03869                 GetParentGallery()->PaintListNow();
03870 
03871             DragManagerOp::RedrawFinished();
03872         }
03873         else
03874         {
03875             // Awooga! We have been forced to fix the scroll offset during a redraw (probably
03876             // because the window size has changed). We thus change the scroll offset, but
03877             // do not try to redraw or anything - hopefully the pending redraw will handle
03878             // everything.
03879 
03880             // Set the new scroll offset, using the gridlocked value returned from ScrollArea
03881             ScrollOffset += ScrollBy.y;
03882         }
03883     }
03884 }
03885 
03886 
03887 
03888 /***********************************************************************************************
03889 
03890 >   INT32 SGDisplayRootScroll::GetScrollOffset(void)
03891 
03892     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03893     Created:    21/1/95
03894 
03895     Returns:    The current Scroll offset. This is a positive MLLIPOINT value. Note that
03896                 virtual coordinates go from 0 to -Extent, so the point of VirtualSpace which
03897                 maps to the top left displayed pixel on screen is at (-ScrollOffset).
03898 
03899     Purpose:    Returns the current scroll offset for the gallery display list represented in
03900                 the tree.
03901 
03902     SeeAlso:    SGDisplayRootScroll::SetScrollOffset; SGDisplayRoot::GetScrollOffset
03903 
03904 ***********************************************************************************************/
03905 
03906 INT32 SGDisplayRootScroll::GetScrollOffset(void)
03907 {
03908     return(ScrollOffset);
03909 }
03910 
03911 
03912 
03913 /***********************************************************************************************
03914 
03915 >   virtual BOOL SGDisplayRootScroll::CalculateScrollRects(SGMiscInfo *MiscInfo,
03916                                         BOOL Translate,
03917                                         DocRect *UpButton, DocRect *DownButton,
03918                                         DocRect *Sausage, DocRect *PageUp = NULL,
03919                                         DocRect *PageDown = NULL, DocRect *ScrollRect = NULL)
03920 
03921     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03922     Created:    28/2/95
03923 
03924     Inputs:     MiscInfo - The misc info, as passed to all event handlers
03925                 Translate - TRUE to translate the returned rectangles to give Display-list
03926                 coordinates, FALSE to give the coordinates as offsets within the visible
03927                 display area on screen.
03928 
03929     Outputs:    The rects - Rectangles in millipoint offsets within the list gadget
03930                 of the various bits of the scrollbar: up/down buttons, plus the
03931                 sausage, and the page-up/down areas above/below the sausage. ScrollRect
03932                 is the all-inclusive rectangle containing all the others.
03933 
03934                 PageUp, PageDown, and ScrollRect may be NULL. All others may not be NULL
03935 
03936     Returns:    TRUE if the scroll rects are valid, FALSE if there is not enough room
03937                 for a scrollbar. If FALSE is returned, none of the outputs are valid, though
03938                 some of them may have been changed/corrupted.
03939 
03940     Purpose:    Determines where in the gadget the scroll bar rectangles are. Used by
03941                 the SGScrollDragTarget in order to determine if the mouse pointer still
03942                 lies over a given rectangle.
03943 
03944 ***********************************************************************************************/
03945 
03946 BOOL SGDisplayRootScroll::CalculateScrollRects(SGMiscInfo *MiscInfo,
03947                                                 BOOL Translate,
03948                                                 DocRect *UpButton, DocRect *DownButton,
03949                                                 DocRect *Sausage, DocRect *PageUp,
03950                                                 DocRect *PageDown, DocRect *pScrollRect)
03951 {
03952     ERROR3IF(MiscInfo == NULL || UpButton == NULL || DownButton == NULL || Sausage == NULL,
03953                 "SGDispayRootScroll::CalculateScrollRects - NULL params are illegal");
03954 
03955     // Calculate the strip into which the scrollbar must fit...
03956     INT32 ScrollWidth = DevicePixels(MiscInfo, ScrollBarWidth);
03957 
03958 #ifdef _DEBUG
03959     if (((MiscInfo->MaxWidth - ScrollWidth) > MiscInfo->MaxWidth) || (0 > MiscInfo->WindowHeight))
03960     {
03961         TRACE( _T("Bad MiscInfo in SGDispayRootScroll::CalculateScrollRects\n"));
03962         TRACE( _T("    MaxWidth     = %d\n"), MiscInfo->MaxWidth);
03963         TRACE( _T("    WindowHeight = %d\n"), MiscInfo->WindowHeight);
03964         TRACE( _T("    DisplayMode  = %d\n"), MiscInfo->DisplayMode);
03965         TRACE( _T("    PixelSize    = %d\n"), MiscInfo->PixelSize);
03966         TRACE( _T("ScrollBarWidth   = %d\n"), ScrollBarWidth);
03967         TRACE( _T("ScrollWidth      = %d\n"), ScrollWidth);
03968     }
03969 #endif
03970 
03971     DocRect ScrollRect(MiscInfo->MaxWidth - ScrollWidth, 0,
03972                         MiscInfo->MaxWidth, MiscInfo->WindowHeight);
03973 
03974     // Convert, if necessary, the position, into display-list offsets
03975     if (Translate)
03976         ScrollRect.Translate(0, -(ScrollOffset + MiscInfo->WindowHeight));
03977     
03978     GridLockRect(MiscInfo, &ScrollRect);
03979 
03980     if (!ScrollRect.IsValid())
03981         return(FALSE);
03982 
03983     // Calculate the squares for the up/down scroll buttons
03984     *UpButton =ScrollRect;
03985     UpButton->lo.x += DevicePixels(MiscInfo, 1);
03986     UpButton->lo.y = (UpButton->hi.y - ScrollWidth) + DevicePixels(MiscInfo, 1);
03987 
03988     *DownButton = *UpButton;
03989     DownButton->Translate(0, -(MiscInfo->WindowHeight -
03990                                 (ScrollWidth - DevicePixels(MiscInfo, 1)) ));
03991 
03992     // And make the scroll rect include only the central strip
03993     ScrollRect.Inflate(0, -(ScrollWidth - DevicePixels(MiscInfo, 1)));
03994 
03995     BOOL Result = CalculateSausageRect(MiscInfo, &ScrollRect, Sausage);
03996 
03997     if (PageUp != NULL)
03998     {
03999         *PageUp = ScrollRect;
04000         PageUp->lo.y = Sausage->hi.y;
04001     }
04002 
04003     if (PageDown != NULL)
04004     {
04005         *PageDown = ScrollRect;
04006         PageDown->hi.y = Sausage->lo.y;
04007     }
04008 
04009     if (pScrollRect != NULL)
04010         *pScrollRect = ScrollRect;
04011 
04012     return(Result);
04013 }
04014 
04015 
04016 
04017 /***********************************************************************************************
04018 
04019 >   BOOL SGDisplayRootScroll::CalculateSausageRect(SGMiscInfo *MiscInfo,
04020                                                 DocRect *ScrollBarRect, DocRect *Result)
04021 
04022     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04023     Created:    21/1/95
04024 
04025     Inputs:     MiscInfo - The misc info, as passed to all event handlers
04026                 ScrollBarRect - The bounds in which the scroll sausage can slide
04027 
04028     Outputs:    Result - Either unchanged (return == FALSE) or the rectangle to use
04029                 for the scroll sausage (return == TRUE)
04030 
04031     Returns:    TRUE if it succeeds; FALSE if it did nothing
04032                 (FALSE is returned when there is no room for a scroll sausage etc)
04033 
04034     Purpose:    Determines where within the given scroller the scroll 'sausage' (thumb)
04035                 should be positioned to give the correct display
04036 
04037     SeeAlso:    SGDisplayRootScroll::SetScrollOffset; SGDisplayRoot::GetScrollOffset
04038 
04039 ***********************************************************************************************/
04040 
04041 BOOL SGDisplayRootScroll::CalculateSausageRect(SGMiscInfo *MiscInfo,
04042                                                 DocRect *ScrollBarRect, DocRect *Result)
04043 {
04044     INT32 PixelSize = MiscInfo->PixelSize;
04045     
04046     // If the scroll bar hasn't got enough room to exist, it does not appear
04047     if (ScrollBarRect->hi.y - ScrollBarRect->lo.y < 5*PixelSize)
04048         return(FALSE);
04049 
04050     *Result = *ScrollBarRect;
04051     Result->Inflate(-PixelSize);    // Scroll sausage sits just inside the scroll rect
04052     Result->hi.x += PixelSize;      // But the window border already does the 1-pixel black
04053                                     // line on the right, so we actually touch the scroll
04054                                     // rect on this edge.
04055     GridLockRect(MiscInfo, Result);
04056 
04057     // If the sausage does not entirely fill the available space (we can scroll), then 
04058     // scale and translate the sausage rect appropriately
04059     if (ScrollExtent > MiscInfo->WindowHeight)
04060     {
04061         // It is necessary to calculate the center of the sausage and grow it outwards
04062         // from that, so that we can ensure it never gets shorter that being a small square.
04063 
04064         // Calculate the size of the proportional scroll sausage
04065         INT32 SausageHeight = (INT32) (((double)Result->Height()) *
04066                                 (((double)MiscInfo->WindowHeight) / (double)ScrollExtent));
04067 
04068         // Don't allow sausage to become smaller than a square
04069         if (SausageHeight < (ScrollBarWidth-2) * PixelSize)
04070             SausageHeight = (ScrollBarWidth-2) * PixelSize;
04071 
04072         // BarTravel is the range in which the *center* of the scroll sausage can move -
04073         // this is limited by the overall bar height and the size of the scroll sausage
04074         INT32 BarTravel = Result->Height() - SausageHeight;
04075 
04076         // Range in which the ScrollOffset is limited
04077         INT32 ScrollTravel = ScrollExtent - MiscInfo->WindowHeight;
04078 
04079         // From which we calculate how far down the bar the sausage center appears
04080         double MidOffset =  ((double)BarTravel *
04081                             (((double)ScrollOffset) / (double)ScrollTravel));
04082 
04083         // Convert this into a position within the scroll rectangle (subtract from the top,
04084         // taking into account the gap at the top of the travel to contain half the sausage)
04085         MidOffset = (double) Result->hi.y - (((double)SausageHeight)/2 + MidOffset);
04086 
04087         // Finally, shift and resize the scroll rectangle to generate the sausage rect
04088         Result->hi.y = (INT32) (-0.5 + MidOffset + SausageHeight/2);
04089         Result->lo.y = (INT32) (-0.5 + MidOffset - SausageHeight/2);
04090         GridLockRect(MiscInfo, Result);
04091     }
04092 
04093     return(TRUE);
04094 }
04095 
04096 
04097 
04098 /***********************************************************************************************
04099 
04100 >   virtual BOOL SGDisplayRootScroll::HandleEvent(SGEventType EventType, void *EventInfo,
04101                                             SGMiscInfo *MiscInfo)
04102 
04103     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04104     Created:    20/10/94
04105 
04106     Inputs:     See SGDisplayNode::HandleEvent
04107 
04108     Returns:    TRUE if the event was handled successfully
04109                 FALSE if it was not
04110 
04111     Purpose:    Handles a SuperGallery DisplayTree event
04112                 For this Scrollbar-providing version of the DisplayRoot node, adds a
04113                 scrollbar at the right side of the window, and updates the event info
04114                 appropriately to make the rest of the tree format to the left of that
04115                 scroll bar. The scrollbar is 100% automated - any attempt to change the
04116                 tree will result in the scrollbar updating as appropriate.
04117 
04118     Notes:      This overrides the pure virtual SGDisplayNode::HandleEvent method
04119 
04120                 A node need not handle a specific event - if it does not handle it, it
04121                 should return FALSE.
04122 
04123                 Redraw and Formatting handlers should never return TRUE, as this will
04124                 prevent the event from continuing through the tree.
04125 
04126                 Non-leaf-nodes must call SGDisplayNode::GiveEventToMyChildren in order
04127                 to pass the event down the tree.
04128 
04129     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayGroup::HandleEvent;
04130                 SGDisplayItem::HandleEvent; SGDisplayRoot::InitFormatInfo;
04131                 SGDisplayRoot::CalculateListExtent;
04132                 SGDisplayNode::GiveEventToMyChildren
04133 
04134 ***********************************************************************************************/
04135 
04136 BOOL SGDisplayRootScroll::HandleEvent(SGEventType EventType, void *EventInfo,
04137                                         SGMiscInfo *MiscInfo)
04138 {
04139     // THUMBMSG and BGFLUSH can be broadcast with MiscInfo == NULL. We ignore these msgs
04140     // so we just pass them on to the base class immediately.
04141     if (MiscInfo == NULL)
04142         return(SGDisplayRoot::HandleEvent(EventType, EventInfo, MiscInfo));
04143 
04144     SGMiscInfo NewMiscInfo(*MiscInfo);  // Ensure we don't corrupt the one passed in
04145 
04146     // Calculate the strip into which the scrollbar must fit...
04147     INT32 ScrollWidth = DevicePixels(MiscInfo, ScrollBarWidth);
04148 
04149     DocRect ScrollRect;
04150     DocRect UpButton;
04151     DocRect DownButton;
04152     DocRect PageUp;
04153     DocRect PageDown;
04154     DocRect SausageRect;
04155     BOOL HasScrollBar = CalculateScrollRects(MiscInfo, TRUE,
04156                                              &UpButton, &DownButton, &SausageRect,
04157                                              &PageUp, &PageDown, &ScrollRect);
04158 
04159     // Convince the DisplayTree the scroll area is outside the region they can use
04160     // We add 1 pixel to put a 1-pixel white gap between the scrollbar and the right end of all items
04161     NewMiscInfo.MaxWidth -= ScrollWidth;
04162 
04163     // Remember what the scroll extent used to be. If formatting causes a chnage to the extent,
04164     // we'll redraw ourself to make sure that our display is up to date.
04165     INT32 OldScrollExtent = ScrollExtent;
04166 
04167     switch (EventType)
04168     {
04169         case SGEVENT_FORMAT:
04170             if (HasScrollBar)
04171             {
04172                 SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
04173 
04174                 // Reset AvailableWidth so the first line doesn't overwrite the scrollbar!
04175                 FormatInfo->AvailableWidth = NewMiscInfo.MaxWidth;
04176             }
04177             break;
04178 
04179 
04180         case SGEVENT_REDRAW:
04181             // Does nothing here - redraw is now delayed until after we have called all our
04182             // children, so that rampant clipping problems in the font gallery do not cause
04183             // the scrollbar to be left in an overwritten state.
04184             break;
04185 
04186 
04187         case SGEVENT_MOUSECLICK:
04188             if (HasScrollBar && ScrollExtent > 0)
04189             {
04190                 SGMouseInfo *MouseInfo = GetMouseInfo(EventType, EventInfo);
04191                 SGDragType DragType = SGDRAG_NONE;
04192 
04193                 if (ScrollRect.ContainsCoord(MouseInfo->Position))
04194                 {
04195                     if (SausageRect.ContainsCoord(MouseInfo->Position))
04196                         DragType = SGDRAG_SAUSAGE;
04197                     else if (SausageRect.hi.y < MouseInfo->Position.y)
04198                         DragType = SGDRAG_PAGEUP;
04199                     else if (SausageRect.lo.y > MouseInfo->Position.y)
04200                         DragType = SGDRAG_PAGEDOWN;
04201                     else
04202                         return(TRUE);
04203                 }
04204 
04205                 if (DragType == SGDRAG_NONE && UpButton.ContainsCoord(MouseInfo->Position))
04206                     DragType = SGDRAG_SCROLLUP;
04207 
04208                 if (DragType == SGDRAG_NONE && DownButton.ContainsCoord(MouseInfo->Position))
04209                     DragType = SGDRAG_SCROLLDOWN;
04210 
04211                 if (DragType != SGDRAG_NONE)
04212                 {
04213                     SGScrollDragInfo *DragInfo =
04214                         new SGScrollDragInfo(this, DragType, MiscInfo,
04215                                             SausageRect.hi.y - MouseInfo->Position.y,
04216                                             MouseInfo->MenuClick);
04217 
04218                     if (DragInfo != NULL)
04219                         DragManagerOp::StartDrag(DragInfo, GetListWindow());
04220                 }
04221             }
04222             break;
04223 
04224 
04225         case SGEVENT_DRAGSTARTED:
04226             {
04227                 DragMessage *Msg = GetDragInfo(EventType, EventInfo);
04228 
04229                 // Is it a Drag Started message?
04230                 if (Msg->State == DragMessage::DRAGSTARTED)
04231                 {
04232                     // Is it a SuperGallery scroll drag?
04233                     // And is it a scroll drag for THIS display tree?
04234                     if (Msg->pInfo->IsKindOf(CC_RUNTIME_CLASS(SGScrollDragInfo)) &&
04235                         ((SGScrollDragInfo *)(Msg->pInfo))->GetDragRootNode() == this)
04236                     {
04237                         SuperGallery *ParentGallery = GetParentGallery();                       
04238 
04239                         // AMB comment out assignment to variable as variable is unused. Can this be right?
04240                         /* SGScrollDragTarget *DragTarget = */
04241                                 new SGScrollDragTarget(ParentGallery,
04242                                                         ParentGallery->GetListGadgetID());
04243                     }
04244                 }
04245             }
04246             break;
04247 
04248             default:
04249                 break;
04250     }
04251 
04252     // And pass the event on to the tree via our base class handler
04253     BOOL Result = SGDisplayRoot::HandleEvent(EventType, EventInfo,
04254                                     (HasScrollBar) ? &NewMiscInfo : MiscInfo);
04255 
04256     if (EventType == SGEVENT_FORMAT && ScrollExtent != OldScrollExtent && HasScrollBar)
04257     {
04258         // The scroll extent has changed as a result of this reformat.
04259         // We'd better invalidate the scrollbar rectangle to make sure it's up to date
04260         SuperGallery *ParentGal = GetParentGallery();
04261 
04262 
04263         // We modify the scrollbar rectangle before invalidating it - if the extent has changed,
04264         // the scrollbar may have been scrolled off the visible area!!
04265         // The easiest way to redraw it is to just redraw everything down the right side of the extent
04266         ScrollRect.hi.y = 0;
04267         ScrollRect.lo.y = -(ScrollExtent + MiscInfo->WindowHeight);
04268         ParentGal->ForceRedrawOfArea(&ScrollRect);
04269 
04270         // If the extent has got smaller, ensure the invalid redraw region includes
04271         // everything to the very bottom of the list
04272         SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
04273         if (FormatInfo->AccumulateBounds)
04274             FormatInfo->InvalidBounds.lo.y = min(-OldScrollExtent, -ScrollExtent);
04275     }
04276 
04277     if (EventType == SGEVENT_REDRAW && HasScrollBar)        // If room for a scrollbar, draw it
04278     {
04279         // Redraw is now left until after all children have been redrawn, as the font gallery
04280         // has a clipping problem where it was overwriting the scrollbar area. By drawing last
04281         // we at least end up in the correct screen display state.
04282         INT32 PixelSize = DevicePixels(MiscInfo, 1);
04283 
04284         SGRedrawInfo *RedrawInfo = GetRedrawInfo(EventType, EventInfo);
04285         RenderRegion *Renderer = RedrawInfo->Renderer;
04286 
04287         StartRendering(RedrawInfo, MiscInfo);
04288 
04289         // Note that we call the base class, so we can rely on it to clear the
04290         // background of the area outside the scrollbar
04291 
04292         // If we don't need to redraw anything, then we, er... don't redraw anything
04293         if (ScrollRect.IsIntersectedWith(RedrawInfo->Bounds) ||
04294             UpButton.IsIntersectedWith(RedrawInfo->Bounds)   ||
04295             DownButton.IsIntersectedWith(RedrawInfo->Bounds))
04296         {
04297             DialogColourInfo RedrawColours;
04298 
04299             // Fill the page-up and down rectangles with background colour.
04300             // We move the left and top/bottom edges by 1 pixel to accomodate
04301             // the black border lines that we draw around these areas.
04302             Renderer->SetLineWidth(0);
04303             DocColour trans(COLOUR_TRANS);
04304             Renderer->SetLineColour(trans);
04305             Renderer->SetFillColour(RedrawColours.ButtonFace());
04306 
04307             PageUp.lo.x += PixelSize;
04308             PageUp.hi.y -= PixelSize;
04309             if (PageUp.IsValid())
04310                 Renderer->DrawRect(&PageUp);
04311 
04312             PageDown.lo.x += PixelSize;
04313             PageDown.lo.y += PixelSize;
04314             if (PageDown.IsValid())
04315                 Renderer->DrawRect(&PageDown);
04316 
04317             // Draw a 1-pixel black border around the region
04318             // (down left edge and between scroll bar and arrows)
04319             // We draw these lines as filled rectangles to avoid the fact that lines
04320             // draw in different blimming places in different render regions
04321             DocColour black(COLOUR_BLACK);
04322             Renderer->SetFillColour(black);
04323 
04324             Renderer->DrawPixelLine(DocCoord(ScrollRect.lo.x, DownButton.lo.y),
04325                                 DocCoord(ScrollRect.lo.x, UpButton.hi.y));
04326             Renderer->DrawPixelLine(DocCoord(ScrollRect.lo.x, ScrollRect.hi.y - PixelSize),
04327                                 DocCoord(ScrollRect.hi.x, ScrollRect.hi.y - PixelSize));
04328             Renderer->DrawPixelLine(DocCoord(ScrollRect.lo.x, ScrollRect.lo.y),
04329                                 DocCoord(ScrollRect.hi.x, ScrollRect.lo.y));
04330 
04331             // Draw the scroll sausage
04332             DrawPlinth(RedrawInfo, MiscInfo, &RedrawColours, &SausageRect, FALSE);
04333 
04334             Renderer->DrawPixelLine(DocCoord(ScrollRect.lo.x, SausageRect.hi.y),
04335                                 DocCoord(ScrollRect.hi.x, SausageRect.hi.y));
04336             Renderer->DrawPixelLine(DocCoord(ScrollRect.lo.x, SausageRect.lo.y - PixelSize),
04337                                 DocCoord(ScrollRect.hi.x, SausageRect.lo.y - PixelSize));
04338 
04339             // Draw the scroll-up and scroll-down buttons
04340             DrawPlinth(RedrawInfo, MiscInfo, &RedrawColours,
04341                             &UpButton, (IndentedButton == IBUTTON_UP),
04342                             _R(IDB_GALLERY_SCROLLUP));
04343 
04344             DrawPlinth(RedrawInfo, MiscInfo, &RedrawColours,
04345                             &DownButton, (IndentedButton == IBUTTON_DOWN),
04346                             _R(IDB_GALLERY_SCROLLDOWN));
04347         }
04348 
04349         StopRendering(RedrawInfo, MiscInfo);
04350     }
04351 
04352     return(Result);
04353 }
04354 
04355 
04356 
04357 /***********************************************************************************************
04358 
04359 >   void SGDisplayRootScroll::RedrawScrollBar(SGMiscInfo *MiscInfo)
04360 
04361     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04362     Created:    3/2/95
04363 
04364     Inputs:     MiscInfo - The normal MiscInfo for SGDisplayNode events
04365 
04366     Purpose:    Causes the scroll bar, if any, to be redrawn, ensuring that it shows the
04367                 correct information for the current format of the display list
04368 
04369     SeeAlso:    SGDisplayRootScroll::RedrawScrollBar
04370 
04371 ***********************************************************************************************/
04372 
04373 void SGDisplayRootScroll::RedrawScrollBar(SGMiscInfo *MiscInfo)
04374 {
04375     // Calculate the strip into which the scrollbar must fit...
04376     INT32 ScrollWidth = DevicePixels(MiscInfo, ScrollBarWidth);
04377 
04378     DocRect ScrollRect(MiscInfo->MaxWidth - ScrollWidth, -MiscInfo->WindowHeight,
04379                         MiscInfo->MaxWidth, 0);
04380     ScrollRect.Translate(0, -ScrollOffset);
04381     GridLockRect(MiscInfo, &ScrollRect);
04382 
04383     GetParentGallery()->ForceRedrawOfArea(&ScrollRect);
04384 }
04385 
04386 
04387 
04388 
04389 
04390 
04391 
04392 
04393 
04394 
04395 
04396 
04397 
04398 
04399 /***********************************************************************************************
04400 
04401 >   SGDisplayGroup::SGDisplayGroup()
04402 
04403     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04404     Created:    31/10/94
04405 
04406     Purpose:    SGDisplayGroup constructor. NOTE that to initialise the parent gallery,
04407                 document, and library pointers, you should use the other constructor.
04408 
04409 ***********************************************************************************************/
04410 
04411 SGDisplayGroup::SGDisplayGroup()
04412 {
04413     Child = NULL;
04414 
04415     ParentGallery  = NULL;
04416     ParentDocument = NULL;
04417     ParentLibrary  = NULL;
04418 
04419     ChildArea.MakeEmpty();
04420 
04421     TRACE( _T("Warning: Using default constructor for SGDisplayGroup is silly\n"));
04422 }
04423 
04424 
04425 
04426 /***********************************************************************************************
04427 
04428 >   SGDisplayGroup::SGDisplayGroup(SuperGallery *ParentGal,
04429                                     Document *ParentDoc = NULL, Library *ParentLib = NULL)
04430 
04431     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04432     Created:    31/10/94
04433 
04434     Inputs:     ParentGal - points to the SuperGallery object which 'owns' this node
04435                 ParentDoc - NULL, or a pointer to the document this group references
04436                 ParentLib - NULL, or a pointer to the library this group references
04437 
04438     Purpose:    SGDisplayGroup constructor. Initialises the Group's parent pointers to
04439                 point at its parent(s). Note that generally speaking, one of ParentDoc,
04440                 ParentLib will be NULL, and the other will be non-NULL. (This is not the
04441                 case, however, for the Font gallery!)
04442 
04443 ***********************************************************************************************/
04444 
04445 SGDisplayGroup::SGDisplayGroup(SuperGallery *ParentGal,
04446                                 Document *ParentDoc, Library *ParentLib)
04447 {
04448     // Sanity checks
04449     ERROR3IF(ParentGal == NULL, "SGDisplayGroup needs a parent gallery!");
04450     ERROR3IF(ParentDoc != NULL && ParentLib != NULL,
04451                 "SGDisplayGroup cannot have BOTH a doc and lib for parents!");
04452 
04453     Child = NULL;
04454 
04455     ParentGallery  = ParentGal;
04456     ParentDocument = ParentDoc;
04457     ParentLibrary  = ParentLib;
04458 
04459     if (ParentLibrary != NULL)      // Library groups (which come off disc) default to folded
04460         Flags.Folded = TRUE;
04461 
04462     ChildArea.MakeEmpty();
04463 
04464     ReadGroupTitle();
04465 }
04466 
04467 
04468 
04469 /***********************************************************************************************
04470 
04471 >   virtual SGDisplayNode *SGDisplayGroup::GetChild(void) const
04472 
04473     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04474     Created:    20/10/94 (Made virtual 13/5/95)
04475     Returns:    A pointer to the first child of this SGDisplayNode object, or NULL
04476 
04477     Purpose:    Finds the child of this DisplayTree Node.
04478                 Returns NULL if you have reached the boundary of the tree
04479 
04480     SeeAlso:    SuperGallery; SGDisplayNode::GetParent;
04481                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
04482 
04483 ***********************************************************************************************/
04484 
04485 SGDisplayNode *SGDisplayGroup::GetChild(void) const
04486 {
04487     return(Child);
04488 }
04489 
04490 
04491 
04492 /***********************************************************************************************
04493 
04494 >   void SGDisplayGroup::SetChild(SGDisplayNode *NewChild)
04495 
04496     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04497     Created:    13/5/95
04498 
04499     Inputs:     A pointer to the new first-child of this SGDisplayNode object
04500 
04501     Purpose:    Sets the child of this DisplayTree Node. Overrides to base class method
04502                 to allow the child to be set.
04503 
04504     SeeAlso:    SuperGallery; SGDisplayNode::GetParent;
04505                 SGDisplayNode::GetNext; SGDisplayNode::GetPrevious
04506 
04507 ***********************************************************************************************/
04508 
04509 void SGDisplayGroup::SetChild(SGDisplayNode *NewChild)
04510 {
04511     Child = NewChild;
04512 }
04513 
04514 
04515 
04516 /***********************************************************************************************
04517 
04518 >   virtual BOOL SGDisplayGroup::SetFoldedState(BOOL NewState, BOOL ForceRedraw = TRUE)
04519 
04520     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04521     Created:    15/4/95
04522 
04523     Inputs:     NewState - TRUE to fold, FALSE to unfold
04524 
04525                 ForceRedraw - TRUE to force a reformat-and-redraw of the group, or FALSE
04526                 to mark the tree format invalid but not bother actually reformatting and
04527                 redrawing just yet (usually only set to FALSE when constructing the
04528                 DisplayTree before the gallery window is opened, for AutoFolding)
04529 
04530     Returns:    TRUE if the new state is different from the old state (if anything has changed)
04531 
04532     Purpose:    Folds or unfolds a display group
04533 
04534     Notes:      If changed, forces an immediate reformat and redraw of the Display list
04535 
04536 ***********************************************************************************************/
04537 
04538 BOOL SGDisplayGroup::SetFoldedState(BOOL NewState, BOOL ForceRedraw)
04539 {
04540     if ((BOOL)Flags.Folded != NewState)
04541     {
04542         SuperGallery *ParentGal = GetParentGallery();
04543 
04544         // Devirtualise the group. If there were problems, keep it folded...
04545         if (!NewState && IsVirtualised())
04546         {
04547             // For library galleries we need to set the Quiet button status ready to un-supress errors
04548             ParentGal->SetQuietStatus(FALSE);
04549 
04550             // On older machines this can take a couple of seconds, so we need some feedback...
04551             Progress ProgMsg(_R(IDS_GALLERY_PREPARE_FOR_UNFOLD), -1, FALSE);
04552 
04553             if(!DeVirtualise()) 
04554                 return FALSE;
04555         }
04556 
04557         Flags.Folded = NewState;
04558         ChildArea.MakeEmpty();
04559 
04560         ParentGal->InvalidateCachedFormat();
04561 
04562         if (ForceRedraw)
04563             ParentGal->ReformatAndRedrawIfNecessary();
04564 
04565         return(TRUE);
04566     }
04567 
04568     return(FALSE);
04569 }
04570 
04571 
04572 
04573 /***********************************************************************************************
04574 
04575 >   virtual void SGDisplayGroup::ForceRedrawOfMyselfAndChildren(void)
04576 
04577     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04578     Created:    17/1/95
04579 
04580     Purpose:    Uses the cached FormatRect to force-redraw the appropriate part of the 
04581                 SuperGallery display window to cause myself to be redrawn. Also redraws
04582                 the entire area that my children will occupy, thus redrawing this entire
04583                 category.
04584 
04585     SeeAlso:    SuperGallery::RedrawArea
04586 
04587 ***********************************************************************************************/
04588 
04589 void SGDisplayGroup::ForceRedrawOfMyselfAndChildren(void)
04590 {
04591     SuperGallery *ParentGallery = GetParentGallery();
04592     if (ParentGallery != NULL)
04593     {
04594         DocRect WholeRect(FormatRect);              // My own rectangle
04595 
04596         if (!Flags.Folded && GetChild() != NULL && !ChildArea.IsEmpty())
04597         {
04598             // Union with child bounds rect, if any
04599             WholeRect.Union(ChildArea);
04600         }
04601 
04602         ParentGallery->ForceRedrawOfArea(&WholeRect);
04603     }
04604 }
04605 
04606 
04607 
04608 /***********************************************************************************************
04609 
04610 >   virtual SuperGallery *SGDisplayGroup::GetParentGallery(void) const
04611 
04612     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04613     Created:    28/10/94
04614 
04615     Returns:    The parent SuperGallery, or NULL
04616 
04617     Purpose:    Returns the SuperGallery which 'owns' this node (and its subtree)
04618 
04619     SeeAlso:    SGDisplayGroup::GetParentLibrary; SGDisplayGroup::GetParentDocument;
04620                 SGDisplayNode::GetParentGallery
04621 
04622 ***********************************************************************************************/
04623 
04624 SuperGallery *SGDisplayGroup::GetParentGallery(void) const
04625 {
04626     return(ParentGallery);
04627 }
04628 
04629 
04630 
04631 /***********************************************************************************************
04632 
04633 >   virtual SGDisplayItem *SGDisplayGroup::FindNextSelectedItem(
04634                                                 SGDisplayItem *CurrentItem = NULL)
04635 
04636     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04637     Created:    18/1/95
04638 
04639     Inputs:     CurrentItem - NULL to find the first selected item, else
04640                 An item indicating the 'position' to search from for the next selected item
04641     
04642     Returns:    NULL (if no selection), or
04643                 A pointer to the first selected child of this node which occurs after
04644                 the 'CurrentItem'. If CurrentItem is NULL, then the first selected
04645                 child node is returned.
04646 
04647     Purpose:    Scanning the selection within a Group (document/library).
04648                 Use after finding the subtree for a given doc/lib with FindSubtree, to
04649                 scan through items in the selection.
04650 
04651     SeeAlso:    SGDisplayNode::FindSubtree; SGDisplayGroup::GetSelectedItemCount()
04652 
04653 ***********************************************************************************************/
04654 
04655 SGDisplayItem *SGDisplayGroup::FindNextSelectedItem(SGDisplayItem *CurrentItem)
04656 {
04657     if (CurrentItem == NULL)
04658         CurrentItem = (SGDisplayItem *) GetChild();
04659     else
04660     {
04661         if (CurrentItem->GetParent() != this)   // Sanity check
04662         {
04663             ERROR3("Asking for next selected when previous was not in this group! Starting from first child");
04664             CurrentItem = (SGDisplayItem *) GetChild();
04665         }
04666         else
04667             CurrentItem = (SGDisplayItem *) CurrentItem->GetNext();
04668     }
04669 
04670     while (CurrentItem != NULL && !CurrentItem->IsSelected())
04671         CurrentItem = (SGDisplayItem *) CurrentItem->GetNext();
04672 
04673     return(CurrentItem);
04674 }
04675 
04676 
04677 
04678 /***********************************************************************************************
04679 
04680 >   virtual INT32 SGDisplayGroup::GetSelectedItemCount(void)
04681 
04682     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04683     Created:    18/1/95
04684 
04685     
04686     Returns:    The number of items in this group which are selected (may return 0)
04687 
04688     Purpose:    To determine how many child items of this group are selected
04689 
04690     Notes:      This scans the selection (is not cached), so calling it multiple times
04691                 should be avoided where possible
04692 
04693     SeeAlso:    SGDisplayNode::FindSubtree; SGDisplayGroup::FindNextSelectedItem
04694 
04695 ***********************************************************************************************/
04696 
04697 INT32 SGDisplayGroup::GetSelectedItemCount(void)
04698 {
04699     INT32 Count = 0;
04700     SGDisplayItem *Item = FindNextSelectedItem(NULL);
04701 
04702     while (Item != NULL)
04703     {
04704         if (Item->IsSelected())
04705             Count++;
04706 
04707         Item = FindNextSelectedItem(Item);
04708     }
04709 
04710     return(Count);
04711 }
04712 
04713 
04714 
04715 /********************************************************************************************
04716 
04717 >   virtual void SGDisplayGroup::SelectItems(BOOL SelectThem, BOOL Exclusive = FALSE,
04718                         Document *ParentDocument = NULL, Library *ParentLibrary = NULL)
04719 
04720     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04721     Created:    20/1/95
04722 
04723     Inputs:     SelectThem - TRUE to select the given items, FALSE to deselect them
04724 
04725                 Exclusive  - TRUE to apply this action to all items *outside* the given
04726                 range, FALSE to apply it to all items *inside* the range.
04727 
04728                 Document - NULL, or the document which defines the range of items to affect
04729 
04730                 Library - NULL, or the library which defines the range of items to affect
04731 
04732     Purpose:    To select/deselect groups of display items in this Gallery display.
04733                 Do not call this method - use the SuperGallery version
04734 
04735     Notes:      To select all items in a range, and deselect all items outside the range,
04736                 you need to use 2 calls to this method.
04737 
04738                 To select/deselect all items in the display, pass FALSE, NULL, NULL to
04739                 the last 3 parameters. (If Doc/Lib are both NULL, 'Exclusive' has no effect)
04740 
04741                 OVERRIDES the base SGDisplayNode method to handle the last 3 paramters
04742                 properly. Firstly, groups won't select themselves; secondly, the call is
04743                 only passed on to my children if they are in the requested range.
04744 
04745     SeeAlso:    SuperGallery::SelectItems; SGDisplayNode::SelectItems
04746 
04747 ********************************************************************************************/
04748 
04749 void SGDisplayGroup::SelectItems(BOOL SelectThem, BOOL Exclusive,
04750                                 Document *ParentDoc, Library *ParentLib)
04751 {
04752     BOOL InRange = TRUE;
04753     
04754     // If we are selecting items, make sure we (a group) are deselected
04755     if (SelectThem && Flags.CanSelect)
04756         SetSelected(FALSE);
04757 
04758     // If anythign other than entire-display-tree was requested, then check if it includes
04759     // our children...
04760     if (ParentDoc != NULL || ParentLib != NULL)
04761     {
04762         // First, do we contain display stuff for the requested Doc/Lib?
04763         InRange = (ParentDocument == ParentDoc) || (ParentLibrary == ParentLib);
04764 
04765         // Next, was the range inclusive (everything in doc/lib) or
04766         // exclusive (everything outside doc/lib)?
04767         if (Exclusive)
04768             InRange = !InRange;
04769     }
04770 
04771     if (!InRange)       // My children are not included in the range specified - return
04772         return;
04773 
04774     // Now, pass the selection request on to my children
04775     SGDisplayNode *Ptr = GetChild();
04776     while (Ptr != NULL)
04777     {
04778         Ptr->SelectItems(SelectThem, Exclusive, ParentDocument, ParentLibrary);
04779         Ptr = Ptr->GetNext();
04780     }
04781 }
04782 
04783 
04784 
04785 /********************************************************************************************
04786 
04787 >   virtual void SGDisplayGroup::SelectGroups(BOOL SelectThem, BOOL Exclusive = FALSE,
04788                         Document *ParentDocument = NULL, Library *ParentLibrary = NULL)
04789 
04790     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04791     Created:    14/5/95
04792 
04793     Inputs:     SelectThem - TRUE to select the given groups, FALSE to deselect them
04794 
04795                 Exclusive  - TRUE to apply this action to all groups *outside* the given
04796                 range, FALSE to apply it to all groups *inside* the range.
04797 
04798                 Document - NULL, or the document which defines the range of groups to affect
04799 
04800                 Library - NULL, or the library which defines the range of groups to affect
04801 
04802     Purpose:    To select/deselect sets of display groups in this Gallery display.
04803                 All groups whose state changes will force redraw themselves
04804 
04805                 If selecting, all non-group nodes in the tree will be deselected
04806 
04807                 Do not call this method - use the SuperGallery version
04808 
04809     Notes:      To select all groups in a range, and deselect all groups outside the range,
04810                 you need to use 2 calls to this method.
04811 
04812                 To select/deselect all groups in the display, pass FALSE, NULL, NULL to
04813                 the last 3 parameters. (If Doc/Lib are both NULL, 'Exclusive' has no effect)
04814 
04815     SeeAlso:    SuperGallery::SelectGroups; SGDisplayGroup::SelectGroups
04816 
04817 ********************************************************************************************/
04818 
04819 void SGDisplayGroup::SelectGroups(BOOL SelectThem, BOOL Exclusive,
04820                                 Document *ParentDoc, Library *ParentLib)
04821 {
04822     BOOL InRange = TRUE;
04823     
04824     // If anything other than entire-display-tree was requested, then check if it includes us
04825     if (ParentDoc != NULL || ParentLib != NULL)
04826     {
04827         // First, do we contain display stuff for the requested Doc/Lib?
04828         InRange = (ParentDocument == ParentDoc) || (ParentLibrary == ParentLib);
04829 
04830         // Next, was the range inclusive (everything in doc/lib) or
04831         // exclusive (everything outside doc/lib)?
04832         if (Exclusive)
04833             InRange = !InRange;
04834     }
04835 
04836     // Select myself if I am withint the specified range
04837     if (InRange && Flags.CanSelect /*&& !Flags.Invisible*/)
04838         SetSelected(SelectThem);
04839 
04840     // Now, pass the selection request on to my children, so the items will be
04841     // deselected if necessary
04842     SGDisplayNode *Ptr = GetChild();
04843     while (Ptr != NULL)
04844     {
04845         Ptr->SelectGroups(SelectThem, Exclusive, ParentDocument, ParentLibrary);
04846         Ptr = Ptr->GetNext();
04847     }
04848 }
04849 
04850 
04851 /***********************************************************************************************
04852 
04853 >   void SGDisplayGroup::ReadGroupTitle(void)
04854 
04855     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04856     Created:    31/10/94
04857 
04858     Purpose:    Reads the title text for this Group from its parent document/library
04859                 This information is cached, but at present it is re-read on each
04860                 redraw request to ensure it is kept up to date
04861 
04862 ***********************************************************************************************/
04863 
04864 void SGDisplayGroup::ReadGroupTitle(void)
04865 {
04866     // Generate the text to be displayed in this group heading
04867     if (ParentDocument != NULL)
04868     {
04869         String_256 NewTitle;
04870         const String_256 DocTitle = ParentDocument->GetTitle();
04871         NewTitle.MakeMsg(_R(IDS_GALGROUPDOCUMENT), (const TCHAR *) DocTitle);
04872 
04873         // Truncate the name into our 64-char buffer
04874         NewTitle.Left(&TitleText, 63);
04875     }
04876 PORTNOTE("galleries", "disabled folder updated stuff")
04877 #ifndef EXCLUDE_FROM_XARALX
04878     else if (ParentLibrary != NULL)
04879     {
04880         ParentLibrary->GetLibraryTitle(&TitleText);
04881         //>> webster (Adrian 02/01/97)
04882         UINT32 nModified = ParentLibrary->GetModified();
04883         if (nModified == FOLDER_UPDATED)
04884         {
04885             String_256 strUpdated(_R(IDS_FOLDERUPDATED));
04886             TitleText += strUpdated;
04887         }
04888         if (nModified == FOLDER_NEW)
04889         {
04890             String_256 strNew(_R(IDS_FOLDERNEW));
04891             TitleText += _T(" ");
04892             TitleText += strNew;
04893         }
04894         //<< webster
04895     }
04896 #endif
04897 }
04898 
04899 
04900 
04901 /***********************************************************************************************
04902 
04903 >   virtual void SGDisplayNode::DragWasReallyAClick(SGMouseInfo *MouseInfo,
04904                                                     SGMiscInfo *MiscInfo)
04905 
04906     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04907     Created:    15/3/95
04908 
04909     Inputs:     MouseInfo - The mouse info passed to the original click handler
04910                 MiscInfo - The misc info passed to the original click handler
04911 
04912     Purpose:    Handles a mouse click event. This is a callback function - drags of
04913                 items from galleries will call this function back if the drag turns
04914                 out to just be a click.
04915 
04916     Notes:      SELECTABLE groups override this method to call DefaultClickHandler when
04917                 drags on them turn into clicks
04918 
04919     Documentation:  docs\howtouse\sgallery.doc
04920 
04921     SeeAlso:    SGDisplayGroup::HandleEvent; SGDisplayNode::DefaultClickHandler
04922 
04923 ***********************************************************************************************/
04924 
04925 void SGDisplayGroup::DragWasReallyAClick(SGMouseInfo *MouseInfo, SGMiscInfo *MiscInfo)
04926 {
04927     // Just get default selection action to be applied for this click.
04928     // The TRUE indicates that this is a drag-click, and we previously called 
04929     // the DefaultPreDragHandler - we don't want it to do those same actions twice!
04930 
04931     // But we only do anything at all if we're a selectable group
04932     if (Flags.CanSelect)
04933         DefaultClickHandler(MouseInfo, MiscInfo, TRUE);
04934 }
04935 
04936 
04937 
04938 /***********************************************************************************************
04939 
04940 >   virtual BOOL SGDisplayGroup::HandleEvent(SGEventType EventType, void *EventInfo,
04941                                              SGMiscInfo *MiscInfo)
04942 
04943     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04944     Created:    20/10/94
04945 
04946     Inputs:     See SGDisplayNode::HandleEvent
04947 
04948     Returns:    TRUE if the event was handled successfully
04949                 FALSE if it was not
04950 
04951     Purpose:    Handles a SuperGallery DisplayTree event
04952 
04953     Notes:      This overrides the pure virtual SGDisplayNode::HandleEvent method
04954 
04955                 A node need not handle a specific event - if it does not handle it, it
04956                 should return FALSE.
04957 
04958                 Redraw and Formatting handlers should never return TRUE, as this will
04959                 prevent the event from continuing through the tree.
04960 
04961                 Non-leaf-nodes must call SGDisplayNode::GiveEventToMyChildren in order
04962                 to pass the event dow the tree.
04963 
04964     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayNode::GiveEventToMyChildren
04965 
04966 ***********************************************************************************************/
04967 
04968 BOOL SGDisplayGroup::HandleEvent(SGEventType EventType, void *EventInfo,
04969                                  SGMiscInfo *MiscInfo)
04970 {
04971     TRACEUSER( "Matt", _T("SGDisplayGroup::HandleEvent called\n"));
04972     // And handle the specific event
04973     switch (EventType)
04974     {
04975         case SGEVENT_FORMAT:
04976             {
04977                 SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
04978 
04979                 // If we've recently folded or unfolded (ChildArea is Empty) then change the
04980                 // format rectangle to ensure everything below this point is redrawn
04981                 if (ChildArea.IsEmpty())
04982                     FormatRect = DocRect(0,0,0,0);
04983 
04984                 if (!Flags.Invisible)
04985                 {
04986                     CalculateFormatRect(FormatInfo, MiscInfo, DefaultGroupWidth, DefaultGroupHeight);
04987                 }
04988                 else
04989                 {
04990                     CalculateFormatRect(FormatInfo, MiscInfo, DefaultGroupWidth, 0);
04991                 }
04992             }
04993             break;
04994 
04995         case SGEVENT_REDRAW:
04996             {
04997                 DocRect MyRect = FormatRect;    // Rely on cached format info being correct
04998 
04999                 SGRedrawInfo *RedrawInfo = GetRedrawInfo(EventType, EventInfo);
05000 
05001                 if (IMustRedraw(RedrawInfo) && !Flags.Invisible && Flags.CanSelect)
05002                 {
05003                     StartRendering(RedrawInfo, MiscInfo);
05004 
05005                     DocColour Col(192, 192, 192);   //128, 128, 128);
05006                     Col.SetSeparable(FALSE);        // Do not colour correct or separate this colour!
05007 
05008                     RedrawInfo->Renderer->SetLineWidth(0);
05009                     RedrawInfo->Renderer->SetLineColour(RedrawInfo->Transparent);
05010                     RedrawInfo->Renderer->SetFillColour(Col);
05011                     RedrawInfo->Renderer->DrawRect(&MyRect);
05012 
05013                     ReadGroupTitle();       // (Re)Cache the title text
05014     
05015                     // Plot the folder glyph (an 18x14 bitmap)
05016                     DocRect GlyphRect(MyRect);
05017                     GlyphRect.lo.x += DevicePixels(MiscInfo, 2);
05018                     GlyphRect.hi.x = GlyphRect.lo.x + DevicePixels(MiscInfo, 18);
05019 
05020                     // Center the bitmap's 14 pixels of height in our strip
05021                     INT32 Excess = GlyphRect.Height() - DevicePixels(MiscInfo, 14);
05022                     Excess = GridLock(MiscInfo, Excess / 2);
05023                     GlyphRect.lo.y += Excess;
05024                     GlyphRect.hi.y = GlyphRect.lo.y + DevicePixels(MiscInfo, 14);
05025                     MyRect.lo.x = GlyphRect.hi.x;
05026 
05027                     #ifdef _DEBUG
05028                         if(IsVirtualised())
05029                         {
05030                             // in debug builds we get a green rectangle to denote virtualised-ness...
05031                             DrawBitmap(RedrawInfo->Renderer, &GlyphRect,
05032                                 (Flags.Folded) ? _R(IDB_GALLERY_FOLD2) : _R(IDB_GALLERY_FOLD0));
05033                         } else {
05034                             DrawBitmap(RedrawInfo->Renderer, &GlyphRect,
05035                                         (Flags.Folded) ? _R(IDB_GALLERY_FOLD1) : _R(IDB_GALLERY_FOLD0));
05036                         }
05037                     #else
05038                         DrawBitmap(RedrawInfo->Renderer, &GlyphRect,
05039                                     (Flags.Folded) ? _R(IDB_GALLERY_FOLD1) : _R(IDB_GALLERY_FOLD0));
05040                     #endif
05041 
05042                     // Mode selection rectangle one pixel to the right so it looks a bit better
05043                     MyRect.lo.x += DevicePixels(MiscInfo, 1);
05044 
05045                     if (MyRect.lo.x < MyRect.hi.x)  // If still room left, draw title
05046                     {
05047                         // Space between text and glyph
05048                         MyRect.lo.x += DevicePixels(MiscInfo, 3);
05049 
05050                         if (Flags.Selected)
05051                         {
05052                             // Fill the entire text background with the 'selected' colour
05053                             RedrawInfo->Renderer->SetFillColour(RedrawInfo->SelBackground);
05054                             RedrawInfo->Renderer->DrawRect(&MyRect);
05055 
05056                             RedrawInfo->Renderer->SetFixedSystemTextColours(&RedrawInfo->SelForeground, &RedrawInfo->SelBackground);
05057                         }
05058                         else
05059                             RedrawInfo->Renderer->SetFixedSystemTextColours(&RedrawInfo->Foreground, &Col);
05060 
05061                         // Space between text and glyph
05062                         MyRect.lo.x += DevicePixels(MiscInfo, 3);
05063     
05064                         if (MyRect.lo.x < MyRect.hi.x)  // If still room left, draw text
05065                             RedrawInfo->Renderer->DrawFixedSystemText(&TitleText, MyRect);
05066                     }
05067 
05068                     StopRendering(RedrawInfo, MiscInfo);
05069                 }
05070 
05071                 // Check if the cliprect overlaps any of our children - if not, then we
05072                 // can return now, passing the event on quickly without bothering to give
05073                 // it to our children.
05074 
05075                 if (Flags.Folded || GetChild() == NULL ||
05076                     !ChildArea.IsIntersectedWith(RedrawInfo->Bounds))
05077                 {
05078                     return(FALSE);
05079                 }
05080             }
05081             break;
05082 
05083 
05084         case SGEVENT_MOUSECLICK:
05085             {
05086                 if (!Flags.Invisible)
05087                 {
05088                     SGMouseInfo *Mouse = GetMouseInfo(EventType, EventInfo);
05089 
05090                     // If the click hit us, we will always claim it to save the event going
05091                     // on through the tree unnecessarily
05092                     if (FormatRect.ContainsCoord(Mouse->Position))
05093                     {
05094                         DocRect FolderRect(FormatRect);
05095                         FolderRect.hi.x = FolderRect.lo.x + DevicePixels(MiscInfo, 24);
05096 
05097                         // Single clicks on the folder icon will fold/unfold the category
05098                         if (FolderRect.ContainsCoord(Mouse->Position))
05099                         {
05100                             if (!Mouse->DoubleClick)    // We ignore double-clicks, though
05101                             {
05102                                 // Toggle the folded state of this group and force a redraw
05103                                 SetFoldedState((Flags.Folded) ? FALSE : TRUE);
05104                             }
05105                         }
05106                         else
05107                         {
05108                             if (Mouse->DoubleClick) // It was a double-click, so {un}fold instead
05109                             {
05110                                 // Toggle the folded state of this group and force a redraw
05111                                 SetFoldedState((Flags.Folded) ? FALSE : TRUE);
05112                             }
05113                             else
05114                             {
05115                                 // Not a click on the folder or a general double-click, so assume it's a
05116                                 // drag of the group
05117                                 // This starts the drag. We will be called back, either with:
05118                                 // a) SGDisplayNode::DragWasReallyAClick(), or
05119                                 // b) Some variant of MoveBefore, MoveAfter, or AddItem
05120                                 //    (possibly via SuperGallery::CopyItem) to rearrange this
05121                                 //    item in the tree
05122     
05123                                 if (Flags.CanSelect)
05124                                     DefaultPreDragHandler(Mouse, MiscInfo);
05125     
05126                                 SGListDragInfo *DragGroup;
05127                                 DragGroup = new SGListDragInfo(GetParentGallery(), this,
05128                                                                 Mouse, Mouse->MenuClick);
05129                                 if (DragGroup != NULL)
05130                                     DragManagerOp::StartDrag(DragGroup, GetListWindow());
05131                                 // The DragWasReallyAClick handler will take care of clicks
05132                             }
05133                         }
05134 
05135                         // Claim this event - nobody else can own this click
05136                         return(TRUE);
05137                     }
05138 
05139                     // Next, if the click cannot hit any of our children, pass the event on
05140                     // without passing it to any of the children, to save time
05141                     if (Flags.Folded || GetChild() == NULL ||
05142                         !ChildArea.ContainsCoord(Mouse->Position))
05143                     {
05144                         return(FALSE);
05145                     }
05146                 }
05147             }
05148             break;      
05149 
05150 
05151         case SGEVENT_CLAIMPOINT:
05152             {
05153                 if (!Flags.Invisible)
05154                 {
05155                     SGClaimPointInfo *PointInfo = GetClaimPointInfo(EventType, EventInfo);
05156 
05157                     // If the point hit us, we must claim the event so that the caller
05158                     // knows which tree item contains the point.
05159                     if (FormatRect.ContainsCoord(PointInfo->Position))
05160                     {
05161                         PointInfo->ClosestSoFar = 0;
05162                         PointInfo->Claimant = this;     // Let 'em know it was me!
05163                         return(TRUE);
05164                     }
05165 
05166                     // OK, we don't OWN the point, but are we closer to it than anyone else
05167                     // checked so far? If so, we update ClosestSoFar and Claimant, but we
05168                     // do not claim the event (so others can check if they are closer)
05169                 
05170                     // Find the distance to the 2 closest edges of the rectangle. Then
05171                     // the approx. distance to the rectangle is the larger of the two.
05172                     INT32 XDist = 0;
05173                     if (FormatRect.lo.x > PointInfo->Position.x)
05174                         XDist = FormatRect.lo.x - PointInfo->Position.x;
05175                     else if (FormatRect.hi.x < PointInfo->Position.x)
05176                         XDist = PointInfo->Position.x - FormatRect.lo.x;
05177     
05178                     INT32 YDist = 0;
05179                     if (FormatRect.lo.y > PointInfo->Position.y)
05180                         YDist = FormatRect.lo.y - PointInfo->Position.y;
05181                     else if (FormatRect.hi.y < PointInfo->Position.y)
05182                         YDist = PointInfo->Position.y - FormatRect.lo.y;
05183     
05184                     XDist = max(XDist, YDist);  // XDist is now approx the dist to the point
05185     
05186                     if (XDist < PointInfo->ClosestSoFar)
05187                     {
05188                         PointInfo->Claimant = this;
05189                         PointInfo->ClosestSoFar = XDist;
05190                         // drop through to pass the event on...
05191                     }
05192 
05193                     // Next, if the point cannot hit any of our children, pass the event on
05194                     // without passing it to any of the children, to save time
05195                     if (Flags.Folded || GetChild() == NULL ||
05196                         !ChildArea.ContainsCoord(PointInfo->Position))
05197                     {
05198                         return(FALSE);
05199                     }
05200                 }
05201             }
05202             break;
05203 
05204         default:
05205             return(SGDisplayNode::HandleEvent(EventType, EventInfo, MiscInfo));
05206     }
05207 
05208     // Pass the event on to my children, as appropriate (but not if it's a thumbmsg)
05209     BOOL Result = GiveEventToMyChildren(EventType, EventInfo, MiscInfo);
05210 
05211     if (EventType == SGEVENT_FORMAT)
05212     {
05213         SGFormatInfo *FormatInfo = GetFormatInfo(EventType, EventInfo);
05214 
05215         // After they have formatted themselves, calculate the area they fill, so we can
05216         // quickly determine if events (clicks/redraws) fall over any of our children.
05217         ChildArea = FormatRect;     // We are 'infinite' width, so copy our rect to get width
05218                                     // and the top of the rectangle.
05219 
05220         // And then move the bottom down to include my children, and not myself
05221         ChildArea.hi.y = ChildArea.lo.y;
05222         ChildArea.lo.y = FormatInfo->LinePos;
05223     }
05224 
05225     // And return...
05226     return(Result);
05227 }
05228 
05229 
05230 
05231 /***********************************************************************************************
05232 
05233 >   void SGDisplayGroup::GetChildArea(DocRect *Result)
05234 
05235     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05236     Created:    13/5/95
05237 
05238     Outputs:    Result - Filled in with a rectangle (may be 'Empty')
05239 
05240     Purpose:    Retrieves the display list rectangle of this group's children. This is a
05241                 bounding rectangle which entirely contains the children of this group.
05242 
05243     Notes:      This rectangle may be Empty (if the group is folded, or if it has not
05244                 yet been reformatted after being unfolded)
05245 
05246                 The rectangle does NOT include the group's ("titlebar") FormatRect - to
05247                 include the group title and all children, you must union with FormatRect
05248 
05249 ***********************************************************************************************/
05250 
05251 void SGDisplayGroup::GetChildArea(DocRect *Result)
05252 {
05253     ERROR3IF(Result == NULL, "Illegal NULL param");
05254 
05255     *Result = ChildArea;
05256 }
05257 
05258 
05259 /***********************************************************************************************
05260 
05261 >   virtual BOOL SGDisplayGroup::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
05262 
05263     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05264     Created:    16/4/95
05265 
05266     Inputs:     Mouse - Information on the mouse state for this click
05267                 MiscInfo - the normal info as passed to event handlers
05268 
05269     Returns:    TRUE if the click caused any action to be taken (selection state to change)
05270                 FALSE if the click was ignored for whatever reason
05271 
05272     Purpose:    Provides part 1 of the default selection model for clicks on gallery display
05273                 items. Should be called by all derived gallery DisplayItems to handle clicks
05274                 upon them, when multiple-selection support is desired.
05275 
05276                 You should call this method immediately prior to starting a drag as a result
05277                 of a click event. Note that it is paired with DefaultClickHandler (which
05278                 should be called when the drag you start turns out to be a click, if you
05279                 want multiple-selection capability).
05280 
05281                 See the SGDisplayColour (kernel\sgcolour.cpp) for an example of use
05282 
05283     Notes:      The code for this has now been moved into the SGDisplayNode base class since
05284                 group selection is now also required.
05285 
05286     SeeAlso:    SGDisplayItem::DefaultClickHandler; SGDisplayColour::HandleEvent
05287 
05288 ***********************************************************************************************/
05289 
05290 BOOL SGDisplayGroup::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
05291 {
05292     return(SGDisplayNode::DefaultPreDragHandler(Mouse, MiscInfo));
05293 }
05294 
05295 
05296 
05297 /***********************************************************************************************
05298 
05299 >   virtual BOOL SGDisplayGroup::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
05300                                                     BOOL AfterDrag = FALSE)
05301 
05302     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05303     Created:    9/2/94
05304 
05305     Inputs:     Mouse - Information on the mouse state for this click
05306                 MiscInfo - the normal info as passed to event handlers
05307                 AfterDrag - TRUE if this is being called when a drag turns into a click, and
05308                 you called DefaultPreDragHandler before the drag started
05309 
05310     Returns:    TRUE if the click caused any action to be taken (selection state to change)
05311                 FALSE if the click was ignored for whatever reason
05312 
05313     Purpose:    Provides the default selection model for clicks on gallery display items.
05314                 Should be called by all derived gallery DisplayItems to handle clicks
05315                 upon them, when multiple-selection support is desired.
05316 
05317     Notes:      The code for this has now been moved into the SGDisplayNode base class since
05318                 group selection is now also required.
05319 
05320     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
05321 
05322 ***********************************************************************************************/
05323 
05324 BOOL SGDisplayGroup::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
05325                                         BOOL AfterDrag)
05326 {
05327     return(SGDisplayNode::DefaultClickHandler(Mouse, MiscInfo, AfterDrag));
05328 }
05329 
05330 
05331 /********************************************************************************************
05332 
05333 >   virtual BOOL SGDisplayGroup::CanVirtualise(void);
05334 
05335     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
05336     Created:    4/1/95
05337 
05338     Returns:    TRUE if it can
05339                 
05340     Purpose:    Most groups shouldn't need to be virtualised out. Really it's just library
05341                 groups - SGLibGroup - that should be.
05342 
05343 ********************************************************************************************/
05344 
05345 BOOL SGDisplayGroup::CanVirtualise(void)
05346 {
05347     return FALSE;
05348 }
05349 
05350 /********************************************************************************************
05351 
05352 >   virtual BOOL SGDisplayGroup::Virtualise(void);
05353 
05354     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
05355     Created:    4/1/95
05356 
05357     Returns:    TRUE if it virtualised out OK (or already was)
05358                 
05359     Purpose:    Virtualise a group out of memory, or rather, vape the items and entire tree
05360                 associcated with the group, leaving a memory-minimal shell.
05361 
05362 ********************************************************************************************/
05363 
05364 BOOL SGDisplayGroup::Virtualise(void)
05365 {
05366     // Group is already virtualised
05367     if(IsVirtualised())
05368         return TRUE;
05369 
05370 PORTNOTE("galleries", "Disabled virtualising switch")
05371 #ifndef EXCLUDE_FROM_XARALX
05372     // Virtualisation disabled...
05373     if(!SGLibGroup::LibraryVirtualisingEnabled)
05374         return FALSE;
05375 #endif
05376 
05377     DestroySubtree(FALSE);  // Delete all items in the group (but not this one obviously)
05378     SetVirtualisedState(TRUE);
05379 
05380 #ifdef _DEBUG
05381     ForceRedrawOfMyselfAndChildren();
05382 #endif
05383 
05384     return TRUE;
05385 }
05386 
05387 /********************************************************************************************
05388 
05389 >   virtual BOOL SGDisplayGroup::DeVirtualise(void);
05390 
05391     Author:     Richard_Millican (Xara Group Ltd) <camelotdev@xara.com>
05392     Created:    4/1/95
05393 
05394     Returns:    TRUE if it devirtualised back in OK (or already had)
05395                 
05396     Purpose:    Virtualise a group back into memory, or rather, add the items associated with
05397                 the group to the group so we can display them, etc.
05398 
05399 ********************************************************************************************/
05400 
05401 BOOL SGDisplayGroup::DeVirtualise(void)
05402 {
05403     if(!IsVirtualised())
05404         return TRUE;
05405 
05406 #ifdef _DEBUG
05407     ForceRedrawOfMyselfAndChildren();
05408 #endif
05409 
05410     // Might need some help with this one - override and implement
05411 
05412     return FALSE;
05413 }
05414 
05415 
05416 
05417 
05418 
05419 
05420 
05421 
05422 
05423 /***********************************************************************************************
05424 
05425 >   SGDisplayItem::SGDisplayItem()
05426 
05427     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05428     Created:    13/5/95
05429 
05430     Purpose:    DisplayItem constructor
05431 
05432 ***********************************************************************************************/
05433 
05434 SGDisplayItem::SGDisplayItem()
05435 {
05436     // Items can be selected
05437     Flags.CanSelect = TRUE;
05438 }
05439 
05440 
05441 
05442 /***********************************************************************************************
05443 
05444 >   virtual BOOL SGDisplayItem::HandleEvent(SGEventType EventType, void *EventInfo,
05445                                              SGMiscInfo *MiscInfo)
05446 
05447     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05448     Created:    20/10/94
05449 
05450     Inputs:     See SGDisplayNode::HandleEvent
05451 
05452     Returns:    TRUE if the event was handled successfully
05453                 FALSE if it was not
05454 
05455     Purpose:    Handles a SuperGallery DisplayTree event
05456 
05457     Notes:      This overrides the pure virtual SGDisplayNode::HandleEvent method
05458 
05459                 A node need not handle a specific event - if it does not handle it, it
05460                 should return FALSE.
05461 
05462                 Redraw and Formatting handlers should never return TRUE, as this will
05463                 prevent the event from continuing through the tree.
05464 
05465                 ClaimPoint handlers should always return FALSE, unless they contain
05466                 the given point, in which case they should return TRUE.
05467 
05468                 Non-leaf-nodes must call SGDisplayNode::GiveEventToMyChildren in order
05469                 to pass the event dow the tree. THIS node is a leaf-node, so it doesn't.
05470 
05471                 Derived DisplayItem classes should call this base class method if they
05472                 wish to handle CLAIMPOINT broadcasts to provide drag-target detection.
05473 
05474     SeeAlso:    SGDisplayNode::HandleEvent; SGDisplayRoot::HandleEvent;
05475                 SGDisplayGroup::HandleEvent
05476 
05477 ***********************************************************************************************/
05478 
05479 BOOL SGDisplayItem::HandleEvent(SGEventType EventType, void *EventInfo,
05480                                  SGMiscInfo *MiscInfo)
05481 {
05482     switch(EventType)
05483     {
05484         case SGEVENT_CLAIMPOINT:
05485             {
05486                 SGClaimPointInfo *PointInfo = GetClaimPointInfo(EventType, EventInfo);
05487 
05488                 // If the point hit us, we must claim the event so that the caller
05489                 // knows which tree item contains the point.
05490                 if (FormatRect.ContainsCoord(PointInfo->Position))
05491                 {
05492                     PointInfo->ClosestSoFar = 0;
05493                     PointInfo->Claimant = this;     // Let 'em know it was me!
05494                     return(TRUE);
05495                 }
05496 
05497                 // OK, we don't OWN the point, but are we closer to it than anyone else
05498                 // checked so far? If so, we update ClosestSoFar and Claimant, but we
05499                 // do not claim the event (so others can check if they are closer)
05500                 
05501                 // Find the distance to the 2 closest edges of the rectangle. Then
05502                 // the approx. distance to the rectangle is the larger of the two.
05503                 INT32 XDist = 0;
05504                 if (FormatRect.lo.x > PointInfo->Position.x)
05505                     XDist = FormatRect.lo.x - PointInfo->Position.x;
05506                 else if (FormatRect.hi.x < PointInfo->Position.x)
05507                     XDist = PointInfo->Position.x - FormatRect.lo.x;
05508 
05509                 INT32 YDist = 0;
05510                 if (FormatRect.lo.y > PointInfo->Position.y)
05511                     YDist = FormatRect.lo.y - PointInfo->Position.y;
05512                 else if (FormatRect.hi.y < PointInfo->Position.y)
05513                     YDist = PointInfo->Position.y - FormatRect.lo.y;
05514 
05515                 XDist = max(XDist, YDist);  // XDist is now approx the dist to the point
05516 
05517                 if (XDist < PointInfo->ClosestSoFar)
05518                 {
05519                     PointInfo->Claimant = this;
05520                     PointInfo->ClosestSoFar = XDist;
05521                     // drop through to pass the event on...
05522                 }
05523             }
05524             break;
05525 
05526 
05527         default:
05528             return(SGDisplayNode::HandleEvent(EventType, EventInfo, MiscInfo));
05529     }
05530 
05531     return(FALSE);
05532 }
05533 
05534 
05535 
05536 /***********************************************************************************************
05537 
05538 >   virtual void SGDisplayItem::AddItem(SGDisplayNode *NodeToInsert,
05539                                         SGSortKey *SortInfo = NULL)
05540     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05541     Created:    20/10/94
05542 
05543     Inputs:     NodeToInsert - the node/subtree to be inserted
05544                 SortInfo - NULL, or an array of MaxSGSortKeys sort key structures, which
05545                 describes the sort mode to be used for determining the position of the
05546                 insertion.
05547 
05548     Purpose:    OVERRIDES the DisplayNode action, and gives an ERROR3 -
05549                 DisplayItems are only allowed to be leaf-nodes, and hence you cannot
05550                 insert items as children of them.
05551 
05552     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
05553 
05554 ***********************************************************************************************/
05555 
05556 void SGDisplayItem::AddItem(SGDisplayNode *NodeToInsert, SGSortKey *SortInfo)
05557 {
05558     ERROR3("You can't SGDisplayItem::AddItem() - DisplayItems are supposed to be leaf nodes");
05559 }
05560 
05561 
05562 
05563 /***********************************************************************************************
05564 
05565 >   virtual void SGDisplayItem::RemoveFromTree(void)
05566 
05567     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05568     Created:    10/2/95
05569 
05570     Purpose:    De-links this node/subtree from the DisplayTree. This DOES NOT DELETE the node,
05571                 just unlinks it in preparation for being deleted.
05572 
05573     Notes:      This method calls SGDisplayNode::RemoveFromTree to do all the work.
05574                 It is overridden simply so that display items can deselect themselves and
05575                 inform the parent gallery if they were the selected node, so that pointers
05576                 to removed nodes are not kept lying around.
05577 
05578     SeeAlso:    SGDisplayNode::RemoveFromTree
05579 
05580 ***********************************************************************************************/
05581 
05582 void SGDisplayItem::RemoveFromTree(void)
05583 {
05584 //  SetSelected(FALSE);
05585     SGDisplayNode::RemoveFromTree();
05586 }
05587 
05588 
05589 
05590 /***********************************************************************************************
05591 
05592 >   virtual BOOL SGDisplayItem::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
05593 
05594     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05595     Created:    16/4/95
05596 
05597     Inputs:     Mouse - Information on the mouse state for this click
05598                 MiscInfo - the normal info as passed to event handlers
05599 
05600     Returns:    TRUE if the click caused any action to be taken (selection state to change)
05601                 FALSE if the click was ignored for whatever reason
05602 
05603     Purpose:    Provides part 1 of the default selection model for clicks on gallery display
05604                 items. Should be called by all derived gallery DisplayItems to handle clicks
05605                 upon them, when multiple-selection support is desired.
05606 
05607                 You should call this method immediately prior to starting a drag as a result
05608                 of a click event. Note that it is paired with DefaultClickHandler (which
05609                 should be called when the drag you start turns out to be a click, if you
05610                 want multiple-selection capability).
05611 
05612                 See the SGDisplayColour (kernel\sgcolour.cpp) for an example of use
05613 
05614     Notes:      The code for this has now been moved into the SGDisplayNode base class since
05615                 group selection is now also required.
05616 
05617     SeeAlso:    SGDisplayItem::DefaultClickHandler; SGDisplayColour::HandleEvent
05618 
05619 ***********************************************************************************************/
05620 
05621 BOOL SGDisplayItem::DefaultPreDragHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo)
05622 {
05623     return(SGDisplayNode::DefaultPreDragHandler(Mouse, MiscInfo));
05624 }
05625 
05626 
05627 
05628 /***********************************************************************************************
05629 
05630 >   virtual BOOL SGDisplayItem::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
05631                                                     BOOL AfterDrag = FALSE,
05632                                                     BOOL AdjustDoubleClick = TRUE)
05633 
05634     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
05635     Created:    9/2/94
05636 
05637     Inputs:     Mouse - Information on the mouse state for this click
05638                 MiscInfo - the normal info as passed to event handlers
05639                 AfterDrag - TRUE if this is being called when a drag turns into a click, and
05640                 you called DefaultPreDragHandler before the drag started
05641 
05642                 AdjustDoubleClick - Override for derived classes to enable/disable the
05643                 default adjust-double-click action (which closes the gallery after apply).
05644                 The colour gallery has a special meaning for adjust-double-click, so overrides
05645                 this variable to pass in FALSE and disable default action.
05646 
05647     Returns:    TRUE if the click caused any action to be taken (selection state to change)
05648                 FALSE if the click was ignored for whatever reason
05649 
05650     Purpose:    Provides the default selection model for clicks on gallery display items.
05651                 Should be called by all derived gallery DisplayItems to handle clicks
05652                 upon them, when multiple-selection support is desired.
05653 
05654     Notes:      The code for this has now been moved into the SGDisplayNode base class since
05655                 group selection is now also required.
05656 
05657     SeeAlso:    SuperGallery; SGDisplayNode::InsertAfter; SGDisplayNode::InsertBefore
05658 
05659 ***********************************************************************************************/
05660 
05661 BOOL SGDisplayItem::DefaultClickHandler(SGMouseInfo *Mouse, SGMiscInfo *MiscInfo,
05662                                         BOOL AfterDrag, BOOL AdjustDoubleClick)
05663 {
05664     return(SGDisplayNode::DefaultClickHandler(Mouse, MiscInfo, AfterDrag, AdjustDoubleClick));
05665 }
05666 
05667 

Generated on Sat Nov 10 03:46:59 2007 for Camelot by  doxygen 1.4.4