Subversion Repositories fastphp

Rev

Rev 44 | 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
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!';
59
  UTF8_BOM    = '';
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
43 daniel-mar 137
      raise EFastNodeException.CreateFmt('FastNode version "%s" not supported.', [magic]);
70 daniel-mar 138
    end;
27 daniel-mar 139
    {$ENDREGION}
140
 
141
    {$REGION 'Recover the previous current state (selected and expanded flags)'}
142
    for i := 0 to Self.Items.Count-1 do
143
    begin
144
      tn := Self.Items.Item[i];
35 daniel-mar 145
      s := _NodeID(tn);
27 daniel-mar 146
      if selected = s then tn.Selected := true;
147
      if expanded.IndexOf(s) >= 0 then tn.Expand(false);
148
    end;
149
    {$ENDREGION}
150
  finally
34 daniel-mar 151
    Self.Items.EndUpdate;
152
    LockWindowUpdate(0);
35 daniel-mar 153
 
154
    SetScrollPos(Handle, SB_HORZ, horPos, false);
155
    SetScrollPos(Handle, SB_VERT, verPos, false);
156
 
27 daniel-mar 157
    expanded.Free;
158
  end;
159
end;
160
 
40 daniel-mar 161
function TTreeViewFastPHP.FillWithFastPHPData(data: string): boolean;
43 daniel-mar 162
var
163
  tn: TTreeNode;
27 daniel-mar 164
begin
44 daniel-mar 165
  result := false;
43 daniel-mar 166
  try
167
    data := Trim(data);
168
    if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
27 daniel-mar 169
 
43 daniel-mar 170
    result := DoFillWithFastPHPData(PChar(data));
171
  except
172
    on E: Exception do
173
    begin
174
      Self.Items.Clear;
175
      tn := Self.Items.Add(nil, 'ERROR: ' + E.Message);
176
      tn.ImageIndex := -1;
177
      tn.SelectedIndex := -1;
178
    end;
179
  end;
27 daniel-mar 180
end;
181
 
182
class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string;
183
begin
184
  result := Copy(string(ptr), 1, len);
185
  inc(ptr, len);
186
end;
187
 
188
procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar);
189
var
28 daniel-mar 190
  typ, icon, lineno, len, caption: string;
27 daniel-mar 191
  lastTn: TTreeNode;
192
begin
193
  try
194
    lastTn := nil;
195
    while true do
196
    begin
197
      repeat
198
        typ := Read(ptr, 1);
199
      until Trim(typ) <> '';
200
      if typ = 'N' then // new node
201
      begin
28 daniel-mar 202
        icon   := Read(ptr, LEN_ICON);
27 daniel-mar 203
        lineno := Read(ptr, LEN_LINENO);
28 daniel-mar 204
        len    := Read(ptr, LEN_DESCLEN);
31 daniel-mar 205
        caption := Utf8Decode(Read(ptr, StrToInt(len)));
27 daniel-mar 206
        if tn = nil then
207
          lastTn := Self.Items.Add(nil, caption)
208
        else
209
          lastTn := Self.Items.AddChild(tn, caption);
36 daniel-mar 210
 
211
        {$REGION 'Determinate icon'}
212
        if icon = '____' then
213
          lastTn.ImageIndex := -1
214
        else
215
          lastTn.ImageIndex := StrToInt(icon);
216
        lastTn.SelectedIndex := lastTn.ImageIndex;
217
        {$ENDREGION}
218
 
219
        lastTn.Data := Pointer(StrToInt(lineno)); // Hack...
27 daniel-mar 220
      end
221
      else if typ = 'I' then // increase level
222
      begin
223
        if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node');
224
        Rec100(lastTn, ptr);
225
      end
226
      else if typ = 'D' then Exit // decrease level
227
      else if typ = 'X' then Abort // exit
228
      else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]);
229
    end;
230
  except
231
    on E: EAbort do
33 daniel-mar 232
      if tn = nil then
233
        exit
234
      else
235
        raise;
27 daniel-mar 236
    else
237
      raise;
238
  end;
239
end;
240
 
241
end.