ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/journaling/ibcontrols/IBTreeView.pas
Revision: 27
Committed: Tue Apr 14 13:10:23 2015 UTC (9 years, 8 months ago) by tony
Content type: text/x-pascal
Original Path: ibx/trunk/ibcontrols/IBTreeView.pas
File size: 21459 byte(s)
Log Message:
Committing updates for Release R1-2-3

File Contents

# User Rev Content
1 tony 21 (*
2     * IBX For Lazarus (Firebird Express)
3     *
4     * The contents of this file are subject to the Initial Developer's
5     * Public License Version 1.0 (the "License"); you may not use this
6     * file except in compliance with the License. You may obtain a copy
7     * of the License here:
8     *
9     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10     *
11     * Software distributed under the License is distributed on an "AS
12     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13     * implied. See the License for the specific language governing rights
14     * and limitations under the License.
15     *
16     * The Initial Developer of the Original Code is Tony Whyman.
17     *
18 tony 23 * The Original Code is (C) 2015 Tony Whyman, MWA Software
19 tony 21 * (http://www.mwasoftware.co.uk).
20     *
21     * All Rights Reserved.
22     *
23     * Contributor(s): ______________________________________.
24     *
25     *)
26     unit IBTreeView;
27    
28     {$mode objfpc}{$H+}
29    
30     interface
31    
32     uses
33     Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls,
34 tony 27 DB, IBSQLParser, IBCustomDataSet;
35 tony 21
36     type
37     {
38     TIBTreeView is intended to be a data aware descendent of TCustomTreeView and used to display
39     hierarchically structured data in a natural manner. Nodes can be deleted, moved
40     and added to the tree and each change is reflected in the underlying dataset. The
41     Node text can similarly be edited.
42     }
43    
44     TVariantArray = array of variant;
45    
46     TIBTreeView = class;
47    
48     { TIBTreeViewDatalink }
49    
50     TIBTreeViewDatalink = class(TDataLink)
51     private
52     FOwner: TIBTreeView;
53     protected
54     procedure ActiveChanged; override;
55     procedure DataSetChanged; override;
56     procedure RecordChanged(Field: TField); override;
57     procedure UpdateData; override;
58     public
59     constructor Create(AOwner: TIBTreeView);
60     end;
61    
62 tony 27 { TIBTreeViewControlLink }
63    
64     TIBTreeViewControlLink = class(TIBControlLink)
65     private
66     FOwner: TIBTreeView;
67     protected
68     procedure UpdateSQL(Sender: TObject); override;
69     procedure UpdateParams(Sender: TObject); override;
70     public
71     constructor Create(AOwner: TIBTreeView);
72     end;
73    
74 tony 21 { TIBTreeNode }
75    
76     TIBTreeNode = class(TTreeNode)
77     private
78     FKeyValue: variant;
79     public
80     procedure DeleteAll;
81     property KeyValue: variant read FKeyValue;
82     end;
83    
84     TIBTreeView = class(TCustomTreeView)
85     private
86     { Private declarations }
87     FDataLink: TIBTreeViewDatalink;
88 tony 27 FIBTreeViewControlLink: TIBTreeViewControlLink;
89 tony 21 FHasChildField: string;
90     FKeyField: string;
91     FTextField: string;
92     FParentField: string;
93     FExpandNode: TTreeNode;
94     FNoAddNodeToDataset: boolean;
95     FRelationName: string;
96     FUpdateNode: TIBTreeNode;
97     FModifiedNode: TIBTreeNode;
98     FUpdating: boolean;
99     FLocatingNode: boolean;
100     FLastSelected: TVariantArray;
101     procedure ActiveChanged(Sender: TObject);
102     procedure AddNodes;
103     procedure DataSetChanged(Sender: TObject);
104     function GetDataSet: TDataSet;
105     function GetDataSource: TDataSource;
106     function GetRelationNameQualifier: string;
107     function GetSelectedKeyValue: variant;
108 tony 27 procedure IBControlLinkChanged;
109 tony 21 procedure NodeMoved(Node: TTreeNode);
110     procedure NodeUpdated(Node: TTreeNode);
111     procedure RecordChanged(Sender: TObject; Field: TField);
112     procedure SetHasChildField(AValue: string);
113     procedure SetKeyField(AValue: string);
114     procedure SetTextField(AValue: string);
115     procedure SetDataSource(AValue: TDataSource);
116     procedure SetParentField(AValue: string);
117     function ScrollToNode(Node: TIBTreeNode): boolean;
118     procedure UpdateData(Sender: TObject);
119     procedure UpdateParams(Sender: TObject; Parser: TSelectSQLParser);
120     procedure UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
121     protected
122     { Protected declarations }
123     procedure Added(Node: TTreeNode); override;
124     procedure Delete(Node: TTreeNode); override;
125     procedure Change(Node: TTreeNode); override;
126     function CreateNode: TTreeNode; override;
127     function CanEdit(Node: TTreeNode): Boolean; override;
128     procedure Expand(Node: TTreeNode); override;
129     procedure Loaded; override;
130     procedure NodeChanged(Node: TTreeNode; ChangeEvent: TTreeNodeChangeReason); override;
131     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
132     procedure Reinitialise;
133     public
134     { Public declarations }
135     constructor Create(TheComponent: TComponent); override;
136     destructor Destroy; override;
137     function FindNode(KeyValuePath: array of variant; SelectNode: boolean): TIBTreeNode; overload;
138     function FindNode(KeyValue: variant): TIBTreeNode; overload;
139     function GetNodePath(Node: TTreeNode): TVariantArray;
140     property DataSet: TDataSet read GetDataSet;
141     property SelectedKeyValue: variant read GetSelectedKeyValue;
142     published
143     { Published declarations }
144     property Align;
145     property Anchors;
146     property AutoExpand;
147     property BorderSpacing;
148     //property BiDiMode;
149     property BackgroundColor;
150     property BorderStyle;
151     property BorderWidth;
152     property Color;
153     property Constraints;
154     property TextField: string read FTextField write SetTextField;
155     property DataSource: TDataSource read GetDataSource write SetDataSource;
156     property DefaultItemHeight;
157     property DragKind;
158     property DragCursor;
159     property DragMode;
160     property Enabled;
161     property ExpandSignColor;
162     property ExpandSignType;
163     property Font;
164     property HideSelection;
165     property HotTrack;
166     property Images;
167     property Indent;
168     property HasChildField: string read FHasChildField write SetHasChildField;
169     property KeyField: string read FKeyField write SetKeyField;
170     property MultiSelect;
171     property MultiSelectStyle;
172     //property ParentBiDiMode;
173     property ParentColor default False;
174     property ParentField: string read FParentField write SetParentField;
175     property ParentFont;
176     property ParentShowHint;
177     property PopupMenu;
178     property ReadOnly;
179     property RelationName: string read FRelationName write FRelationName;
180     property RightClickSelect;
181     property RowSelect;
182     property ScrollBars;
183     property SelectionColor;
184     property ShowButtons;
185     property ShowHint;
186     property ShowLines;
187     property ShowRoot;
188     property StateImages;
189     property TabOrder;
190     property TabStop default True;
191     property Tag;
192     property ToolTips;
193     property Visible;
194     property OnAddition;
195     property OnAdvancedCustomDraw;
196     property OnAdvancedCustomDrawItem;
197     property OnChange;
198     property OnChanging;
199     property OnClick;
200     property OnCollapsed;
201     property OnCollapsing;
202     property OnCompare;
203     property OnContextPopup;
204     property OnCreateNodeClass;
205     property OnCustomCreateItem;
206     property OnCustomDraw;
207     property OnCustomDrawItem;
208     property OnDblClick;
209     property OnDeletion;
210     property OnDragDrop;
211     property OnDragOver;
212     property OnEdited;
213     property OnEditing;
214     property OnEditingEnd;
215     //property OnEndDock;
216     property OnEndDrag;
217     property OnEnter;
218     property OnExit;
219     property OnExpanded;
220     property OnExpanding;
221     property OnGetImageIndex;
222     property OnGetSelectedIndex;
223     property OnKeyDown;
224     property OnKeyPress;
225     property OnKeyUp;
226     property OnMouseDown;
227     property OnMouseEnter;
228     property OnMouseLeave;
229     property OnMouseMove;
230     property OnMouseUp;
231     property OnNodeChanged;
232     property OnSelectionChanged;
233     property OnShowHint;
234     //property OnStartDock;
235     property OnStartDrag;
236     property OnUTF8KeyPress;
237     property Options;
238     property Items;
239     property TreeLineColor;
240     property TreeLinePenStyle;
241     end;
242    
243     function StrIntListToVar(s: string): TVariantArray;
244     function VarToStrIntList(a: TVariantArray): string;
245    
246     implementation
247    
248 tony 27 uses IBQuery,Variants;
249 tony 21
250     function StrIntListToVar(s: string): TVariantArray;
251     var i, idx: integer;
252     List: TStringList;
253     begin
254     List := TStringList.Create;
255     try
256     idx := 1;
257     List.Clear;
258     while idx <= Length(s) do
259     List.Add(ExtractFieldName(s,idx));
260    
261     Setlength(Result,List.Count);
262     for i := 0 to List.Count - 1 do
263     Result[i] := StrToInt(List[i])
264     finally
265     List.Free
266     end;
267     end;
268    
269     function VarToStrIntList(a: TVariantArray): string;
270     var i: integer;
271     begin
272     for i := 0 to Length(a) - 1 do
273     if VarIsOrdinal(a[i]) then
274     begin
275     if i = 0 then
276     Result := IntToStr(a[i])
277     else
278     Result := Result + ';' + IntToStr(a[i])
279     end
280     else
281     raise Exception.Create('Ordinal Type Expected when converting to integer string');
282     end;
283    
284 tony 27 { TIBTreeViewControlLink }
285    
286     constructor TIBTreeViewControlLink.Create(AOwner: TIBTreeView);
287     begin
288     inherited Create;
289     FOwner := AOwner;
290     end;
291    
292     procedure TIBTreeViewControlLink.UpdateParams(Sender: TObject);
293     begin
294     FOwner.UpdateParams(self,TIBParserDataSet(Sender).Parser)
295     end;
296    
297     procedure TIBTreeViewControlLink.UpdateSQL(Sender: TObject);
298     begin
299     FOwner.UpdateSQL(self,TIBParserDataSet(Sender).Parser)
300     end;
301    
302 tony 21 { TIBTreeNode }
303    
304     procedure TIBTreeNode.DeleteAll;
305     var Node, NextNode: TTreeNode;
306     begin
307     Expand(true);
308     Node := GetFirstChild;
309     while Node <> nil do
310     begin
311     NextNode := Node.GetNextSibling;
312     TIBTreeNode(Node).DeleteAll;
313     Node := NextNode;
314     end;
315     Delete
316     end;
317    
318     { TIBTreeView }
319    
320     procedure TIBTreeView.ActiveChanged(Sender: TObject);
321     var AtTopLevel: boolean;
322     begin
323     if (csDesigning in ComponentState) then Exit;
324 tony 27 IBControlLinkChanged;
325 tony 21 if assigned(DataSet) and not DataSet.Active then
326     begin
327     if not assigned(FExpandNode) and not assigned(FUpdateNode) then {must really be closing}
328     Reinitialise
329     end
330     else
331     begin
332     AtTopLevel := Items.TopLvlCount = 0;
333     AddNodes;
334     if not FLocatingNode and (Selected = nil) and (Items.TopLvlCount > 0) then
335     begin
336     if Length(FLastSelected) > 0 then
337     Selected := FindNode(FLastSelected,true)
338     else
339     Selected := Items.TopLvlItems[0];
340     end
341     end
342     end;
343    
344     procedure TIBTreeView.AddNodes;
345     var Node: TTreeNode;
346     ChildCount: integer;
347     begin
348     if assigned(FExpandNode) or (Items.Count = 0) then
349     begin
350     ChildCount := 0;
351     FNoAddNodeToDataset := true;
352     try
353     DataSet.First;
354     while not DataSet.EOF do
355     begin
356     Node := Items.AddChild(FExpandNode,DataSet.FieldByName(TextField).AsString);
357     TIBTreeNode(Node).FKeyValue := DataSet.FieldByName(KeyField).AsVariant;
358     Node.HasChildren := (HasChildField = '') or (DataSet.FieldByName(HasChildField).AsInteger <> 0);
359     Inc(ChildCount);
360     DataSet.Next
361     end;
362     finally
363     FNoAddNodeToDataset := false
364     end;
365     if assigned(FExpandNode) then
366     FExpandNode.HasChildren := ChildCount > 0;
367     FExpandNode := nil
368     end
369     end;
370    
371     procedure TIBTreeView.DataSetChanged(Sender: TObject);
372     begin
373     // Reinitialise
374     end;
375    
376     function TIBTreeView.GetDataSet: TDataSet;
377     begin
378     Result := FDataLink.DataSet
379     end;
380    
381     function TIBTreeView.GetDataSource: TDataSource;
382     begin
383     Result := FDataLink.DataSource
384     end;
385    
386     function TIBTreeView.GetRelationNameQualifier: string;
387     begin
388     if FRelationName <> '' then
389     Result := FRelationName + '.'
390     else
391     Result := ''
392     end;
393    
394     function TIBTreeView.GetSelectedKeyValue: variant;
395     begin
396     Result := NULL;
397     if assigned(Selected) and (Selected is TIBTreeNode) then
398     Result := TIBTreeNode(Selected).KeyValue
399     end;
400    
401     procedure TIBTreeView.NodeMoved(Node: TTreeNode);
402     begin
403     {Need to update Parent}
404     if ScrollToNode(TIBTreeNode(Node)) then
405     begin
406     FDataLink.Edit;
407     FModifiedNode := TIBTreeNode(Node)
408     end;
409     end;
410    
411     procedure TIBTreeView.NodeUpdated(Node: TTreeNode);
412     begin
413     {Need to Update List Field}
414     if ScrollToNode(TIBTreeNode(Node)) then
415     begin
416     FDataLink.Edit;
417     FModifiedNode := TIBTreeNode(Node);
418     FDataLink.UpdateRecord
419     end;
420     end;
421    
422     procedure TIBTreeView.RecordChanged(Sender: TObject; Field: TField);
423     var Node: TIBTreeNode;
424     Destination: TIBTreeNode;
425     begin
426 tony 27 if DataSet.State = dsInsert then Exit;
427    
428 tony 21 if assigned(Field) and (Field.FieldName = TextField) then
429     begin
430     Node := FindNode(DataSet.FieldByName(KeyField).AsVariant);
431     if assigned(Node) then
432     begin
433     FUpdating := true;
434     try
435     Node.Text := Field.Text
436     finally
437     FUpdating := false
438     end;
439     end;
440     end
441     else
442     if assigned(Field) and (Field.FieldName = ParentField) then
443     begin
444     Node := FindNode(DataSet.FieldByName(KeyField).AsVariant);
445     if assigned(Node) then
446     begin
447     if DataSet.FieldByName(ParentField).IsNull then
448     Destination := nil
449     else
450     Destination := FindNode(DataSet.FieldByName(ParentField).AsVariant);
451    
452     if Destination = Node.Parent then Exit;
453    
454     FUpdating := true;
455     try
456     Node.MoveTo(Destination,naAddChild);
457     finally
458     FUpdating := false
459     end;
460     end;
461     end
462     end;
463    
464     procedure TIBTreeView.SetHasChildField(AValue: string);
465     begin
466     if FHasChildField = AValue then Exit;
467     FHasChildField := AValue;
468     Reinitialise
469     end;
470    
471     procedure TIBTreeView.SetKeyField(AValue: string);
472     begin
473     if FKeyField = AValue then Exit;
474     FKeyField := AValue;
475     Reinitialise
476     end;
477    
478     procedure TIBTreeView.SetTextField(AValue: string);
479     begin
480     if FTextField = AValue then Exit;
481     FTextField := AValue;
482     Reinitialise
483     end;
484    
485     procedure TIBTreeView.SetDataSource(AValue: TDataSource);
486     begin
487 tony 27 FDataLink.DataSource := AValue;
488     IBControlLinkChanged;
489 tony 21 end;
490    
491     procedure TIBTreeView.SetParentField(AValue: string);
492     begin
493     if FParentField = AValue then Exit;
494     FParentField := AValue;
495     Reinitialise
496     end;
497    
498     function TIBTreeView.ScrollToNode(Node: TIBTreeNode): boolean;
499     begin
500     Result := assigned(DataSet) and DataSet.Active and assigned(Node) and not varIsNull(Node.KeyValue);
501     if Result then
502     begin
503     if DataSet.Active and (DataSet.RecordCount > 0)
504     and (Node.KeyValue = DataSet.FieldByName(KeyField).AsVariant) then Exit;
505    
506     FUpdateNode := Node;
507     try
508     DataSet.Active := false;
509     DataSet.Active := true;
510     finally
511     FUpdateNode := nil
512     end;
513     Result := DataSet.FieldByName(KeyField).AsVariant = Node.KeyValue
514     end;
515     end;
516    
517     procedure TIBTreeView.UpdateData(Sender: TObject);
518     begin
519     if assigned(FModifiedNode) then
520     begin
521     DataSet.FieldByName(TextField).AsString := FModifiedNode.Text;
522     if FModifiedNode.Parent = nil then
523     DataSet.FieldByName(ParentField).Clear
524     else
525     DataSet.FieldByName(ParentField).AsVariant := TIBTreeNode(FModifiedNode.Parent).KeyValue;
526     FModifiedNode := nil
527     end
528     end;
529    
530     procedure TIBTreeView.UpdateParams(Sender: TObject; Parser: TSelectSQLParser);
531     begin
532     if not assigned(FExpandNode) and assigned(FUpdateNode) then {Scrolling dataset}
533     begin
534 tony 27 if DataSource.DataSet is TIBQuery then
535     TIBQuery(DataSource.DataSet).ParamByName('IBX_KEY_VALUE').Value :=
536 tony 21 FUpdateNode.KeyValue
537     else
538 tony 27 if DataSource.DataSet is TIBDataSet then
539     TIBDataSet(DataSource.DataSet).ParamByName('IBX_KEY_VALUE').Value :=
540 tony 21 FUpdateNode.KeyValue
541     end
542     else
543     if assigned(FExpandNode) then
544     begin
545 tony 27 if DataSource.DataSet is TIBQuery then
546     TIBQuery(DataSource.DataSet).ParamByName('IBX_PARENT_VALUE').Value :=
547 tony 21 TIBTreeNode(FExpandNode).KeyValue
548     else
549 tony 27 if DataSource.DataSet is TIBDataSet then
550     TIBDataSet(DataSource.DataSet).ParamByName('IBX_PARENT_VALUE').Value :=
551 tony 21 TIBTreeNode(FExpandNode).KeyValue
552     end;
553     end;
554    
555     procedure TIBTreeView.UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
556     begin
557     if not assigned(FExpandNode) and assigned(FUpdateNode) then {Scrolling dataset}
558     Parser.Add2WhereClause(GetRelationNameQualifier + '"' + FKeyField + '" = :IBX_KEY_VALUE')
559     else
560     if (Items.Count = 0) then
561     {Need to Load Root Nodes}
562     Parser.Add2WhereClause(GetRelationNameQualifier + '"' + FParentField + '" is null')
563     else
564     if assigned(FExpandNode) then
565     Parser.Add2WhereClause(GetRelationNameQualifier + '"' + FParentField + '" = :IBX_PARENT_VALUE');
566     end;
567    
568     procedure TIBTreeView.Added(Node: TTreeNode);
569     begin
570     if assigned(DataSet) and DataSet.Active and not FNoAddNodeToDataset then
571     begin
572     DataSet.Append;
573     TIBTreeNode(Node).FKeyValue := DataSet.FieldByName(KeyField).AsVariant;
574     if (Node.Text = '') and not DataSet.FieldByName(TextField).IsNull then
575     Node.Text := DataSet.FieldByName(TextField).AsString;
576     FModifiedNode := TIBTreeNode(Node);
577     FDataLink.UpdateRecord
578     end;
579     inherited Added(Node);
580     end;
581    
582     procedure TIBTreeView.Delete(Node: TTreeNode);
583     begin
584     if not (tvsUpdating in States) {TreeNodes being cleared}
585     and not (tvsManualNotify in States) {Tree Collapse with node delete}
586     and ScrollToNode(TIBTreeNode(Node)) then
587     DataSet.Delete;
588     inherited Delete(Node);
589     end;
590    
591     procedure TIBTreeView.Change(Node: TTreeNode);
592     begin
593     inherited Change(Node);
594     ScrollToNode(TIBTreeNode(Node));
595     end;
596    
597     function TIBTreeView.CreateNode: TTreeNode;
598     var
599     NewNodeClass: TTreeNodeClass;
600     begin
601     Result := nil;
602     if Assigned(OnCustomCreateItem) then
603     OnCustomCreateItem(Self, Result);
604     if Result = nil then
605     begin
606     NewNodeClass:=TIBTreeNode;
607     if Assigned(OnCreateNodeClass) then
608     OnCreateNodeClass(Self,NewNodeClass);
609     Result := NewNodeClass.Create(Items);
610     end;
611     end;
612    
613     function TIBTreeView.CanEdit(Node: TTreeNode): Boolean;
614     begin
615     Result := inherited CanEdit(Node)
616     and assigned(DataSet) and not DataSet.FieldByName(TextField).ReadOnly
617     end;
618    
619     procedure TIBTreeView.Expand(Node: TTreeNode);
620     begin
621     inherited Expand(Node);
622     if Node.HasChildren and assigned(DataSet) and (Node.GetFirstChild = nil) then
623     begin
624     FExpandNode := Node;
625     DataSet.Active := false;
626     DataSet.Active := true;
627     if assigned(Selected) then
628     ScrollToNode(TIBTreeNode(Selected))
629     end;
630     end;
631    
632 tony 27 procedure TIBTreeView.IBControlLinkChanged;
633     begin
634     if assigned(DataSource) and (DataSource.DataSet <> nil) and (DataSource.DataSet is TIBParserDataset) then
635     FIBTreeViewControllink.IBDataSet := TIBCustomDataSet(DataSource.DataSet)
636     else
637     FIBTreeViewControllink.IBDataSet := nil;
638     end;
639    
640 tony 21 procedure TIBTreeView.Loaded;
641     begin
642     inherited Loaded;
643     Reinitialise
644     end;
645    
646     procedure TIBTreeView.NodeChanged(Node: TTreeNode;
647     ChangeEvent: TTreeNodeChangeReason);
648     begin
649     inherited NodeChanged(Node, ChangeEvent);
650     if not FNoAddNodeToDataset and not FUpdating then
651     case ChangeEvent of
652     ncTextChanged:
653     NodeUpdated(Node);
654     ncParentChanged:
655     NodeMoved(Node);
656     end;
657     end;
658    
659     procedure TIBTreeView.Notification(AComponent: TComponent; Operation: TOperation
660     );
661     begin
662     inherited Notification(AComponent, Operation);
663     if (Operation = opRemove) and
664 tony 27 (FDataLink <> nil) and (AComponent = DataSource) then
665     DataSource := nil;
666 tony 21 end;
667    
668     procedure TIBTreeView.Reinitialise;
669     begin
670     if [csDesigning,csLoading] * ComponentState <> [] then Exit;
671     FLastSelected := GetNodePath(Selected);
672     Items.Clear;
673     end;
674    
675     constructor TIBTreeView.Create(TheComponent: TComponent);
676     begin
677     inherited Create(TheComponent);
678     FDataLink := TIBTreeViewDatalink.Create(self);
679 tony 27 FIBTreeViewControlLink := TIBTreeViewControlLink.Create(self);
680 tony 21 end;
681    
682     destructor TIBTreeView.Destroy;
683     begin
684     if assigned(FDataLink) then FDataLink.Free;
685 tony 27 if assigned(FIBTreeViewControlLink) then FIBTreeViewControlLink.Free;
686 tony 21 inherited Destroy;
687     end;
688    
689     function TIBTreeView.FindNode(KeyValuePath: array of variant;
690     SelectNode: boolean): TIBTreeNode;
691     var Node: TTreeNode;
692     i,j: integer;
693     begin
694     Result := nil;
695     FLocatingNode := true;
696     try
697     for j := 0 to Items.TopLvlCount - 1 do
698     begin
699     Node := Items.TopLvlItems[j];
700     i := 0;
701     Node.Expand(false);
702     while assigned(Node) do
703     begin
704     if TIBTreeNode(Node).KeyValue = KeyValuePath[i] then
705     begin
706     Inc(i);
707     if i = Length(KeyValuePath) then
708     begin
709     Result := TIBTreeNode(Node);
710     if SelectNode then
711     Selected := Node;
712     Exit
713     end
714     else
715     begin
716     Node.Expand(false);
717     Node := Node.GetFirstChild;
718     end
719     end
720     else
721     Node := Node.GetNextSibling
722     end
723     end
724     finally
725     FLocatingNode := false
726     end
727     end;
728    
729     function TIBTreeView.FindNode(KeyValue: variant): TIBTreeNode;
730     var i: integer;
731     begin
732     Result := nil;
733     if (Selected <> nil) and (TIBTreeNode(Selected).KeyValue = KeyValue) then
734     Result := TIBTreeNode(Selected)
735     else
736     {Find it the hard way}
737     begin
738     FullExpand;
739     for i := 0 to Items.Count -1 do
740     if TIBTreeNode(Items[i]).KeyValue = KeyValue then
741     begin
742     Result := TIBTreeNode(Items[i])
743     end;
744     end;
745     end;
746    
747     function TIBTreeView.GetNodePath(Node: TTreeNode): TVariantArray;
748     var aParent: TTreeNode;
749     i: integer;
750     begin
751     if not assigned(Node) or not (Node is TIBTreeNode) then
752     SetLength(Result,0)
753     else
754     begin
755     {Count length of Path}
756     i := 1;
757     aParent := Node.Parent;
758     while (aParent <> nil) do
759     begin
760     Inc(i);
761     aParent := aParent.Parent
762     end;
763    
764     {Save Path}
765     Setlength(Result,i);
766     while i > 0 do
767     begin
768     Dec(i);
769     Result[i] := TIBTreeNode(Node).KeyValue;
770     Node := Node.Parent
771     end;
772     end;
773     end;
774    
775     { TIBTreeViewDatalink }
776    
777     procedure TIBTreeViewDatalink.ActiveChanged;
778     begin
779     FOwner.ActiveChanged(self)
780     end;
781    
782     procedure TIBTreeViewDatalink.DataSetChanged;
783     begin
784     FOwner.DataSetChanged(self)
785     end;
786    
787     procedure TIBTreeViewDatalink.RecordChanged(Field: TField);
788     begin
789     FOwner.RecordChanged(self,Field);
790     end;
791    
792     procedure TIBTreeViewDatalink.UpdateData;
793     begin
794     FOwner.UpdateData(self)
795     end;
796    
797     constructor TIBTreeViewDatalink.Create(AOwner: TIBTreeView);
798     begin
799     inherited Create;
800     FOwner := AOwner
801     end;
802    
803     end.