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