Subversion Repositories fastphp

Rev

Rev 70 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit FastPHPTreeView;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Classes, ComCtrls;
  7.  
  8. (*
  9.  
  10. <FastPHPData>    ::= <FastPHPData100> .
  11.  
  12. Version 1.00 (signature "FAST100!"):
  13.  
  14. <FastPHPData100> ::= <Signature100> ( <Nodes> <Exit> | <Exit> ) .
  15. <Signature100>   ::= "F" "A" "S" "T" "1" "0" "0" "!" .
  16. <Nodes>          ::= <Node> | ( <Nodes> <Node> ) .
  17. <Node>           ::= <LeafNode> | ( <LeafNode> <IncreaseLevel> <Nodes> <DecreaseLevel> ) .
  18.  
  19. <LeafNode>       ::= "N" <Icon> <LineNo> <DescLen> <Desc> .
  20. <IncreaseLevel>  ::= "I" .
  21. <DecreaseLevel>  ::= "D" .
  22. <Exit>           ::= "X" .
  23.  
  24. <LineNo>         ::= <Int8> .
  25. <DescLen>        ::= <Int4> .
  26. <Desc>           ::= (Utf8-String) .
  27. <Icon>           ::= <NoIcon> | <ImageIndex> .
  28.  
  29. <NoIcon>         ::= "_" "_" "_" "_" .
  30. <ImageIndex>     ::= <Int4> .
  31.  
  32. <Int1>           ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" .
  33. <Int2>           ::= <Int1> <Int1> .
  34. <Int4>           ::= <Int2> <Int2> .
  35. <Int8>           ::= <Int4> <Int4> .
  36.  
  37. *)
  38.  
  39. type
  40.   TTreeViewFastPHP = class helper for TTreeView
  41.   private
  42.     class function Read(var ptr: PChar; len: integer): string; inline;
  43.     procedure Rec100(tn: TTreeNode; var ptr: PChar);
  44.   protected
  45.     function DoFillWithFastPHPData(ptr: PChar): boolean;
  46.   public
  47.     function FillWithFastPHPData(data: string): boolean;
  48.   end;
  49.  
  50.   EFastNodeException = class(Exception);
  51.  
  52. implementation
  53.  
  54. uses
  55.   StrUtils, Windows;
  56.  
  57. const
  58.   MAGIC_V100  = 'FAST100!';
  59.   UTF8_BOM    = #$EF#$BB#$BF;
  60.   LEN_ICON    = 4;
  61.   LEN_LINENO  = 8;
  62.   LEN_DESCLEN = 4;
  63.  
  64. {$EXTERNALSYM LockWindowUpdate}
  65. function LockWindowUpdate(hWndLock: HWND): BOOL; stdcall; external user32 name 'LockWindowUpdate';
  66.  
  67. function HexToInt(HexNum: string): LongInt;
  68. begin
  69.   Result := StrToInt('$' + HexNum);
  70. end;
  71.  
  72. function TTreeViewFastPHP.DoFillWithFastPHPData(ptr: PChar): boolean;
  73.  
  74.   function _NodeID(tn: TTreeNode): string;
  75.   var
  76.     tmp: TTreeNode;
  77.   begin
  78.     // Attention: This function requires that the tree node items are unique
  79.     //            e.g. Class1->function1() is unique
  80.     result := tn.Text;
  81.     tmp := tn.Parent;
  82.     while tmp <> nil do
  83.     begin
  84.       result := tmp.Text + #0 + result;
  85.       tmp := tmp.Parent;
  86.     end;
  87.   end;
  88.  
  89. var
  90.   s: String;
  91.   tn: TTreeNode;
  92.   expanded: TStringList;
  93.   selected, magic: string;
  94.   horPos, verPos: integer;
  95.   i: integer;
  96. begin
  97.   // No update if the user is dragging the scrollbar
  98.   // (otherwise the program will somehow lock up)
  99.   result := GetCapture <> Handle;
  100.   if not result then exit;
  101.  
  102.   selected := '';
  103.   expanded := TStringList.Create;
  104.   horPos := GetScrollPos(Handle, SB_HORZ);
  105.   verPos := GetScrollPos(Handle, SB_VERT);
  106.   LockWindowUpdate(Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong?
  107.   Self.Items.BeginUpdate;
  108.   try
  109.     {$REGION 'Remember our current state (selected and expanded flags)'}
  110.     for i := 0 to Self.Items.Count-1 do
  111.     begin
  112.       tn := Self.Items.Item[i];
  113.       s := _NodeID(tn);
  114.       if tn.Selected then selected := s;
  115.       if tn.Expanded and tn.HasChildren then expanded.Add(s);
  116.     end;
  117.     {$ENDREGION}
  118.  
  119.     {$REGION 'Update the treeview'}
  120.     Self.Items.Clear;
  121.  
  122.     {$REGION 'Remove UTF8-BOMs'}
  123.     repeat
  124.       magic := Read(ptr, Length(UTF8_BOM));
  125.     until magic <> UTF8_BOM;
  126.     ptr := ptr - Length(UTF8_BOM);
  127.     {$ENDREGION}
  128.  
  129.     magic := Read(ptr, length(MAGIC_V100));
  130.  
  131.     if magic = MAGIC_V100 then
  132.     begin
  133.       Rec100(nil, ptr)
  134.     end
  135.     else
  136.     begin
  137.       // Can happen if codeexplorer.php did output a warning
  138.       raise EFastNodeException.CreateFmt('FastNode version "%s" not supported. More content: %s', [magic, Read(ptr,1000)]);
  139.     end;
  140.     {$ENDREGION}
  141.  
  142.     {$REGION 'Recover the previous current state (selected and expanded flags)'}
  143.     for i := 0 to Self.Items.Count-1 do
  144.     begin
  145.       tn := Self.Items.Item[i];
  146.       s := _NodeID(tn);
  147.       if selected = s then tn.Selected := true;
  148.       if expanded.IndexOf(s) >= 0 then tn.Expand(false);
  149.     end;
  150.     {$ENDREGION}
  151.   finally
  152.     Self.Items.EndUpdate;
  153.     LockWindowUpdate(0);
  154.  
  155.     SetScrollPos(Handle, SB_HORZ, horPos, false);
  156.     SetScrollPos(Handle, SB_VERT, verPos, false);
  157.  
  158.     expanded.Free;
  159.   end;
  160. end;
  161.  
  162. function TTreeViewFastPHP.FillWithFastPHPData(data: string): boolean;
  163. var
  164.   tn: TTreeNode;
  165. begin
  166.   result := false;
  167.   try
  168.     data := Trim(data);
  169.     if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
  170.  
  171.     result := DoFillWithFastPHPData(PChar(data));
  172.   except
  173.     on E: Exception do
  174.     begin
  175.       Self.Items.Clear;
  176.       tn := Self.Items.Add(nil, 'ERROR: ' + E.Message);
  177.       tn.ImageIndex := -1;
  178.       tn.SelectedIndex := -1;
  179.     end;
  180.   end;
  181. end;
  182.  
  183. class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string;
  184. begin
  185.   result := Copy(string(ptr), 1, len);
  186.   inc(ptr, len);
  187. end;
  188.  
  189. procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar);
  190. var
  191.   typ, icon, lineno, len, caption: string;
  192.   lastTn: TTreeNode;
  193. begin
  194.   try
  195.     lastTn := nil;
  196.     while true do
  197.     begin
  198.       repeat
  199.         typ := Read(ptr, 1);
  200.       until Trim(typ) <> '';
  201.       if typ = 'N' then // new node
  202.       begin
  203.         icon   := Read(ptr, LEN_ICON);
  204.         lineno := Read(ptr, LEN_LINENO);
  205.         len    := Read(ptr, LEN_DESCLEN);
  206.         caption := Utf8Decode(Read(ptr, StrToInt(len)));
  207.         if tn = nil then
  208.           lastTn := Self.Items.Add(nil, caption)
  209.         else
  210.           lastTn := Self.Items.AddChild(tn, caption);
  211.  
  212.         {$REGION 'Determinate icon'}
  213.         if icon = '____' then
  214.           lastTn.ImageIndex := -1
  215.         else
  216.           lastTn.ImageIndex := StrToInt(icon);
  217.         lastTn.SelectedIndex := lastTn.ImageIndex;
  218.         {$ENDREGION}
  219.  
  220.         lastTn.Data := Pointer(StrToInt(lineno)); // Hack...
  221.       end
  222.       else if typ = 'I' then // increase level
  223.       begin
  224.         if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node');
  225.         Rec100(lastTn, ptr);
  226.       end
  227.       else if typ = 'D' then Exit // decrease level
  228.       else if typ = 'X' then Abort // exit
  229.       else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]);
  230.     end;
  231.   except
  232.     on E: EAbort do
  233.       if tn = nil then
  234.         exit
  235.       else
  236.         raise;
  237.     else
  238.       raise;
  239.   end;
  240. end;
  241.  
  242. end.
  243.