📄 fr_cross1.pas
字号:
{******************************************}
{ }
{ FastReport v2.4 }
{ Cross methods }
{ }
{ Copyright (c) 1998-2000 by Tzyganenko A. }
{ }
{******************************************}
unit FR_Cross1;
interface
{$I FR.inc}
uses
Windows, SysUtils, Classes, FR_Class, FR_Utils, FR_DBRel, FR_Pars
{$IFNDEF IBO}
, DB
{$ELSE}
, IB_Components
{$ENDIF};
type
TfrArray = class(TObject)
private
FArray: TStringList;
FColumns: TStringList;
FCellItemsCount: Integer;
function GetCell(Index1, Index2: String; Index3: Integer): Variant;
procedure SetCell(Index1, Index2: String; Index3: Integer; Value: Variant);
function GetCellByIndex(Index1, Index2, Index3: Integer): Variant;
function GetCellArray(Index1, Index2: Integer): Variant;
procedure SetCellArray(Index1, Index2: Integer; Value: Variant);
public
constructor Create(CellItemsCount: Integer);
destructor Destroy; override;
procedure Clear;
property Columns: TStringList read FColumns;
property Rows: TStringList read FArray;
property CellItemsCount: Integer read FCellItemsCount;
property Cell[Index1, Index2: String; Index3: Integer]: Variant read GetCell write SetCell;
property CellByIndex[Index1, Index2, Index3: Integer]: Variant read GetCellByIndex;
property CellArray[Index1, Index2: Integer]: Variant read GetCellArray write SetCellArray;
end;
TfrCross = class(TfrArray)
private
FDataSet: TfrTDataSet;
FRowFields, FColFields, FCellFields: TStringList;
FRowTypes, FColTypes: Array[0..31] of Variant;
FTopLeftSize: TSize;
FHeaderString: String;
FRowTotalString: String;
FRowGrandTotalString: String;
FColumnTotalString: String;
FColumnGrandTotalString: String;
function GetIsTotalRow(Index: Integer): Boolean;
function GetIsTotalColumn(Index: Integer): Boolean;
public
{$IFNDEF IBO}
constructor Create(DS: TDataSet; RowFields, ColFields, CellFields: String);
{$ELSE}
constructor Create(DS: TIB_Dataset; RowFields, ColFields, CellFields: String);
{$ENDIF}
destructor Destroy; override;
procedure Build;
property HeaderString: String read FHeaderString write FHeaderString;
property RowTotalString: String read FRowTotalString write FRowTotalString;
property RowGrandTotalString: String read FRowGrandTotalString write FRowGrandTotalString;
property ColumnTotalString: String read FColumnTotalString write FColumnTotalString;
property ColumnGrandTotalString: String read FColumnGrandTotalString write FColumnGrandTotalString;
property TopLeftSize: TSize read FTopLeftSize;
property IsTotalRow[Index: Integer]: Boolean read GetIsTotalRow;
property IsTotalColumn[Index: Integer]: Boolean read GetIsTotalColumn;
end;
implementation
{$IFDEF Delphi6}
uses Variants;
{$ENDIF}
type
PfrArrayCell = ^TfrArrayCell;
TfrArrayCell = record
Items: Variant;
end;
TfrCrossGroupItem = class(TObject)
private
Parent: TfrCross;
FArray: Variant;
FCellItemsCount: Integer;
FGroupName: TStringList;
FIndex: Integer;
FCount: Variant;
FStartFrom: Integer;
procedure Reset(NewGroupName: String; StartFrom: Integer);
procedure AddValue(Value: Variant);
function IsBreak(GroupName: String): Boolean;
procedure CheckAvg;
property Value: Variant read FArray;
public
constructor Create(AParent: TfrCross; GroupName: String; Index, CellItemsCount: Integer);
destructor Destroy; override;
end;
function HasTotal(s: String): Boolean;
begin
Result := Pos('+', s) <> 0;
end;
function FuncName(s: String): String;
begin
if HasTotal(s) then
begin
Result := LowerCase(Copy(s, Pos('+', s) + 1, 255));
if Result = '' then
Result := 'sum';
end
else
Result := '';
end;
function PureName(s: String): String;
begin
if HasTotal(s) then
Result := Copy(s, 1, Pos('+', s) - 1) else
Result := s;
end;
function CharCount(ch: Char; s: String): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(s) do
if s[i] = ch then
Inc(Result);
end;
{ TfrCrossGroupItem }
constructor TfrCrossGroupItem.Create(AParent: TfrCross; GroupName: String;
Index, CellItemsCount: Integer);
begin
inherited Create;
Parent := AParent;
FCellItemsCount := CellItemsCount;
FArray := VarArrayCreate([0, CellItemsCount - 1], varVariant);
FCount := VarArrayCreate([0, CellItemsCount - 1], varInteger);
FGroupName := TStringList.Create;
FIndex := Index;
Reset(GroupName, 0);
end;
destructor TfrCrossGroupItem.Destroy;
begin
FGroupName.Free;
VarClear(FArray);
VarClear(FCount);
inherited Destroy;
end;
procedure TfrCrossGroupItem.Reset(NewGroupName: String; StartFrom: Integer);
var
i: Integer;
s: String;
begin
FStartFrom := StartFrom;
frSetCommaText(NewGroupName, FGroupName);
for i := 0 to FCellItemsCount - 1 do
begin
FCount[i] := 0;
s := FuncName(Parent.FCellFields[i]);
if (s = 'max') or (s = 'min') then
FArray[i] := Null else
FArray[i] := 0;
end;
end;
function TfrCrossGroupItem.IsBreak(GroupName: String): Boolean;
var
sl: TStringList;
begin
sl := TStringList.Create;
frSetCommaText(GroupName, sl);
Result := (FIndex < sl.Count) and (FIndex < FGroupName.Count) and
(sl[FIndex] <> FGroupName[FIndex]);
sl.Free;
end;
procedure TfrCrossGroupItem.AddValue(Value: Variant);
var
i: Integer;
s: String;
begin
if TVarData(Value).VType >= varArray then
for i := 0 to FCellItemsCount - 1 do
if (Value[i] <> Null) and HasTotal(Parent.FCellFields[i]) then
begin
s := FuncName(Parent.FCellFields[i]);
if (s = 'sum') or (s = 'count') then
FArray[i] := FArray[i] + Value[i]
else if s = 'min' then
begin
if (FArray[i] = Null) or (FArray[i] > Value[i]) then
FArray[i] := Value[i];
end
else if s = 'max' then
begin
if FArray[i] < Value[i] then
FArray[i] := Value[i];
end
else if s = 'avg' then
begin
FArray[i] := FArray[i] + Value[i];
FCount[i] := FCount[i] + 1;
end;
end;
end;
procedure TfrCrossGroupItem.CheckAvg;
var
i: Integer;
s: String;
begin
for i := 0 to FCellItemsCount - 1 do
begin
s := FuncName(Parent.FCellFields[i]);
if s = 'avg' then
if FCount[i] <> 0 then
FArray[i] := FArray[i] / FCount[i] else
FArray[i] := Null;
end;
end;
{ TfrArray }
constructor TfrArray.Create(CellItemsCount: Integer);
begin
inherited Create;
FCellItemsCount := CellItemsCount;
FArray := TStringList.Create;
FArray.Sorted := True;
FColumns := TStringList.Create;
FColumns.Sorted := True;
end;
destructor TfrArray.Destroy;
begin
Clear;
FArray.Free;
FColumns.Free;
inherited Destroy;
end;
procedure TfrArray.Clear;
var
i, j: Integer;
sl: TList;
p: PfrArrayCell;
begin
for i := 0 to FArray.Count - 1 do
begin
sl := Pointer(FArray.Objects[i]);
if sl <> nil then
for j := 0 to sl.Count - 1 do
begin
p := sl[j];
if p <> nil then
begin
VarClear(p.Items);
Dispose(p);
end;
end;
sl.Free;
end;
FArray.Clear;
end;
function TfrArray.GetCell(Index1, Index2: String; Index3: Integer): Variant;
var
i1, i2: Integer;
sl: TList;
p: PfrArrayCell;
begin
Result := Null;
i1 := FArray.IndexOf(Index1);
i2 := FColumns.IndexOf(Index2);
if (i1 = -1) or (i2 = -1) or (Index3 >= FCellItemsCount) then Exit;
i2 := Integer(FColumns.Objects[i2]);
if i1 < FArray.Count then
sl := Pointer(FArray.Objects[i1]) else
sl := nil;
if sl <> nil then
begin
if i2 < sl.Count then
p := sl[i2] else
p := nil;
if p <> nil then
Result := p^.Items[Index3];
end;
end;
procedure TfrArray.SetCell(Index1, Index2: String; Index3: Integer; Value: Variant);
var
i, j, i1, i2: Integer;
sl: TList;
p: PfrArrayCell;
begin
i1 := FArray.IndexOf(Index1);
i2 := FColumns.IndexOf(Index2);
if i2 <> -1 then
i2 := Integer(FColumns.Objects[i2]);
if i1 = -1 then // row does'nt exists, so create it
begin
sl := TList.Create;
FArray.AddObject(Index1, sl);
i1 := FArray.IndexOf(Index1);
end;
if i2 = -1 then // column does'nt exists, so create it
begin
FColumns.AddObject(Index2, TObject(FColumns.Count));
i2 := FColumns.Count - 1;
end;
sl := Pointer(FArray.Objects[i1]);
p := nil;
if i2 < sl.Count then
p := sl[i2]
else
begin
i2 := i2 - sl.Count;
for i := 0 to i2 do
begin
New(p);
p^.Items := VarArrayCreate([-1, FCellItemsCount - 1], varVariant);
for j := -1 to FCellItemsCount - 1 do
p^.Items[j] := Null;
sl.Add(p);
end;
end;
p^.Items[Index3] := Value;
end;
function TfrArray.GetCellByIndex(Index1, Index2, Index3: Integer): Variant;
var
sl: TList;
p: PfrArrayCell;
begin
Result := Null;
if (Index1 = -1) or (Index2 = -1) or (Index3 >= FCellItemsCount) then Exit;
if Index2 < FColumns.Count then
Index2 := Integer(FColumns.Objects[Index2]);
if Index1 < FArray.Count then
sl := Pointer(FArray.Objects[Index1]) else
sl := nil;
if sl <> nil then
begin
if Index2 < sl.Count then
p := sl[Index2] else
p := nil;
if p <> nil then
Result := p^.Items[Index3];
end;
end;
function TfrArray.GetCellArray(Index1, Index2: Integer): Variant;
var
sl: TList;
p: PfrArrayCell;
begin
Result := Null;
if (Index1 = -1) or (Index2 = -1) then Exit;
if Index2 < FColumns.Count then
Index2 := Integer(FColumns.Objects[Index2]);
if Index1 < FArray.Count then
sl := Pointer(FArray.Objects[Index1]) else
sl := nil;
if sl <> nil then
begin
if Index2 < sl.Count then
p := sl[Index2] else
p := nil;
if p <> nil then
Result := p^.Items;
end;
end;
procedure TfrArray.SetCellArray(Index1, Index2: Integer; Value: Variant);
var
i: Integer;
sl: TList;
p: PfrArrayCell;
begin
if (Index1 = -1) or (Index2 = -1) then Exit;
Cell[FArray[Index1], Columns[Index2], 0] := 0;
if Index2 < FColumns.Count then
Index2 := Integer(FColumns.Objects[Index2]);
if Index1 < FArray.Count then
sl := Pointer(FArray.Objects[Index1]) else
sl := nil;
if sl <> nil then
begin
if Index2 < sl.Count then
p := sl[Index2] else
p := nil;
if p <> nil then
for i := 0 to FCellItemsCount - 1 do
p^.Items[i] := Value[i];
end;
end;
{ TfrCross }
{$IFNDEF IBO}
constructor TfrCross.Create(DS: TDataSet; RowFields, ColFields, CellFields: String);
{$ELSE}
constructor TfrCross.Create(DS: TIB_Dataset; RowFields, ColFields, CellFields: String);
{$ENDIF}
begin
FDataSet := TfrTDataSet(DS);
FRowFields := TStringList.Create;
FColFields := TStringList.Create;
FCellFields := TStringList.Create;
while RowFields[Length(RowFields)] in ['+', ';'] do
RowFields := Copy(RowFields, 1, Length(RowFields) - 1);
while ColFields[Length(ColFields)] in ['+', ';'] do
ColFields := Copy(ColFields, 1, Length(ColFields) - 1);
frSetCommaText(RowFields, FRowFields);
frSetCommaText(ColFields, FColFields);
frSetCommaText(CellFields, FCellFields);
inherited Create(FCellFields.Count);
end;
destructor TfrCross.Destroy;
begin
FRowFields.Free;
FColFields.Free;
FCellFields.Free;
inherited Destroy;
end;
procedure TfrCross.Build;
var
i: Integer;
f: TfrTField;
v: Variant;
s1, s2: String;
function GetFieldValues(sl: TStringList): String;
var
i, j, n: Integer;
s: String;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -