Subversion Repositories fastphp

Rev

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

Rev Author Line No. Line
82 daniel-mar 1
unit FastPHPTreeView;
27 daniel-mar 2
 
3
interface
4
 
5
uses
6
  SysUtils, Classes, ComCtrls;
7
 
8
(*
9
 
10
<FastPHPData>    ::= <FastPHPData100> .
11
 
36 daniel-mar 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> .
27 daniel-mar 20
<IncreaseLevel>  ::= "I" .
21
<DecreaseLevel>  ::= "D" .
22
<Exit>           ::= "X" .
23
 
36 daniel-mar 24
<LineNo>         ::= <Int8> .
25
<DescLen>        ::= <Int4> .
26
<Desc>           ::= (Utf8-String) .
27
<Icon>           ::= <NoIcon> | <ImageIndex> .
33 daniel-mar 28
 
36 daniel-mar 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
 
27 daniel-mar 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
40 daniel-mar 45
    function DoFillWithFastPHPData(ptr: PChar): boolean;
27 daniel-mar 46
  public
40 daniel-mar 47
    function FillWithFastPHPData(data: string): boolean;
27 daniel-mar 48
  end;
49
 
50
  EFastNodeException = class(Exception);
51
 
52
implementation
53
 
54
uses
34 daniel-mar 55
  StrUtils, Windows;
27 daniel-mar 56
 
57
const
70 daniel-mar 58
  MAGIC_V100  = 'FAST100!';
82 daniel-mar 59
  UTF8_BOM    = #$EF#$BB#$BF;
36 daniel-mar 60
  LEN_ICON    = 4;
27 daniel-mar 61
  LEN_LINENO  = 8;
62
  LEN_DESCLEN = 4;
63
 
34 daniel-mar 64
{$EXTERNALSYM LockWindowUpdate}
65
function LockWindowUpdate(hWndLock: HWND): BOOL; stdcall; external user32 name 'LockWindowUpdate';
66
 
36 daniel-mar 67
function HexToInt(HexNum: string): LongInt;
68
begin
69
  Result := StrToInt('$' + HexNum);
70
end;
71
 
40 daniel-mar 72
function TTreeViewFastPHP.DoFillWithFastPHPData(ptr: PChar): boolean;
35 daniel-mar 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
 
27 daniel-mar 89
var
90
  s: String;
35 daniel-mar 91
  tn: TTreeNode;
27 daniel-mar 92
  expanded: TStringList;
35 daniel-mar 93
  selected, magic: string;
94
  horPos, verPos: integer;
27 daniel-mar 95
  i: integer;
96
begin
36 daniel-mar 97
  // No update if the user is dragging the scrollbar
98
  // (otherwise the program will somehow lock up)
40 daniel-mar 99
  result := GetCapture <> Handle;
100
  if not result then exit;
36 daniel-mar 101
 
27 daniel-mar 102
  selected := '';
103
  expanded := TStringList.Create;
35 daniel-mar 104
  horPos := GetScrollPos(Handle, SB_HORZ);
105
  verPos := GetScrollPos(Handle, SB_VERT);
40 daniel-mar 106
  LockWindowUpdate(Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong?
27 daniel-mar 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];
35 daniel-mar 113
      s := _NodeID(tn);
27 daniel-mar 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;
70 daniel-mar 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
43 daniel-mar 133
      Rec100(nil, ptr)
70 daniel-mar 134
    end
43 daniel-mar 135
    else
70 daniel-mar 136
    begin
82 daniel-mar 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)]);
70 daniel-mar 139
    end;
27 daniel-mar 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];
35 daniel-mar 146
      s := _NodeID(tn);
27 daniel-mar 147
      if selected = s then tn.Selected := true;
148
      if expanded.IndexOf(s) >= 0 then tn.Expand(false);
149
    end;
150
    {$ENDREGION}
151
  finally
34 daniel-mar 152
    Self.Items.EndUpdate;
153
    LockWindowUpdate(0);
35 daniel-mar 154
 
155
    SetScrollPos(Handle, SB_HORZ, horPos, false);
156
    SetScrollPos(Handle, SB_VERT, verPos, false);
157
 
27 daniel-mar 158
    expanded.Free;
159
  end;
160
end;
161
 
40 daniel-mar 162
function TTreeViewFastPHP.FillWithFastPHPData(data: string): boolean;
43 daniel-mar 163
var
164
  tn: TTreeNode;
27 daniel-mar 165
begin
44 daniel-mar 166
  result := false;
43 daniel-mar 167
  try
168
    data := Trim(data);
169
    if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
27 daniel-mar 170
 
43 daniel-mar 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;
27 daniel-mar 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
28 daniel-mar 191
  typ, icon, lineno, len, caption: string;
27 daniel-mar 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
28 daniel-mar 203
        icon   := Read(ptr, LEN_ICON);
27 daniel-mar 204
        lineno := Read(ptr, LEN_LINENO);
28 daniel-mar 205
        len    := Read(ptr, LEN_DESCLEN);
31 daniel-mar 206
        caption := Utf8Decode(Read(ptr, StrToInt(len)));
27 daniel-mar 207
        if tn = nil then
208
          lastTn := Self.Items.Add(nil, caption)
209
        else
210
          lastTn := Self.Items.AddChild(tn, caption);
36 daniel-mar 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...
27 daniel-mar 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
33 daniel-mar 233
      if tn = nil then
234
        exit
235
      else
236
        raise;
27 daniel-mar 237
    else
238
      raise;
239
  end;
240
end;
241
 
242
end.