mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-17 22:42:42 +02:00
754 lines
23 KiB
ObjectPascal
754 lines
23 KiB
ObjectPascal
unit DrawTreeDemo;
|
|
|
|
{$MODE Delphi}
|
|
|
|
// Virtual Treeview sample form demonstrating following features:
|
|
// - General use of TVirtualDrawTree.
|
|
// - Use of vertical node image alignment.
|
|
// - Effective use of node initialization on demand to load images.
|
|
// Written by Mike Lischke.
|
|
//
|
|
// Note: define the symbol "GraphicEx" if you have my GraphicEx library
|
|
// available (see http://www.delphi-gems.com) which allows to load
|
|
// more image formats into the application.
|
|
// Otherwise disable the conditional symbol to compile this demo.
|
|
|
|
|
|
interface
|
|
|
|
uses
|
|
{$ifdef Windows}
|
|
Windows,
|
|
{$endif}
|
|
Types, SysUtils, Classes,
|
|
LCLIntf, LCLType, LResources, ComCtrls, Graphics, Controls, Forms, Dialogs, StdCtrls,
|
|
Laz.VirtualTrees, shlobjext, delphicompat,
|
|
LazFileUtils;
|
|
|
|
type
|
|
TDrawTreeForm = class(TForm)
|
|
VDT1: TLazVirtualDrawTree;
|
|
Label7: TLabel;
|
|
SystemImages: TImageList;
|
|
Label1: TLabel;
|
|
TrackBar1: TTrackBar;
|
|
Label3: TLabel;
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure VDT1CompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex;
|
|
var Result: Integer);
|
|
procedure VDT1DrawHint(Sender: TBaseVirtualTree; Canvas: TCanvas; Node: PVirtualNode; R: TRect; Column: TColumnIndex);
|
|
procedure VDT1DrawNode(Sender: TBaseVirtualTree; const PaintInfo: TVTPaintInfo);
|
|
procedure VDT1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
|
procedure VDT1GetHintSize(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var R: TRect);
|
|
procedure VDT1GetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
|
|
var Ghosted: Boolean; var Index: Integer);
|
|
procedure VDT1GetNodeWidth(Sender: TBaseVirtualTree; Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
|
|
var NodeWidth: Integer);
|
|
procedure VDT1HeaderClick(Sender: TVTHeader; Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
procedure VDT1InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
|
|
procedure VDT1InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
|
|
var InitialStates: TVirtualNodeInitStates);
|
|
procedure TrackBar1Change(Sender: TObject);
|
|
procedure VDT1StateChange(Sender: TBaseVirtualTree; Enter, Leave: TVirtualTreeStates);
|
|
private
|
|
FThumbSize: Integer;
|
|
FExtensionsInitialized: Boolean;
|
|
FExtensionList: TStringList;
|
|
FDriveStrings: string;
|
|
function CanDisplay(const Name: String): Boolean;
|
|
function GetDriveString(Index: Integer): string;
|
|
function ReadAttributes(const Name: String): Cardinal;
|
|
procedure RescaleImage(Source, Target: TBitmap);
|
|
end;
|
|
|
|
var
|
|
DrawTreeForm: TDrawTreeForm;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
uses
|
|
States;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
type
|
|
// This data record contains all necessary information about a particular file system object.
|
|
// This can either be a folder (virtual or real) or an image file.
|
|
PShellObjectData = ^TShellObjectData;
|
|
TShellObjectData = record
|
|
FullPath,
|
|
Display: String;
|
|
Attributes: Cardinal;
|
|
OpenIndex,
|
|
CloseIndex: Integer; // image indices into the system image list
|
|
Image: TBitmap;
|
|
Properties: String; // some image properties, preformatted
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function HasChildren(const Folder: string): Boolean;
|
|
|
|
// Determines whether folder contains other file objects.
|
|
|
|
var
|
|
SR: TSearchRec;
|
|
|
|
begin
|
|
Result := FindFirstUTF8(IncludeTrailingPathDelimiter(Folder) + {$ifdef Windows}'*.*'{$else}'*'{$endif},
|
|
faAnyFile, SR) = 0;
|
|
if Result then
|
|
FindCloseUTF8(SR);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function GetIconIndex(Name: string; Flags: Cardinal): Integer;
|
|
|
|
// Returns the index of the system icon for the given file object.
|
|
{
|
|
var
|
|
SFI: TSHFileInfo;
|
|
}
|
|
begin
|
|
Result := -1;
|
|
//todo
|
|
{
|
|
if SHGetFileInfo(PChar(Name), 0, SFI, SizeOf(TSHFileInfo), Flags) = 0 then
|
|
Result := -1
|
|
else
|
|
Result := SFI.iIcon;
|
|
}
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure GetOpenAndClosedIcons(Name: string; var Open, Closed: Integer);
|
|
|
|
begin
|
|
//todo
|
|
Closed := 0;
|
|
Open := 0;
|
|
{
|
|
Closed := GetIconIndex(Name, SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
|
|
Open := GetIconIndex(Name, SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
|
|
}
|
|
end;
|
|
|
|
//----------------- TDrawTreeForm --------------------------------------------------------------------------------------
|
|
|
|
procedure GetLogicalDrivesInfo(var DriveStrings: String; var DriveCount: Integer);
|
|
var
|
|
I,
|
|
BufferSize,
|
|
DriveMap,
|
|
Mask: Cardinal;
|
|
|
|
begin
|
|
{$ifdef Windows}
|
|
DriveCount := 0;
|
|
DriveMap := GetLogicalDrives;
|
|
Mask := 1;
|
|
for I := 0 to 25 do
|
|
begin
|
|
if (DriveMap and Mask) <> 0 then
|
|
Inc(DriveCount);
|
|
Mask := Mask shl 1;
|
|
end;
|
|
BufferSize := GetLogicalDriveStrings(0, nil);
|
|
SetLength(DriveStrings, BufferSize);
|
|
GetLogicalDriveStrings(BufferSize, PChar(DriveStrings));
|
|
{$else}
|
|
DriveCount := 1;
|
|
DriveStrings := '/';
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TDrawTreeForm.FormCreate(Sender: TObject);
|
|
|
|
var
|
|
//SFI: TSHFileInfo;
|
|
I,
|
|
Count: Integer;
|
|
|
|
begin
|
|
VDT1.NodeDataSize := SizeOf(TShellObjectData);
|
|
GetLogicalDrivesInfo(FDriveStrings,Count);
|
|
VDT1.RootNodeCount := Count;
|
|
|
|
//todo
|
|
{
|
|
SystemImages.Handle := SHGetFileInfo('', 0, SFI, SizeOf(SFI), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
|
|
SystemImages.ShareImages := True;
|
|
}
|
|
|
|
FThumbSize := 200;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TDrawTreeForm.CanDisplay(const Name: string): Boolean;
|
|
|
|
// Determines whether the given file is one we can display in the image tree.
|
|
|
|
var
|
|
Ext: string;
|
|
I: Integer;
|
|
|
|
begin
|
|
if not FExtensionsInitialized then
|
|
begin
|
|
FExtensionsInitialized := True;
|
|
FExtensionList := TStringList.Create;
|
|
FExtensionList.Sorted := True;
|
|
{$ifdef GraphicEx}
|
|
FileFormatList.GetExtensionList(FExtensionList);
|
|
for I := 0 to FExtensionList.Count - 1 do
|
|
FExtensionList[I] := '.' + FExtensionList[I];
|
|
{$else}
|
|
// GraphicEx is not used so add some default extensions
|
|
with FExtensionList do
|
|
begin
|
|
Add('.bmp');
|
|
Add('.ico');
|
|
Add('.jpg');
|
|
Add('.jpeg');
|
|
//Add('.wmf');
|
|
//Add('.emf');
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
Ext := ExtractFileExt(Name);
|
|
Result := FExtensionList.Find(Ext, I);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TDrawTreeForm.GetDriveString(Index: Integer): string;
|
|
|
|
// Helper method to extract a sub string (given by Index) from FDriveStrings.
|
|
|
|
var
|
|
Head, Tail: PChar;
|
|
|
|
begin
|
|
Head := PChar(FDriveStrings);
|
|
Result := '';
|
|
repeat
|
|
Tail := Head;
|
|
while Tail^ <> #0 do
|
|
Inc(Tail);
|
|
if Index = 0 then
|
|
begin
|
|
SetString(Result, Head, Tail - Head);
|
|
Break;
|
|
end;
|
|
Dec(Index);
|
|
Head := Tail + 1;
|
|
until Head^ = #0;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
function TDrawTreeForm.ReadAttributes(const Name: String): Cardinal;
|
|
|
|
// Determines the attributes of the given shell object (file, folder).
|
|
|
|
const
|
|
SFGAO_CONTENTSMASK = $F0000000; // This value is wrongly defined in ShlObj.
|
|
|
|
//var
|
|
//Desktop: IShellFolder;
|
|
{
|
|
Eaten: Cardinal;
|
|
PIDL: PItemIDList;
|
|
Malloc: IMalloc;
|
|
}
|
|
begin
|
|
Result := 0;
|
|
//todo
|
|
{
|
|
// Get the root folder of the shell name space.
|
|
SHGetDesktopFolder(Desktop);
|
|
// While parsing the name also the shell object's attributes are determined.
|
|
// These is what we are really interested in.
|
|
Result := SFGAO_DISPLAYATTRMASK or SFGAO_CONTENTSMASK or SFGAO_COMPRESSED;
|
|
Desktop.ParseDisplayName(0, nil, PWideChar(Name), Eaten, PIDL, Result);
|
|
// Don't forget to free the returned PIDL. The shell folder is released automatically.
|
|
SHGetMalloc(Malloc);
|
|
Malloc.Free(PIDL);
|
|
}
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.RescaleImage(Source, Target: TBitmap);
|
|
|
|
// if source is in at least one dimension larger than the thumb size then
|
|
// rescale source but keep aspect ratio
|
|
|
|
var
|
|
NewWidth,
|
|
NewHeight: Integer;
|
|
begin
|
|
if (Source.Width > FThumbSize) or (Source.Height > FThumbSize) then
|
|
begin
|
|
if Source.Width > Source.Height then
|
|
begin
|
|
NewWidth := FThumbSize;
|
|
NewHeight := Round(FThumbSize * Source.Height / Source.Width);
|
|
end
|
|
else
|
|
begin
|
|
NewHeight := FThumbSize;
|
|
NewWidth := Round(FThumbSize * Source.Width / Source.Height);
|
|
end;
|
|
|
|
Target.Width := NewWidth;
|
|
Target.Height := NewHeight;
|
|
SetStretchBltMode(Target.Canvas.Handle, HALFTONE);
|
|
StretchBlt(Target.Canvas.Handle, 0, 0, NewWidth, NewHeight,
|
|
Source.Canvas.Handle, 0, 0, Source.Width, Source.Height, SRCCOPY);
|
|
end
|
|
else
|
|
Target.Assign(Source);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
|
|
var InitialStates: TVirtualNodeInitStates);
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
Picture: TPicture;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
if ParentNode = nil then
|
|
begin
|
|
// top level node, initialize first enumeration
|
|
Data.FullPath := GetDriveString(Node.Index);
|
|
Data.Display := Data.FullPath;
|
|
GetOpenAndClosedIcons(Data.FullPath, Data.OpenIndex, Data.CloseIndex);
|
|
end
|
|
else
|
|
begin
|
|
Picture := TPicture.Create;
|
|
Data.Display := ExtractFileName(ExcludeTrailingPathDelimiter(Data.FullPath));
|
|
if (Data.Attributes and SFGAO_FOLDER) = 0 then
|
|
try
|
|
try
|
|
Data.Image := TBitmap.Create;
|
|
Picture.LoadFromFile(Data.FullPath);
|
|
if not (Picture.Graphic is TBitmap) then
|
|
begin
|
|
// Some extra steps needed to keep non TBitmap descentants alive when
|
|
// scaling. This is needed because when accessing Picture.Bitmap all
|
|
// non-TBitmap content will simply be erased (definitly the wrong
|
|
// action, but we can't do anything to prevent this). Hence we
|
|
// must explicitly draw the graphic to a bitmap.
|
|
with Data.Image do
|
|
begin
|
|
Width := Picture.Width;
|
|
Height := Picture.Height;
|
|
Canvas.Draw(0, 0, Picture.Graphic);
|
|
end;
|
|
Picture.Bitmap.Assign(Data.Image);
|
|
end;
|
|
RescaleImage(Picture.Bitmap, Data.Image);
|
|
|
|
// Collect some additional image properties.
|
|
Data.Properties := Data.Properties + Format('%d x %d pixels', [Picture.Width, Picture.Height]);
|
|
case Picture.Bitmap.PixelFormat of
|
|
pf1bit:
|
|
Data.Properties := Data.Properties + ', 2 colors';
|
|
pf4bit:
|
|
Data.Properties := Data.Properties + ', 16 colors';
|
|
pf8bit:
|
|
Data.Properties := Data.Properties + ', 256 colors';
|
|
pf15bit:
|
|
Data.Properties := Data.Properties + ', 32K colors';
|
|
pf16bit:
|
|
Data.Properties := Data.Properties + ', 64K colors';
|
|
pf24bit:
|
|
Data.Properties := Data.Properties + ', 16M colors';
|
|
pf32bit:
|
|
Data.Properties := Data.Properties + ', 16M+ colors';
|
|
end;
|
|
if Cardinal(Data.Image.Height) + 4 > TLazVirtualDrawTree(Sender).DefaultNodeHeight then
|
|
Sender.NodeHeight[Node] := Data.Image.Height + 4;
|
|
except
|
|
Data.Image.Free;
|
|
Data.Image := nil;
|
|
end;
|
|
finally
|
|
Picture.Free;
|
|
end;
|
|
end;
|
|
//todo
|
|
|
|
//Data.Attributes := ReadAttributes(Data.FullPath);
|
|
//if ((Data.Attributes and SFGAO_HASSUBFOLDER) <> 0) or
|
|
// (((Data.Attributes and SFGAO_FOLDER) <> 0) and HasChildren(Data.FullPath)) then
|
|
if HasChildren(Data.FullPath) then
|
|
Include(InitialStates, ivsHasChildren);
|
|
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1FreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
Data.Image.Free;
|
|
Finalize(Data^); // Clear string data.
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1DrawNode(Sender: TBaseVirtualTree; const PaintInfo: TVTPaintInfo);
|
|
|
|
// This is the main paint routine for a node in a draw tree. There is nothing special here. Demonstrating the
|
|
// specific features of a draw tree (compared to the string tree) is a bit difficult, since the only difference is
|
|
// that the draw tree does not handle node content (captions in the case of the string tree).
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
X: Integer;
|
|
S: String;
|
|
R: TRect;
|
|
|
|
begin
|
|
with Sender as TLazVirtualDrawTree, PaintInfo do
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
if (Column = FocusedColumn) and (Node = FocusedNode) then
|
|
Canvas.Font.Color := clHighlightText
|
|
else
|
|
if (Data.Attributes and SFGAO_COMPRESSED) <> 0 then
|
|
Canvas.Font.Color := clBlue
|
|
else
|
|
Canvas.Font.Color := clWindowText;
|
|
|
|
SetBKMode(Canvas.Handle, TRANSPARENT);
|
|
|
|
R := ContentRect;
|
|
InflateRect(R, -TextMargin, 0);
|
|
Dec(R.Right);
|
|
Dec(R.Bottom);
|
|
S := '';
|
|
case Column of
|
|
0, 2:
|
|
begin
|
|
if Column = 2 then
|
|
begin
|
|
if Assigned(Data.Image) then
|
|
S:= Data.Properties;
|
|
end
|
|
else
|
|
S := Data.Display;
|
|
if Length(S) > 0 then
|
|
begin
|
|
with R do
|
|
begin
|
|
if (NodeWidth - 2 * Margin) > (Right - Left) then
|
|
S := ShortenString(Canvas.Handle, S, Right - Left);
|
|
end;
|
|
DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_TOP or DT_LEFT or DT_VCENTER or DT_SINGLELINE);
|
|
end;
|
|
end;
|
|
1:
|
|
begin
|
|
if Assigned(Data.Image) then
|
|
begin
|
|
X := ContentRect.Left + (VDT1.Header.Columns[1].Width - Data.Image.Width - Margin) div 2;
|
|
BitBlt(Canvas.Handle, X, ContentRect.Top + 2, Data.Image.Width, Data.Image.Height, Data.Image.Canvas.Handle,
|
|
0, 0, SRCCOPY);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1GetNodeWidth(Sender: TBaseVirtualTree; Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
|
|
var NodeWidth: Integer);
|
|
|
|
// Since the draw tree does not know what is in a cell, we have to return the width of the content (not the entire
|
|
// cell width, this could be determined by the column width).
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
AMargin: Integer;
|
|
|
|
begin
|
|
with Sender as TLazVirtualDrawTree do
|
|
AMargin := TextMargin;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
case Column of
|
|
0:
|
|
begin
|
|
if Node.Parent = Sender.RootNode then
|
|
NodeWidth := Canvas.TextWidth(Data.FullPath) + 2 * AMargin
|
|
else
|
|
NodeWidth := Canvas.TextWidth(ExtractFileName(Data.FullPath)) + 2 * AMargin;
|
|
end;
|
|
1:
|
|
begin
|
|
if Assigned(Data.Image) then
|
|
NodeWidth := Data.Image.Width;
|
|
end;
|
|
2:
|
|
NodeWidth := Canvas.TextWidth(Data.Properties) + 2 * AMargin;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal);
|
|
|
|
// Called just before a node with children (only folder nodes can have children) is expanded.
|
|
|
|
var
|
|
Data,
|
|
ChildData: PShellObjectData;
|
|
SR: TSearchRec;
|
|
ChildNode: PVirtualNode;
|
|
NewName: String;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
if FindFirstUTF8(IncludeTrailingPathDelimiter(Data.FullPath) + {$ifdef Windows}'*.*'{$else}'*'{$endif},
|
|
faAnyFile, SR) = 0 then
|
|
begin
|
|
Screen.Cursor := crHourGlass;
|
|
try
|
|
repeat
|
|
if (SR.Name <> '.') and (SR.Name <> '..') then
|
|
begin
|
|
NewName := IncludeTrailingPathDelimiter(Data.FullPath) + SR.Name;
|
|
if (SR.Attr and faDirectory <> 0) or CanDisplay(NewName) then
|
|
begin
|
|
ChildNode := Sender.AddChild(Node);
|
|
ChildData := Sender.GetNodeData(ChildNode);
|
|
ChildData.FullPath := NewName;
|
|
ChildData.Attributes := 0; //ReadAttributes(NewName);
|
|
//if (ChildData.Attributes and SFGAO_FOLDER) = 0 then
|
|
if (SR.Attr and faDirectory = 0) then
|
|
ChildData.Properties := Format('%n KB, ', [SR.Size / 1024])
|
|
else
|
|
ChildData.Attributes := SFGAO_FOLDER;
|
|
GetOpenAndClosedIcons(ChildData.FullPath, ChildData.OpenIndex, ChildData.CloseIndex);
|
|
|
|
Sender.ValidateNode(Node, False);
|
|
end;
|
|
end;
|
|
until FindNextUTF8(SR) <> 0;
|
|
ChildCount := Sender.ChildCount[Node];
|
|
|
|
// finally sort node
|
|
if ChildCount > 0 then
|
|
Sender.Sort(Node, 0, TLazVirtualStringTree(Sender).Header.SortDirection, False);
|
|
finally
|
|
FindCloseUTF8(SR);
|
|
Screen.Cursor := crDefault;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1GetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
|
|
Column: TColumnIndex; var Ghosted: Boolean; var Index: Integer);
|
|
|
|
// Returns the proper node image which has been determine on initialization time. Also overlay images are
|
|
// used properly for shared folders.
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
|
|
begin
|
|
if Column = 0 then
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
case Kind of
|
|
ikNormal,
|
|
ikSelected:
|
|
begin
|
|
if Sender.Expanded[Node] then
|
|
Index := Data.OpenIndex
|
|
else
|
|
Index := Data.CloseIndex;
|
|
end;
|
|
ikOverlay:
|
|
if (Data.Attributes and SFGAO_SHARE) <> 0 then
|
|
Index := 0
|
|
else
|
|
if (Data.Attributes and SFGAO_LINK) <> 0 then
|
|
Index := 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1GetHintSize(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var R: TRect);
|
|
|
|
// Draw trees must manage parts of the hints themselves. Here we return the size of the hint window we want to show
|
|
// or an empty rectangle in the case we don't want a hint at all.
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
if Assigned(Data) and Assigned(Data.Image) and (Column = 1) then
|
|
R := Rect(0, 0, 2 * Data.Image.Width, 2 * Data.Image.Height)
|
|
else
|
|
R := Rect(0, 0, 0, 0);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1DrawHint(Sender: TBaseVirtualTree; Canvas: TCanvas; Node: PVirtualNode; R: TRect;
|
|
Column: TColumnIndex);
|
|
|
|
// Here we actually paint the hint. It is the image in a larger size.
|
|
|
|
var
|
|
Data: PShellObjectData;
|
|
|
|
begin
|
|
Data := Sender.GetNodeData(Node);
|
|
if Assigned(Data) and Assigned(Data.Image) and (Column = 1) then
|
|
begin
|
|
SetStretchBltMode(Canvas.Handle, HALFTONE);
|
|
StretchBlt(Canvas.Handle, 0, 0, 2 * Data.Image.Width, 2 * Data.Image.Height, Data.Image.Canvas.Handle, 0, 0,
|
|
Data.Image.Width, Data.Image.Height, SRCCOPY);
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1CompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex;
|
|
var Result: Integer);
|
|
|
|
// The node comparison routine is the heart of the tree sort. Here we have to tell the caller which node we consider
|
|
// being "larger" or "smaller".
|
|
|
|
var
|
|
Data1,
|
|
Data2: PShellObjectData;
|
|
|
|
begin
|
|
Data1 := Sender.GetNodeData(Node1);
|
|
Data2 := Sender.GetNodeData(Node2);
|
|
|
|
// Folder are always before files. Check if *both* are folders or *both* are non-folders, but not different.
|
|
if ((Data1.Attributes xor Data2.Attributes) and SFGAO_FOLDER) <> 0 then
|
|
begin
|
|
// One of both is a folder the other is a file.
|
|
if (Data1.Attributes and SFGAO_FOLDER) <> 0 then
|
|
Result := -1
|
|
else
|
|
Result := 1;
|
|
end
|
|
else
|
|
// Both are of same type (folder or file). Just compare captions.
|
|
// Note that we use ANSI comparison, while the strings are Unicode. Since this will implicitely convert the captions
|
|
// to ANSI for comparation it might happen that the sort order is wrong for names which contain text in a language
|
|
// other than the current OS language. A full blown Unicode comparison is beyond the scope of this demo.
|
|
Result := CompareText(Data1.FullPath, Data2.FullPath);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1HeaderClick(Sender: TVTHeader; Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
|
|
// Click handler to switch the column on which will be sorted. Since we cannot sort image data sorting is actually
|
|
// limited to the main column.
|
|
|
|
begin
|
|
if Button = mbLeft then
|
|
begin
|
|
with Sender do
|
|
begin
|
|
if Column <> MainColumn then
|
|
SortColumn := NoColumn
|
|
else
|
|
begin
|
|
if SortColumn = NoColumn then
|
|
begin
|
|
SortColumn := Column;
|
|
SortDirection := sdAscending;
|
|
end
|
|
else
|
|
if SortDirection = sdAscending then
|
|
SortDirection := sdDescending
|
|
else
|
|
SortDirection := sdAscending;
|
|
Treeview.SortTree(SortColumn, SortDirection, False);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.TrackBar1Change(Sender: TObject);
|
|
|
|
// This part has nothing to do with the tree content and is only to show the effect of vertical image alignment for nodes
|
|
// (since this does not justify an own demo).
|
|
// Btw: look how fast this stuff is. Even with several thousands of nodes you still can adjust the position interactively.
|
|
|
|
var
|
|
Run: PVirtualNode;
|
|
|
|
begin
|
|
Label3.Caption := Format('%d%%', [Trackbar1.Position]);
|
|
with VDT1, Trackbar1 do
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Run := GetFirst;
|
|
while Assigned(Run) do
|
|
begin
|
|
VerticalAlignment[Run] := Position;
|
|
Run := GetNextVisible(Run);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
procedure TDrawTreeForm.VDT1StateChange(Sender: TBaseVirtualTree; Enter, Leave: TVirtualTreeStates);
|
|
|
|
begin
|
|
if not (csDestroying in ComponentState) then
|
|
UpdateStateDisplay(Sender.TreeStates, Enter, Leave);
|
|
end;
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
end.
|