home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / outline.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  70KB  |  2,586 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Borland Delphi Visual Component Library         }
  5. {                                                       }
  6. {       Copyright (c) 1995,99 Inprise Corporation       }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Outline;
  11.  
  12. {$R-,H+,X+}
  13.  
  14. interface
  15.  
  16. {$R OUTLINE}
  17.  
  18. uses Windows, Messages, Forms, Classes, Graphics, Menus, StdCtrls, Grids,
  19.   Controls, SysUtils;
  20.  
  21. type
  22.   OutlineError = class(TObject); { Raised by GetNodeAtIndex }
  23.   EOutlineError = class(Exception);
  24.   TOutlineNodeCompare = (ocLess, ocSame, ocGreater, ocInvalid);
  25.   TAttachMode = (oaAdd, oaAddChild, oaInsert);
  26.   TChangeRange = -1..1;
  27.   TCustomOutline = class;
  28.  
  29. { TOutlineNode }
  30.  
  31. { The TOutlineNode is an encapsulation of an outliner item.  Access
  32.   to a TOutlineNode is via the container class TOutline.  Each
  33.   TOutlineNode contains user defined text and data.
  34.   An item is also capable of containing up to 16368 sub-items.
  35.   TOutlineNodes are also persistent.
  36.  
  37.   A TOutlineNode item can be interrogated about its current state :
  38.     Expanded
  39.       Whether the node is open or closed.
  40.     Index
  41.       The current Index of the node.  This changes as items are inserted and
  42.       deleted.  The index will range from 1..n
  43.     Level
  44.       The current depth of the node with 1 being the top level
  45.     HasItems
  46.       Whether the item contains items
  47.     IsVisible
  48.       Whether the item is capable of being displayed. This value is only
  49.       True if all its parent items are visible
  50.     TopItem
  51.       Obtains the parent of the item that resides at level 1
  52.     FullPath
  53.       Returns the fully qualified name of the item starting from its
  54.       level 1 parent.  Each item is separated by the separator string
  55.       specified in the TOutline Container
  56.     Text
  57.       Used to set and get the items text value
  58.     Data
  59.       Used to get and set the items data }
  60.  
  61.   TOutlineNode = class(TPersistent)
  62.   private
  63.     FList: TList;
  64.     FText: string;
  65.     FData: Pointer;
  66.     FParent: TOutlineNode;
  67.     FIndex: LongInt;
  68.     FState: Boolean;
  69.     FOutline: TCustomOutline;
  70.     FExpandCount: LongInt;
  71.     procedure ChangeExpandedCount(Value: LongInt);
  72.     procedure CloseNode;
  73.     procedure Clear;
  74.     procedure Error(const ErrorString: string);
  75.     function GetExpandedNodeCount: LongInt;
  76.     function GetFullPath: string;
  77.     function GetIndex: LongInt;
  78.     function GetLastIndex: LongInt;
  79.     function GetLevel: Cardinal;
  80.     function GetList: TList;
  81.     function GetMaxDisplayWidth(Value: Cardinal): Cardinal;
  82.     function GetNode(Index: LongInt): TOutlineNode;
  83.     function GetTopItem: Longint;
  84.     function GetVisibleParent: TOutlineNode;
  85.     function HasChildren: Boolean;
  86.     function HasVisibleParent: Boolean;
  87.     function IsEqual(Value: TOutlineNode): Boolean;
  88.     procedure ReIndex(StartNode, EndNode: TOutlineNode; NewIndex: LongInt;
  89.       IncludeStart: Boolean);
  90.     procedure Repaint;
  91.     function Resync(var NewIndex: LongInt; EndNode: TOutlineNode): Boolean;
  92.     procedure SetExpandedState(Value: Boolean);
  93.     procedure SetGoodIndex;
  94.     procedure SetHorzScrollBar;
  95.     procedure SetLevel(Level: Cardinal);
  96.     procedure SetText(const Value: string);
  97.   protected
  98.     function GetVisibleNode(TargetCount: LongInt): TOutlineNode;
  99.     function AddNode(Value: TOutlineNode): LongInt;
  100.     function InsertNode(Index: LongInt; Value: TOutlineNode): LongInt;
  101.     function GetNodeAtIndex(TargetIndex: LongInt): TOutlineNode;
  102.     function GetDataItem(Value: Pointer): LongInt;
  103.     function GetTextItem(const Value: string): LongInt;
  104.     function HasAsParent(Value: TOutlineNode): Boolean;
  105.     function GetRowOfNode(TargetNode: TOutlineNode;
  106.       var RowCount: Longint): Boolean;
  107.     procedure InternalRemove(Value: TOutlineNode; Index: Integer);
  108.     procedure Remove(Value: TOutlineNode);
  109.     procedure WriteNode(Buffer: PChar; Stream: TStream);
  110.     property Outline: TCustomOutline read FOutline;
  111.     property List: TList read GetList;
  112.     property ExpandCount: LongInt read FExpandCount;
  113.     property Items[Index: LongInt]: TOutlineNode read GetNode; default;
  114.   public
  115.     constructor Create(AOwner: TCustomOutline);
  116.     destructor Destroy; override;
  117.     procedure ChangeLevelBy(Value: TChangeRange);
  118.     procedure Collapse;
  119.     procedure Expand;
  120.     procedure FullExpand;
  121.     function GetDisplayWidth: Integer;
  122.     function getFirstChild: LongInt;
  123.     function GetLastChild: LongInt;
  124.     function GetNextChild(Value: LongInt): LongInt;
  125.     function GetPrevChild(Value: LongInt): LongInt;
  126.     procedure MoveTo(Destination: LongInt; AttachMode: TAttachMode);
  127.     property Parent: TOutlineNode read FParent;
  128.     property Expanded: Boolean read FState write SetExpandedState;
  129.     property Text: string read FText write SetText;
  130.     property Data: Pointer read FData write FData;
  131.     property Index: LongInt read GetIndex;
  132.     property Level: Cardinal read GetLevel write SetLevel;
  133.     property HasItems: Boolean read HasChildren;
  134.     property IsVisible: Boolean read HasVisibleParent;
  135.     property TopItem: Longint read GetTopItem;
  136.     property FullPath: string read GetFullPath;
  137.   end;
  138.  
  139. { TCustomOutline }
  140.  
  141. { The TCustomOutline object is a container class for TOutlineNodes.
  142.   All TOutlineNodes contained within a TOutline are presented
  143.   to the user as a flat array of TOutlineNodes, with a parent
  144.   TOutlineNode containing an index value that is one less than
  145.   its first child (if it has any children).
  146.  
  147.   Interaction with a TOutlineNode is typically accomplished through
  148.   the TCustomOutline using the following properties:
  149.     CurItem
  150.       Reads and writes the current item
  151.     ItemCount
  152.       Returns the total number of TOutlineNodes with the TCustomOutline.
  153.       Note this can be computationally expensive as all indexes will
  154.       be forced to be updated!!
  155.     Items
  156.       Allows Linear indexing into the hierarchical list of TOutlineNodes
  157.     SelectedItem
  158.       Returns the Index of the TOutlineNode which has the focus or 0 if
  159.       no TOutlineNode has been selected
  160.  
  161.   The TCustomOutline has a number of properties which will affect all
  162.   TOutlineNodes owned by the TCustomOutline:
  163.     OutlineStyle
  164.       Sets the visual style of the outliner
  165.     ItemSeparator
  166.       Sets the delimiting string for all TOutlineNodes
  167.     PicturePlus, PictureMinus, PictureOpen, PictureClosed, PictureLeaf
  168.       Sets custom bitmaps for these items }
  169.  
  170.   TBitmapArrayRange = 0..4;
  171.   EOutlineChange = procedure (Sender: TObject; Index: LongInt) of object;
  172.   TOutlineStyle = (osText, osPlusMinusText, osPictureText,
  173.     osPlusMinusPictureText, osTreeText, osTreePictureText);
  174.   TOutlineBitmap = (obPlus, obMinus, obOpen, obClose, obLeaf);
  175.   TOutlineBitmaps = set of TOutlineBitmap;
  176.   TBitmapArray = array[TBitmapArrayRange] of TBitmap;
  177.   TOutlineType = (otStandard, otOwnerDraw);
  178.   TOutlineOption = (ooDrawTreeRoot, ooDrawFocusRect, ooStretchBitmaps);
  179.   TOutlineOptions = set of TOutlineOption;
  180.  
  181.  
  182.   TCustomOutline = class(TCustomGrid)
  183.   private
  184.     FBlockInsert: Boolean;
  185.     FRootNode: TOutlineNode;
  186.     FGoodNode: TOutlineNode;
  187.     UpdateCount: Integer;
  188.     FCurItem: TOutlineNode;
  189.     FSeparator: string;
  190.     FFontSize: Integer;
  191.     FStrings: TStrings;
  192.     FUserBitmaps: TOutlineBitmaps;
  193.     FOldBitmaps: TOutlineBitmaps;
  194.     FPictures: TBitmapArray;
  195.     FOnExpand: EOutlineChange;
  196.     FOnCollapse: EOutlineChange;
  197.     FOutlineStyle: TOutlineStyle;
  198. //    FMaskColor: TColor;
  199.     FItemHeight: Integer;
  200.     FStyle: TOutlineType;
  201.     FOptions: TOutlineOptions;
  202.     FIgnoreScrollResize: Boolean;
  203.     FSelectedItem: TOutlineNode;
  204.     FOnDrawItem: TDrawItemEvent;
  205.     FSettingWidth: Boolean;
  206.     FSettingHeight: Boolean;
  207.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  208.     function GetItemCount: LongInt;
  209.     function AttachNode(Index: LongInt; Str: string;
  210.       Ptr: Pointer; AttachMode: TAttachMode): LongInt;
  211.     function Get(Index: LongInt): TOutlineNode;
  212.     function GetSelectedItem: LongInt;
  213.     procedure SetSelectedItem(Value: Longint);
  214.     function CompareNodes(Value1, Value2: TOutlineNode): TOutlineNodeCompare;
  215.     procedure Error(const ErrorString: string);
  216.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  217.     function ResizeGrid: Boolean;
  218.     procedure DoExpand(Node: TOutlineNode);
  219.     procedure Init;
  220.     procedure MoveNode(Destination, Source: LongInt;
  221.       AttachMode: TAttachMode);
  222.     procedure ClearBitmap(var Bitmap: TBitmap; Kind: TOutlineBitmap);
  223.     procedure ChangeBitmap(Value: TBitmap; Kind: TOutlineBitmap);
  224.     procedure SetRowHeight;
  225.     procedure SetCurItem(Value: LongInt);
  226.     procedure CreateGlyph;
  227.     procedure SetStrings(Value: TStrings);
  228.     function GetStrings: TStrings;
  229.     function IsCurItem(Value: LongInt): Boolean;
  230.     procedure SetPicture(Index: Integer; Value: TBitmap);
  231.     function GetPicture(Index: Integer): TBitmap;
  232.     procedure DrawPictures(BitMaps: array of TBitmap; ARect: TRect);
  233.     procedure DrawText(Node: TOutlineNode; Rect: TRect);
  234.     procedure SetOutlineStyle(Value: TOutlineStyle);
  235.     procedure DrawTree(ARect: TRect; Node: TOutlineNode);
  236. //    procedure SetMaskColor(Value: TColor);
  237.     procedure SetItemHeight(Value: Integer);
  238.     procedure SetStyle(Value: TOutlineType);
  239.     procedure SetOutlineOptions(Value: TOutlineOptions);
  240.     function StoreBitmap(Index: Integer): Boolean;
  241.     procedure ReadBinaryData(Stream: TStream);
  242.     procedure WriteBinaryData(Stream: TStream);
  243.     procedure SetHorzScrollBar;
  244.     procedure ResetSelectedItem;
  245.     procedure SetRowFromNode(Node: TOutlineNode);
  246.   protected
  247.     procedure Loaded; override;
  248.     procedure Click; override;
  249.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  250.     procedure KeyPress(var Key: Char); override;
  251.     function SetGoodIndex(Value: TOutlineNode): TOutlineNode;
  252.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
  253.       AState: TGridDrawState); override;
  254.     procedure DblClick; override;
  255.     procedure SetLevel(Node: TOutlineNode; CurLevel, NewLevel: Cardinal);
  256.     function BadIndex(Value: TOutlineNode): Boolean;
  257.     procedure DeleteNode(Node: TOutlineNode; CurIndex: LongInt);
  258.     procedure Expand(Index: LongInt); dynamic;
  259.     procedure Collapse(Index: LongInt); dynamic;
  260.     procedure DefineProperties(Filer: TFiler); override;
  261.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  262.       X, Y: Integer); override;
  263.     procedure Move(Destination, Source: LongInt; AttachMode: TAttachMode);
  264.     procedure SetDisplayWidth(Value: Integer);
  265.     property Lines: TStrings read GetStrings write SetStrings;
  266.     property OutlineStyle: TOutlineStyle read FOutlineStyle write SetOutlineStyle default osTreePictureText;
  267.     property OnExpand: EOutlineChange read FOnExpand write FOnExpand;
  268.     property OnCollapse: EOutlineChange read FOnCollapse write FOnCollapse;
  269.     property Options: TOutlineOptions read FOptions write SetOutlineOptions
  270.       default [ooDrawTreeRoot, ooDrawFocusRect];
  271.     property Style: TOutlineType read FStyle write SetStyle default otStandard;
  272.     property ItemHeight: Integer read FItemHeight write SetItemHeight;
  273.     property OnDrawItem: TDrawItemEvent read FOnDrawItem write FOnDrawItem;
  274.     property ItemSeparator: string read FSeparator write FSeparator;
  275.     property PicturePlus: TBitmap index 0 read GetPicture write SetPicture stored StoreBitmap;
  276.     property PictureMinus: TBitmap index 1 read GetPicture write SetPicture stored StoreBitmap;
  277.     property PictureOpen: TBitmap index 2 read GetPicture write SetPicture stored StoreBitmap;
  278.     property PictureClosed: TBitmap index 3 read GetPicture write SetPicture stored StoreBitmap;
  279.     property PictureLeaf: TBitmap index 4 read GetPicture write SetPicture stored StoreBitmap;
  280.   public
  281.     constructor Create(AOwner: TComponent); override;
  282.     destructor Destroy; override;
  283.     function Add(Index: LongInt; const Text: string): LongInt;
  284.     function AddChild(Index: LongInt; const Text: string): LongInt;
  285.     function AddChildObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  286.     function AddObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  287.     function Insert(Index: LongInt; const Text: string): LongInt;
  288.     function InsertObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  289.     procedure Delete(Index: LongInt);
  290.     function GetDataItem(Value: Pointer): Longint;
  291.     function GetItem(X, Y: Integer): LongInt;
  292.     function GetNodeDisplayWidth(Node: TOutlineNode): Integer;
  293.     function GetTextItem(const Value: string): Longint;
  294.     function GetVisibleNode(Index: LongInt): TOutlineNode;
  295.     procedure FullExpand;
  296.     procedure FullCollapse;
  297.     procedure LoadFromFile(const FileName: string);
  298.     procedure LoadFromStream(Stream: TStream);
  299.     procedure SaveToFile(const FileName: string);
  300.     procedure SaveToStream(Stream: TStream);
  301.     procedure BeginUpdate;
  302.     procedure EndUpdate;
  303.     procedure SetUpdateState(Value: Boolean);
  304.     procedure Clear;
  305.     property ItemCount: LongInt read GetItemCount;
  306.     property Items[Index: LongInt]: TOutlineNode read Get; default;
  307.     property SelectedItem: Longint read GetSelectedItem write SetSelectedItem;
  308.     property Row;
  309.     property Canvas;
  310.   end;
  311.  
  312.   TOutline = class(TCustomOutline)
  313.   published
  314.     property Lines;
  315.     property OutlineStyle;
  316.     property OnExpand;
  317.     property OnCollapse;
  318.     property Options;
  319.     property Style;
  320.     property ItemHeight;
  321.     property OnDrawItem;
  322.     property Align;
  323.     property Enabled;
  324.     property Font;
  325.     property Color;
  326.     property ParentColor;
  327.     property ParentCtl3D;
  328.     property Ctl3D;
  329.     property TabOrder;
  330.     property TabStop;
  331.     property Visible;
  332.     property OnClick;
  333.     property DragMode;
  334.     property DragKind;
  335.     property DragCursor;
  336.     property OnDragDrop;
  337.     property OnDragOver;
  338.     property OnEndDock;
  339.     property OnEndDrag;
  340.     property OnStartDock;
  341.     property OnStartDrag;
  342.     property OnEnter;
  343.     property OnExit;
  344.     property OnMouseDown;
  345.     property OnMouseMove;
  346.     property OnMouseUp;
  347.     property OnDblClick;
  348.     property OnKeyDown;
  349.     property OnKeyPress;
  350.     property OnKeyUp;
  351.     property BorderStyle;
  352.     property ItemSeparator;
  353.     property PicturePlus;
  354.     property PictureMinus;
  355.     property PictureOpen;
  356.     property PictureClosed;
  357.     property PictureLeaf;
  358.     property ParentFont;
  359.     property ParentShowHint;
  360.     property ShowHint;
  361.     property PopupMenu;
  362.     property ScrollBars;
  363.     property OnContextPopup;
  364.   end;
  365.  
  366. implementation
  367.  
  368. uses Consts;
  369.  
  370. const
  371.   MaxLevels = 255;
  372.   TAB = Chr(9);
  373.   InvalidIndex = -1;
  374.   BitmapWidth = 14;
  375.   BitmapHeight = 14;
  376.  
  377. type
  378.  
  379. { TOutlineStrings }
  380.  
  381.   TOutlineStrings = class(TStrings)
  382.   private
  383.     Outline: TCustomOutline;
  384.     procedure ReadData(Reader: TReader);
  385.     procedure WriteData(Writer: TWriter);
  386.   protected
  387.     procedure DefineProperties(Filer: TFiler); override;
  388.     function Get(Index: Integer): string; override;
  389.     function GetCount: Integer; override;
  390.   public
  391.     function Add(const S: string): Integer; override;
  392.     procedure Clear; override;
  393.     procedure Delete(Index: Integer); override;
  394.     procedure Insert(Index: Integer; const S: string); override;
  395.     procedure PutObject(Index: Integer; AObject: TObject); override;
  396.     function GetObject(Index: Integer): TObject; override;
  397.   end;
  398.  
  399. function GetBufStart(Buffer: PChar; var Level: Cardinal): PChar;
  400. begin
  401.   Level := 0;
  402.   while Buffer^ in [' ', #9] do
  403.   begin
  404.     Inc(Buffer);
  405.     Inc(Level);
  406.   end;
  407.   Result := Buffer;
  408. end;
  409.  
  410. function PutString(BufPtr: PChar; const S: string): PChar;
  411. var
  412.   I: Integer;
  413. begin
  414.   for I := 1 to Length(S) do
  415.   begin
  416.     BufPtr^ := S[I];
  417.     Inc(BufPtr);
  418.   end;
  419.   Word(Pointer(BufPtr)^) := $0A0D;
  420.   Inc(BufPtr, 2);
  421.   Result := BufPtr;
  422. end;
  423.  
  424.  
  425. {TOutlineNode}
  426.  
  427. constructor TOutlineNode.Create(AOwner: TCustomOutline);
  428. begin
  429.   FOutline := AOwner;
  430. end;
  431.  
  432. destructor TOutlineNode.Destroy;
  433. var
  434.   CurIndex: LongInt;
  435.   LastNode: Boolean;
  436. begin
  437.   with Outline do
  438.     if FRootNode = Self then FIgnoreScrollResize := True;
  439.   try
  440.     CurIndex := 0;
  441.     if Parent <> nil then CurIndex := Outline.FCurItem.Index;
  442.     if FList <> nil then Clear;
  443.     if Outline.FSelectedItem = Self then Outline.ResetSelectedItem;
  444.     if Parent <> nil then
  445.     begin
  446.       LastNode := Parent.List.Last = Self;
  447.       Parent.Remove(Self);
  448.       if Parent.List.Count = 0 then
  449.         Outline.SetRowFromNode(Parent)
  450.       else if LastNode then
  451.         Outline.SetRowFromNode(TOutlineNode(Parent.List.Last));
  452.       Outline.DeleteNode(Self, CurIndex);
  453.     end;
  454.   finally
  455.     with Outline do
  456.       if FRootNode = Self then FIgnoreScrollResize := False;
  457.   end;
  458.   inherited Destroy;
  459. end;
  460.  
  461. procedure TOutlineNode.Clear;
  462. var
  463.   I: Integer;
  464.   Node: TOutlineNode;
  465. begin
  466.   for I := 0 to FList.Count - 1 do
  467.   begin
  468.     Node := FList.Items[I];
  469.     Node.FParent := nil;
  470.     Node.Destroy;
  471.   end;
  472.   FList.Free;
  473.   FList := nil;
  474. end;
  475.  
  476. procedure TOutlineNode.SetHorzScrollBar;
  477. begin
  478.   if (Parent <> nil) and Parent.Expanded then
  479.     Outline.SetHorzScrollBar;
  480. end;
  481.  
  482. function TOutlineNode.GetList: TList;
  483. begin
  484.   if FList = nil then FList := TList.Create;
  485.   Result := FList;
  486. end;
  487.  
  488. function TOutlineNode.GetNode(Index: LongInt): TOutlineNode;
  489. begin
  490.   Result := List[Index];
  491. end;
  492.  
  493. function TOutlineNode.GetLastIndex: LongInt;
  494. begin
  495.   if List.Count <> 0 then
  496.     Result := TOutlineNode(List.Last).GetLastIndex
  497.   else
  498.     Result := Index;
  499. end;
  500.  
  501. procedure TOutlineNode.SetText(const Value: string);
  502. var
  503.  NodeRow: LongInt;
  504. begin
  505.   FText := Value;
  506.   if not Assigned(FParent) then Exit;
  507.  
  508.   if Parent.Expanded then
  509.   begin
  510.     NodeRow := 0;
  511.     with Outline do
  512.     begin
  513.       FRootNode.GetRowOfNode(Self, NodeRow);
  514.       InvalidateCell(0, NodeRow - 2);
  515.     end;
  516.   end;
  517.   SetHorzScrollBar;
  518. end;
  519.  
  520. procedure TOutlineNode.ChangeExpandedCount(Value: LongInt);
  521. begin
  522.   if not Expanded then Exit;
  523.   Inc(FExpandCount, Value);
  524.   if Parent <> nil then Parent.ChangeExpandedCount(Value);
  525. end;
  526.  
  527. function TOutlineNode.GetIndex: LongInt;
  528. begin
  529.   if Outline.BadIndex(Self) then SetGoodIndex;
  530.   Result := FIndex;
  531. end;
  532.  
  533. function TOutlineNode.GetLevel: Cardinal;
  534. var
  535.   Node: TOutlineNode;
  536. begin
  537.   Result := 0;
  538.   Node := Parent;
  539.   while Node <> nil do
  540.   begin
  541.     Inc(Result);
  542.     Node := Node.Parent;
  543.   end;
  544. end;
  545.  
  546. procedure TOutlineNode.SetLevel(Level: Cardinal);
  547. var
  548.   CurLevel: Cardinal;
  549. begin
  550.   CurLevel := GetLevel;
  551.   if Level = CurLevel then Exit;
  552.   Outline.SetLevel(Self, CurLevel, Level);
  553. end;
  554.  
  555. procedure TOutlineNode.ChangeLevelBy(Value: TChangeRange);
  556. begin
  557.   Level := Cardinal(Integer(Level) + Value);
  558. end;
  559.  
  560. function TOutlineNode.GetDisplayWidth: Integer;
  561. begin
  562.   Result := Outline.GetNodeDisplayWidth(Self);
  563. end;
  564.  
  565. function TOutlineNode.HasVisibleParent: Boolean;
  566. begin
  567.   Result := (Parent <> nil) and (Parent.Expanded);
  568. end;
  569.  
  570. function TOutlineNode.GetVisibleParent: TOutlineNode;
  571. begin
  572.   Result := Self;
  573.   while (Result.Parent <> nil) and not Result.Parent.Expanded do
  574.     Result := Result.Parent;
  575. end;
  576.  
  577. function TOutlineNode.GetFullPath: string;
  578. begin
  579.   if Parent <> nil then
  580.     if Parent.Parent <> nil then
  581.       Result := Parent.GetFullPath + Outline.ItemSeparator + Text
  582.     else
  583.       Result := Text
  584.   else Result := EmptyStr;
  585. end;
  586.  
  587. function TOutlineNode.HasAsParent(Value: TOutlineNode): Boolean;
  588. begin
  589.   if Self = Value then
  590.     Result := True
  591.   else if Parent <> nil then Result := Parent.HasAsParent(Value)
  592.   else Result := False;
  593. end;
  594.  
  595. function TOutlineNode.GetTopItem: Longint;
  596. var
  597.   Node: TOutlineNode;
  598. begin
  599.   Result := 0;
  600.   if Parent = nil then Exit;
  601.   Node := Self;
  602.   while Node.Parent <> nil do
  603.   begin
  604.     if Node.Parent.Parent = nil then
  605.       Result := Node.FIndex;
  606.     Node := Node.Parent;
  607.   end;
  608. end;
  609.  
  610. function TOutlineNode.getFirstChild: LongInt;
  611. begin
  612.   if List.Count > 0 then Result := Items[0].Index
  613.   else Result := InvalidIndex;
  614. end;
  615.  
  616. function TOutlineNode.GetLastChild: LongInt;
  617. begin
  618.   if List.Count > 0 then Result := Items[List.Count - 1].Index
  619.   else Result := InvalidIndex;
  620. end;
  621.  
  622. function TOutlineNode.GetNextChild(Value: LongInt): LongInt;
  623. var
  624.  I: Integer;
  625. begin
  626.   Result := InvalidIndex;
  627.   for I := 0 to List.Count - 1 do
  628.   begin
  629.     if Items[I].Index = Value then
  630.     begin
  631.       if I < List.Count - 1 then Result := Items[I + 1].Index;
  632.       Break;
  633.     end;
  634.   end;
  635. end;
  636.  
  637. function TOutlineNode.GetPrevChild(Value: LongInt): LongInt;
  638. var
  639.  I: Integer;
  640. begin
  641.   Result := InvalidIndex;
  642.   for I := List.Count - 1 downto 0 do
  643.   begin
  644.     if Items[I].Index = Value then
  645.     begin
  646.       if I > 0 then Result := Items[I - 1].Index;
  647.       Break;
  648.     end;
  649.   end;
  650. end;
  651.  
  652. procedure TOutlineNode.MoveTo(Destination: LongInt; AttachMode: TAttachMode);
  653. begin
  654.   Outline.Move(Destination, Index, AttachMode);
  655. end;
  656.  
  657. procedure TOutlineNode.FullExpand;
  658. var
  659.   I: Integer;
  660. begin
  661.   if HasItems then
  662.   begin
  663.     Expanded := True;
  664.     for I := 0 to List.Count - 1 do
  665.       Items[I].FullExpand;
  666.   end;
  667. end;
  668.  
  669. function TOutlineNode.GetRowOfNode(TargetNode: TOutlineNode;
  670.   var RowCount: Longint): Boolean;
  671. var
  672.   I: Integer;
  673. begin
  674.   Inc(RowCount);
  675.   if TargetNode = Self then
  676.   begin
  677.     Result := True;
  678.     Exit;
  679.   end;
  680.  
  681.   Result := False;
  682.   if not Expanded then Exit;
  683.  
  684.   for I := 0 to List.Count - 1 do
  685.   begin
  686.     Result := Items[I].GetRowOfNode(TargetNode, RowCount);
  687.     if Result then Exit
  688.   end;
  689. end;
  690.  
  691. function TOutlineNode.GetVisibleNode(TargetCount: LongInt): TOutlineNode;
  692. var
  693.   I, J: Integer;
  694.   ExpandedCount, NodeCount, NodesParsed: LongInt;
  695.   Node: TOutlineNode;
  696.   Count: Integer;
  697. begin
  698.   if TargetCount = 0 then
  699.   begin
  700.     Result := Self;
  701.     Exit;
  702.   end;
  703.  
  704.   Result := nil;
  705.   Count := List.Count;
  706.   NodesParsed := 0;
  707.  
  708.   { Quick exit if we are lucky }
  709.   if ExpandCount = Count then
  710.   begin
  711.     Result := Items[TargetCount - 1];
  712.     Exit;
  713.   end;
  714.  
  715.   I := 0;
  716.   while I <= Count - 1 do
  717.   begin
  718.     for J := I to Count - 1 do
  719.       if Items[J].Expanded then Break;
  720.  
  721.     if J > I then
  722.     begin
  723.       if J - I >= TargetCount then
  724.       begin
  725.         Result := Items[I + TargetCount - 1];
  726.         Break;
  727.       end;
  728.       Dec(TargetCount, J - I);
  729.     end;
  730.  
  731.     Node := Items[J];
  732.     NodeCount := Node.ExpandCount + 1;
  733.     ExpandedCount := NodeCount + J - I;
  734.  
  735.     Inc(NodesParsed, ExpandedCount);
  736.     if NodeCount >= TargetCount then
  737.     begin
  738.       Result := Node.GetVisibleNode(Pred(TargetCount));
  739.       Break;
  740.     end
  741.     else if ExpandCount - NodesParsed = Count - (J + 1) then
  742.     begin
  743.       Result := Items[TargetCount - NodeCount + J];
  744.       Exit;
  745.     end
  746.     else begin
  747.       Dec(TargetCount, NodeCount);
  748.       I := J;
  749.     end;
  750.     Inc(I);
  751.   end;
  752.   if Result = nil then Error(SOutlineIndexError);
  753. end;
  754.  
  755. function TOutlineNode.GetNodeAtIndex(TargetIndex: LongInt): TOutlineNode;
  756. var
  757.   I: Integer;
  758.   Node: TOutlineNode;
  759.   Lower: Integer;
  760.   Upper: Integer;
  761.  
  762.   function RecurseNode: TOutlineNode;
  763.   begin
  764.     if Node.Index = TargetIndex then
  765.       Result := Node
  766.     else
  767.       Result := Node.GetNodeAtIndex(TargetIndex);
  768.   end;
  769.  
  770. begin
  771.   if TargetIndex = Index then
  772.   begin
  773.     Result := Self;
  774.     Exit;
  775.   end;
  776.  
  777.   Lower := 0;
  778.   Upper := List.Count - 1;
  779.   Result := nil;
  780.   while Upper >= Lower do
  781.   begin
  782.     I := (Lower + Upper) div 2;
  783.     Node := Items[I];
  784.     if Lower = Upper then
  785.     begin
  786.       Result := RecurseNode;
  787.       Break;
  788.     end
  789.     else if Node.Index > TargetIndex then Upper := Pred(I)
  790.     else if (Node.Index < TargetIndex) and (I < Upper) and
  791.       (Items[I + 1].Index <= TargetIndex) then Lower := Succ(I)
  792.     else begin
  793.       Result := RecurseNode;
  794.       Break;
  795.     end;
  796.   end;
  797.   if Result = nil then Raise OutlineError.Create;
  798. end;
  799.  
  800. function TOutlineNode.GetDataItem(Value: Pointer): LongInt;
  801. var
  802.   I: Integer;
  803. begin
  804.   if Value = Data then
  805.   begin
  806.     Result := Index;
  807.     Exit;
  808.   end;
  809.  
  810.   Result := 0;
  811.   for I := 0 to List.Count - 1 do
  812.   begin
  813.     Result := Items[I].GetDataItem(Value);
  814.     if Result <> 0 then Break;
  815.   end;
  816. end;
  817.  
  818. function TOutlineNode.GetTextItem(const Value: string): LongInt;
  819. var
  820.   I: Integer;
  821. begin
  822.   if Value = Text then
  823.   begin
  824.     Result := Index;
  825.     Exit;
  826.   end;
  827.  
  828.   Result := 0;
  829.   for I := 0 to List.Count - 1 do
  830.   begin
  831.     Result := Items[I].GetTextItem(Value);
  832.     if Result <> 0 then Break;
  833.   end;
  834. end;
  835.  
  836. procedure TOutlineNode.Expand;
  837. begin
  838.   Expanded := True;
  839. end;
  840.  
  841. procedure TOutlineNode.Collapse;
  842. begin
  843.   Expanded := False;
  844. end;
  845.  
  846. procedure TOutlineNode.SetExpandedState(Value: Boolean);
  847. var
  848.   ParentNode: TOutlineNode;
  849. begin
  850.   if FState <> Value then
  851.   begin
  852.     if Value then
  853.     begin
  854.       ParentNode := Self.Parent;
  855.       while ParentNode <> nil do
  856.       begin
  857.         if not ParentNode.Expanded then Error(SOutlineExpandError);
  858.         ParentNode := ParentNode.Parent;
  859.       end;
  860.       Outline.Expand(Index);
  861.       FState := True;
  862.       ChangeExpandedCount(List.Count);
  863.     end
  864.     else begin
  865.       CloseNode;
  866.       if List.Count > 0 then ChangeExpandedCount(-List.Count);
  867.       if Outline.ResizeGrid then Outline.Invalidate;
  868.       Outline.Collapse(Index);
  869.       FState := False;
  870.     end;
  871.     SetHorzScrollBar;
  872.     Repaint;
  873.   end;
  874. end;
  875.  
  876. procedure TOutlineNode.CloseNode;
  877. var
  878.   I: Integer;
  879. begin
  880.   for I := 0 to List.Count - 1 do
  881.     Items[I].CloseNode;
  882.   if List.Count > 0 then ChangeExpandedCount(-List.Count);
  883.   FState := False;
  884. end;
  885.  
  886. procedure TOutlineNode.Repaint;
  887. begin
  888.   if Outline <> nil then
  889.     if Outline.ResizeGrid then Outline.Invalidate;
  890. end;
  891.  
  892. procedure TOutlineNode.SetGoodIndex;
  893. var
  894.   StartNode: TOutlineNode;
  895.   ParentNode: TOutlineNode;
  896. begin
  897.   StartNode := Outline.SetGoodIndex(Self);
  898.   ParentNode := StartNode.Parent;
  899.   if ParentNode <> nil then
  900.     ParentNode.ReIndex(StartNode, Self, StartNode.FIndex, True)
  901.   else if Self <> Outline.FRootNode then
  902.     FIndex := Succ(StartNode.FIndex);
  903.   Outline.FGoodNode := Self;
  904. end;
  905.  
  906. function TOutlineNode.AddNode(Value: TOutlineNode): LongInt;
  907. begin
  908.   List.Add(Value);
  909.   Value.FParent := Self;
  910.   ChangeExpandedCount(Value.ExpandCount + 1);
  911.   if not Outline.FBlockInsert then Value.SetGoodIndex;
  912.   with Value do
  913.   begin
  914.     Result := FIndex;
  915.     SetHorzScrollBar;
  916.   end;
  917. end;
  918.  
  919. function TOutlineNode.InsertNode(Index: LongInt; Value: TOutlineNode): LongInt;
  920. var
  921.   CurIndex: LongInt;
  922.   I: Integer;
  923. begin
  924.   for I := 0 to List.Count - 1 do
  925.   begin
  926.     CurIndex := Items[I].FIndex;
  927.     if CurIndex = Index then
  928.     begin
  929.       List.Insert(I, Value);
  930.       Value.FParent := Self;
  931.       Break;
  932.     end;
  933.   end;
  934.   ChangeExpandedCount(Value.ExpandCount + 1);
  935.   if not Outline.FBlockInsert then Value.SetGoodIndex;
  936.   with Value do
  937.   begin
  938.     Result := FIndex;
  939.     SetHorzScrollBar;
  940.   end;
  941. end;
  942.  
  943. procedure TOutlineNode.InternalRemove(Value: TOutlineNode; Index: Integer);
  944. begin
  945.   if Index <> 0 then
  946.     Outline.SetGoodIndex(Items[Index - 1]) else
  947.     Outline.SetGoodIndex(Self);
  948.   List.Delete(Index);
  949.   ChangeExpandedCount(-(Value.ExpandCount + 1));
  950.   if (List.Count = 0) and (Parent <> nil) then Expanded := False;
  951.   SetHorzScrollBar;
  952. end;
  953.  
  954. procedure TOutlineNode.Remove(Value: TOutlineNode);
  955. begin
  956.   InternalRemove(Value, List.IndexOf(Value));
  957. end;
  958.  
  959. procedure TOutlineNode.ReIndex(StartNode, EndNode: TOutlineNode;
  960.   NewIndex: LongInt; IncludeStart: Boolean);
  961. var
  962.   I: Integer;
  963. begin
  964.   for I := List.IndexOf(StartNode) to List.Count - 1 do
  965.   begin
  966.     if IncludeStart then
  967.     begin
  968.       if Items[I].Resync(NewIndex, EndNode) then Exit;
  969.     end
  970.     else
  971.       IncludeStart := True;
  972.   end;
  973.  
  974.   if Parent <> nil then
  975.     Parent.ReIndex(Self, EndNode, NewIndex, False);
  976. end;
  977.  
  978. function TOutlineNode.Resync(var NewIndex: LongInt; EndNode: TOutlineNode): Boolean;
  979. var
  980.   I: Integer;
  981. begin
  982.   FIndex := NewIndex;
  983.   if EndNode = Self then
  984.   begin
  985.     Result := True;
  986.     Exit;
  987.   end;
  988.  
  989.   Result := False;
  990.   Inc(NewIndex);
  991.   for I := 0 to List.Count - 1 do
  992.   begin
  993.     Result := Items[I].Resync(NewIndex, EndNode);
  994.     if Result then Exit;
  995.   end;
  996. end;
  997.  
  998. function TOutlineNode.GetExpandedNodeCount: LongInt;
  999. var
  1000.   I : Integer;
  1001. begin
  1002.   Result := 1;
  1003.   if Expanded then
  1004.     for I := 0 to List.Count - 1 do
  1005.       Inc(Result, Items[I].GetExpandedNodeCount);
  1006. end;
  1007.  
  1008.  
  1009. function TOutlineNode.GetMaxDisplayWidth(Value: Cardinal): Cardinal;
  1010. var
  1011.   I : Integer;
  1012.   Width: Cardinal;
  1013. begin
  1014.   Width := GetDisplayWidth;
  1015.   if Width > Value then Result := Width
  1016.   else Result := Value;
  1017.   if Expanded then
  1018.     for I := 0 to List.Count - 1 do
  1019.       Result := Items[I].GetMaxDisplayWidth(Result);
  1020. end;
  1021.  
  1022. procedure TOutlineNode.Error(const ErrorString: string);
  1023. begin
  1024.   raise EOutlineError.Create(ErrorString);
  1025. end;
  1026.  
  1027. function TOutlineNode.HasChildren: Boolean;
  1028. begin
  1029.   Result := List.Count > 0;
  1030. end;
  1031.  
  1032. procedure TOutlineNode.WriteNode(Buffer: PChar; Stream: TStream);
  1033. var
  1034.   BufPtr: PChar;
  1035.   NodeLevel: Word;
  1036.   I: Integer;
  1037. begin
  1038.   if Parent <> nil then
  1039.   begin
  1040.     BufPtr := Buffer;
  1041.     NodeLevel := Level;
  1042.     while NodeLevel > 1 do
  1043.     begin
  1044.       BufPtr^ := Tab;
  1045.       Dec(NodeLevel);
  1046.       Inc(BufPtr);
  1047.     end;
  1048.     BufPtr := PutString(BufPtr, Text);
  1049.     Stream.WriteBuffer(Buffer[0], BufPtr - Buffer);
  1050.   end;
  1051.   for I := 0 to List.Count - 1 do
  1052.     Items[I].WriteNode(Buffer, Stream);
  1053. end;
  1054.  
  1055. function TOutlineNode.IsEqual(Value: TOutlineNode): Boolean;
  1056. begin
  1057.   Result := (Text = Value.Text) and (Data = Value.Data) and
  1058.     (ExpandCount = Value.ExpandCount);
  1059. end;
  1060.  
  1061. { TOutlineStrings }
  1062.  
  1063. function TOutlineStrings.Get(Index: Integer): string;
  1064. var
  1065.   Node: TOutlineNode;
  1066.   Level: Word;
  1067.   I: Integer;
  1068. begin
  1069.   Node := Outline[Index + 1];
  1070.   Level := Node.Level;
  1071.   Result := EmptyStr;
  1072.   for I := 0 to Level - 2 do
  1073.     Result := Result + TAB;
  1074.   Result := Result + Node.Text;
  1075. end;
  1076.  
  1077. function TOutlineStrings.GetCount: Integer;
  1078. begin
  1079.   Result := Outline.ItemCount;
  1080. end;
  1081.  
  1082. procedure TOutlineStrings.Clear;
  1083. begin
  1084.   Outline.Clear;
  1085. end;
  1086.  
  1087. procedure TOutlineStrings.DefineProperties(Filer: TFiler);
  1088.  
  1089.   function WriteNodes: Boolean;
  1090.   var
  1091.     I: Integer;
  1092.     Ancestor: TOutlineStrings;
  1093.   begin
  1094.     Ancestor := TOutlineStrings(Filer.Ancestor);
  1095.     if (Ancestor <> nil) and (Ancestor.Outline.ItemCount = Outline.ItemCount) and
  1096.       (Ancestor.Outline.ItemCount > 0) then
  1097.     begin
  1098.       Result := False;
  1099.       for I := 1 to Outline.ItemCount - 1 do
  1100.       begin
  1101.         Result := not Outline[I].IsEqual(Ancestor.Outline[I]);
  1102.         if Result then Break;
  1103.       end
  1104.     end else Result := Outline.ItemCount > 0;
  1105.   end;
  1106.  
  1107. begin
  1108.   Filer.DefineProperty('Nodes', ReadData, WriteData, WriteNodes);
  1109. end;
  1110.  
  1111. procedure TOutlineStrings.ReadData(Reader: TReader);
  1112. var
  1113.   StringList: TStringList;
  1114.   MemStream: TMemoryStream;
  1115. begin
  1116.   Reader.ReadListBegin;
  1117.   StringList := TStringList.Create;
  1118.   try
  1119.     while not Reader.EndOfList do StringList.Add(Reader.ReadString);
  1120.     MemStream := TMemoryStream.Create;
  1121.     try
  1122.       StringList.SaveToStream(MemStream);
  1123.       MemStream.Position := 0;
  1124.       Outline.LoadFromStream(MemStream);
  1125.     finally
  1126.       MemStream.Free;
  1127.     end;
  1128.   finally
  1129.     StringList.Free;
  1130.   end;
  1131.   Reader.ReadListEnd;
  1132. end;
  1133.  
  1134. procedure TOutlineStrings.WriteData(Writer: TWriter);
  1135. var
  1136.   I: Integer;
  1137.   MemStream: TMemoryStream;
  1138.   StringList: TStringList;
  1139. begin
  1140.   Writer.WriteListBegin;
  1141.   MemStream := TMemoryStream.Create;
  1142.   try
  1143.     Outline.SaveToStream(MemStream);
  1144.     MemStream.Position := 0;
  1145.     StringList := TStringList.Create;
  1146.     try
  1147.       StringList.LoadFromStream(MemStream);
  1148.       for I := 0 to StringList.Count - 1 do
  1149.         Writer.WriteString(StringList.Strings[I]);
  1150.     finally
  1151.       StringList.Free;
  1152.     end;
  1153.   finally
  1154.     MemStream.Free;
  1155.   end;
  1156.   Writer.WriteListEnd;
  1157. end;
  1158.  
  1159. function TOutlineStrings.Add(const S: string): Integer;
  1160. var
  1161.   Level, OldLevel, I: Cardinal;
  1162.   NewStr: string;
  1163.   NumNodes: LongInt;
  1164.   LastNode: TOutlineNode;
  1165. begin
  1166.   NewStr := GetBufStart(PChar(S), Level);
  1167.   NumNodes := Outline.ItemCount;
  1168.   if NumNodes > 0 then LastNode := Outline[Outline.ItemCount]
  1169.   else LastNode := Outline.FRootNode;
  1170.   OldLevel := LastNode.Level;
  1171.   if (Level > OldLevel) or (LastNode = Outline.FRootNode) then
  1172.   begin
  1173.     if Level - OldLevel > 1 then Outline.Error(SOutlineFileLoad);
  1174.   end
  1175.   else begin
  1176.     for I := OldLevel downto Level + 1 do
  1177.     begin
  1178.       LastNode := LastNode.Parent;
  1179.       if not Assigned(LastNode) then Outline.Error(SOutlineFileLoad);
  1180.     end;
  1181.   end;
  1182.   Result := Outline.AddChild(LastNode.Index, NewStr) - 1;
  1183. end;
  1184.  
  1185. procedure TOutlineStrings.Delete(Index: Integer);
  1186. begin
  1187.   Outline.Delete(Index + 1);
  1188. end;
  1189.  
  1190. procedure TOutlineStrings.Insert(Index: Integer; const S: string);
  1191. begin
  1192.   Outline.Insert(Index + 1, S);
  1193. end;
  1194.  
  1195. procedure TOutlineStrings.PutObject(Index: Integer; AObject: TObject);
  1196. var
  1197.   Node: TOutlineNode;
  1198. begin
  1199.   Node := Outline[Index + 1];
  1200.   Node.Data := Pointer(AObject);
  1201. end;
  1202.  
  1203. function TOutlineStrings.GetObject(Index: Integer): TObject;
  1204. begin
  1205.   Result := TObject(Outline[Index + 1].Data);
  1206. end;
  1207.  
  1208.  
  1209. {TCustomOutline}
  1210.  
  1211. const
  1212.   Images: array[TBitmapArrayRange] of PChar = ('PLUS', 'MINUS', 'OPEN', 'CLOSED', 'LEAF');
  1213.  
  1214. constructor TCustomOutline.Create(AOwner: TComponent);
  1215. begin
  1216.   inherited Create(AOwner);
  1217.   Width := 121;
  1218.   Height := 97;
  1219.   Color := clWindow;
  1220.   ParentColor := False;
  1221.   SetRowHeight;
  1222.   RowCount := 0;
  1223.   ColCount := 1;
  1224.   FixedCols := 0;
  1225.   FixedRows := 0;
  1226.   DefaultDrawing := False;
  1227.   Init;
  1228.   FStrings := TOutlineStrings.Create;
  1229.   TOutlineStrings(FStrings).Outline := Self;
  1230.   inherited Options := [];
  1231.   Options := [ooDrawTreeRoot, ooDrawFocusRect];
  1232.   ItemSeparator := '\';
  1233.   FOutlineStyle := osTreePictureText;
  1234.   CreateGlyph;
  1235. end;
  1236.  
  1237. destructor TCustomOutline.Destroy;
  1238. var
  1239.   I: Integer;
  1240. begin
  1241.   FStrings.Free;
  1242.   FRootNode.Free;
  1243.   for I := Low(FPictures) to High(FPictures) do FPictures[I].Free;
  1244.   inherited Destroy;
  1245. end;
  1246.  
  1247. procedure TCustomOutline.Init;
  1248. begin
  1249.   if FRootNode = nil then FRootNode := TOutlineNode.Create(Self);
  1250.   FRootNode.FState := True;
  1251.   ResetSelectedItem;
  1252.   FGoodNode := FRootNode;
  1253.   FCurItem := FRootNode;
  1254.   FBlockInsert := False;
  1255.   UpdateCount := 0;
  1256.   ResizeGrid;
  1257. end;
  1258.  
  1259. procedure TCustomOutline.CreateGlyph;
  1260. var
  1261.   I: Integer;
  1262. begin
  1263.   FUserBitmaps := [];
  1264.   FOldBitmaps := [];
  1265.   for I := Low(FPictures) to High(FPictures) do
  1266.   begin
  1267.     FPictures[I] := TBitmap.Create;
  1268.     FPictures[I].Handle := LoadBitmap(HInstance, Images[I]);
  1269.   end;
  1270. end;
  1271.  
  1272. procedure TCustomOutline.SetRowHeight;
  1273. var
  1274.   ScreenDC: HDC;
  1275. begin
  1276.   if Style <> otOwnerDraw then
  1277.   begin
  1278.     ScreenDC := GetDC(0);
  1279.     try
  1280.       FFontSize := MulDiv(Font.Size, GetDeviceCaps(ScreenDC, LOGPIXELSY), 72);
  1281.       DefaultRowHeight := MulDiv(FFontSize, 120, 100);
  1282.       FItemHeight := DefaultRowHeight;
  1283.     finally
  1284.       ReleaseDC(0, ScreenDC);
  1285.     end;
  1286.   end
  1287. end;
  1288.  
  1289. procedure TCustomOutline.Clear;
  1290. begin
  1291.   FRootNode.Destroy;
  1292.   FRootNode := nil;
  1293.   Init;
  1294. end;
  1295.  
  1296. procedure TCustomOutline.DefineProperties(Filer: TFiler);
  1297.  
  1298.   function WriteOutline: Boolean;
  1299.   var
  1300.     Ancestor: TCustomOutline;
  1301.   begin
  1302.     Ancestor := TCustomOutline(Filer.Ancestor);
  1303.     if Ancestor <> nil then
  1304.       Result := (Ancestor.FUserBitmaps <> []) and
  1305.         (Ancestor.FUserBitmaps - FUserBitmaps <> [])
  1306.     else Result := FUserBitmaps <> [];
  1307.   end;
  1308.  
  1309. begin
  1310.   inherited DefineProperties(Filer);
  1311.   Filer.DefineBinaryProperty('Data', ReadBinaryData, WriteBinaryData,
  1312.     WriteOutline);
  1313. end;
  1314.  
  1315. procedure TCustomOutline.ReadBinaryData(Stream: TStream);
  1316. begin
  1317.   Stream.ReadBuffer(FOldBitmaps, SizeOf(FOldBitmaps));
  1318. end;
  1319.  
  1320. procedure TCustomOutline.WriteBinaryData(Stream: TStream);
  1321. begin
  1322.   Stream.WriteBuffer(FuserBitmaps, SizeOf(FUserBitmaps));
  1323. end;
  1324.  
  1325. function TCustomOutline.IsCurItem(Value: LongInt): Boolean;
  1326. begin
  1327.   Result := Value = FCurItem.Index;
  1328. end;
  1329.  
  1330. function TCustomOutline.GetItemCount: LongInt;
  1331. begin
  1332.   Result := FRootNode.GetLastIndex;
  1333. end;
  1334.  
  1335. procedure TCustomOutline.MoveNode(Destination, Source: LongInt;
  1336.   AttachMode: TAttachMode);
  1337. var
  1338.   SourceNode: TOutlineNode;
  1339.   DestNode: TOutLineNode;
  1340.   OldParent: TOutlineNode;
  1341.   OldIndex: Integer;
  1342. begin
  1343.   if Destination = Source then Exit;
  1344.   DestNode := FCurItem;
  1345.   if not IsCurItem(Destination) then
  1346.     try
  1347.       DestNode := FRootNode.GetNodeAtIndex(Destination);
  1348.     except
  1349.       on OutlineError do Error(SOutlineIndexError);
  1350.     end;
  1351.  
  1352.   SourceNode := FCurItem;
  1353.   if not IsCurItem(Source) then
  1354.     try
  1355.       SourceNode := FRootNode.GetNodeAtIndex(Source);
  1356.     except
  1357.       on OutlineError do Error(SOutlineIndexError);
  1358.     end;
  1359.  
  1360.   if DestNode.HasAsParent(SourceNode) then Exit;
  1361.  
  1362.   if DestNode.GetLevel > MaxLevels then Error(SOutlineMaxLevels);
  1363.   if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
  1364.     TOutlineNode(FRootNode[0]).SetGoodIndex;
  1365.   OldParent := SourceNode.Parent;
  1366.   OldIndex := -1;
  1367.   case AttachMode of
  1368.     oaInsert:
  1369.       begin
  1370.         if DestNode.Parent = OldParent then
  1371.         begin
  1372.           OldIndex := OldParent.List.IndexOf(SourceNode);
  1373.           if OldParent.List.IndexOf(DestNode) < OldIndex then
  1374.             OldIndex := OldIndex + 1 else
  1375.             OldIndex := -1;
  1376.         end;
  1377.         DestNode.Parent.InsertNode(DestNode.Index, SourceNode);
  1378.       end;
  1379.     oaAddChild: DestNode.AddNode(SourceNode);
  1380.     oaAdd: DestNode.Parent.AddNode(SourceNode);
  1381.   end;
  1382.   if OldIndex <> -1 then
  1383.     OldParent.InternalRemove(SourceNode, OldIndex) else
  1384.     OldParent.Remove(SourceNode);
  1385.   if not DestNode.Expanded then SourceNode.Expanded := False;
  1386.   if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
  1387.     TOutlineNode(FRootNode[0]).SetGoodIndex;
  1388.   ResizeGrid;
  1389.   Invalidate;
  1390. end;
  1391.  
  1392. function TCustomOutline.AttachNode(Index: LongInt; Str: string;
  1393.   Ptr: Pointer; AttachMode: TAttachMode): LongInt;
  1394. var
  1395.   NewNode: TOutlineNode;
  1396.   CurrentNode: TOutLineNode;
  1397. begin
  1398.   Result := 0;
  1399.   NewNode := TOutlineNode.Create(Self);
  1400.   with NewNode do
  1401.   begin
  1402.     Text := Str;
  1403.     Data := Ptr;
  1404.     FIndex := InvalidIndex;
  1405.   end;
  1406.   try
  1407.     CurrentNode := FCurItem;
  1408.     if not IsCurItem(Index) then
  1409.       try
  1410.         CurrentNode := FRootNode.GetNodeAtIndex(Index);
  1411.       except
  1412.         on OutlineError do Error(SOutlineIndexError);
  1413.       end;
  1414.  
  1415.     if AttachMode = oaAdd then
  1416.     begin
  1417.       CurrentNode := CurrentNode.Parent;
  1418.       if CurrentNode = nil then Error(SOutlineError);
  1419.       AttachMode := oaAddChild;
  1420.     end;
  1421.  
  1422.     with CurrentNode do
  1423.     begin
  1424.       case AttachMode of
  1425.         oaInsert: Result := Parent.InsertNode(Index, NewNode);
  1426.         oaAddChild:
  1427.           begin
  1428.              if GetLevel > MaxLevels then Error(SOutlineMaxLevels);
  1429.              Result := AddNode(NewNode);
  1430.           end;
  1431.       end;
  1432.     end;
  1433.     if ResizeGrid then Invalidate;
  1434.   except
  1435.     NewNode.Destroy;
  1436.     Application.HandleException(Self);
  1437.   end;
  1438. end;
  1439.  
  1440. function TCustomOutline.Get(Index: LongInt): TOutlineNode;
  1441. begin
  1442.   Result := FCurItem;
  1443.   if not IsCurItem(Index) then
  1444.     try
  1445.       Result := FRootNode.GetNodeAtIndex(Index);
  1446.     except
  1447.       on OutlineError do Error(SOutlineIndexError);
  1448.     end;
  1449.   if Result = FRootNode then Error(SOutlineError);
  1450. end;
  1451.  
  1452. function TCustomOutline.GetSelectedItem: LongInt;
  1453. begin
  1454.   if FSelectedItem <> FRootNode then
  1455.   begin
  1456.     if not FSelectedItem.IsVisible then
  1457.       FSelectedItem := FSelectedItem.GetVisibleParent;
  1458.   end
  1459.   else if FRootNode.List.Count > 0 then
  1460.     FSelectedItem := FRootNode.GetVisibleNode(Row + 1);
  1461.   Result := FSelectedItem.Index
  1462. end;
  1463.  
  1464. procedure TCustomOutline.ResetSelectedItem;
  1465. begin
  1466.   FSelectedItem := FRootNode;
  1467. end;
  1468.  
  1469. procedure TCustomOutline.SetRowFromNode(Node: TOutlineNode);
  1470. var
  1471.   RowValue: LongInt;
  1472. begin
  1473.   if Node <> FRootNode then
  1474.   begin
  1475.     RowValue := 0;
  1476.     FRootNode.GetRowOfNode(Node, RowValue);
  1477.     Row := RowValue - 2;
  1478.   end;
  1479. end;
  1480.  
  1481. procedure TCustomOutline.SetSelectedItem(Value: Longint);
  1482. var
  1483.   Node: TOutlineNode;
  1484. begin
  1485.   if FBlockInsert then Exit;
  1486.   if (Value = 0) and (FRootNode.List.Count > 0) then Value := 1;
  1487.   if Value > 0 then
  1488.   begin
  1489.     Node := FSelectedItem;
  1490.     if Value <> FSelectedItem.Index then
  1491.     try
  1492.       Node := FRootNode.GetNodeAtIndex(Value);
  1493.     except
  1494.       on OutlineError do Error(SOutlineIndexError);
  1495.     end;
  1496.     if not Node.IsVisible then Node := Node.GetVisibleParent;
  1497.     FSelectedItem := Node;
  1498.     SetRowFromNode(Node);
  1499.   end
  1500.   else Error(SOutlineSelection);
  1501. end;
  1502.  
  1503. function TCustomOutline.Insert(Index: LongInt; const Text: string): LongInt;
  1504. begin
  1505.   Result := InsertObject(Index, Text, nil);
  1506. end;
  1507.  
  1508. function TCustomOutline.InsertObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  1509. begin
  1510.   Result := -1;
  1511.   if Index > 0 then Result := AttachNode(Index, Text, Data, oaInsert)
  1512.   else if Index = 0 then Result := AddChildObject(Index, Text, Data)
  1513.   else Error(SOutlineError);
  1514.   SetCurItem(Index);
  1515. end;
  1516.  
  1517. function TCustomOutline.Add(Index: LongInt; const Text: string): LongInt;
  1518. begin
  1519.   Result := AddObject(Index, Text, nil);
  1520. end;
  1521.  
  1522. function TCustomOutline.AddObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  1523. begin
  1524.   Result := -1;
  1525.   if Index > 0 then Result := AttachNode(Index, Text, Data, oaAdd)
  1526.   else If Index = 0 then Result := AddChildObject(Index, Text, Data)
  1527.   else Error(SOutlineError);
  1528.   SetCurItem(Index);
  1529. end;
  1530.  
  1531. function TCustomOutline.AddChild(Index: LongInt; const Text: string): LongInt;
  1532. begin
  1533.   Result := AddChildObject(Index, Text, nil);
  1534. end;
  1535.  
  1536. function TCustomOutline.AddChildObject(Index: LongInt; const Text: string; const Data: Pointer): LongInt;
  1537. begin
  1538.   Result := -1;
  1539.   if Index >= 0 then Result := AttachNode(Index, Text, Data, oaAddChild)
  1540.   else Error(SOutlineError);
  1541.   SetCurItem(Index);
  1542. end;
  1543.  
  1544. procedure TCustomOutline.Delete(Index: LongInt);
  1545. begin
  1546.   if Index > 0 then
  1547.   begin
  1548.     try
  1549.       FRootNode.GetNodeAtIndex(Index).Free;
  1550.     except
  1551.       on OutlineError do Error(SOutlineIndexError);
  1552.     end;
  1553.   end
  1554.   else Error(SOutlineError);
  1555. end;
  1556.  
  1557. procedure TCustomOutline.Move(Destination, Source: LongInt; AttachMode: TAttachMode);
  1558. begin
  1559.   if (AttachMode = oaAddChild) or (Destination > 0) then
  1560.     MoveNode(Destination, Source, AttachMode)
  1561.   else Error(SOutlineError);
  1562. end;
  1563.  
  1564. procedure TCustomOutline.DeleteNode(Node: TOutlineNode; CurIndex: LongInt);
  1565. begin
  1566.   if (FGoodNode = FRootNode) and (FRootNode.List.Count <> 0) then
  1567.     FRootNode[0].SetGoodIndex;
  1568.   try
  1569.     FCurItem := FRootNode.GetNodeAtIndex(CurIndex);
  1570.   except
  1571.     on OutlineError do FCurItem := FRootNode;
  1572.   end;
  1573.   if (FSelectedItem = FRootNode) and (Node <> FRootNode) then
  1574.     GetSelectedItem;
  1575.   if ResizeGrid then Invalidate;
  1576. end;
  1577.  
  1578. procedure TCustomOutline.SetLevel(Node: TOutlineNode; CurLevel, NewLevel: Cardinal);
  1579. var
  1580.   NumLevels: Integer;
  1581.  
  1582.   procedure MoveUp(Node: TOutlineNode; NumLevels: Cardinal);
  1583.   var
  1584.     Parent: TOutlineNode;
  1585.     I: Cardinal;
  1586.     Index: Integer;
  1587.   begin
  1588.     Parent := Node;
  1589.     for I := NumLevels downto 1 do
  1590.       Parent := Parent.Parent;
  1591.     Index := Parent.Parent.GetNextChild(Parent.Index);
  1592.     if Index = InvalidIndex then Node.MoveTo(Parent.Parent.Index, oaAddChild)
  1593.     else Node.MoveTo(Index, oaInsert);
  1594.   end;
  1595.  
  1596.   procedure MoveDown(Node: TOutlineNode; NumLevels: Cardinal);
  1597.   var
  1598.     Parent: TOutlineNode;
  1599.     I: Cardinal;
  1600.   begin
  1601.     while NumLevels > 0 do
  1602.     begin
  1603.       Parent := Node.Parent;
  1604.       for I := Parent.List.Count - 1 downto 0 do
  1605.         if Parent.Items[I].Index = Node.Index then Break;
  1606.       if I > 0 then
  1607.       begin
  1608.         Parent := Parent.Items[I - 1];
  1609.         Node.MoveTo(Parent.Index, oaAddChild);
  1610.       end else Error(SOutlineBadLevel);
  1611.       Dec(NumLevels);
  1612.     end;
  1613.   end;
  1614.  
  1615. begin
  1616.   NumLevels := CurLevel - NewLevel;
  1617.   if (NewLevel > 0) then
  1618.   begin
  1619.     if (NumLevels > 0) then MoveUp(Node, NumLevels)
  1620.     else MoveDown(Node, ABS(NumLevels));
  1621.   end
  1622.   else Error(SOutlineBadLevel);
  1623. end;
  1624.  
  1625. procedure TCustomOutline.Click;
  1626. begin
  1627.   if FRootNode.List.Count > 0 then
  1628.     SelectedItem := FRootNode.GetVisibleNode(Row + 1).Index;
  1629.   inherited Click;
  1630. end;
  1631.  
  1632. procedure TCustomOutline.WMSize(var Message: TWMSize);
  1633. begin
  1634.   inherited;
  1635.   if FSettingWidth or FSettingHeight then Exit;
  1636.   if (ScrollBars in [ssNone, ssVertical]) or
  1637.     ((Style = otOwnerDraw) and Assigned(FOnDrawItem)) then
  1638.     DefaultColWidth := ClientWidth
  1639.   else SetHorzScrollBar;
  1640. end;
  1641.  
  1642. procedure TCustomOutline.KeyPress(var Key: Char);
  1643. begin
  1644.   inherited KeyPress(Key);
  1645.   if FSelectedItem <> FRootNode then
  1646.     case Key of
  1647.       '+': FSelectedItem.Expanded := True;
  1648.       '-': FSelectedItem.Expanded := False;
  1649.       '*': FSelectedItem.FullExpand;
  1650.     end;
  1651. end;
  1652.  
  1653. procedure TCustomOutline.KeyDown(var Key: Word; Shift: TShiftState);
  1654. var
  1655.   Node: TOutlineNode;
  1656. begin
  1657.   inherited KeyDown(Key, Shift);
  1658.   if FRootNode.List.Count = 0 then Exit;
  1659.   Node := FRootNode.GetVisibleNode(Row + 1);
  1660.   case Key of
  1661.     VK_HOME:
  1662.       begin
  1663.         SelectedItem := TOutlineNode(FRootNode.List.First).Index;
  1664.         Exit;
  1665.       end;
  1666.     VK_END:
  1667.       begin
  1668.         Node := TOutlineNode(FRootNode.List.Last);
  1669.         while Node.Expanded and Node.HasItems do
  1670.           Node := TOutlineNode(Node.List.Last);
  1671.         SelectedItem := Node.Index;
  1672.         Exit;
  1673.       end;
  1674.     VK_RETURN:
  1675.       begin
  1676.         Node.Expanded := not Node.Expanded;
  1677.         Exit;
  1678.       end;
  1679.     VK_MULTIPLY:
  1680.       begin
  1681.         if ssCtrl in Shift then
  1682.         begin
  1683.           FullExpand;
  1684.           Exit;
  1685.         end;
  1686.       end;
  1687.     VK_RIGHT:
  1688.       begin
  1689.         if (not Node.HasItems) or (not Node.Expanded) then MessageBeep(0)
  1690.         else SelectedItem := SelectedItem + 1;
  1691.         Exit;
  1692.       end;
  1693.     VK_LEFT:
  1694.       begin
  1695.         if Node.Parent = FRootNode then MessageBeep(0)
  1696.         else SelectedItem := Node.Parent.Index;
  1697.         Exit;
  1698.       end;
  1699.     VK_UP:
  1700.       if ssCtrl in Shift then
  1701.       begin
  1702.         with Node.Parent do
  1703.         begin
  1704.           if List.First = Node then MessageBeep(0)
  1705.           else SelectedItem := Items[List.IndexOf(Node) - 1].Index;
  1706.         end;
  1707.         Exit;
  1708.       end;
  1709.     VK_DOWN:
  1710.       if ssCtrl in Shift then
  1711.       begin
  1712.         with Node.Parent do
  1713.         begin
  1714.           if List.Last = Node then MessageBeep(0)
  1715.           else SelectedItem := Items[List.IndexOf(Node) + 1].Index;
  1716.         end;
  1717.         Exit;
  1718.       end;
  1719.   end;
  1720.   SelectedItem := FRootNode.GetVisibleNode(Row + 1).Index;
  1721. end;
  1722.  
  1723. procedure TCustomOutline.DblClick;
  1724. var
  1725.   Node: TOutlineNode;
  1726. begin
  1727.   inherited DblClick;
  1728.   Node := FSelectedItem;
  1729.   if Node <> FRootNode then DoExpand(Node);
  1730. end;
  1731.  
  1732. procedure TCustomOutline.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1733.   X, Y: Integer);
  1734. begin
  1735.   inherited MouseDown(Button, Shift, X, Y);
  1736.   ResetSelectedItem;
  1737.   GetSelectedItem;
  1738. end;
  1739.  
  1740. procedure TCustomOutline.FullExpand;
  1741. begin
  1742.   FRootNode.FullExpand;
  1743. end;
  1744.  
  1745. procedure TCustomOutline.FullCollapse;
  1746. var
  1747.   I: Integer;
  1748. begin
  1749.   for I := 0 to FRootNode.List.Count - 1 do
  1750.     FRootNode.Items[I].Expanded := False;
  1751. end;
  1752.  
  1753. procedure TCustomOutline.SetHorzScrollBar;
  1754. begin
  1755.   if (ScrollBars in [ssHorizontal, ssBoth]) and
  1756.     (UpdateCount <= 0) and not FIgnoreScrollResize and
  1757.     not ((Style = otOwnerDraw) and Assigned(FOnDrawItem)) then
  1758.     SetDisplayWidth(FRootNode.GetMaxDisplayWidth(0));
  1759. end;
  1760.  
  1761. procedure TCustomOutline.DoExpand(Node: TOutlineNode);
  1762. begin
  1763.   with Node do
  1764.     Expanded := not Expanded;
  1765. end;
  1766.  
  1767. procedure TCustomOutline.BeginUpdate;
  1768. begin
  1769.   if UpdateCount = 0 then SetUpdateState(True);
  1770.   Inc(UpdateCount);
  1771. end;
  1772.  
  1773. procedure TCustomOutline.EndUpdate;
  1774. begin
  1775.   Dec(UpdateCount);
  1776.   if UpdateCount = 0 then SetUpdateState(False);
  1777. end;
  1778.  
  1779. procedure TCustomOutline.SetUpdateState(Value: Boolean);
  1780. begin
  1781.   if FBlockInsert <> Value then
  1782.   begin
  1783.     FBlockInsert := Value;
  1784.     if not FBlockInsert then
  1785.     begin
  1786.       if ResizeGrid then Invalidate;
  1787.       if FRootNode.List.Count > 0 then
  1788.         TOutlineNode(FRootNode.List.First).SetGoodIndex
  1789.       else
  1790.         FRootNode.SetGoodIndex;
  1791.       SetHorzScrollBar;
  1792.     end;
  1793.   end;
  1794. end;
  1795.  
  1796. function TCustomOutline.ResizeGrid: Boolean;
  1797. var
  1798.   OldRowCount: LongInt;
  1799. begin
  1800.   Result := False;
  1801.   if not FBlockInsert then
  1802.   begin
  1803.     OldRowCount := RowCount;
  1804.     FSettingHeight := True;
  1805.     try
  1806.       RowCount := FRootNode.ExpandCount;
  1807.     finally
  1808.       FSettingHeight := False;
  1809.     end;
  1810.     Result := RowCount <> OldRowCount;
  1811.     if FSelectedItem <> FRootNode then SelectedItem := FSelectedItem.Index;
  1812.   end;
  1813. end;
  1814.  
  1815. function TCustomOutline.BadIndex(Value: TOutlineNode): Boolean;
  1816. begin
  1817.   Result := CompareNodes(Value, FGoodNode) = ocGreater;
  1818. end;
  1819.  
  1820. function TCustomOutline.SetGoodIndex(Value: TOutlineNode): TOutlineNode;
  1821. var
  1822.   ParentNode: TOutlineNode;
  1823.   Index: Integer;
  1824.   Compare: TOutlineNodeCompare;
  1825. begin
  1826.   Compare := CompareNodes(FGoodNode, Value);
  1827.  
  1828.   case Compare of
  1829.     ocLess,
  1830.     ocSame:
  1831.       Result := FGoodNode;
  1832.     ocGreater:
  1833.       begin
  1834.         ParentNode := Value.Parent;
  1835.         Index := ParentNode.List.IndexOf(Value);
  1836.         if Index <> 0 then
  1837.           Result := ParentNode[Index - 1]
  1838.         else
  1839.           Result := ParentNode;
  1840.       end;
  1841.     ocInvalid:
  1842.       Result := FRootNode;
  1843.   else
  1844.     Result := FRootNode;    
  1845.   end;
  1846.  
  1847.   FGoodNode := Result;
  1848. end;
  1849.  
  1850. function TCustomOutline.CompareNodes(Value1, Value2: TOutlineNode): TOutlineNodeCompare;
  1851. var
  1852.   Level1: Integer;
  1853.   Level2: Integer;
  1854.   Index1: Integer;
  1855.   Index2: Integer;
  1856.   Value1ParentNode: TOutlineNode;
  1857.   Value2ParentNode: TOutlineNode;
  1858.   CommonNode: TOutlineNode;
  1859.  
  1860.   function GetParentNodeAtLevel(Value: TOutlineNode; Level: Integer): TOutlineNode;
  1861.   begin
  1862.     while Level > 0 do
  1863.     begin
  1864.       Value := Value.Parent;
  1865.       Dec(Level);
  1866.     end;
  1867.   Result := Value;
  1868.   end;
  1869.  
  1870. begin
  1871.   if Value1 = Value2 then
  1872.   begin
  1873.     Result := ocSame;
  1874.     Exit;
  1875.   end;
  1876.  
  1877.   Value1ParentNode := Value1;
  1878.   Value2ParentNode := Value2;
  1879.  
  1880.   Level1 := Value1.GetLevel;
  1881.   Level2 := Value2.GetLevel;
  1882.  
  1883.   if Level1 > Level2 then
  1884.     Value1ParentNode := GetParentNodeAtLevel(Value1, Level1 - Level2)
  1885.   else if Level2 > Level1 then
  1886.     Value2ParentNode := GetParentNodeAtLevel(Value2, Level2 - Level1);
  1887.  
  1888.   while Value1ParentNode.Parent <> Value2ParentNode.Parent do
  1889.   begin
  1890.     Value1ParentNode := Value1ParentNode.Parent;
  1891.     Value2ParentNode := Value2ParentNode.Parent;
  1892.   end;
  1893.  
  1894.   CommonNode := Value1ParentNode.Parent;
  1895.   if CommonNode <> nil then
  1896.   begin
  1897.     Index1 := CommonNode.List.IndexOf(Value1ParentNode);
  1898.     Index2 := CommonNode.List.IndexOf(Value2ParentNode);
  1899.     if Index1 < Index2 then Result := ocLess
  1900.     else if Index2 < Index1 then Result := ocGreater
  1901.     else begin
  1902.       if Level1 > Level2 then Result := ocGreater
  1903.       else if Level1 = Level2 then Result := ocSame
  1904.       else Result := ocLess;
  1905.     end
  1906.   end
  1907.   else
  1908.     Result := ocInvalid;
  1909. end;
  1910.  
  1911. function TCustomOutline.GetDataItem(Value: Pointer): Longint;
  1912. begin
  1913.   Result := FRootNode.GetDataItem(Value);
  1914. end;
  1915.  
  1916. function TCustomOutline.GetItem(X, Y: Integer): LongInt;
  1917. var
  1918.   Value: TGridCoord;
  1919. begin
  1920.   Result := -1;
  1921.   Value := MouseCoord(X, Y);
  1922.   with Value do
  1923.    if (Y > 0) or (FRootNode.List.Count > 0) then
  1924.      Result := FRootNode.GetVisibleNode(Y + 1).Index;
  1925. end;
  1926.  
  1927. function TCustomOutline.GetTextItem(const Value: string): Longint;
  1928. begin
  1929.   Result := FRootNode.GetTextItem(Value);
  1930. end;
  1931.  
  1932. procedure TCustomOutline.SetCurItem(Value: LongInt);
  1933. begin
  1934.   if Value < 0 then Error(SInvalidCurrentItem);
  1935.   if not IsCurItem(Value) then
  1936.     try
  1937.       FCurItem := FRootNode.GetNodeAtIndex(Value);
  1938.     except
  1939.       on OutlineError do Error(SOutlineIndexError);
  1940.     end;
  1941. end;
  1942.  
  1943. procedure TCustomOutline.SetOutlineStyle(Value: TOutlineStyle);
  1944. begin
  1945.   if FOutlineStyle <> Value then
  1946.   begin
  1947.     FOutlineStyle := Value;
  1948.     SetHorzScrollBar;
  1949.     Invalidate;
  1950.   end;
  1951. end;
  1952.  
  1953. procedure TCustomOutline.CMFontChanged(var Message: TMessage);
  1954. begin
  1955.   inherited;
  1956.   SetRowHeight;
  1957.   SetHorzScrollBar;
  1958. end;
  1959.  
  1960. procedure TCustomOutline.SetDisplayWidth(Value: Integer);
  1961. begin
  1962.   FSettingWidth := True;
  1963.   try
  1964.     if DefaultColWidth <> Value then DefaultColWidth := Value;
  1965.   finally
  1966.     FSettingWidth := False;
  1967.   end;
  1968. end;
  1969.  
  1970. function TCustomOutline.GetNodeDisplayWidth(Node: TOutlineNode): Integer;
  1971. var
  1972.   Delta: Integer;
  1973.   TextLength: Integer;
  1974. begin
  1975.   Result := 0;
  1976.   Delta := (DefaultRowHeight - FFontSize) div 2;
  1977.  
  1978.   with Canvas do
  1979.   begin
  1980.     Font := Self.Font;
  1981.     TextLength := TextWidth(Node.Text) + 1;
  1982.   end;
  1983.  
  1984.   case OutlineStyle of
  1985.     osText: Inc(Result, DefaultRowHeight * (Integer(Node.Level) - 1));
  1986.     osPlusMinusPictureText: Inc(Result, DefaultRowHeight * (Integer(Node.Level) + 1));
  1987.     osPlusMinusText,
  1988.     osPictureText: Inc(Result, DefaultRowHeight * Integer(Node.Level));
  1989.     osTreeText:
  1990.       begin
  1991.         Inc(Result, DefaultRowHeight * (Integer(Node.Level) - 1) - Delta);
  1992.         if ooDrawTreeRoot in Options then Inc(Result, DefaultRowHeight);
  1993.       end;
  1994.     osTreePictureText:
  1995.       begin
  1996.         Inc(Result, DefaultRowHeight * (Integer(Node.Level)) - Delta);
  1997.         if ooDrawTreeRoot in Options then Inc(Result, DefaultRowHeight);
  1998.       end;
  1999.   end;
  2000.   Inc(Result, TextLength);
  2001.   if Result < 0 then Result := 0;
  2002. end;
  2003.  
  2004. function TCustomOutline.GetVisibleNode(Index: LongInt): TOutlineNode;
  2005. begin
  2006.   Result := FRootNode.GetVisibleNode(Index + 1);
  2007. end;
  2008.  
  2009. procedure TCustomOutline.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  2010. var
  2011.   Node: TOutlineNode;
  2012.   Expanded: Boolean;
  2013.   HasChildren: Boolean;
  2014.   IndentLevel: Word;
  2015.   Bitmap1, Bitmap2: TBitmap;
  2016.   TextLength: Integer;
  2017.   Delta: Integer;
  2018.   InitialLeft: Integer;
  2019.  
  2020.   function GetBitmap(Value: TOutlineBitmap): TBitmap;
  2021.   begin
  2022.     Result := FPictures[Ord(Value)];
  2023.   end;
  2024.  
  2025.   procedure DrawFocusCell;
  2026.   begin
  2027.     Inc(ARect.Right, TextLength);
  2028.     if (Row = ARow) and (Node.Text <> '') then
  2029.       Canvas.FillRect(ARect);
  2030.   end;
  2031.  
  2032.   procedure DrawTheText;
  2033.   begin
  2034.     Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
  2035.     ARect.Right := ARect.Left;
  2036.     DrawFocusCell;
  2037.     DrawText(Node, ARect);
  2038.   end;
  2039.  
  2040.   procedure DrawPlusMinusPicture;
  2041.   begin
  2042.     Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
  2043.     if HasChildren then
  2044.     begin
  2045.       if Expanded then
  2046.       begin
  2047.         Bitmap1 := GetBitmap(obMinus);
  2048.         Bitmap2 := GetBitmap(obOpen);
  2049.       end
  2050.       else begin
  2051.         Bitmap1 := GetBitmap(obPlus);
  2052.         Bitmap2 := GetBitmap(obClose);
  2053.       end;
  2054.     end
  2055.     else begin
  2056.       Bitmap1 := nil;
  2057.       Bitmap2 := GetBitmap(obLeaf);
  2058.     end;
  2059.     ARect.Left := ARect.Left + DefaultRowHeight * 2;
  2060.     ARect.Right := ARect.Left;
  2061.     DrawFocusCell;
  2062.     DrawText(Node, ARect);
  2063.     Dec(ARect.Left, DefaultRowHeight * 2);
  2064.     DrawPictures([Bitmap1, Bitmap2], ARect);
  2065.   end;
  2066.  
  2067.   procedure DrawPictureText;
  2068.   var
  2069.     Style: TOutlineBitmap;
  2070.   begin
  2071.     Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1));
  2072.     if HasChildren then
  2073.     begin
  2074.       if Expanded then Style := obOpen
  2075.       else Style := obClose
  2076.     end
  2077.     else Style := obLeaf;
  2078.     Bitmap1 := GetBitmap(Style);
  2079.     ARect.Left := ARect.Left + DefaultRowHeight;
  2080.     ARect.Right := ARect.Left;
  2081.     DrawFocusCell;
  2082.     DrawText(Node, ARect);
  2083.     Dec(ARect.Left, DefaultRowHeight);
  2084.     DrawPictures([Bitmap1], ARect);
  2085.   end;
  2086.  
  2087.   procedure DrawPlusMinusText;
  2088.   var
  2089.     Style: TOutlineBitmap;
  2090.   begin
  2091.     Inc(ARect.Left, DefaultRowHeight * IndentLevel);
  2092.     ARect.Right := ARect.Left;
  2093.     DrawFocusCell;
  2094.     DrawText(Node, ARect);
  2095.     if HasChildren then
  2096.     begin
  2097.       if Expanded then Style := obMinus
  2098.       else Style := obPlus;
  2099.       Bitmap1 := GetBitmap(Style);
  2100.       Dec(ARect.Left, DefaultRowHeight);
  2101.       DrawPictures([Bitmap1], ARect);
  2102.     end;
  2103.   end;
  2104.  
  2105.   procedure DrawTheTree;
  2106.   begin
  2107.     DrawTree(ARect, Node);
  2108.     Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1) - Delta);
  2109.     if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
  2110.     ARect.Right := ARect.Left + Delta;
  2111.     DrawFocusCell;
  2112.     Inc(ARect.Left, Delta);
  2113.     DrawText(Node, ARect);
  2114.   end;
  2115.  
  2116.   procedure DrawTreePicture;
  2117.   var
  2118.     Style: TOutlineBitmap;
  2119.   begin
  2120.     DrawTree(ARect, Node);
  2121.     Inc(ARect.Left, DefaultRowHeight * (IndentLevel - 1) - Delta);
  2122.     if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
  2123.     ARect.Left := ARect.Left + DefaultRowHeight;
  2124.     ARect.Right := ARect.Left + Delta;
  2125.     DrawFocusCell;
  2126.     DrawText(Node, ARect);
  2127.     Dec(ARect.Left, DefaultRowHeight - Delta);
  2128.     if HasChildren then
  2129.     begin
  2130.       if Expanded then Style := obOpen
  2131.       else Style := obClose;
  2132.     end
  2133.     else Style := obLeaf;
  2134.     Bitmap1 := GetBitmap(Style);
  2135.     DrawPictures([Bitmap1], ARect);
  2136.   end;
  2137.  
  2138. begin
  2139.   if FRootNode.List.Count = 0 then
  2140.   begin
  2141.     with Canvas do
  2142.     begin
  2143.       Brush.Color := Color;
  2144.       FillRect(ARect);
  2145.     end;
  2146.     Exit;
  2147.   end;
  2148.  
  2149.   if (Style = otOwnerDraw) and Assigned(FOnDrawItem) then
  2150.   begin
  2151.     if Row = ARow then
  2152.     begin
  2153.       if GetFocus = Self.Handle then
  2154.       begin
  2155.         FOnDrawItem(Self, ARow, ARect, [odFocused, odSelected]);
  2156.         if ooDrawFocusRect in Options then
  2157.           DrawFocusRect(Canvas.Handle, ARect);
  2158.       end
  2159.       else FOnDrawItem(Self, ARow, ARect, [odSelected])
  2160.     end
  2161.     else OnDrawItem(Self, ARow, ARect, []);
  2162.     Exit;
  2163.   end;
  2164.  
  2165.   InitialLeft := ARect.Left;
  2166.   Node := GetVisibleNode(ARow);
  2167.   Delta := (ARect.Bottom - ARect.Top - FFontSize) div 2;
  2168.  
  2169.   with Canvas do
  2170.   begin
  2171.     Font := Self.Font;
  2172.     Brush.Color := Color;
  2173.     FillRect(ARect);
  2174.     TextLength := TextWidth(Node.Text) + 1;
  2175.     if Row = ARow then
  2176.     begin
  2177.       Brush.Color := clHighlight;
  2178.       Font.Color := clHighlightText;
  2179.     end;
  2180.   end;
  2181.  
  2182.   Expanded := Node.Expanded;
  2183.   HasChildren := Node.HasItems;
  2184.   IndentLevel := Node.GetLevel;
  2185.   case OutlineStyle of
  2186.     osText: DrawTheText;
  2187.     osPlusMinusText: DrawPlusMinusText;
  2188.     osPlusMinusPictureText: DrawPlusMinusPicture;
  2189.     osPictureText: DrawPictureText;
  2190.     osTreeText: DrawTheTree;
  2191.     osTreePictureText: DrawTreePicture;
  2192.   end;
  2193.  
  2194.   if (Row = ARow) and (Node.Text <> '') then
  2195.   begin
  2196.     ARect.Left := InitialLeft + DefaultRowHeight * (IndentLevel - 1);
  2197.     if OutlineStyle >= osTreeText then
  2198.     begin
  2199.       Dec(ARect.Left, Delta);
  2200.       if ooDrawTreeRoot in Options then Inc(ARect.Left, DefaultRowHeight);
  2201.     end;
  2202.     if (OutlineStyle <> osText) and (OutlineStyle <> osTreeText) then
  2203.       Inc(ARect.Left, DefaultRowHeight);
  2204.     if OutlineStyle = osPlusMinusPictureText then
  2205.       Inc(ARect.Left, DefaultRowHeight);
  2206.     if (GetFocus = Self.Handle) and (ooDrawFocusRect in Options) then
  2207.       DrawFocusRect(Canvas.Handle, ARect);
  2208.   end;
  2209. end;
  2210.  
  2211. procedure TCustomOutline.DrawTree(ARect: TRect; Node: TOutlineNode);
  2212. var
  2213.   Offset: Word;
  2214.   Height: Word;
  2215.   OldPen: TPen;
  2216.   I: Integer;
  2217.   ParentNode: TOutlineNode;
  2218.   IndentLevel: Integer;
  2219. begin
  2220.   Offset := DefaultRowHeight div 2;
  2221.   Height := ARect.Bottom;
  2222.   IndentLevel := Node.GetLevel;
  2223.   I := IndentLevel - 3;
  2224.   if ooDrawTreeRoot in Options then Inc(I);
  2225.   OldPen := TPen.Create;
  2226.   try
  2227.     OldPen.Assign(Canvas.Pen);
  2228.     with Canvas do
  2229.     begin
  2230.       Pen.Color := clBlack;
  2231.       Pen.Width := 1;
  2232.       try
  2233.         ParentNode := Node.Parent;
  2234.         while (ParentNode.Parent <> nil) and
  2235.           ((ooDrawTreeRoot in Options) or
  2236.           (ParentNode.Parent.Parent <> nil)) do
  2237.         begin
  2238.           with ParentNode.Parent do
  2239.           begin
  2240.             if List.IndexOf(ParentNode) < List.Count - 1 then
  2241.             begin
  2242.               Canvas.MoveTo(ARect.Left + DefaultRowHeight * I + Offset, ARect.Top);
  2243.               Canvas.LineTo(ARect.Left + DefaultRowHeight * I + Offset, Height);
  2244.             end;
  2245.           end;
  2246.           ParentNode := ParentNode.Parent;
  2247.           Dec(I);
  2248.         end;
  2249.  
  2250.         with Node.Parent do
  2251.           if List.IndexOf(Node) = List.Count - 1 then
  2252.             Height := ARect.Top + Offset;
  2253.  
  2254.         if (ooDrawTreeRoot in Options) or (IndentLevel > 1) then
  2255.         begin
  2256.           if not (ooDrawTreeRoot in Options) then Dec(IndentLevel);
  2257.           with ARect do
  2258.           begin
  2259.             Inc(Left, DefaultRowHeight * (IndentLevel - 1));
  2260.             MoveTo(Left + Offset, Top);
  2261.             LineTo(Left + Offset, Height);
  2262.             MoveTo(Left + Offset, Top + Offset);
  2263.             LineTo(Left + Offset + FFontSize div 2, Top + Offset);
  2264.           end;
  2265.         end;
  2266.       finally
  2267.         Pen.Assign(OldPen);
  2268.       end;
  2269.     end;
  2270.   finally
  2271.     OldPen.Destroy;
  2272.   end;
  2273. end;
  2274.  
  2275. procedure TCustomOutline.DrawPictures(BitMaps: array of TBitmap; ARect: TRect);
  2276. var
  2277.   I: Word;
  2278.   Rect: TRect;
  2279.   Value: TBitmap;
  2280.   Offset: Word;
  2281.   Delta: Integer;
  2282.   OldTop: Integer;
  2283.   OldColor: TColor;
  2284. begin
  2285.   OldColor := Canvas.Brush.Color;
  2286.   Canvas.Brush.Color := Color;
  2287.   Offset := (DefaultRowHeight - FFontSize) div 2;
  2288.   Rect.Top := ARect.Top + Offset;
  2289.   Rect.Bottom := Rect.Top + FFontSize;
  2290.   for I := Low(Bitmaps) to High(Bitmaps) do
  2291.   begin
  2292.     Value := BitMaps[I];
  2293.     Rect.Left := ARect.Left + Offset - 1;
  2294.     Rect.Right := Rect.Left + FFontSize;
  2295.     Inc(ARect.Left, DefaultRowHeight);
  2296.     if Value <> nil then
  2297.     begin
  2298.       if not (ooStretchBitmaps in Options) then
  2299.       begin
  2300.         if Rect.Top + Value.Height < Rect.Bottom then
  2301.           Rect.Bottom := Rect.Top + Value.Height;
  2302.         if Rect.Left + Value.Width < Rect.Right then
  2303.           Rect.Right := Rect.Left + Value.Width;
  2304.         Delta := (FFontSize - (Rect.Bottom - Rect.Top)) div 2;
  2305.         if Delta > 0 then
  2306.         begin
  2307.           Delta := (DefaultRowHeight - (Rect.Bottom - Rect.Top)) div 2;
  2308.           OldTop := Rect.Top;
  2309.           Rect.Top := ARect.Top + Delta;
  2310.           Rect.Bottom := Rect.Bottom - OldTop + Rect.Top;
  2311.         end;
  2312.         Canvas.BrushCopy(Rect, Value,
  2313.           Bounds(0, 0, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top),
  2314.           Value.TransparentColor);
  2315.       end else
  2316.         Canvas.BrushCopy(Rect, Value,
  2317.           Bounds(0, 0, Value.Width, Value.Height),
  2318.           Value.TransparentColor);
  2319.     end;
  2320.   end;
  2321.   Canvas.Brush.Color := OldColor;
  2322. end;
  2323.  
  2324. procedure TCustomOutline.DrawText(Node: TOutlineNode; Rect: TRect);
  2325. begin
  2326.   Windows.DrawText(Canvas.Handle, PChar(Node.Text), Length(Node.Text), Rect,
  2327.     DT_LEFT or DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
  2328. end;
  2329.  
  2330. function TCustomOutline.StoreBitmap(Index: Integer): Boolean;
  2331. begin
  2332.   Result := TOutlineBitmap(Index) in FUserBitmaps;
  2333. end;
  2334.  
  2335. procedure TCustomOutline.ClearBitmap(var Bitmap: TBitmap; Kind: TOutlineBitmap);
  2336. begin
  2337.   if Bitmap <> nil then
  2338.   begin
  2339.     Bitmap.Free;
  2340.     Bitmap := nil;
  2341.   end;
  2342. end;
  2343.  
  2344. procedure TCustomOutline.ChangeBitmap(Value: TBitmap; Kind: TOutlineBitmap);
  2345. var
  2346.   Bitmap: ^TBitmap;
  2347. begin
  2348.   Bitmap := @FPictures[Ord(Kind)];
  2349.   Include(FUserBitmaps, Kind);
  2350.   if Value = nil then ClearBitmap(Bitmap^, Kind)
  2351.   else Bitmap^.Assign(Value);
  2352.   Invalidate;
  2353. end;
  2354.  
  2355. procedure TCustomOutline.SetPicture(Index: Integer; Value: TBitmap);
  2356. begin
  2357.   ChangeBitmap(Value, TOutlineBitmap(Index));
  2358. end;
  2359.  
  2360. function TCustomOutline.GetPicture(Index: Integer): TBitmap;
  2361. begin
  2362.   if csLoading in ComponentState then
  2363.     Include(FUserBitmaps, TOutlineBitmap(Index));
  2364.   Result := FPictures[Index];
  2365. end;
  2366.  
  2367. procedure TCustomOutline.LoadFromFile(const FileName: string);
  2368. var
  2369.   Stream: TStream;
  2370. begin
  2371.   Stream := TFileStream.Create(FileName, fmOpenRead);
  2372.   try
  2373.     LoadFromStream(Stream);
  2374.   finally
  2375.     Stream.Free;
  2376.   end;
  2377. end;
  2378.  
  2379. {procedure TCustomOutline.SetMaskColor(Value: TColor);
  2380. begin
  2381.   FMaskColor := Value;
  2382.   Invalidate;
  2383. end;}
  2384.  
  2385. procedure TCustomOutline.SetItemHeight(Value: Integer);
  2386. begin
  2387.   FItemHeight := Value;
  2388.   if Style <> otOwnerDraw then SetRowHeight
  2389.   else begin
  2390.     DefaultRowHeight := ItemHeight;
  2391.     FFontSize := MulDiv(ItemHeight, 100, 120);
  2392.     Invalidate;
  2393.   end;
  2394. end;
  2395.  
  2396. procedure TCustomOutline.SetStyle(Value: TOutlineType);
  2397. begin
  2398.   if Style <> Value then
  2399.   begin
  2400.     FStyle := Value;
  2401.     if Value = otStandard then SetRowHeight;
  2402.   end;
  2403. end;
  2404.  
  2405. procedure TCustomOutline.SetOutlineOptions(Value: TOutlineOptions);
  2406. begin
  2407.   if Value <> FOptions then
  2408.   begin
  2409.     FOptions := Value;
  2410.     Invalidate;
  2411.   end;
  2412. end;
  2413.  
  2414. function LineStart(Buffer, BufPos: PChar): PChar;
  2415. begin
  2416.   if BufPos - Buffer - 2 > 0 then
  2417.   begin
  2418.     Dec(BufPos, 2);
  2419.     while (BufPos^ <> #$0D) and (BufPos > Buffer) do Dec(BufPos);
  2420.     if BufPos > Buffer then
  2421.     begin
  2422.       Inc(BufPos);
  2423.       if BufPos^ = #$0A then Inc(BufPos);
  2424.     end;
  2425.     Result := BufPos;
  2426.   end
  2427.   else Result := Buffer;
  2428. end;
  2429.  
  2430. function GetString(BufPtr: PChar; var S: string): PChar;
  2431. var
  2432.   Start: PChar;
  2433. begin
  2434.   Start := BufPtr;
  2435.   while not (BufPtr^ in [#13, #26]) do Inc(BufPtr);
  2436.   SetString(S, Start, Integer(BufPtr - Start));
  2437.   if BufPtr^ = #13 then Inc(BufPtr);
  2438.   if BufPtr^ = #10 then Inc(BufPtr);
  2439.   Result := BufPtr;
  2440. end;
  2441.  
  2442. procedure TCustomOutline.LoadFromStream(Stream: TStream);
  2443. const
  2444.   EOF = Chr($1A);
  2445.   BufSize = 4096;
  2446. var
  2447.   Count: Integer;
  2448.   Buffer, BufPtr, BufEnd, BufTop: PChar;
  2449.   ParentNode, NewNode: TOutlineNode;
  2450.   Str: string;
  2451.   Level, OldLevel: Cardinal;
  2452.   I: Integer;
  2453. begin
  2454.   GetMem(Buffer, BufSize);
  2455.   try
  2456.     OldLevel := 0;
  2457.     Clear;
  2458.     ParentNode := FRootNode;
  2459.     BufEnd := Buffer + BufSize;
  2460.     BufTop := BufEnd;
  2461.     repeat
  2462.       Count := BufEnd - BufTop;
  2463.       if Count <> 0 then System.Move(BufTop[0], Buffer[0], Count);
  2464.       BufTop := Buffer + Count;
  2465.       Inc(BufTop, Stream.Read(BufTop[0], BufEnd - BufTop));
  2466.       if BufTop < BufEnd then BufTop[0] := EOF else
  2467.       begin
  2468.         BufTop := LineStart(Buffer, BufTop);
  2469.         if BufTop = Buffer then Error(SOutlineLongLine);
  2470.       end;
  2471.       BufPtr := Buffer;
  2472.       while (BufPtr < BufTop) and (BufPtr[0] <> EOF) do
  2473.       begin
  2474.         BufPtr := GetBufStart(BufPtr, Level);
  2475.         BufPtr := GetString(BufPtr, Str);
  2476.         NewNode := TOutlineNode.Create(Self);
  2477.         try
  2478.           NewNode.Text := Str;
  2479.           if (Level > OldLevel) or (ParentNode = FRootNode) then
  2480.           begin
  2481.             if Level - OldLevel > 1 then Error(SOutlineFileLoad);
  2482.           end
  2483.           else
  2484.           begin
  2485.             for I := OldLevel downto Level do
  2486.             begin
  2487.               ParentNode := ParentNode.Parent;
  2488.               if ParentNode = nil then Error(SOutlineFileLoad);
  2489.             end;
  2490.           end;
  2491.           ParentNode.List.Add(NewNode);
  2492.           NewNode.FParent := ParentNode;
  2493.           ParentNode := NewNode;
  2494.           OldLevel := Level;
  2495.         except
  2496.           NewNode.Free;
  2497.           Raise;
  2498.         end;
  2499.       end;
  2500.     until (BufPtr < BufEnd) and (BufPtr[0] = EOF);
  2501.   finally
  2502.     FreeMem(Buffer, BufSize);
  2503.     if not (csLoading in ComponentState) then Loaded;
  2504.   end;
  2505. end;
  2506.  
  2507. procedure TCustomOutline.Loaded;
  2508. var
  2509.   Item: TOutlineBitmap;
  2510. begin
  2511.   inherited Loaded;
  2512.   with FRootNode do
  2513.   begin
  2514.     FExpandCount := List.Count;
  2515.     Row := 0;
  2516.     ResetSelectedItem;
  2517.     if ResizeGrid then Invalidate;
  2518.     if List.Count > 0 then
  2519.     begin
  2520.       TOutlineNode(List.First).SetGoodIndex;
  2521.       FSelectedItem := List.First;
  2522.     end;
  2523.     if csDesigning in ComponentState then FullExpand;
  2524.   end;
  2525.   for Item := obPlus to obLeaf do
  2526.     if (Item in FOldBitmaps) and not (Item in FUserBitmaps) then
  2527.       ChangeBitmap(nil, Item);
  2528.   FOldBitmaps := [];
  2529.   SetHorzScrollBar;
  2530. end;
  2531.  
  2532. procedure TCustomOutline.SaveToFile(const FileName: string);
  2533. var
  2534.   Stream: TStream;
  2535. begin
  2536.   Stream := TFileStream.Create(FileName, fmCreate);
  2537.   try
  2538.     SaveToStream(Stream);
  2539.   finally
  2540.     Stream.Free;
  2541.   end;
  2542. end;
  2543.  
  2544. procedure TCustomOutline.SaveToStream(Stream: TStream);
  2545. const
  2546.   BufSize = 4096;
  2547. var
  2548.   Buffer: PChar;
  2549. begin
  2550.   GetMem(Buffer, BufSize);
  2551.   try
  2552.     FRootNode.WriteNode(Buffer, Stream);
  2553.   finally
  2554.     FreeMem(Buffer, BufSize);
  2555.   end;
  2556. end;
  2557.  
  2558. procedure TCustomOutline.SetStrings(Value: TStrings);
  2559. begin
  2560.   FStrings.Assign(Value);
  2561.   if csDesigning in ComponentState then FRootNode.FullExpand;
  2562.   SetHorzScrollBar;
  2563. end;
  2564.  
  2565. function TCustomOutline.GetStrings: TStrings;
  2566. begin
  2567.   Result := FStrings;
  2568. end;
  2569.  
  2570. procedure TCustomOutline.Error(const ErrorString: string);
  2571. begin
  2572.   Raise EOutlineError.Create(ErrorString);
  2573. end;
  2574.  
  2575. procedure TCustomOutline.Expand(Index: LongInt);
  2576. begin
  2577.   if Assigned(FOnExpand) then FOnExpand(Self, Index);
  2578. end;
  2579.  
  2580. procedure TCustomOutline.Collapse(Index: LongInt);
  2581. begin
  2582.   if Assigned(FOnCollapse) then FOnCollapse(Self, Index);
  2583. end;
  2584.  
  2585. end.
  2586.