You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
18 KiB
Plaintext
621 lines
18 KiB
Plaintext
unit VirtualTreeWrapper;
|
|
|
|
// The contents of this file are subject to
|
|
// GNU Lesser General Public License as published by the Free Software Foundation;
|
|
// either version 2.1 of the License, or (at your option) any later version.
|
|
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
|
|
//
|
|
// Software distributed under the License is distributed on an "AS IS" basis,
|
|
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
|
|
// specific language governing rights and limitations under the License.
|
|
//
|
|
// Original code released 02-15-2011
|
|
//
|
|
// Copyright (C) 2011 VUTS Liberec (Jan Rames ramejan@gmail.com)
|
|
|
|
interface
|
|
|
|
uses SysUtils, Classes, Controls, VirtualTrees, Generics.Collections.Static,
|
|
RTLConsts;
|
|
|
|
type
|
|
/// |
|
/// Provides basic record wrapper functionality in terms of initializating
|
|
/// and finalizating the record's members (strings, interfaces) to prevent
|
|
/// memory leaks
|
|
///
|
|
TBaseVirtualTreeWrapper |
|
public
|
|
type P = ^T;
|
|
type TFreeProc = reference to procedure(var UserData : T);
|
|
private
|
|
FFreeNode : TVTFreeNodeEvent;
|
|
FFreeProc : TFreeProc;
|
|
protected
|
|
/// |
|
/// Assigns Tree's Properties and Events
|
|
///
|
|
procedure Init; virtual;
|
|
function Tree : TVirtualStringTree; inline;
|
|
|
|
//No virtual functions needed, just reassign particular events in create
|
|
//of descendants
|
|
{procedure InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
|
|
var InitialStates: TVirtualNodeInitStates); Cannot be relied upon, it'll
|
|
be called later even after the AddChild returns}
|
|
procedure FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
|
public
|
|
constructor Create(AOwner : TComponent); overload; override;
|
|
constructor Create(AOwner : TComponent; FreeProc : TFreeProc); reintroduce; overload;
|
|
|
|
/// |
|
/// Adds a node and initializes its data (do not ever call
|
|
/// TVirtualStringTree.AddChild as it would fail to initialize it)
|
|
///
|
|
function AddChild(Parent : PVirtualNode) : PVirtualNode; inline;
|
|
function GetUserData(Node : PVirtualNode) : P; inline;
|
|
|
|
property Data[Node : PVIrtualNode] : P read GetUserData;
|
|
end;
|
|
|
|
TVirtualTreeWrapper |
|
|
|
/// |
|
/// Record that stores Item's common data
|
|
///
|
|
TVSTData = record
|
|
Caption : string;
|
|
Hint : string;
|
|
ImageIndex : Integer;
|
|
end;
|
|
PVSTData = ^TVSTData;
|
|
|
|
TVirtualNodeEnumerator |
|
/// |
|
/// Record that can be used to operate the node with object-like manner
|
|
///
|
|
TVirtualNode |
|
strict private
|
|
Tree : TVirtualTreeWrapper |
|
FNode : PVirtualNode;
|
|
FData : PVSTData;
|
|
|
|
function GetData : TBaseVirtualTreeWrapper |
|
|
|
function GetCaption : string;
|
|
procedure SetCaption(const ACaption : string);
|
|
function GetHint : string;
|
|
procedure SetHint(const AHint : string);
|
|
function GetImageIndex : Integer;
|
|
procedure SetImageIndex(const AImageIndex : Integer);
|
|
function GetCheckState : TCheckState;
|
|
procedure SetCheckState(Value: TCheckState);
|
|
function GetCheckType : TCheckType;
|
|
procedure SetCheckType(Value: TCheckType);
|
|
function GetLevel : Integer;
|
|
function GetIndex : Integer;
|
|
function GetItem(Index : Cardinal) : TVirtualNode |
|
function GetChildCount : Cardinal;
|
|
|
|
private
|
|
procedure Create(ATree : TVirtualTreeWrapper |
|
function GetIsEmpty: Boolean;
|
|
public
|
|
class function Empty : TVirtualNode |
|
|
|
function AddChild : TVirtualNode |
|
function AddChild(const ACaption : string) : TVirtualNode |
|
procedure Delete;
|
|
procedure MakeVisible(Recursive : Boolean = false);
|
|
|
|
function Parent: TVirtualNode |
|
function FirstChild : TVirtualNode |
|
function NextSibling : TVirtualNode |
|
function PrevSiblinng : TVirtualNode |
|
|
|
function GetEnumerator : TVirtualNodeEnumerator |
|
|
|
property Node : PVirtualNode read FNode;
|
|
property Level : Integer read GetLevel;
|
|
property Index : Integer read GetIndex;
|
|
property ChildCount : Cardinal read GetChildCount;
|
|
property Data : TBaseVirtualTreeWrapper |
|
property Caption : string read GetCaption write SetCaption;
|
|
property Hint : string read GetHint write SetHint;
|
|
property ImageIndex : Integer read GetImageIndex write SetImageIndex;
|
|
property CheckState : TCheckState read GetCheckState write SetCheckState;
|
|
property CheckType : TCheckType read GetCheckType write SetCheckType;
|
|
property Items[Index : Cardinal] : TVirtualNode |
|
property IsEmpty : Boolean read GetIsEmpty;
|
|
end;
|
|
|
|
/// |
|
/// Provides enhanced record wrapper functionality with caption, hint, etc.
|
|
/// make sure that your record begins with TVSTData:
|
|
/// record
|
|
/// Info : TVSTData;
|
|
/// Data1: Type1;
|
|
/// Data2: Type2;
|
|
/// ...
|
|
/// end;
|
|
///
|
|
TVirtualTreeWrapper |
|
public
|
|
type P = TBaseVirtualTreeWrapper |
|
private
|
|
FUpdateCount : Integer;
|
|
function GetItem(Node: PVirtualNode): TVirtualNode |
|
protected
|
|
procedure Init; override;
|
|
function GetData(Node : PVirtualNode) : PVSTData; inline;
|
|
procedure NodeUpdated(Node : PVirtualNode); inline;
|
|
|
|
//No virtual functions needed, just reassign particular events in create
|
|
//of descendants
|
|
|
|
procedure DoGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
|
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
|
|
procedure DoGetHint(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
|
|
var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: UnicodeString);
|
|
procedure DoGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
|
|
var Ghosted: Boolean; var ImageIndex: Integer);
|
|
public
|
|
constructor Create(AOwner : TComponent); override;
|
|
|
|
function AddChild(ParentNode : PVirtualNode) : PVirtualNode; overload; inline;
|
|
function AddChild(ParentNode : PVirtualNode; const Caption : UnicodeString) : PVirtualNode; overload; inline;
|
|
function AddChild(ParentNode : PVirtualNode; const Caption, Hint : UnicodeString) : PVirtualNode; overload; inline;
|
|
procedure DeleteNode(Node: PVirtualNode); inline;
|
|
|
|
function GetCaption(Node : PVirtualNode) : string; inline;
|
|
procedure SetCaption(Node : PVirtualNode; const ACaption : string); inline;
|
|
function GetHint(Node : PVirtualNode) : string; inline;
|
|
procedure SetHint(Node : PVirtualNode; const AHint : string); inline;
|
|
function GetImageIndex(Node : PVirtualNode) : Integer; inline;
|
|
procedure SetImageIndex(Node : PVirtualNode; const AImageIndex : Integer); inline;
|
|
|
|
/// |
|
/// Finds node with given Caption returns first node found or nil
|
|
/// (if no match was found). Search is only limited to Childs of
|
|
/// ParentNode. If ParentNode is nil, base of the tree is searched.
|
|
/// For extended search use Incremental search feature of the VirtualTree
|
|
///
|
|
function FindNode(const ACaption : string; ParentNode : PVirtualNode = nil) : PVirtualNode;
|
|
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
|
|
/// |
|
/// Returns IVirtualNode nased on PVirtualNode (if set to nil, root is
|
|
/// returned)
|
|
///
|
|
property Items[Node : PVirtualNode] : TVirtualNode |
|
end;
|
|
|
|
TVirtualNodeEnumerator |
|
strict private
|
|
Node : PVirtualNode;
|
|
FCurrent : PVirtualNode;
|
|
Tree : TVirtualTreeWrapper |
|
function GetCurrent : TVirtualNode |
|
public
|
|
constructor Create(ATree : TVirtualTreeWrapper |
|
function MoveNext : Boolean; inline;
|
|
property Current : TVirtualNode |
|
end;
|
|
|
|
implementation
|
|
|
|
{ TBaseVirtualTreeViewWrapper |
|
|
|
function TBaseVirtualTreeWrapper |
|
var Ptr : P;
|
|
begin
|
|
Result:=Tree.AddChild(Parent);
|
|
//Treat the node as if it has some initial data which causes calling of
|
|
//OnFreeNode even if the node hasn't been already initialized
|
|
Include(Result^.States, vsInitialUserData);
|
|
Ptr:=Tree.GetNodeData(Result);
|
|
//Not needed as VirtualTree uses AllocMem which nils (it zeros the entire
|
|
//memory block) all pointers that Initialize nils (basically this is the
|
|
//same thing that SetLength for dynamic arrays does).
|
|
//Initialize(Ptr^);
|
|
end;
|
|
|
|
constructor TBaseVirtualTreeWrapper |
|
begin
|
|
Create(AOwner);
|
|
FFreeProc:=FreeProc;
|
|
end;
|
|
|
|
constructor TBaseVirtualTreeWrapper |
|
begin
|
|
inherited Create(AOwner as TVirtualStringTree); //Make sure Owner is set correctly
|
|
Init;
|
|
end;
|
|
|
|
procedure TBaseVirtualTreeWrapper |
|
Node: PVirtualNode);
|
|
var P : ^T;
|
|
begin
|
|
if (Assigned(FFreeNode)) then FFreeNode(Sender, Node);
|
|
|
|
P:=Sender.GetNodeData(Node);
|
|
if (Assigned(FFreeProc)) then FFreeProc(P^);
|
|
|
|
Finalize(P^);
|
|
end;
|
|
|
|
function TBaseVirtualTreeWrapper |
|
begin
|
|
Result:=Tree.GetNodeData(Node);
|
|
end;
|
|
|
|
procedure TBaseVirtualTreeWrapper |
|
begin
|
|
Tree.NodeDataSize:=sizeof(T);
|
|
|
|
//Tree.OnInitNode:=InitNode;
|
|
FFreeNode:=Tree.OnFreeNode;
|
|
Tree.OnFreeNode:=FreeNode;
|
|
end;
|
|
|
|
function TBaseVirtualTreeWrapper |
|
begin
|
|
Result:=TVirtualStringTree(Owner);
|
|
end;
|
|
|
|
{ TVirtualTreeViewWrapper |
|
|
|
function TVirtualTreeWrapper |
|
var Data : P;
|
|
begin
|
|
Result:=inherited AddChild(ParentNode);
|
|
Data:=GetUserData(Result);
|
|
PVSTData(Data)^.ImageIndex:=-1;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
const Caption: UnicodeString): PVirtualNode;
|
|
var Data : P;
|
|
begin
|
|
Result:=inherited AddChild(ParentNode);
|
|
Data:=GetUserData(Result);
|
|
PVSTData(Data)^.Caption:=Caption;
|
|
PVSTData(Data)^.ImageIndex:=-1;
|
|
NodeUpdated(Result);
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
const Caption, Hint: UnicodeString): PVirtualNode;
|
|
var Data : P;
|
|
begin
|
|
Result:=inherited AddChild(ParentNode);
|
|
Data:=GetUserData(Result);
|
|
PVSTData(Data)^.Caption:=Caption;
|
|
PVSTData(Data)^.Hint:=Hint;
|
|
PVSTData(Data)^.ImageIndex:=-1;
|
|
NodeUpdated(Result);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
Inc(FUpdateCount);
|
|
Tree.BeginUpdate;
|
|
end;
|
|
|
|
constructor TVirtualTreeWrapper |
|
begin
|
|
inherited;
|
|
FUpdateCount:=0;
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
Dec(FUpdateCount);
|
|
Tree.EndUpdate;
|
|
if (FUpdateCount <= 0) then begin
|
|
//Tree.InvalidateChildren(nil, true);
|
|
FUpdateCount:=0;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
ParentNode: PVirtualNode = nil): PVirtualNode;
|
|
begin
|
|
if (ParentNode = nil) then ParentNode:=Tree.RootNode;
|
|
|
|
ParentNode:=ParentNode^.FirstChild;
|
|
Result:=nil;
|
|
while ParentNode <> nil do begin
|
|
if (GetData(ParentNode)^.Caption = ACaption) then Exit(ParentNode);
|
|
ParentNode:=ParentNode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
begin
|
|
Result:=GetData(Node)^.Caption;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
begin
|
|
Result:=Tree.GetNodeData(Node);
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
begin
|
|
Result:=GetData(Node)^.Hint;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
begin
|
|
Result:=GetData(Node)^.ImageIndex;
|
|
end;
|
|
|
|
function TVirtualTreeWrapper |
|
begin
|
|
Result.Create(Self, Node);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
Tree.DeleteNode(Node);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
Node: PVirtualNode; Column: TColumnIndex;
|
|
var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: UnicodeString);
|
|
begin
|
|
HintText:=GetData(Node)^.Hint;
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
|
|
var Ghosted: Boolean; var ImageIndex: Integer);
|
|
begin
|
|
ImageIndex:=GetData(Node)^.ImageIndex;
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
|
|
var CellText: string);
|
|
begin
|
|
CellText:=GetData(Node)^.Caption;
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
inherited;
|
|
|
|
Tree.OnGetText:=DoGetText;
|
|
Tree.OnGetHint:=DoGetHint;
|
|
Tree.OnGetImageIndex:=DoGetImageIndex;
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
if (FUpdateCount = 0) then Tree.InvalidateNode(Node);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
const ACaption: string);
|
|
begin
|
|
GetData(Node)^.Caption:=ACaption;
|
|
NodeUpdated(Node);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
begin
|
|
GetData(Node)^.Hint:=AHint;
|
|
NodeUpdated(Node);
|
|
end;
|
|
|
|
procedure TVirtualTreeWrapper |
|
const AImageIndex: Integer);
|
|
begin
|
|
NodeUpdated(Node);
|
|
end;
|
|
|
|
{ TVirtualNodeImpl }
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result.Create(Tree, Tree.AddChild(FNode));
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result.Create(Tree, Tree.AddChild(FNode, ACaption));
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
ANode: PVirtualNode);
|
|
begin
|
|
Tree:=ATree;
|
|
FNode:=ANode;
|
|
if (FNode = nil) then FData:=nil
|
|
else FData:=ATree.GetData(FNode);
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FData^.Caption;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=Tree.Tree.CheckState[FNode];
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=Tree.Tree.CheckType[FNode];
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FNode^.ChildCount;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=Pointer(FData);
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=TVirtualNodeEnumerator |
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FData^.Hint;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FData^.ImageIndex;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FNode^.Index;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=FNode = nil;
|
|
end;
|
|
|
|
function TVirtualNode |
|
var i : Integer;
|
|
ANode : PVirtualNode;
|
|
begin
|
|
if (Index >= FNode^.ChildCount) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
ANode:=FNode^.FirstChild;
|
|
if (Index > 0) then for i:=0 to Index - 1 do begin
|
|
Assert(ANode <> nil, 'Node shouldn''t be nil, ChildCount incorrect');
|
|
ANode:=ANode^.NextSibling;
|
|
end;
|
|
Result:=Tree.GetItem(ANode);
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=Tree.Tree.GetNodeLevel(FNode);
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
var AParent : PVirtualNode;
|
|
List : TList |
|
i : Integer;
|
|
begin
|
|
with Tree.Tree do begin
|
|
List.Init;
|
|
AParent:=FNode.Parent;
|
|
// The root node is marked by having its NextSibling (and PrevSibling) pointing to itself.
|
|
while (AParent <> nil) and (AParent^.NextSibling <> AParent) do begin
|
|
if (vsExpanded in AParent^.States) then Break;
|
|
List.Add(AParent);
|
|
AParent:=AParent^.Parent;
|
|
end;
|
|
for i:=List.Count - 1 downto 0 do Expanded[List[i]]:=true;
|
|
|
|
if (Recursive) then FullExpand(FNode)
|
|
else Expanded[FNode]:=true;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
if (FNode^.NextSibling = nil) then Exit(Empty);
|
|
|
|
Result:=Tree.GetItem(FNode^.NextSibling);
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
Result:=Tree.GetItem(FNode^.Parent);
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
if (FNode^.PrevSibling = nil) then Exit(Empty);
|
|
|
|
Result:=Tree.GetItem(FNode^.PrevSibling);
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
Tree.DeleteNode(FNode);
|
|
end;
|
|
|
|
class function TVirtualNode |
|
begin
|
|
Result.FNode:=nil;
|
|
Result.FData:=nil;
|
|
end;
|
|
|
|
function TVirtualNode |
|
begin
|
|
if (FNode^.FirstChild = nil) then Exit(Empty);
|
|
|
|
Result:=Tree.GetItem(FNode^.FirstChild);
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
FData^.Caption:=ACaption;
|
|
Tree.NodeUpdated(FNode);
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
Tree.Tree.CheckState[FNode]:=Value;
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
Tree.Tree.CheckType[FNode]:=Value;
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
FData^.Hint:=AHint;
|
|
Tree.NodeUpdated(FNode);
|
|
end;
|
|
|
|
procedure TVirtualNode |
|
begin
|
|
FData^.ImageIndex:=AImageIndex;
|
|
Tree.NodeUpdated(FNode);
|
|
end;
|
|
|
|
{ TVirtualNodeEnumerator |
|
|
|
constructor TVirtualNodeEnumerator |
|
ANode: PVirtualNode);
|
|
begin
|
|
Node:=ANode;
|
|
FCurrent:=nil;
|
|
Tree:=ATree;
|
|
end;
|
|
|
|
function TVirtualNodeEnumerator |
|
begin
|
|
Result:=Tree.GetItem(FCurrent);
|
|
end;
|
|
|
|
function TVirtualNodeEnumerator |
|
begin
|
|
if (FCurrent = nil) then FCurrent:=Node^.FirstChild
|
|
else FCurrent:=FCurrent^.NextSibling;
|
|
|
|
Result:=FCurrent <> nil;
|
|
end;
|
|
|
|
end.
|