MMOutline.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:71k
源码类别:

Delphi控件源码

开发平台:

Delphi

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