Subversion Repositories fastphp

Rev

Rev 40 | 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
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
 
40 daniel-mar 71
function TTreeViewFastPHP.DoFillWithFastPHPData(ptr: PChar): boolean;
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;
95
begin
36 daniel-mar 96
  // No update if the user is dragging the scrollbar
97
  // (otherwise the program will somehow lock up)
40 daniel-mar 98
  result := GetCapture <> Handle;
99
  if not result then exit;
36 daniel-mar 100
 
27 daniel-mar 101
  selected := '';
102
  expanded := TStringList.Create;
35 daniel-mar 103
  horPos := GetScrollPos(Handle, SB_HORZ);
104
  verPos := GetScrollPos(Handle, SB_VERT);
40 daniel-mar 105
  LockWindowUpdate(Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong?
27 daniel-mar 106
  Self.Items.BeginUpdate;
107
  try
108
    {$REGION 'Remember our current state (selected and expanded flags)'}
109
    for i := 0 to Self.Items.Count-1 do
110
    begin
111
      tn := Self.Items.Item[i];
35 daniel-mar 112
      s := _NodeID(tn);
27 daniel-mar 113
      if tn.Selected then selected := s;
114
      if tn.Expanded and tn.HasChildren then expanded.Add(s);
115
    end;
116
    {$ENDREGION}
117
 
118
    {$REGION 'Update the treeview'}
119
    Self.Items.Clear;
43 daniel-mar 120
    magic := Read(ptr, LEN_MAGIC);
121
    if magic = 'FAST100!' then
122
      Rec100(nil, ptr)
123
    else
124
      raise EFastNodeException.CreateFmt('FastNode version "%s" not supported.', [magic]);
27 daniel-mar 125
    {$ENDREGION}
126
 
127
    {$REGION 'Recover the previous current state (selected and expanded flags)'}
128
    for i := 0 to Self.Items.Count-1 do
129
    begin
130
      tn := Self.Items.Item[i];
35 daniel-mar 131
      s := _NodeID(tn);
27 daniel-mar 132
      if selected = s then tn.Selected := true;
133
      if expanded.IndexOf(s) >= 0 then tn.Expand(false);
134
    end;
135
    {$ENDREGION}
136
  finally
34 daniel-mar 137
    Self.Items.EndUpdate;
138
    LockWindowUpdate(0);
35 daniel-mar 139
 
140
    SetScrollPos(Handle, SB_HORZ, horPos, false);
141
    SetScrollPos(Handle, SB_VERT, verPos, false);
142
 
27 daniel-mar 143
    expanded.Free;
144
  end;
145
end;
146
 
40 daniel-mar 147
function TTreeViewFastPHP.FillWithFastPHPData(data: string): boolean;
43 daniel-mar 148
var
149
  tn: TTreeNode;
27 daniel-mar 150
begin
43 daniel-mar 151
  try
152
    data := Trim(data);
153
    if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
27 daniel-mar 154
 
43 daniel-mar 155
    result := DoFillWithFastPHPData(PChar(data));
156
  except
157
    on E: Exception do
158
    begin
159
      Self.Items.Clear;
160
      tn := Self.Items.Add(nil, 'ERROR: ' + E.Message);
161
      tn.ImageIndex := -1;
162
      tn.SelectedIndex := -1;
163
    end;
164
  end;
27 daniel-mar 165
end;
166
 
167
class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string;
168
begin
169
  result := Copy(string(ptr), 1, len);
170
  inc(ptr, len);
171
end;
172
 
173
procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar);
174
var
28 daniel-mar 175
  typ, icon, lineno, len, caption: string;
27 daniel-mar 176
  lastTn: TTreeNode;
177
begin
178
  try
179
    lastTn := nil;
180
    while true do
181
    begin
182
      repeat
183
        typ := Read(ptr, 1);
184
      until Trim(typ) <> '';
185
      if typ = 'N' then // new node
186
      begin
28 daniel-mar 187
        icon   := Read(ptr, LEN_ICON);
27 daniel-mar 188
        lineno := Read(ptr, LEN_LINENO);
28 daniel-mar 189
        len    := Read(ptr, LEN_DESCLEN);
31 daniel-mar 190
        caption := Utf8Decode(Read(ptr, StrToInt(len)));
27 daniel-mar 191
        if tn = nil then
192
          lastTn := Self.Items.Add(nil, caption)
193
        else
194
          lastTn := Self.Items.AddChild(tn, caption);
36 daniel-mar 195
 
196
        {$REGION 'Determinate icon'}
197
        if icon = '____' then
198
          lastTn.ImageIndex := -1
199
        else
200
          lastTn.ImageIndex := StrToInt(icon);
201
        lastTn.SelectedIndex := lastTn.ImageIndex;
202
        {$ENDREGION}
203
 
204
        lastTn.Data := Pointer(StrToInt(lineno)); // Hack...
27 daniel-mar 205
      end
206
      else if typ = 'I' then // increase level
207
      begin
208
        if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node');
209
        Rec100(lastTn, ptr);
210
      end
211
      else if typ = 'D' then Exit // decrease level
212
      else if typ = 'X' then Abort // exit
213
      else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]);
214
    end;
215
  except
216
    on E: EAbort do
33 daniel-mar 217
      if tn = nil then
218
        exit
219
      else
220
        raise;
27 daniel-mar 221
    else
222
      raise;
223
  end;
224
end;
225
 
226
end.