home *** CD-ROM | disk | FTP | other *** search
/ Chip 1997 April / Chip_1997-04_cd.bin / prezent / cb / data.z / OUTLINE.PAS < prev    next >
Pascal/Delphi Source File  |  1997-01-16  |  69KB  |  2,573 lines

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