Subversion Repositories fastphp

Rev

Rev 33 | Rev 35 | 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
 
12
Version 1.00:
13
<FastPHPData100> ::= "FAST100!" (<Nodes> <Exit> | <Exit>) .
14
<Nodes>          ::= <Node> | <Nodes> <Node> .
15
<Node>           ::= <LeafNode> | <LeafNode> <IncreaseLevel> <Nodes> <DecreaseLevel> .
31 daniel-mar 16
<LeafNode>       ::= "N" <Icon(8)> <LineNo(8)> <DescLen(4)> <Desc(Utf8)> .
27 daniel-mar 17
<IncreaseLevel>  ::= "I" .
18
<DecreaseLevel>  ::= "D" .
19
<Exit>           ::= "X" .
20
 
33 daniel-mar 21
<Icon(8)>        ::= <Type(4)> <Attr(4)> .
22
 
27 daniel-mar 23
*)
24
 
25
type
26
  TTreeViewFastPHP = class helper for TTreeView
27
  private
28
    //FPrevFastPHPData: string;
29
    class function Read(var ptr: PChar; len: integer): string; inline;
30
    procedure Rec100(tn: TTreeNode; var ptr: PChar);
31
  protected
32
    procedure DoFillWithFastPHPData(ptr: PChar);
33
  public
34
    procedure FillWithFastPHPData(data: string);
35
  end;
36
 
37
  EFastNodeException = class(Exception);
38
 
39
implementation
40
 
41
uses
34 daniel-mar 42
  StrUtils, Windows;
27 daniel-mar 43
 
44
const
45
  LEN_MAGIC   = 8;
28 daniel-mar 46
  LEN_ICON    = 8;
27 daniel-mar 47
  LEN_LINENO  = 8;
48
  LEN_DESCLEN = 4;
49
 
34 daniel-mar 50
{$EXTERNALSYM LockWindowUpdate}
51
function LockWindowUpdate(hWndLock: HWND): BOOL; stdcall; external user32 name 'LockWindowUpdate';
52
 
27 daniel-mar 53
procedure TTreeViewFastPHP.DoFillWithFastPHPData(ptr: PChar);
54
var
55
  s: String;
56
  tn, tmp: TTreeNode;
57
  expanded: TStringList;
34 daniel-mar 58
  selected, top, magic: string;
27 daniel-mar 59
  i: integer;
60
begin
61
  selected := '';
62
  expanded := TStringList.Create;
34 daniel-mar 63
  LockWindowUpdate(Parent.Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong?
27 daniel-mar 64
  Self.Items.BeginUpdate;
65
  try
66
    {$REGION 'Remember our current state (selected and expanded flags)'}
67
    for i := 0 to Self.Items.Count-1 do
68
    begin
69
      tn := Self.Items.Item[i];
70
      s := tn.Text;
71
      tmp := tn.Parent;
72
      while tmp <> nil do
73
      begin
74
        s := tmp.Text + #0 + s;
75
        tmp := tmp.Parent;
76
      end;
77
      if tn.Selected then selected := s;
34 daniel-mar 78
      if TopItem = tn then top := s;
27 daniel-mar 79
      if tn.Expanded and tn.HasChildren then expanded.Add(s);
80
    end;
81
    {$ENDREGION}
82
 
83
    {$REGION 'Update the treeview'}
84
    Self.Items.Clear;
85
    magic := Read(ptr, LEN_MAGIC);
86
    if magic = 'FAST100!' then
87
      Rec100(nil, ptr)
88
    else
89
      raise EFastNodeException.CreateFmt('FastNode version "%s" not supported.', [magic]);
90
    {$ENDREGION}
91
 
92
    {$REGION 'Recover the previous current state (selected and expanded flags)'}
93
    for i := 0 to Self.Items.Count-1 do
94
    begin
95
      tn := Self.Items.Item[i];
96
      s := tn.Text;
97
      tmp := tn.Parent;
98
      while tmp <> nil do
99
      begin
100
        s := tmp.Text + #0 + s;
101
        tmp := tmp.Parent;
102
      end;
103
      if selected = s then tn.Selected := true;
34 daniel-mar 104
      if top = s then
105
      begin
106
        // TODO: Does not work!
107
        //       Even if EndUpdate and LockWindowUpdate are removed, the behavior is weird (it keeps jumping back...)
108
        TopItem := tn;
109
      end;
27 daniel-mar 110
      if expanded.IndexOf(s) >= 0 then tn.Expand(false);
111
    end;
112
    {$ENDREGION}
113
  finally
34 daniel-mar 114
    Self.Items.EndUpdate;
115
    LockWindowUpdate(0);
27 daniel-mar 116
    expanded.Free;
117
  end;
118
end;
119
 
120
procedure TTreeViewFastPHP.FillWithFastPHPData(data: string);
121
begin
122
  //if FPrevFastPHPData = data then exit;
123
  //FPrevFastPHPData := data;
124
 
125
  data := Trim(data);
126
  if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"');
127
 
128
  DoFillWithFastPHPData(PChar(data));
129
end;
130
 
131
class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string;
132
begin
133
  result := Copy(string(ptr), 1, len);
134
  inc(ptr, len);
135
end;
136
 
137
procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar);
138
var
28 daniel-mar 139
  typ, icon, lineno, len, caption: string;
27 daniel-mar 140
  lastTn: TTreeNode;
141
begin
142
  try
143
    lastTn := nil;
144
    while true do
145
    begin
146
      repeat
147
        typ := Read(ptr, 1);
148
      until Trim(typ) <> '';
149
      if typ = 'N' then // new node
150
      begin
28 daniel-mar 151
        icon   := Read(ptr, LEN_ICON);
27 daniel-mar 152
        lineno := Read(ptr, LEN_LINENO);
28 daniel-mar 153
        len    := Read(ptr, LEN_DESCLEN);
31 daniel-mar 154
        caption := Utf8Decode(Read(ptr, StrToInt(len)));
27 daniel-mar 155
        if tn = nil then
156
          lastTn := Self.Items.Add(nil, caption)
157
        else
158
          lastTn := Self.Items.AddChild(tn, caption);
33 daniel-mar 159
        // lastTn.ImageIndex := // TODO
27 daniel-mar 160
        lastTn.Data := Pointer(StrToInt(lineno)); // TODO: is this good?
161
      end
162
      else if typ = 'I' then // increase level
163
      begin
164
        if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node');
165
        Rec100(lastTn, ptr);
166
      end
167
      else if typ = 'D' then Exit // decrease level
168
      else if typ = 'X' then Abort // exit
169
      else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]);
170
    end;
171
  except
172
    on E: EAbort do
33 daniel-mar 173
      if tn = nil then
174
        exit
175
      else
176
        raise;
27 daniel-mar 177
    else
178
      raise;
179
  end;
180
end;
181
 
182
end.