home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vp21beta.zip / ATVSRC.RAR / OUTLINE.PAS < prev    next >
Pascal/Delphi Source File  |  2000-08-15  |  27KB  |  931 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal Version 7.0                        }
  5. {       Turbo Vision Unit                               }
  6. {                                                       }
  7. {       Copyright (c) 1992 Borland International        }
  8. {                                                       }
  9. {       Virtual Pascal v2.1                             }
  10. {       Copyright (C) 1996-2000 vpascal.com             }
  11. {                                                       }
  12. {*******************************************************}
  13.  
  14. unit Outline;
  15.  
  16. {$X+,I-,S-,R-,Cdecl-,Use32+}
  17.  
  18. interface
  19.  
  20. uses Objects, Drivers, Views;
  21.  
  22. const
  23.   ovExpanded = $01;
  24.   ovChildren = $02;
  25.   ovLast     = $04;
  26.  
  27. const
  28.   cmOutlineItemSelected = 301;
  29.  
  30. const
  31.   COutlineViewer = CScroller + #8#8;
  32.  
  33. type
  34.  
  35. { TOutlineViewer  object }
  36.  
  37.   { Palette layout }
  38.   { 1 = Normal color }
  39.   { 2 = Focus color }
  40.   { 3 = Select color }
  41.   { 4 = Not expanded color }
  42.  
  43.   POutlineViewer = ^TOutlineViewer;
  44.   TOutlineViewer = object(TScroller)
  45.     Foc: Integer;
  46.     constructor Init(var Bounds: TRect; AHScrollBar,
  47.       AVScrollBar: PScrollBar);
  48.     constructor Load(var S: TStream);
  49.     procedure Adjust(Node: Pointer; Expand: Boolean); virtual;
  50.     function CreateGraph(Level: Integer; Lines: LongInt; Flags: Word;
  51.       LevWidth, EndWidth: Integer; const Chars: String): String;
  52.     procedure Draw; virtual;
  53.     procedure ExpandAll(Node: Pointer);
  54.     function FirstThat(Test: Pointer): Pointer;
  55.     procedure Focused(I: Integer); virtual;
  56.     function ForEach(Action: Pointer): Pointer;
  57.     function GetChild(Node: Pointer; I: Integer): Pointer; virtual;
  58.     function GetGraph(Level: Integer; Lines: LongInt; Flags: Word): String; virtual;
  59.     function GetNumChildren(Node: Pointer): Integer; virtual;
  60.     function GetNode(I: Integer): Pointer;
  61.     function GetPalette: PPalette; virtual;
  62.     function GetRoot: Pointer; virtual;
  63.     function GetText(Node: Pointer): String; virtual;
  64.     procedure HandleEvent(var Event: TEvent); virtual;
  65.     function HasChildren(Node: Pointer): Boolean; virtual;
  66.     function IsExpanded(Node: Pointer): Boolean; virtual;
  67.     function IsSelected(I: Integer): Boolean; virtual;
  68.     procedure Selected(I: Integer); virtual;
  69.     procedure SetState(AState: Word; Enable: Boolean); virtual;
  70.     procedure Store(var S: TStream);
  71.     procedure Update;
  72.   private
  73.     procedure AdjustFocus(NewFocus: Integer);
  74.     function Iterate(Action: Pointer; CallerFrame: Word;
  75.       CheckRslt: Boolean): Pointer;
  76.   end;
  77.  
  78. { TNode }
  79.  
  80.   PNode = ^TNode;
  81.   TNode = record
  82.     Next: PNode;
  83.     Text: PString;
  84.     ChildList: PNode;
  85.     Expanded: Boolean;
  86.   end;
  87.  
  88. { TOutline object }
  89.  
  90.   { Palette layout }
  91.   { 1 = Normal color }
  92.   { 2 = Focus color }
  93.   { 3 = Select color }
  94.  
  95.   POutline = ^TOutline;
  96.   TOutline = object(TOutlineViewer)
  97.     Root: PNode;
  98.  
  99.     constructor Init(var Bounds: TRect; AHScrollBar,
  100.       AVScrollBar: PScrollBar; ARoot: PNode);
  101.     constructor Load(var S: TStream);
  102.     destructor Done; virtual;
  103.  
  104.     procedure Adjust(Node: Pointer; Expand: Boolean); virtual;
  105.     function GetRoot: Pointer; virtual;
  106.     function GetNumChildren(Node: Pointer): Integer; virtual;
  107.     function GetChild(Node: Pointer; I: Integer): Pointer; virtual;
  108.     function GetText(Node: Pointer): String; virtual;
  109.     function IsExpanded(Node: Pointer): Boolean; virtual;
  110.     function HasChildren(Node: Pointer): Boolean; virtual;
  111.     procedure Store(var S: TStream);
  112.   end;
  113.  
  114. const
  115.   ROutline: TStreamRec = (
  116.      ObjType: 91;
  117.      VmtLink: Ofs(TypeOf(TOutline)^);
  118.      Load:    @TOutline.Load;
  119.      Store:   @TOutline.Store
  120.   );
  121.  
  122. procedure RegisterOutline;
  123. function NewNode(const AText: String; AChildren, ANext: PNode): PNode;
  124. procedure DisposeNode(Node: PNode);
  125.  
  126. implementation
  127.  
  128. { TOutlineViewer }
  129.  
  130. constructor TOutlineViewer.Init(var Bounds: TRect; AHScrollBar,
  131.   AVScrollBar: PScrollBar);
  132. begin
  133.   inherited Init(Bounds, AHScrollBar, AVScrollBar);
  134.   GrowMode := gfGrowHiX + gfGrowHiY;
  135.   Foc := 0;
  136. end;
  137.  
  138. constructor TOutlineViewer.Load(var S: TStream);
  139. begin
  140.   inherited Load(S);
  141.   S.Read(Foc, SizeOf(Foc));
  142. end;
  143.  
  144. { Called when the user requests Node to be contracted or
  145.   expanded (i.e. its children to be hidden or shown) }
  146.  
  147. procedure TOutlineViewer.Adjust(Node: Pointer; Expand: Boolean);
  148. begin
  149.   Abstract;
  150. end;
  151.  
  152. { Called internally to ensure the focus is within range and displayed }
  153.  
  154. procedure TOutlineViewer.AdjustFocus(NewFocus: Integer);
  155. begin
  156.   if NewFocus < 0 then NewFocus := 0
  157.   else if NewFocus >= Limit.Y then NewFocus := Limit.Y - 1;
  158.   if Foc <> NewFocus then Focused(NewFocus);
  159.   if NewFocus < Delta.Y then
  160.     ScrollTo(Delta.X, NewFocus)
  161.   else if NewFocus - Size.Y >= Delta.Y then
  162.     ScrollTo(Delta.X, NewFocus - Size.Y + 1);
  163. end;
  164.  
  165. { Called to draw the outline }
  166.  
  167. procedure TOutlineViewer.Draw;
  168. var
  169.   NrmColor, SelColor, FocColor: Word;
  170.   B: TDrawBuffer;
  171.   I: Integer;
  172.  
  173.   function DrawTree(Cur: Pointer; Level, Position: Integer; Lines: LongInt;
  174.     Flags: Word): Boolean; far;
  175.   var
  176.     Color: Word;
  177.     S: String;
  178.   begin
  179.     DrawTree := False;
  180.  
  181.     if Position >= Delta.Y then
  182.     begin
  183.       if Position >= Delta.Y + Size.Y then
  184.       begin
  185.         DrawTree := True;
  186.         Exit;
  187.       end;
  188.  
  189.       if (Position = Foc) and (State and sfFocused <> 0) then
  190.         Color := FocColor
  191.       else if IsSelected(Position) then
  192.         Color := SelColor
  193.       else
  194.         Color := NrmColor;
  195.       MoveChar(B, ' ', Color, Size.X);
  196.       S := GetGraph(Level, Lines, Flags);
  197.       if Flags and  ovExpanded = 0 then
  198.         S := Concat(S, '~', GetText(Cur), '~')
  199.       else
  200.         S := Concat(S, GetText(Cur));
  201.       MoveCStr(B, Copy(S, Delta.X + 1, 255), Color);
  202.       WriteLine(0, Position - Delta.Y, Size.X, 1, B);
  203.       I := Position;
  204.     end;
  205.   end;
  206.  
  207. begin
  208.   NrmColor := GetColor($0401);
  209.   FocColor := GetColor($0202);
  210.   SelColor := GetColor($0303);
  211.   FirstThat(@DrawTree);
  212.   MoveChar(B, ' ', NrmColor, Size.X);
  213.   WriteLine(0, I + 1, Size.X, Size.Y - (I - Delta.Y), B);
  214. end;
  215.  
  216. { ExpandAll expands the current node and all child nodes }
  217.  
  218. procedure TOutlineViewer.ExpandAll(Node: Pointer);
  219. var
  220.   I, N: Integer;
  221. begin
  222.   if HasChildren(Node) then
  223.   begin
  224.     Adjust(Node, True);
  225.     N := GetNumChildren(Node) - 1;
  226.     for I := 0 to N do
  227.       ExpandAll(GetChild(Node, I));
  228.   end;
  229. end;
  230.  
  231. { Draws a graph string suitable for returning from GetGraph.  Level
  232.   indicates the outline level.  Lines is the set of bits decribing
  233.   the which levels have a "continuation" mark (usually a vertical
  234.   lines).  If bit 3 is set, level 3 is continued beyond this level.
  235.   Flags gives extra information about how to draw the end of the
  236.   graph (see the ovXXX constants).  LevWidth is how many characters
  237.   to indent for each level.  EndWidth is the length the end characters.
  238.  
  239.   The graphics is divided into two parts: the level marks, and the end
  240.   or node graphic.  The level marks consist of the Level Mark character
  241.   separated by Level Filler.  What marks are present is determined by
  242.   Lines.  The end graphic is constructed by placing on of the End First
  243.   charcters followed by EndWidth-4 End Filler characters, followed by the
  244.   End Child character, followed by the Retract/Expand character.  If
  245.   EndWidth equals 2, End First and Retract/Expand are used.  If EndWidth
  246.   equals 1, only the Retract/Expand character is used.  Which characters
  247.   are selected is determined by Flags.
  248.  
  249.   The layout for the characters in the Chars is:
  250.  
  251.    1: Level Filler
  252.      Typically a space.  Used between level markers.
  253.    2: Level Mark
  254.      Typically a vertical bar.  Used to mark the levels currenly active.
  255.    3: End First (not last child)
  256.      Typically a sideways T.  Used as the first character of the end part
  257.      of a node graphic if the node is not the last child of the parent.
  258.    4: End First (last child)
  259.      Typically a L shape.  Used as the first character of the end part
  260.      of a node graphic if the node is the last child of the parent.
  261.    5: End Filler
  262.      Typically a horizontal line.  Used as filler for the end part of a
  263.      node graphic.
  264.    6: End Child position
  265.      Typically not used.  If EndWidth > LevWidth this character will
  266.      be placed on top of the markers for next level.  If used it is
  267.      typically a T.
  268.    7: Retracted character
  269.      Typically a '+'.  Displayed as the last character of the end
  270.      node if the level has children and they are not expanded.
  271.    8: Expanded character
  272.      Typically as straight line. Displayed as the last character of
  273.      the end node if the level has children and they are expanded.
  274.  
  275.   As an example GetGraph calls CreateGraph with the following paramters:
  276.  
  277.     CreateGraph(Level, Lines, Flags, 3, 3, ' '#179#195#192#196#196'+'#196);
  278.  
  279.   To use double, instead of single lines use:
  280.  
  281.     CreateGraph(Level, Lines, Flags, 3, 3, ' '#186#204#200#205#205'+'#205);
  282.  
  283.   To have the children line drop off prior to the text instead of underneath,
  284.   use the following call:
  285.  
  286.     CreateGraph(Level, Lines, Flags, 2, 4, ' '#179#195#192#196#194'+'#196);
  287.  
  288.   }
  289.  
  290. function TOutlineViewer.CreateGraph(Level: Integer; Lines: LongInt;
  291.   Flags: Word; LevWidth, EndWidth: Integer;
  292.   const Chars: String): String; assembler; {$USES ebx,esi,edi} {$FRAME-}
  293. const
  294.   FillerOrBar   = 0;
  295.   YorL          = 2;
  296.   StraightOrTee = 4;
  297.   Retracted     = 6;
  298. var
  299.   Last, Children, Expanded: Boolean;
  300. asm
  301.                 cld
  302.  
  303.         { Break out flags }
  304.                 xor     ebx,ebx
  305.                 mov     eax,Flags
  306.                 mov     Expanded,bl
  307.                 shr     eax,1
  308.                 adc     Expanded,bl
  309.                 mov     Children,bl
  310.                 shr     eax,1
  311.                 adc     Children,bl
  312.                 mov     Last,bl
  313.                 shr     eax,1
  314.                 adc     Last,bl
  315.  
  316.         { Load registers }
  317.                 mov     esi,Chars
  318.                 inc     esi
  319.                 mov     edi,@Result
  320.                 inc     edi
  321.                 mov     edx,Lines
  322.                 inc     Level
  323.  
  324.         { Write bar characters }
  325.                 jmp     @@2
  326.               @@1:
  327.                 xor     ebx,ebx
  328.                 shr     edx,1
  329.                 rcl     ebx,1
  330.                 mov     al,[esi].FillerOrBar[ebx]
  331.                 stosb
  332.                 mov     al,[esi].FillerOrBar
  333.                 mov     ecx,LevWidth
  334.                 dec     ecx
  335.                 rep     stosb
  336.               @@2:
  337.                 dec     Level
  338.                 jnz     @@1
  339.  
  340.         { Write end characters }
  341.                 mov     ecx,EndWidth
  342.                 dec     ecx
  343.                 jz      @@4
  344.                 mov     bl,Last
  345.                 mov     al,[esi].YorL[ebx]
  346.                 stosb
  347.                 dec     ecx
  348.                 jz      @@4
  349.                 dec     ecx
  350.                 jz      @@3
  351.                 mov     al,[esi].StraightOrTee
  352.                 rep     stosb
  353.               @@3:
  354.                 mov     bl,Children
  355.                 mov     al,[esi].StraightOrTee[ebx]
  356.                 stosb
  357.               @@4:
  358.                 mov     bl,Expanded
  359.                 mov     al,[esi].Retracted[ebx]
  360.                 stosb
  361.                 mov     eax,edi
  362.                 mov     edi,@Result
  363.                 sub     eax,edi
  364.                 dec     eax
  365.                 stosb
  366. end;
  367.  
  368. { Internal function used to fetch the caller's stack frame }
  369.  
  370. function CallerFrame: Word; assembler; {$USES None} {$FRAME-}
  371. asm
  372.                 mov     eax,[ebp]
  373. end;
  374.  
  375. { FirstThat iterates over the nodes of the outline until the given
  376.   local function returns true. The declaration for the local function
  377.   must look like (save for the names, of course):
  378.  
  379.     function MyIter(Cur: Pointer; Level, Position: Integer;
  380.       Lines: LongInt; Flags: Word); far;
  381.  
  382.   The parameters are as follows:
  383.  
  384.     Cur:        A pointer to the node being checked.
  385.     Level:      The level of the node (how many node above it it has)
  386.                 Level is 0 based.  This can be used to a call to
  387.                 either GetGraph or CreateGraph.
  388.     Position:   The display order position of the node in the list.
  389.                 This can be used in a call to Focused or Selected.
  390.                 If in range, Position - Delta.Y is location the node
  391.                 is displayed on the view.
  392.     Lines:      Bits indicating the active levels.  This can be used in a
  393.                 call to GetGraph or CreateGraph. It dicatates which
  394.                 horizontal lines need to be drawn.
  395.     Flags:      Various flags for drawing (see ovXXXX flags).  Can be used
  396.                 in a call to GetGraph or CreateGraph. }
  397.  
  398. function TOutlineViewer.FirstThat(Test: Pointer): Pointer;
  399. begin
  400.   FirstThat := Iterate(Test, CallerFrame, True);
  401. end;
  402.  
  403. { Called whenever Node receives focus }
  404.  
  405. procedure TOutlineViewer.Focused(I: Integer);
  406. begin
  407.   Foc := I;
  408. end;
  409.  
  410. { Iterates over all the nodes.  See FirstThat for a more details }
  411.  
  412. function TOutlineViewer.ForEach(Action: Pointer): Pointer;
  413. begin
  414.   Iterate(Action, CallerFrame, False);
  415. end;
  416.  
  417. { Returns the outline palette }
  418.  
  419. function TOutlineViewer.GetPalette: PPalette;
  420. const
  421.   P: String[Length(COutlineViewer)] = COutlineViewer;
  422. begin
  423.   GetPalette := @P;
  424. end;
  425.  
  426. { Overridden to return a pointer to the root of the outline }
  427.  
  428. function TOutlineViewer.GetRoot: Pointer;
  429. begin
  430.   Abstract;
  431. end;
  432.  
  433. { Called to retrieve the characters to display prior to the
  434.   text returned by GetText.  Can be overridden to return
  435.   change the appearance of the outline. My default calls
  436.   CreateGraph with the default. }
  437.  
  438. function TOutlineViewer.GetGraph(Level: Integer; Lines: LongInt;
  439.   Flags: Word): String;
  440. {const
  441.   LevelWidth = 2;
  442.   EndWidth   = LevelWidth + 2;
  443.   GraphChars = ' '#179#195#192#196#194'+'#196; }
  444. {  GraphChars = ' '#186#204#200#205#203'+'#205;}
  445. const
  446.   LevelWidth = 3;
  447.   EndWidth   = LevelWidth;
  448.   GraphChars = ' '#179#195#192#196#196'+'#196;
  449. {  GraphChars = ' '#186#204#200#205#205'+'#205;}
  450. begin
  451.   GetGraph := Copy(CreateGraph(Level, Lines, Flags, LevelWidth, EndWidth,
  452.     GraphChars), EndWidth, 255);
  453. end;
  454.  
  455. { Returns a pointer to the node that is to be shown on line I }
  456.  
  457. function TOutlineViewer.GetNode(I: Integer): Pointer;
  458. var
  459.   Cur: Pointer;
  460.  
  461.   function IsNode(Node: Pointer; Level, Position: Integer; Lines: LongInt;
  462.     Flags: Word): Boolean; far;
  463.   begin
  464.     IsNode := I = Position;
  465.   end;
  466.  
  467. begin
  468.   GetNode := FirstThat(@IsNode);
  469. end;
  470.  
  471. { Overridden to return the number of children in Node. Will not be
  472.   called if HasChildren returns false.  }
  473.  
  474. function TOutlineViewer.GetNumChildren(Node: Pointer): Integer;
  475. begin
  476.   Abstract;
  477. end;
  478.  
  479. { Overriden to return the I'th child of Node. Will not be called if
  480.   HasChildren returns false. }
  481.  
  482. function TOutlineViewer.GetChild(Node: Pointer; I: Integer): Pointer;
  483. begin
  484.   Abstract;
  485. end;
  486.  
  487. { Overridden to return the text of Node }
  488.  
  489. function TOutlineViewer.GetText(Node: Pointer): String;
  490. begin
  491.   Abstract;
  492. end;
  493.  
  494. { Overriden to return if Node's children should be displayed.  Will
  495.   never be called if HasChildren returns False. }
  496.  
  497. function TOutlineViewer.IsExpanded(Node: Pointer): Boolean;
  498. begin
  499.   Abstract;
  500. end;
  501.  
  502. { Returns if Node is selected.  By default, returns true if Node is
  503.   Focused (i.e. single selection).  Can be overriden to handle
  504.   multiple selections. }
  505.  
  506. function TOutlineViewer.IsSelected(I: Integer): Boolean;
  507. begin
  508.   IsSelected := Foc = I;
  509. end;
  510.  
  511. { Internal function used by both FirstThat and ForEach to do the
  512.   actual iteration over the data. See FirstThat for more details }
  513.  
  514. function TOutlineViewer.Iterate(Action: Pointer; CallerFrame: Word;
  515.   CheckRslt: Boolean): Pointer;
  516. var
  517.   Position: Integer;
  518.  
  519.   function TraverseTree(Cur: Pointer; Level: Integer;
  520.     Lines: LongInt; LastChild: Boolean): Pointer; far;
  521.   var
  522.     J, ChildCount: Integer;
  523.     Ret: Pointer;
  524.     Flags: Word;
  525.     Children,Done: Boolean;
  526.   begin
  527.     TraverseTree := Cur;
  528.     if Cur = nil then Exit;
  529.  
  530.     Children := HasChildren(Cur);
  531.  
  532.     Flags := 0;
  533.     if LastChild then Inc(Flags, ovLast);
  534.     if Children and IsExpanded(Cur) then Inc(Flags, ovChildren);
  535.     if not Children or IsExpanded(Cur) then Inc(Flags, ovExpanded);
  536.  
  537.     Inc(Position);
  538.  
  539.     { Perform call }
  540.     { IMPORTANT! Virtual Pascal's code generation differs from BP's one.}
  541.     { In the prolog of a nested procedure Virtual Pascal uses 'ENTER'   }
  542.     { CPU instruction with the appropriate lexical (nested) level.      }
  543.     { Exact lexical level of a procedure that issues FirstThat or       }
  544.     { ForEach is NOT known here. So I made an assumption that FirstThat }
  545.     { or ForEach CANNOT BE ISSUED FROM THE PROCEDURE WITH THE LEXICAL   }
  546.     { LEVEL GREATER THAN 3. For example, FirstThat function is called   }
  547.     { in a GetFocusedGraphic function which lexical level is 2.         }
  548.     asm
  549.                 push    DWord Ptr [ebp-12] { Preserve local variables   }
  550.                 push    DWord Ptr [ebp-8]
  551.                 mov     ecx,[ebp-4]     { Load parent frame into ecx    }
  552.                 push    ecx             { Save it on stack              }
  553.                 push    Cur             { [1] = Cur: Pointer;           }
  554.                 push    Level           { [2] = Level: Integer;         }
  555.                 push    DWord Ptr [ecx+OFFSET Position] { [3] = Position: Integer; }
  556.                 push    Lines           { [4] = Lines: LongInt;         }
  557.                 push    Flags           { [5] = Flags: Word;            }
  558.                 mov     edx,[ecx+OFFSET CallerFrame]
  559.                 mov     eax,[edx-4]     { Copy stack frames of the      }
  560.                 mov     [ebp-4],eax     { parents                       }
  561.                 mov     eax,[edx-8]
  562.                 mov     [ebp-8],eax
  563.                 mov     eax,[edx-12]
  564.                 mov     [ebp-12],eax
  565.                 Call    DWord Ptr [ecx+OFFSET Action]
  566.                 pop     ecx
  567.                 pop     DWord Ptr [ebp-8]
  568.                 pop     DWord Ptr [ebp-12]
  569.                 mov     [ebp-4],ecx     { Restore parent frame          }
  570.                 and     al,[ecx+OFFSET CheckRslt].Boolean { Force to 0 if CheckRslt False }
  571.                 setnz   Done
  572.     end;
  573.  
  574.     if Done then Exit;
  575.  
  576.     if Children and IsExpanded(Cur) then
  577.     begin
  578.       ChildCount := GetNumChildren(Cur);
  579.  
  580.       if not LastChild then Lines := Lines or (1 shl Level);
  581.       for J := 0 to ChildCount - 1 do
  582.       begin
  583.         Ret := TraverseTree(GetChild(Cur, J), Level + 1, Lines,
  584.           J = (ChildCount - 1));
  585.         TraverseTree := Ret;
  586.         if Ret <> nil then Exit;
  587.       end;
  588.     end;
  589.     TraverseTree := nil;
  590.   end;
  591.  
  592. begin
  593.   Position := -1;
  594.  
  595.   asm   {$SAVES ALL}            { Convert 0, 1 to 0, FF }
  596.         DEC     CheckRslt
  597.         NOT     CheckRslt
  598.   end;
  599.  
  600.   Iterate := TraverseTree(GetRoot, 0, 0, True);
  601. end;
  602.  
  603. { Called to handle an event }
  604.  
  605. procedure TOutlineViewer.HandleEvent(var Event: TEvent);
  606. const
  607.   MouseAutoToSkip = 3;
  608. var
  609.   Mouse: TPoint;
  610.   Cur: Pointer;
  611.   NewFocus: Integer;
  612.   Count: Integer;
  613.   Graph: String;
  614.   Dragged: Byte;
  615.  
  616.   function GetFocusedGraphic(var Graph: String): Pointer;
  617.   var
  618.     Lvl: Integer;
  619.     Lns: LongInt;
  620.     Flgs: Word;
  621.  
  622.     function IsFocused(Cur: Pointer; Level, Position: Integer;
  623.       Lines: LongInt; Flags: Word): Boolean; far;
  624.     begin
  625.       if Position = Foc then
  626.       begin
  627.         IsFocused := True;
  628.         Lvl := Level;
  629.         Lns := Lines;
  630.         Flgs := Flags;
  631.       end
  632.       else IsFocused := False;
  633.     end;
  634.  
  635.   begin
  636.     GetFocusedGraphic := FirstThat(@IsFocused);
  637.     Graph := GetGraph(Lvl, Lns, Flgs);
  638.   end;
  639.  
  640.  
  641. begin
  642.   inherited HandleEvent(Event);
  643.   case Event.What of
  644.     evMouseDown:
  645.       begin
  646.         Count := 0;
  647.         Dragged := 0;
  648.         repeat
  649.           if Dragged < 2 then Inc(Dragged);
  650.           MakeLocal(Event.Where, Mouse);
  651.           if MouseInView(Event.Where) then
  652.             NewFocus := Delta.Y + Mouse.Y
  653.           else
  654.           begin
  655.             if Event.What = evMouseAuto then Inc(Count);
  656.             if Count = MouseAutoToSkip then
  657.             begin
  658.               Count := 0;
  659.               if Mouse.Y < 0 then Dec(NewFocus);
  660.               if Mouse.Y >= Size.Y then Inc(NewFocus);
  661.             end;
  662.           end;
  663.           if Foc <> NewFocus then
  664.           begin
  665.             AdjustFocus(NewFocus);
  666.             DrawView;
  667.           end;
  668.         until not MouseEvent(Event, evMouseMove + evMouseAuto);
  669.         if Event.Double then Selected(Foc)
  670.         else
  671.         begin
  672.           if Dragged < 2 then
  673.           begin
  674.             Cur := GetFocusedGraphic(Graph);
  675.             if Mouse.X < Length(Graph) then
  676.             begin
  677.               Adjust(Cur, not IsExpanded(Cur));
  678.               Update;
  679.               DrawView;
  680.             end;
  681.           end;
  682.         end;
  683.       end;
  684.     evKeyboard:
  685.       begin
  686.         NewFocus := Foc;
  687.         case CtrlToArrow(Event.KeyCode) of
  688.           kbUp, kbLeft:    Dec(NewFocus);
  689.           kbDown, kbRight: Inc(NewFocus);
  690.           kbPgDn:          Inc(NewFocus, Size.Y - 1);
  691.           kbPgUp:          Dec(NewFocus, Size.Y - 1);
  692.           kbHome:          NewFocus := Delta.Y;
  693.           kbEnd:           NewFocus := Delta.Y + Size.Y - 1;
  694.           kbCtrlPgUp:      NewFocus := 0;
  695.           kbCtrlPgDn:      NewFocus := Limit.Y - 1;
  696.           kbCtrlEnter,
  697.           kbEnter:         Selected(NewFocus);
  698.         else
  699.           case Event.CharCode of
  700.             '-', '+': Adjust(GetNode(NewFocus), Event.CharCode = '+');
  701.             '*':      ExpandAll(GetNode(NewFocus));
  702.           else
  703.             Exit;
  704.           end;
  705.           Update;
  706.         end;
  707.         ClearEvent(Event);
  708.         AdjustFocus(NewFocus);
  709.         DrawView;
  710.       end;
  711.   end;
  712. end;
  713.  
  714. { Called to determine if the given node has children }
  715.  
  716. function TOutlineViewer.HasChildren(Node: Pointer): Boolean;
  717. begin
  718.   Abstract;
  719. end;
  720.  
  721. { Called whenever Node is selected by the user either via keyboard
  722.   control or by the mouse. }
  723.  
  724. procedure TOutlineViewer.Selected(I: Integer);
  725. begin
  726. end;
  727.  
  728. { Redraws the outline if the outliner sfFocus state changes }
  729.  
  730. procedure TOutlineViewer.SetState(AState: Word; Enable: Boolean);
  731. begin
  732.   inherited SetState(AState, Enable);
  733.   if AState and sfFocused <> 0 then DrawView;
  734. end;
  735.  
  736. { Store the object to a stream }
  737.  
  738. procedure TOutlineViewer.Store(var S: TStream);
  739. begin
  740.   inherited Store(S);
  741.   S.Write(Foc, SizeOf(Foc));
  742. end;
  743.  
  744. { Updates the limits of the outline viewer.  Should be called whenever
  745.   the data of the outline viewer changes.  This includes during
  746.   the initalization of base classes.  TOutlineViewer assumes that
  747.   the outline is empty.  If the outline becomes non-empty during the
  748.   initialization, Update must be called. Also, if during the operation
  749.   of the TOutlineViewer the data being displayed changes, Update
  750.   and DrawView must be called. }
  751.  
  752. procedure TOutlineViewer.Update;
  753. var
  754.   Count, MaxX: Integer;
  755.  
  756.   function CountNode(P: Pointer; Level, Position: Integer; Lines: LongInt;
  757.     Flags: Word): Boolean; far;
  758.   var
  759.     Len: Integer;
  760.   begin
  761.     Inc(Count);
  762.     Len := Length(GetText(P)) + Length(GetGraph(Level, Lines, Flags));
  763.     if MaxX < Len then MaxX := Len;
  764.     CountNode := False;
  765.   end;
  766.  
  767. begin
  768.   Count := 0;
  769.   MaxX := 0;
  770.   FirstThat(@CountNode);
  771.   SetLimit(MaxX, Count);
  772.   AdjustFocus(Foc);
  773. end;
  774.  
  775. { TOutline }
  776.  
  777. constructor TOutline.Init(var Bounds: TRect; AHScrollBar, AVScrollBar: PScrollBar;
  778.   ARoot: PNode);
  779. begin
  780.   inherited Init(Bounds, AHScrollBar, AVScrollBar);
  781.   Root := ARoot;
  782.   Update;
  783. end;
  784.  
  785. constructor TOutline.Load(var S: TStream);
  786.  
  787.   function LoadNode: PNode;
  788.   var
  789.     IsNode: Boolean;
  790.     Node: PNode;
  791.   begin
  792.     S.Read(IsNode, SizeOf(IsNode));
  793.     if IsNode then
  794.     begin
  795.       New(Node);
  796.       with Node^ do
  797.       begin
  798.         S.Read(Expanded, SizeOf(Expanded));
  799.         Text := S.ReadStr;
  800.         ChildList := LoadNode;
  801.         Next := LoadNode;
  802.       end;
  803.       LoadNode := Node;
  804.     end
  805.     else
  806.       LoadNode := nil;
  807.   end;
  808.  
  809. begin
  810.   inherited Load(S);
  811.   Root := LoadNode;
  812. end;
  813.  
  814. destructor TOutline.Done;
  815. begin
  816.   DisposeNode(Root);
  817.   inherited Done;
  818. end;
  819.  
  820. procedure TOutline.Adjust(Node: Pointer; Expand: Boolean);
  821. begin
  822.   PNode(Node)^.Expanded := Expand;
  823. end;
  824.  
  825. function TOutline.GetRoot: Pointer;
  826. begin
  827.   GetRoot := Root;
  828. end;
  829.  
  830. function TOutline.GetNumChildren(Node: Pointer): Integer;
  831. var
  832.   I: Integer;
  833.   P: PNode;
  834. begin
  835.   P := PNode(Node)^.ChildList;
  836.   I := 0;
  837.   while P <> nil do
  838.   begin
  839.     P := P^.Next;
  840.     Inc(I);
  841.   end;
  842.   GetNumChildren := I;
  843. end;
  844.  
  845. function TOutline.GetChild(Node: Pointer; I: Integer): Pointer;
  846. var
  847.   P: PNode;
  848. begin
  849.   P := PNode(Node)^.ChildList;
  850.   while (I <> 0) and (P <> nil) do
  851.   begin
  852.     P := P^.Next;
  853.     Dec(I);
  854.   end;
  855.   GetChild := P;
  856. end;
  857.  
  858. function TOutline.GetText(Node: Pointer): String;
  859. begin
  860.   GetText := PNode(Node)^.Text^;
  861. end;
  862.  
  863. function TOutline.IsExpanded(Node: Pointer): Boolean;
  864. begin
  865.   IsExpanded := PNode(Node)^.Expanded;
  866. end;
  867.  
  868. function TOutline.HasChildren(Node: Pointer): Boolean;
  869. begin
  870.   HasChildren := PNode(Node)^.ChildList <> nil;
  871. end;
  872.  
  873. procedure TOutline.Store(var S: TStream);
  874.  
  875.   procedure StoreNode(Node: PNode);
  876.   var
  877.     IsNode: Boolean;
  878.   begin
  879.     IsNode := Node <> nil;
  880.     S.Write(IsNode, SizeOf(IsNode));
  881.     if IsNode then
  882.     begin
  883.       with Node^ do
  884.       begin
  885.         S.Write(Expanded, SizeOf(Expanded));
  886.         S.WriteStr(Text);
  887.         StoreNode(ChildList);
  888.         StoreNode(Next);
  889.       end;
  890.     end;
  891.   end;
  892.  
  893. begin
  894.   inherited Store(S);
  895.   StoreNode(Root);
  896. end;
  897.  
  898. function NewNode(const AText: String; AChildren, ANext: PNode): PNode;
  899. var
  900.   P: PNode;
  901. begin
  902.   New(P);
  903.   with P^ do
  904.   begin
  905.     Text := NewStr(AText);
  906.     Next := ANext;
  907.     ChildList := AChildren;
  908.     Expanded := True;
  909.   end;
  910.   NewNode := P;
  911. end;
  912.  
  913. procedure DisposeNode(Node: PNode);
  914. begin
  915.   if Node <> nil then
  916.     with Node^ do
  917.     begin
  918.       DisposeNode(ChildList);
  919.       DisposeNode(Next);
  920.       DisposeStr(Text);
  921.       Dispose(Node);
  922.     end;
  923. end;
  924.  
  925. procedure RegisterOutline;
  926. begin
  927.   RegisterType(ROutline);
  928. end;
  929.  
  930. end.
  931.