ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBTreeView.pas
Revision: 80
Committed: Mon Jan 1 11:31:07 2018 UTC (6 years, 3 months ago) by tony
Content type: text/x-pascal
File size: 21771 byte(s)
Log Message:
Fixes merged into public release

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