You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1107 8e941d3f-bd1b-0410-a28a-d453659cc2b4
517 lines
18 KiB
ObjectPascal
517 lines
18 KiB
ObjectPascal
{*********************************************************************** }
|
|
{ File: VTDBExample.pas }
|
|
{ }
|
|
{ Purpose: }
|
|
{ source file to illustrate how to get started with VT }
|
|
{ <-- Database example 1. --> }
|
|
{ }
|
|
{ Credits: }
|
|
{ taken + modified from example by Mike Lischke }
|
|
{ }
|
|
{ Module Record: }
|
|
{ }
|
|
{ Date AP Details }
|
|
{ -------- -- -------------------------------------- }
|
|
{ 05-Nov-2002 TC Created (tomc@gripsystems.com) }
|
|
{**********************************************************************}
|
|
unit VTDBExample;
|
|
|
|
{$mode delphi}
|
|
{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
delphicompat, LCLIntf, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
|
|
VirtualTrees, StdCtrls, ExtCtrls, sqlite3ds, Menus, VTreeData, Buttons, LResources;
|
|
|
|
type
|
|
|
|
{ TfrmVTDBExample }
|
|
|
|
TfrmVTDBExample =
|
|
class(TForm)
|
|
Label1: TLabel;
|
|
FDataset: TSqlite3Dataset;
|
|
Panel1: TPanel;
|
|
VT: TVirtualStringTree;
|
|
imgMaster: TImageList;
|
|
Panel2: TPanel;
|
|
Label2: TLabel;
|
|
ed: TEdit;
|
|
AddOneButton: TButton;
|
|
Button1: TButton;
|
|
btnHasChildren: TButton;
|
|
chkShowIDs: TCheckBox;
|
|
Label3: TLabel;
|
|
btnToggleVisibility: TButton;
|
|
chkAllVisible: TCheckBox;
|
|
chkDynHt: TCheckBox;
|
|
Label4: TLabel;
|
|
Bevel1: TBevel;
|
|
Label5: TLabel;
|
|
|
|
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
|
procedure AddButtonClick(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
|
|
var Text: String);
|
|
procedure VTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
|
procedure VTInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
|
|
var InitialStates: TVirtualNodeInitStates);
|
|
procedure FormActivate(Sender: TObject);
|
|
procedure VTGetImageIndex(Sender: TBaseVirtualTree;
|
|
Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
|
|
var Ghosted: Boolean; var ImageIndex: Integer);
|
|
procedure chkShowIDsClick(Sender: TObject);
|
|
procedure VTPaintText(Sender: TBaseVirtualTree;
|
|
const TargetCanvas: TCanvas; Node: PVirtualNode;
|
|
Column: TColumnIndex; TextType: TVSTTextType);
|
|
procedure VTHeaderClick(Sender: TVTHeader; Column: TColumnIndex;
|
|
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
procedure VTCompareNodes(Sender: TBaseVirtualTree; Node1,
|
|
Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
|
|
procedure VTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
|
|
procedure VTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: String);
|
|
procedure VTInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
|
|
procedure btnHasChildrenClick(Sender: TObject);
|
|
procedure btnToggleVisibilityClick(Sender: TObject);
|
|
procedure chkAllVisibleClick(Sender: TObject);
|
|
procedure VTFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode;
|
|
OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
|
|
procedure chkDynHtClick(Sender: TObject);
|
|
procedure VTIncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const SearchText: String;
|
|
var Result: Integer);
|
|
|
|
private
|
|
procedure LoadDataset;
|
|
|
|
procedure HideNodes(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean);
|
|
|
|
end;
|
|
|
|
implementation
|
|
{.$R *.DFM}
|
|
|
|
const
|
|
FLDN_CustNo = 0;
|
|
FLDN_Company = 1;
|
|
FLDN_Addr1 = 2;
|
|
FLDN_Addr2 = 3;
|
|
FLDN_City = 4;
|
|
FLDN_State = 5;
|
|
FLDN_Zip = 6;
|
|
FLDN_Country = 7;
|
|
FLDN_Phone = 8;
|
|
FLDN_FAX = 9;
|
|
FLDN_TaxRate = 10;
|
|
FLDN_Contact = 11;
|
|
FLDN_LastInvoiceDate = 12;
|
|
|
|
procedure TfrmVTDBExample.FormActivate(Sender: TObject);
|
|
var
|
|
r : TRect;
|
|
begin
|
|
{get size of desktop}
|
|
//todo: not implemented under gtk
|
|
{$ifdef Windows}
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, @r, 0);
|
|
Height := r.Bottom-Top;
|
|
Width := r.Right-Left;
|
|
Application.ProcessMessages;
|
|
{$endif}
|
|
|
|
|
|
LoadDataset;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.FormClose(Sender: TObject; var Action: TCloseAction);
|
|
begin
|
|
Action := caFree;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.LoadDataset;
|
|
var
|
|
Node : PVirtualNode;
|
|
begin
|
|
with FDataset do
|
|
begin
|
|
VT.BeginUpdate;
|
|
try
|
|
FileName := 'customers.db';
|
|
TableName:='customers';
|
|
PrimaryKey := 'CustNo';
|
|
Active := True;
|
|
|
|
while not eof do
|
|
begin
|
|
{--------------------------------------------------------------------------------
|
|
add a node, call validate to explicitly trigger InitNode *Now* rather than later
|
|
as cds will be sitting on the current record in InitNode. Other options are to
|
|
'Findkey' as required, Bookmark, etc, etc.
|
|
|
|
I think that this actually goes against Mike's intention for this component and in
|
|
fact this example uses both methods - ie. see cds.Lookup
|
|
--------------------------------------------------------------------------------}
|
|
Node := VT.AddChild(nil);
|
|
VT.ValidateNode( Node, False );
|
|
|
|
Next;
|
|
end;
|
|
|
|
finally
|
|
VT.EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
|
|
begin
|
|
NodeDataSize := SizeOf(TBasicNodeRec); // Let the tree know how much data space we need.
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
|
|
var InitialStates: TVirtualNodeInitStates);
|
|
var
|
|
Data : PBasicNodeRec;
|
|
iCustNo : integer;
|
|
iImage : integer;
|
|
begin
|
|
// setup our node data here. This event is triggered once for each node but
|
|
// appears asynchronously, which means when the node is displayed not when it is added
|
|
Data := Sender.GetNodeData(Node);
|
|
|
|
iImage := -1;
|
|
if Sender.GetNodeLevel( Node ) = 0 then
|
|
begin
|
|
iImage := 3;
|
|
if (Node.Index < 10) then {as an example - see VTInitChildren}
|
|
begin
|
|
iImage := 31;
|
|
InitialStates := InitialStates + [ivsHasChildren]; // <- important line here
|
|
end;
|
|
end;
|
|
Data.bnd := TBasicNodeAddData.Create( FDataset.Fields[FLDN_Company].AsString, FDataset.Fields[FLDN_CustNo].AsInteger, iImage );
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
|
|
begin
|
|
// here we only need to specify if, and how many, children this node has. This will then
|
|
// put a checkbox mark on the parentnode. Node OnInitNode will be called for each of the
|
|
// children later - maybe much later - when it actually needs to be displayed/accessed.
|
|
ChildCount := 1;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
|
var
|
|
Data: PBasicNodeRec;
|
|
begin
|
|
// Explicitely free memory, the VCL cannot know that there is one but needs to free it nonetheless.
|
|
// For more fields in such a record which must be freed use Finalize(Data^) instead touching
|
|
// every member individually.
|
|
Data := Sender.GetNodeData(Node);
|
|
Data.bnd.Free;
|
|
Finalize( Data^ );
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
|
Column: TColumnIndex; TextType: TVSTTextType; var Text: String);
|
|
var
|
|
Data : PBasicNodeRec;
|
|
bnd : TBasicNodeAddData;
|
|
iLevel : integer;
|
|
iCustNo : integer;
|
|
begin
|
|
// A handler for the OnGetText event is always needed to provide tree with the string data to display.
|
|
Text := '';
|
|
Data := Sender.GetNodeData(Node);
|
|
|
|
if Assigned(Data) and (Data.bnd <> nil) then with Data.bnd do
|
|
begin
|
|
iLevel := Sender.GetNodeLevel( Node );
|
|
|
|
case Column of
|
|
0: // main column + level 2 = address
|
|
begin
|
|
case TextType of
|
|
ttNormal:
|
|
begin
|
|
{must have been a good reason why level is not stored in TVirtualNode -
|
|
if 255 is an acceptable limit then maybe dummy could be used?}
|
|
if iLevel = 0 then
|
|
begin
|
|
if chkShowIDs.checked then
|
|
Text := Format( '%s [%d]', [Caption, ID] )
|
|
else
|
|
Text := Caption;
|
|
end
|
|
else {we need to look it up}
|
|
begin
|
|
if FDataset.Locate('CustNo',ID,[]) then with FDataset do
|
|
Text := Trim( Fields[ FLDN_Addr1 ].AsString + ' ' +
|
|
Fields[ FLDN_Addr2 ].AsString + ' ' +
|
|
Fields[ FLDN_City ].AsString + ' ' +
|
|
Fields[ FLDN_State ].AsString + ' ' +
|
|
Fields[ FLDN_Zip ].AsString + ' ' +
|
|
Fields[ FLDN_Country ].AsString );
|
|
//Text := bnd.Add1 + ', ' + bnd.Add2 + ', ' + bnd.Add3;
|
|
end;
|
|
|
|
end;
|
|
|
|
ttStatic:
|
|
begin
|
|
Text := '';
|
|
(*Text := Data.bnd.JobTitle;
|
|
if Text <> '' then
|
|
Text := '(' + Data.bnd.JobTitle + ')';
|
|
*)
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
1: // contact
|
|
begin
|
|
bnd := TBasicNodeAddData( Data.bnd );
|
|
case TextType of
|
|
ttNormal:
|
|
begin
|
|
if (iLevel = 0) and (FDataset.Locate('CustNo', ID, [] )) then with FDataset do
|
|
Text := Fields[ FLDN_Contact ].AsString;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
2: // status in position 0
|
|
begin
|
|
if TextType = ttNormal then
|
|
Text := ' ';
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTPaintText(Sender: TBaseVirtualTree;
|
|
const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
|
|
TextType: TVSTTextType);
|
|
var
|
|
Data: PBasicNodeRec;
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
case Column of
|
|
0: // main column
|
|
begin
|
|
case TextType of
|
|
ttNormal:
|
|
begin
|
|
if Sender.GetNodeLevel( Node ) > 0 then
|
|
TargetCanvas.Font.Color := clBlue;
|
|
end;
|
|
|
|
ttStatic:
|
|
begin
|
|
TargetCanvas.Font.Color := clBlue;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
|
|
Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
|
|
var
|
|
Data : PBasicNodeRec;
|
|
iLevel: integer;
|
|
begin
|
|
ImageIndex := -1;
|
|
Data := Sender.GetNodeData(Node);
|
|
iLevel:= Sender.GetNodeLevel( Node );
|
|
|
|
if (Column = 2 ) then
|
|
begin
|
|
if ( Kind in [ ikNormal, ikSelected ] ) and (iLevel=0) then // status in position 0
|
|
ImageIndex := 20
|
|
end
|
|
else if Assigned(Data) and (Data.bnd <> nil) and (Column = 0) then // main column
|
|
ImageIndex := Data.bnd.ImageIndex;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.chkShowIDsClick(Sender: TObject);
|
|
begin
|
|
VT.Refresh;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTHeaderClick(Sender: TVTHeader; Column: TColumnIndex;
|
|
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if (VT.Header.SortColumn <> Column) then
|
|
VT.Header.SortColumn := Column
|
|
else if (VT.Header.SortDirection = sdAscending) then
|
|
VT.Header.SortDirection := sdDescending
|
|
else
|
|
VT.Header.SortDirection := sdAscending;
|
|
|
|
VT.SortTree( Column, VT.Header.SortDirection );
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTCompareNodes(Sender: TBaseVirtualTree; Node1,
|
|
Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
|
|
var
|
|
Data1 : PBasicNodeRec;
|
|
Data2 : PBasicNodeRec;
|
|
begin
|
|
Data1 := Sender.GetNodeData(Node1);
|
|
Data2 := Sender.GetNodeData(Node2);
|
|
|
|
case Column of
|
|
0: Result := CompareText( Data1.bnd.Caption, Data2.bnd.Caption )
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTNewText(Sender: TBaseVirtualTree; Node: PVirtualNode;
|
|
Column: TColumnIndex; NewText: String);
|
|
var
|
|
Data : PBasicNodeRec;
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
case Column of
|
|
0: Data.bnd.Caption := Newtext;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrmVTDBExample.AddButtonClick(Sender: TObject);
|
|
var
|
|
i, Cnt: Cardinal;
|
|
Node : PVirtualNode;
|
|
Data : PBasicNodeRec;
|
|
begin
|
|
with VT do
|
|
begin
|
|
Cnt := StrToInt(ed.text);
|
|
case (Sender as TButton).Tag of
|
|
0: // add to root
|
|
begin
|
|
// other method is RootNodeCount := RootNodeCount + Count;
|
|
for i := 0 to Cnt-1 do
|
|
begin
|
|
Node := VT.AddChild(VT.RootNode); // adds a node as the last child
|
|
Data := VT.GetNodeData(Node);
|
|
Data.bnd := TBasicNodeAddData.Create('Root Child ' + IntToStr(i), 0, 0 );
|
|
end;
|
|
end;
|
|
|
|
1: // add as child
|
|
if Assigned(FocusedNode) then
|
|
begin
|
|
// other method is ChildCount[FocusedNode] := ChildCount[FocusedNode] + Count;
|
|
for i := 0 to Cnt-1 do
|
|
begin
|
|
Node := VT.AddChild(VT.FocusedNode); // adds a node as the last child
|
|
Data := VT.GetNodeData(Node);
|
|
Data.bnd := TBasicNodeAddData.Create('Child ' + IntToStr(i), 0, 0 );
|
|
end;
|
|
Expanded[FocusedNode] := True;
|
|
InvalidateToBottom(FocusedNode);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.FormCreate(Sender: TObject);
|
|
begin
|
|
FDataset:=TSqlite3Dataset.Create(Self);
|
|
end;
|
|
|
|
|
|
procedure TfrmVTDBExample.btnHasChildrenClick(Sender: TObject);
|
|
begin
|
|
VT.HasChildren[VT.focusedNode] := not VT.HasChildren[VT.focusedNode];
|
|
VT.InvalidateNode(VT.focusedNode);
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.btnToggleVisibilityClick(Sender: TObject);
|
|
begin
|
|
VT.IsVisible[VT.focusedNode] := not VT.IsVisible[VT.focusedNode];
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.chkAllVisibleClick(Sender: TObject);
|
|
begin
|
|
if chkAllVisible.Checked then
|
|
begin
|
|
VT.BeginUpdate;
|
|
try
|
|
VT.IterateSubtree( nil, HideNodes, nil );
|
|
finally
|
|
VT.EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.HideNodes(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean);
|
|
begin
|
|
Sender.IsVisible[Node] := True;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode;
|
|
OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
|
|
begin
|
|
if chkDynHt.checked then with TVirtualStringTree(Sender) do
|
|
begin
|
|
NodeHeight[OldNode] := DefaultNodeHeight;
|
|
NodeHeight[NewNode] := DefaultNodeHeight * 2;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.chkDynHtClick(Sender: TObject);
|
|
begin
|
|
{example of resetting dynamically changing node heights}
|
|
with VT do
|
|
begin
|
|
if not Assigned(FocusedNode) then
|
|
ShowMessage( 'You need to select a node first' )
|
|
else
|
|
begin
|
|
if chkDynHt.checked then
|
|
NodeHeight[FocusedNode] := DefaultNodeHeight * 2
|
|
else
|
|
NodeHeight[FocusedNode] := DefaultNodeHeight;
|
|
|
|
InvalidateNode(FocusedNode);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmVTDBExample.VTIncrementalSearch(Sender: TBaseVirtualTree;
|
|
Node: PVirtualNode; const SearchText: String; var Result: Integer);
|
|
|
|
function Min(const A, B: Integer): Integer; {save us linking in math.pas}
|
|
begin
|
|
if A < B then
|
|
Result := A
|
|
else
|
|
Result := B;
|
|
end;
|
|
|
|
var
|
|
sCompare1, sCompare2 : string;
|
|
DisplayText : String;
|
|
|
|
begin
|
|
VT.IncrementalSearchDirection := sdForward; // note can be backward
|
|
|
|
VTGetText( Sender, Node, 0 {Column}, ttNormal, DisplayText );
|
|
sCompare1 := SearchText;
|
|
sCompare2 := DisplayText;
|
|
|
|
// By using StrLIComp we can specify a maximum length to compare. This allows us to find also nodes
|
|
// which match only partially. Don't forget to specify the shorter string length as search length.
|
|
Result := StrLIComp( pchar(sCompare1), pchar(sCompare2), Min(Length(sCompare1), Length(sCompare2)) )
|
|
end;
|
|
|
|
initialization
|
|
{$I VTDBExample.lrs}
|
|
|
|
end.
|