452 lines
12 KiB
ObjectPascal
452 lines
12 KiB
ObjectPascal
|
|
||
|
{******************************************}
|
||
|
{ }
|
||
|
{ FastReport VCL }
|
||
|
{ Report Tree }
|
||
|
{ }
|
||
|
{ Copyright (c) 1998-2021 }
|
||
|
{ by Fast Reports Inc. }
|
||
|
{ }
|
||
|
{******************************************}
|
||
|
|
||
|
unit frxReportTree;
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$I frx.inc}
|
||
|
|
||
|
uses
|
||
|
{$IFNDEF FPC}
|
||
|
Windows, Messages,
|
||
|
{$ENDIF}
|
||
|
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
|
||
|
ComCtrls, frxClass, frxBaseForm, Types, frxComCtrls, frxDock
|
||
|
{$IFDEF FPC}
|
||
|
, LCLType
|
||
|
{$ENDIF}
|
||
|
{$IFDEF Delphi6}
|
||
|
, Variants
|
||
|
{$ENDIF};
|
||
|
|
||
|
|
||
|
type
|
||
|
TfrxReportTreeForm = class(TfrxDockForm)
|
||
|
procedure FormShow(Sender: TObject);
|
||
|
procedure TreeChange(Sender: TObject; Node: TTreeNode);
|
||
|
procedure FormCreate(Sender: TObject);
|
||
|
procedure TreeKeyDown(Sender: TObject; var Key: Word;
|
||
|
Shift: TShiftState);
|
||
|
procedure TreeDragOver(Sender, Source: TObject; X, Y: Integer;
|
||
|
State: TDragState; var Accept: Boolean);
|
||
|
procedure TreeDragDrop(Sender, Source: TObject; X, Y: Integer);
|
||
|
procedure TreeMouseDown(Sender: TObject; Button: TMouseButton;
|
||
|
Shift: TShiftState; X, Y: Integer);
|
||
|
private
|
||
|
FTreePanel: TfrxTreePanel;
|
||
|
FTree: TfrxTreeView;
|
||
|
FComponents: TList;
|
||
|
FDesigner: TfrxCustomDesigner;
|
||
|
FNodes: TList;
|
||
|
FReport: TfrxReport;
|
||
|
FUpdating: Boolean;
|
||
|
FOnSelectionChanged: TNotifyEvent;
|
||
|
FSelectedNodeList: TList;
|
||
|
public
|
||
|
constructor Create(AOwner: TComponent); override;
|
||
|
destructor Destroy; override;
|
||
|
procedure LoadFormPreferences(PreferencesStorage: TObject; DefPreferencesStorage: TObject); override;
|
||
|
procedure SaveFormPreferences(PreferencesStorage: TObject; DefPreferencesStorage: TObject); override;
|
||
|
procedure SetColor_(Value: TColor);
|
||
|
procedure UpdateItems;
|
||
|
procedure UpdateSelection;
|
||
|
procedure UpdateFormPPI(aNewPPI: Integer); override;
|
||
|
property OnSelectionChanged: TNotifyEvent read FOnSelectionChanged
|
||
|
write FOnSelectionChanged;
|
||
|
end;
|
||
|
|
||
|
|
||
|
implementation
|
||
|
|
||
|
{$IFDEF FPC}
|
||
|
{$R *.lfm}
|
||
|
{$ELSE}
|
||
|
{$R *.DFM}
|
||
|
{$ENDIF}
|
||
|
|
||
|
uses frxRes, frxDesgn, frxDsgnIntf{$IFDEF FPC}, ExtCtrls{$ENDIF}, IniFiles;
|
||
|
|
||
|
type
|
||
|
THackWinControl = class(TWinControl);
|
||
|
|
||
|
|
||
|
{ TfrxReportTreeForm }
|
||
|
|
||
|
constructor TfrxReportTreeForm.Create(AOwner: TComponent);
|
||
|
begin
|
||
|
inherited;
|
||
|
FComponents := TList.Create;
|
||
|
FNodes := TList.Create;
|
||
|
FTreePanel := TfrxTreePanel.Create(Self);
|
||
|
FTreePanel.Parent := Self;
|
||
|
FTreePanel.Align := alClient;
|
||
|
FTreePanel.BorderStyle := bsNone;
|
||
|
FTreePanel.BorderWidth := 0;
|
||
|
{$IFNDEF FPC}
|
||
|
FTreePanel.BevelKind := bkFlat;
|
||
|
FTreePanel.BevelWidth := 1;
|
||
|
FTreePanel.ToolPanel.BevelKind := bkNone;
|
||
|
{$ENDIF}
|
||
|
FTreePanel.AutoSize := False;
|
||
|
FTreePanel.ToolPanel.AutoSize := False;
|
||
|
FTreePanel.ToolPanel.BorderStyle := bsNone;
|
||
|
FTreePanel.ToolPanel.ImageList := frxResources.MainButtonImages;
|
||
|
FTreePanel.FilterActiveImageIndex := 121;
|
||
|
FTreePanel.FilterUnactiveImageIndex := 122;
|
||
|
FTreePanel.TreeView.DrawExpandButton := True;
|
||
|
FTreePanel.FilterAlign := fraWidth;
|
||
|
FTreePanel.ToolPanelAlign := ftaBottom;
|
||
|
FTreePanel.ToolVisible := False;
|
||
|
|
||
|
FTree := FTreePanel.TreeView;
|
||
|
FTree.Align := alClient;
|
||
|
FTree.TabOrder := 0;
|
||
|
FTree.BorderStyle := bsNone;
|
||
|
FTree.DragMode := dmAutomatic;
|
||
|
FTree.ShowRoot := False;
|
||
|
FTree.ReadOnly := True;
|
||
|
FTree.HideSelection := False;
|
||
|
FTree.MultiSelect := True;
|
||
|
FTree.MultiSelectStyle := [msControlSelect, msVisibleOnly];
|
||
|
FTree.OnChange := TreeChange;
|
||
|
FTree.OnDragDrop := TreeDragDrop;
|
||
|
FTree.OnDragOver := TreeDragOver;
|
||
|
FTree.OnKeyDown := TreeKeyDown;
|
||
|
FTree.OnMouseDown := TreeMouseDown;
|
||
|
{$IFDEF UseTabset}
|
||
|
{$IFNDEF FPC}
|
||
|
FTree.BevelKind := bkNone;
|
||
|
{$ENDIF}
|
||
|
{$ELSE}
|
||
|
FTree.BorderStyle := bsSingle;
|
||
|
{$ENDIF}
|
||
|
FSelectedNodeList := TList.Create;
|
||
|
end;
|
||
|
|
||
|
destructor TfrxReportTreeForm.Destroy;
|
||
|
begin
|
||
|
FComponents.Free;
|
||
|
FNodes.Free;
|
||
|
FSelectedNodeList.Free;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.FormShow(Sender: TObject);
|
||
|
begin
|
||
|
UpdateItems;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.LoadFormPreferences(PreferencesStorage,
|
||
|
DefPreferencesStorage: TObject);
|
||
|
var
|
||
|
Ini: TCustomIniFile;
|
||
|
begin
|
||
|
inherited;
|
||
|
if not(PreferencesStorage is TCustomIniFile) then Exit;
|
||
|
Ini := TCustomIniFile(PreferencesStorage);
|
||
|
FTreePanel.ToolVisible := Ini.ReadBool(GetFormSectionName, 'ToolVisible', FTreePanel.ToolVisible);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.UpdateFormPPI(aNewPPI: Integer);
|
||
|
begin
|
||
|
inherited;
|
||
|
FTree.Images := frxResources.ObjectImages;
|
||
|
FTreePanel.ToolImageList := frxResources.MainButtonImages;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.UpdateItems;
|
||
|
|
||
|
procedure SetImageIndex(Node: TTreeNode; Index: Integer);
|
||
|
begin
|
||
|
Node.ImageIndex := Index;
|
||
|
Node.StateIndex := Index;
|
||
|
Node.SelectedIndex := Index;
|
||
|
end;
|
||
|
|
||
|
procedure EnumItems(c: TfrxComponent; RootNode: TTreeNode);
|
||
|
var
|
||
|
i: Integer;
|
||
|
Node: TTreeNode;
|
||
|
Item: TfrxObjectItem;
|
||
|
begin
|
||
|
if (c is TfrxDataPage) and (frxDesignerComp <> nil) and
|
||
|
(drDontEditInternalDatasets in frxDesignerComp.Restrictions) then
|
||
|
Exit;
|
||
|
Node := FTree.Items.AddChild(RootNode, c.Name);
|
||
|
FComponents.Add(c);
|
||
|
FNodes.Add(Node);
|
||
|
Node.Data := c;
|
||
|
if c is TfrxReport then
|
||
|
begin
|
||
|
Node.Text := 'Report';
|
||
|
SetImageIndex(Node, 34);
|
||
|
end
|
||
|
else if c is TfrxReportPage then
|
||
|
SetImageIndex(Node, 35)
|
||
|
else if c is TfrxDialogPage then
|
||
|
SetImageIndex(Node, 36)
|
||
|
else if c is TfrxDataPage then
|
||
|
SetImageIndex(Node, 37)
|
||
|
else if c is TfrxBand then
|
||
|
SetImageIndex(Node, 40)
|
||
|
else
|
||
|
begin
|
||
|
for i := 0 to frxObjects.Count - 1 do
|
||
|
begin
|
||
|
Item := frxObjects[i];
|
||
|
if Item.ClassRef = c.ClassType then
|
||
|
begin
|
||
|
SetImageIndex(Node, Item.ButtonImageIndex);
|
||
|
break;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
if c is TfrxDataPage then
|
||
|
begin
|
||
|
for i := 0 to c.Objects.Count - 1 do
|
||
|
if TObject(c.Objects[i]) is TfrxDialogComponent then
|
||
|
EnumItems(TfrxComponent(c.Objects[i]), Node)
|
||
|
end
|
||
|
else
|
||
|
for i := 0 to c.Objects.Count - 1 do
|
||
|
EnumItems(TfrxComponent(c.Objects[i]), Node);
|
||
|
end;
|
||
|
|
||
|
begin
|
||
|
FTree.Items.BeginUpdate;
|
||
|
FTree.Items.Clear;
|
||
|
FComponents.Clear;
|
||
|
FNodes.Clear;
|
||
|
EnumItems(FReport, nil);
|
||
|
FTree.FullExpand;
|
||
|
UpdateSelection;
|
||
|
FTree.Items.EndUpdate;
|
||
|
FTree.UpdateFilter;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.TreeChange(Sender: TObject; Node: TTreeNode);
|
||
|
var
|
||
|
i: Integer;
|
||
|
begin
|
||
|
{$IFDEF LCLGTK2}
|
||
|
if Assigned(FReport.Designer) then
|
||
|
if TfrxDesignerForm(FReport.Designer).LockSelectionChanged then
|
||
|
begin
|
||
|
TfrxDesignerForm(FReport.Designer).LockSelectionChanged := False;
|
||
|
Exit;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
if FUpdating then Exit;
|
||
|
FDesigner.SelectedObjects.Clear;
|
||
|
FSelectedNodeList.Clear;
|
||
|
if (FTree.Selected <> nil) and (FTree.Selected.Data <> nil) then
|
||
|
begin
|
||
|
if FTree.SelectionCount <= 1 then
|
||
|
begin
|
||
|
FDesigner.SelectedObjects.Add(FTree.Selected.Data);
|
||
|
FSelectedNodeList.Add(FTree.Selected);
|
||
|
end
|
||
|
else
|
||
|
begin
|
||
|
for i := 0 to FTree.SelectionCount - 1 do
|
||
|
begin
|
||
|
FDesigner.SelectedObjects.Add(FTree.Selections[i].Data);
|
||
|
FSelectedNodeList.Add(FTree.Selections[i]);
|
||
|
end;
|
||
|
end;
|
||
|
if Assigned(FOnSelectionChanged) then
|
||
|
FOnSelectionChanged(Self);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.TreeDragDrop(Sender, Source: TObject; X,
|
||
|
Y: Integer);
|
||
|
var
|
||
|
Node, SelectedNode: TTreeNode;
|
||
|
i: Integer;
|
||
|
c: TfrxReportComponent;
|
||
|
Modified: Boolean;
|
||
|
begin
|
||
|
Modified := False;
|
||
|
if Source = FTree then
|
||
|
begin
|
||
|
Node := FTree.GetNodeAt(X, Y);
|
||
|
if Node <> nil then
|
||
|
begin
|
||
|
if (TObject(Node.Data) is TfrxBand) or (TObject(Node.Data) is TfrxReportPage) then
|
||
|
begin
|
||
|
FTree.Items.BeginUpdate;
|
||
|
for i := 0 to FSelectedNodeList.Count - 1 do
|
||
|
begin
|
||
|
SelectedNode := TTreeNode(FSelectedNodeList[i]);
|
||
|
if (TObject(SelectedNode.Data) is TfrxBand) and (SelectedNode <> Node) then
|
||
|
begin
|
||
|
while SelectedNode.Count <> 0 do
|
||
|
if (TObject(SelectedNode.{$IFDEF FPC}Items{$ELSE}Item{$ENDIF}[0].Data) is TfrxReportComponent) and not (TObject(SelectedNode.{$IFDEF FPC}Items{$ELSE}Item{$ENDIF}[0].Data) is TfrxDialogComponent) then
|
||
|
begin
|
||
|
TfrxReportComponent(SelectedNode.{$IFDEF FPC}Items{$ELSE}Item{$ENDIF}[0].Data).Parent := TfrxReportComponent(Node.Data);
|
||
|
SelectedNode.{$IFDEF FPC}Items{$ELSE}Item{$ENDIF}[0].MoveTo(Node, naAddChild);
|
||
|
Modified := True;
|
||
|
end;
|
||
|
end
|
||
|
else if (TObject(SelectedNode.Data) is TfrxReportComponent) then
|
||
|
begin
|
||
|
c := TObject(SelectedNode.Data) as TfrxReportComponent;
|
||
|
if (c <> nil) and not (c is TfrxDialogComponent) and not(csContained in c.frComponentStyle) then
|
||
|
begin
|
||
|
SelectedNode.MoveTo(Node, naAddChild);
|
||
|
if c <> Node.Data then
|
||
|
c.Parent := TfrxReportComponent(Node.Data);
|
||
|
Modified := True;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
FTree.Items.EndUpdate;
|
||
|
// for any case, to avoid TreeChange call
|
||
|
FUpdating := True;
|
||
|
{$IFDEF FPC}
|
||
|
//ApplyStoredSelection
|
||
|
{$ELSE}
|
||
|
FTree.Select(FSelectedNodeList);
|
||
|
{$ENDIF}
|
||
|
FUpdating := False;
|
||
|
if Assigned(FOnSelectionChanged) then
|
||
|
FOnSelectionChanged(Self);
|
||
|
if Modified then
|
||
|
TfrxDesignerForm(FDesigner).Modified := True;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.TreeDragOver(Sender, Source: TObject; X,
|
||
|
Y: Integer; State: TDragState; var Accept: Boolean);
|
||
|
var
|
||
|
Node: TTreeNode;
|
||
|
begin
|
||
|
Node := FTree.GetNodeAt(X, Y);
|
||
|
if Node <> nil then
|
||
|
Accept := (TObject(Node.Data) is TfrxBand) or (TObject(Node.Data) is TfrxReportPage);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.SaveFormPreferences(PreferencesStorage,
|
||
|
DefPreferencesStorage: TObject);
|
||
|
var
|
||
|
Ini: TCustomIniFile;
|
||
|
begin
|
||
|
inherited;
|
||
|
if not(PreferencesStorage is TCustomIniFile) then Exit;
|
||
|
Ini := TCustomIniFile(PreferencesStorage);
|
||
|
Ini.WriteBool(GetFormSectionName, 'ToolVisible', FTreePanel.ToolVisible);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.SetColor_(Value: TColor);
|
||
|
begin
|
||
|
FTree.Color := Value;
|
||
|
FTreePanel.FilterColor := Value;
|
||
|
UpdateItems;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.FormCreate(Sender: TObject);
|
||
|
begin
|
||
|
FDesigner := TfrxCustomDesigner(Owner);
|
||
|
FReport := FDesigner.Report;
|
||
|
|
||
|
Caption := frxGet(2200);
|
||
|
|
||
|
if UseRightToLeftAlignment then
|
||
|
FlipChildren(True);
|
||
|
end;
|
||
|
|
||
|
{$IFDEF FPC}
|
||
|
type
|
||
|
THackCustomTreeView = class(TCustomTreeView)
|
||
|
public
|
||
|
property ChangeTimer: TTimer read FChangeTimer write FChangeTimer;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
|
||
|
procedure TfrxReportTreeForm.UpdateSelection;
|
||
|
var
|
||
|
c: TComponent;
|
||
|
i, idx: Integer;
|
||
|
SelList: {$IFDEF FPC}Classes.{$ENDIF}TList;
|
||
|
begin
|
||
|
if FDesigner.SelectedObjects.Count = 0 then
|
||
|
Exit;
|
||
|
|
||
|
FUpdating := True;
|
||
|
FTree.Items.BeginUpdate;
|
||
|
SelList := {$IFDEF FPC}Classes.{$ENDIF}TList.Create;
|
||
|
try
|
||
|
//Tree.ClearSelection();
|
||
|
for idx := 0 to FDesigner.SelectedObjects.Count - 1 do
|
||
|
begin
|
||
|
c := TComponent(FDesigner.SelectedObjects[idx]);
|
||
|
i := FComponents.IndexOf(c);
|
||
|
if i <> -1 then
|
||
|
SelList.Add(FNodes[i]);
|
||
|
{$IFDEF FPC}
|
||
|
// OLD code - remove
|
||
|
// if i <> -1 then
|
||
|
// begin
|
||
|
//SelList.Add(FNodes[i]);
|
||
|
// if idx = 0 then
|
||
|
// TTreeNode(FNodes[i]).Selected := True
|
||
|
// else
|
||
|
// TTreeNode(FNodes[i]).Selected := True;
|
||
|
|
||
|
// Tree.TopItem := TTreeNode(FNodes[i]);
|
||
|
// end;
|
||
|
{$ENDIF}
|
||
|
end;
|
||
|
//{$IFNDEF FPC}
|
||
|
FTree.Select(SelList);
|
||
|
if SelList.Count > 0 then
|
||
|
FTree.TopItem := TTreeNode(SelList[SelList.Count - 1]);
|
||
|
//{$ENDIF}
|
||
|
finally
|
||
|
FTree.Items.EndUpdate;
|
||
|
{$IFDEF FPC}
|
||
|
THackCustomTreeView(FTree).ChangeTimer.Enabled := False;
|
||
|
{$ENDIF}
|
||
|
FUpdating := False;
|
||
|
SelList.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.TreeKeyDown(Sender: TObject; var Key: Word;
|
||
|
Shift: TShiftState);
|
||
|
begin
|
||
|
if Key = vk_Delete then
|
||
|
begin
|
||
|
THackWinControl(TfrxDesignerForm(FDesigner).Workspace).KeyDown(Key, Shift);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxReportTreeForm.TreeMouseDown(Sender: TObject;
|
||
|
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
||
|
var
|
||
|
p: TPoint;
|
||
|
i: Integer;
|
||
|
begin
|
||
|
if (PopupMenu = nil) or not (ssRight in Shift) then Exit;
|
||
|
for i := 0 to FTree.SelectionCount - 1 do
|
||
|
if FTree.Selections[i].Text = 'Report' then
|
||
|
Exit;
|
||
|
p := ClientToScreen(Point(X, Y));
|
||
|
PopupMenu.Popup(p.X, p.Y);
|
||
|
end;
|
||
|
|
||
|
end.
|