Subversion Repositories fastphp

Rev

Rev 35 | Rev 40 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
27 daniel-mar 1
unit FastPHPTreeView;
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
45
    procedure DoFillWithFastPHPData(ptr: PChar);
46
  public
47
    procedure FillWithFastPHPData(data: string);
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
58
  LEN_MAGIC   = 8;
36 daniel-mar 59
  LEN_ICON    = 4;
27 daniel-mar 60
  LEN_LINENO  = 8;
61
  LEN_DESCLEN = 4;
62
 
34 daniel-mar 63
{$EXTERNALSYM LockWindowUpdate}
64
function LockWindowUpdate(hWndLock: HWND): BOOL; stdcall; external user32 name 'LockWindowUpdate';
65
 
36 daniel-mar 66
function HexToInt(HexNum: string): LongInt;
67
begin
68
  Result := StrToInt('$' + HexNum);
69
end;
70
 
27 daniel-mar 71
procedure TTreeViewFastPHP.DoFillWithFastPHPData(ptr: PChar);
35 daniel-mar 72
 
73
  function _NodeID(tn: TTreeNode): string;
74
  var
75
    tmp: TTreeNode;
76
  begin
77
    // Attention: This function requires that the tree node items are unique
78
    //            e.g. Class1->function1() is unique
79
    result := tn.Text;
80
    tmp := tn.Parent;
81
    while tmp <> nil do
82
    begin
83
      result := tmp.Text + #0 + result;
84
      tmp := tmp.Parent;
85
    end;
86
  end;
87
 
27 daniel-mar 88
var
89
  s: String;
35 daniel-mar 90
  tn: TTreeNode;
27 daniel-mar 91
  expanded: TStringList;
35 daniel-mar 92
  selected, magic: string;
93
  horPos, verPos: integer;
27 daniel-mar 94
  i: integer;
36 daniel-mar 95
  crc32: string;
27 daniel-mar 96
begin
36 daniel-mar 97
  // No update if the user is dragging the scrollbar
98
  // (otherwise the program will somehow lock up)
99
  if GetCapture = Handle then exit;
100
 
101
  // Don't rebuild the treeview if nothing has changed (use the Tag property to store the CRC32)
102
  crc32 := Read(ptr, 8);
103
  if crc32 = IntToHex(Tag, 8) then exit;
104
  Tag := HexToInt(crc32);
105
 
27 daniel-mar 106
  selected := '';
107
  expanded := TStringList.Create;
35 daniel-mar 108
  horPos := GetScrollPos(Handle, SB_HORZ);
109
  verPos := GetScrollPos(Handle, SB_VERT);
34 daniel-mar 110
  LockWindowUpdate(Parent.Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong?
27 daniel-mar 111
  Self.Items.BeginUpdate;
112
  try
113
    {$REGION 'Remember our current state (selected and expanded flags)'}
114
    for i := 0 to Self.Items.Count-1 do
115
    begin
116
      tn := Self.Items.Item[i];
35 daniel-mar 117
      s := _NodeID(tn);
27 daniel-mar 118
      if tn.Selected then selected := s;
119
      if tn.Expanded and tn.HasChildren then expanded.Add(s);
120
    end;
121
    {$ENDREGION}
122
 
123
    {$REGION 'Update the treeview'}
124
    Self.Items.Clear;
36 daniel-mar 125
    try
126
      magic := Read(ptr, LEN_MAGIC);
127
      if magic = 'FAST100!' then
128
        Rec100(nil, ptr)
129
      else
130
        raise EFastNodeException.CreateFmt('FastNode version "%s" not supported.', [magic]);
131
    except
132
      on E: Exception do
133
      begin
134
        Self.Items.Clear;
135
        tn := Self.Items.Add(nil, 'ERROR: ' + E.Message);
136
        tn.ImageIndex := -1;
137
        tn.SelectedIndex := -1;
138
      end;
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
 
162
procedure TTreeViewFastPHP.FillWithFastPHPData(data: string);
163
begin
164
  data := Trim(data);
165
  if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
166
 
167
  DoFillWithFastPHPData(PChar(data));
168
end;
169
 
170
class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string;
171
begin
172
  result := Copy(string(ptr), 1, len);
173
  inc(ptr, len);
174
end;
175
 
176
procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar);
177
var
28 daniel-mar 178
  typ, icon, lineno, len, caption: string;
27 daniel-mar 179
  lastTn: TTreeNode;
180
begin
181
  try
182
    lastTn := nil;
183
    while true do
184
    begin
185
      repeat
186
        typ := Read(ptr, 1);
187
      until Trim(typ) <> '';
188
      if typ = 'N' then // new node
189
      begin
28 daniel-mar 190
        icon   := Read(ptr, LEN_ICON);
27 daniel-mar 191
        lineno := Read(ptr, LEN_LINENO);
28 daniel-mar 192
        len    := Read(ptr, LEN_DESCLEN);
31 daniel-mar 193
        caption := Utf8Decode(Read(ptr, StrToInt(len)));
27 daniel-mar 194
        if tn = nil then
195
          lastTn := Self.Items.Add(nil, caption)
196
        else
197
          lastTn := Self.Items.AddChild(tn, caption);
36 daniel-mar 198
 
199
        {$REGION 'Determinate icon'}
200
        if icon = '____' then
201
          lastTn.ImageIndex := -1
202
        else
203
          lastTn.ImageIndex := StrToInt(icon);
204
        lastTn.SelectedIndex := lastTn.ImageIndex;
205
        {$ENDREGION}
206
 
207
        lastTn.Data := Pointer(StrToInt(lineno)); // Hack...
27 daniel-mar 208
      end
209
      else if typ = 'I' then // increase level
210
      begin
211
        if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node');
212
        Rec100(lastTn, ptr);
213
      end
214
      else if typ = 'D' then Exit // decrease level
215
      else if typ = 'X' then Abort // exit
216
      else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]);
217
    end;
218
  except
219
    on E: EAbort do
33 daniel-mar 220
      if tn = nil then
221
        exit
222
      else
223
        raise;
27 daniel-mar 224
    else
225
      raise;
226
  end;
227
end;
228
 
229
end.