Subversion Repositories fastphp

Rev

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