Rev 34 | Rev 36 | 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); |
35 | daniel-mar | 54 | |
55 | function _NodeID(tn: TTreeNode): string; |
||
56 | var |
||
57 | tmp: TTreeNode; |
||
58 | begin |
||
59 | // Attention: This function requires that the tree node items are unique |
||
60 | // e.g. Class1->function1() is unique |
||
61 | result := tn.Text; |
||
62 | tmp := tn.Parent; |
||
63 | while tmp <> nil do |
||
64 | begin |
||
65 | result := tmp.Text + #0 + result; |
||
66 | tmp := tmp.Parent; |
||
67 | end; |
||
68 | end; |
||
69 | |||
27 | daniel-mar | 70 | var |
71 | s: String; |
||
35 | daniel-mar | 72 | tn: TTreeNode; |
27 | daniel-mar | 73 | expanded: TStringList; |
35 | daniel-mar | 74 | selected, magic: string; |
75 | horPos, verPos: integer; |
||
27 | daniel-mar | 76 | i: integer; |
77 | begin |
||
78 | selected := ''; |
||
79 | expanded := TStringList.Create; |
||
35 | daniel-mar | 80 | horPos := GetScrollPos(Handle, SB_HORZ); |
81 | verPos := GetScrollPos(Handle, SB_VERT); |
||
34 | daniel-mar | 82 | LockWindowUpdate(Parent.Handle); // Parent is better choice for FastPHP... but for other applications it might be wrong? |
27 | daniel-mar | 83 | Self.Items.BeginUpdate; |
84 | try |
||
85 | {$REGION 'Remember our current state (selected and expanded flags)'} |
||
86 | for i := 0 to Self.Items.Count-1 do |
||
87 | begin |
||
88 | tn := Self.Items.Item[i]; |
||
35 | daniel-mar | 89 | s := _NodeID(tn); |
27 | daniel-mar | 90 | if tn.Selected then selected := s; |
91 | if tn.Expanded and tn.HasChildren then expanded.Add(s); |
||
92 | end; |
||
93 | {$ENDREGION} |
||
94 | |||
95 | {$REGION 'Update the treeview'} |
||
96 | Self.Items.Clear; |
||
97 | magic := Read(ptr, LEN_MAGIC); |
||
98 | if magic = 'FAST100!' then |
||
99 | Rec100(nil, ptr) |
||
100 | else |
||
101 | raise EFastNodeException.CreateFmt('FastNode version "%s" not supported.', [magic]); |
||
102 | {$ENDREGION} |
||
103 | |||
104 | {$REGION 'Recover the previous current state (selected and expanded flags)'} |
||
105 | for i := 0 to Self.Items.Count-1 do |
||
106 | begin |
||
107 | tn := Self.Items.Item[i]; |
||
35 | daniel-mar | 108 | s := _NodeID(tn); |
27 | daniel-mar | 109 | if selected = s then tn.Selected := true; |
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); |
||
35 | daniel-mar | 116 | |
117 | // TODO: Bug! When the user keeps pressing the scrollbar, the program hangs and locks up |
||
118 | SetScrollPos(Handle, SB_HORZ, horPos, false); |
||
119 | SetScrollPos(Handle, SB_VERT, verPos, false); |
||
120 | |||
27 | daniel-mar | 121 | expanded.Free; |
122 | end; |
||
123 | end; |
||
124 | |||
125 | procedure TTreeViewFastPHP.FillWithFastPHPData(data: string); |
||
126 | begin |
||
127 | //if FPrevFastPHPData = data then exit; |
||
128 | //FPrevFastPHPData := data; |
||
129 | |||
130 | data := Trim(data); |
||
131 | if not EndsStr('X', data) then raise EFastNodeException.Create('FastNode string must end with "X"'); |
||
132 | |||
133 | DoFillWithFastPHPData(PChar(data)); |
||
134 | end; |
||
135 | |||
136 | class function TTreeViewFastPHP.Read(var ptr: PChar; len: integer): string; |
||
137 | begin |
||
138 | result := Copy(string(ptr), 1, len); |
||
139 | inc(ptr, len); |
||
140 | end; |
||
141 | |||
142 | procedure TTreeViewFastPHP.Rec100(tn: TTreeNode; var ptr: PChar); |
||
143 | var |
||
28 | daniel-mar | 144 | typ, icon, lineno, len, caption: string; |
27 | daniel-mar | 145 | lastTn: TTreeNode; |
146 | begin |
||
147 | try |
||
148 | lastTn := nil; |
||
149 | while true do |
||
150 | begin |
||
151 | repeat |
||
152 | typ := Read(ptr, 1); |
||
153 | until Trim(typ) <> ''; |
||
154 | if typ = 'N' then // new node |
||
155 | begin |
||
28 | daniel-mar | 156 | icon := Read(ptr, LEN_ICON); |
27 | daniel-mar | 157 | lineno := Read(ptr, LEN_LINENO); |
28 | daniel-mar | 158 | len := Read(ptr, LEN_DESCLEN); |
31 | daniel-mar | 159 | caption := Utf8Decode(Read(ptr, StrToInt(len))); |
27 | daniel-mar | 160 | if tn = nil then |
161 | lastTn := Self.Items.Add(nil, caption) |
||
162 | else |
||
163 | lastTn := Self.Items.AddChild(tn, caption); |
||
33 | daniel-mar | 164 | // lastTn.ImageIndex := // TODO |
27 | daniel-mar | 165 | lastTn.Data := Pointer(StrToInt(lineno)); // TODO: is this good? |
166 | end |
||
167 | else if typ = 'I' then // increase level |
||
168 | begin |
||
169 | if LastTn = nil then raise EFastNodeException.Create('Fast100: Increase command requires previous node'); |
||
170 | Rec100(lastTn, ptr); |
||
171 | end |
||
172 | else if typ = 'D' then Exit // decrease level |
||
173 | else if typ = 'X' then Abort // exit |
||
174 | else raise EFastNodeException.CreateFmt('Fast100: Command "%s" unknown', [typ]); |
||
175 | end; |
||
176 | except |
||
177 | on E: EAbort do |
||
33 | daniel-mar | 178 | if tn = nil then |
179 | exit |
||
180 | else |
||
181 | raise; |
||
27 | daniel-mar | 182 | else |
183 | raise; |
||
184 | end; |
||
185 | end; |
||
186 | |||
187 | end. |