FastReport_2022_VCL/LibD28x64/frxCross.pas
2024-01-01 16:13:08 +01:00

5166 lines
141 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport VCL }
{ Cross object }
{ }
{ Copyright (c) 1998-2021 }
{ by Fast Reports Inc. }
{ }
{******************************************}
unit frxCross;
interface
{$I frx.inc}
uses
{$IFNDEF FPC}Windows, {$ENDIF}Types, SysUtils, Classes, Controls, Graphics, Forms,
frxClass, Variants
{$IFDEF FPC}
, LCLType, LMessages, LazHelper, LazarusPackageIntf
{$ENDIF}
{$IFDEF DELPHI16}
, System.UITypes
{$ENDIF}
;
type
{$IFDEF DELPHI16}
/// <summary>
/// The TfrxCrossObject lets you use the Cross-tab component in your report.
/// TfrxCrossObject is an empty component. It is used to add the frxCross.pas
/// file to the "uses" list. The main component is TfrxCrossView and
/// TfrxDBCrossView.
/// </summary>
[ComponentPlatformsAttribute(pidWin32 or pidWin64)]
{$ENDIF}
TfrxCrossObject = class(TComponent); // fake component
TfrxPrintCellEvent = type String;
TfrxPrintHeaderEvent = type String;
TfrxCalcWidthEvent = type String;
TfrxCalcHeightEvent = type String;
TfrxOnPrintCellEvent = procedure (Memo: TfrxCustomMemoView;
RowIndex, ColumnIndex, CellIndex: Integer;
const RowValues, ColumnValues, Value: Variant) of object;
TfrxOnPrintHeaderEvent = procedure (Memo: TfrxCustomMemoView;
const HeaderIndexes, HeaderValues, Value: Variant) of object;
TfrxOnCalcWidthEvent = procedure (ColumnIndex: Integer;
const ColumnValues: Variant; var Width: Extended) of object;
TfrxOnCalcHeightEvent = procedure (RowIndex: Integer;
const RowValues: Variant; var Height: Extended) of object;
{ the record represents one cell of cross matrix }
PfrCrossCell = ^TfrxCrossCell;
TfrxCrossCell = packed record
Value: Variant;
Count: Integer;
Next: PfrCrossCell; { pointer to the next value in the same cell }
end;
TfrxCrossEditGrid = (seLeftTop, seLeftBottom, seRightTop, seRightBottom);
/// <summary>
/// Sort order of cross header values.
/// </summary>
TfrxCrossSortOrder = (soAscending, soDescending, soNone, soGrouping);
/// <summary>
/// Type of aggregate function used in the crosstab cells.
/// </summary>
TfrxCrossFunction = (cfNone, cfSum, cfMin, cfMax, cfAvg, cfCount);
TfrxVariantArray = array of Variant;
TfrxSortArray = array [0..63] of TfrxCrossSortOrder;
{ the base class for column/row item. Contains Indexes array that
identifies a column/row }
TfrxIndexItem = class(TCollectionItem)
private
FIndexes: TfrxVariantArray;
public
destructor Destroy; override;
property Indexes: TfrxVariantArray read FIndexes write FIndexes;
end;
{ the base collection for column/row items. Contains methods for working
with Indexes and sorting them }
TfrxIndexCollection = class(TCollection)
private
FIndexesCount: Integer;
FSortOrder: TfrxSortArray;
function GetItems(Index: Integer): TfrxIndexItem;
public
function Find(const Indexes: array of Variant; var Index: Integer): Boolean;
function InsertItem(Index: Integer; const Indexes: array of Variant): TfrxIndexItem; virtual;
property Items[Index: Integer]: TfrxIndexItem read GetItems; default;
end;
{ the class representing a single row item }
TfrxCrossRow = class(TfrxIndexItem)
private
FCellLevels: Integer;
FCells: TList;
procedure CreateCell(Index: Integer);
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
function GetCell(Index: Integer): PfrCrossCell;
function GetCellValue(Index1, Index2: Integer): Variant;
procedure SetCellValue(Index1, Index2: Integer; const Value: Variant);
end;
{ the class representing row items }
TfrxCrossRows = class(TfrxIndexCollection)
private
FCellLevels: Integer;
function GetItems(Index: Integer): TfrxCrossRow;
public
constructor Create;
function InsertItem(Index: Integer; const Indexes: array of Variant): TfrxIndexItem; override;
function Row(const Indexes: array of Variant): TfrxCrossRow;
property Items[Index: Integer]: TfrxCrossRow read GetItems; default;
end;
{ the class representing a single column item }
TfrxCrossColumn = class(TfrxIndexItem)
private
FCellIndex: Integer;
public
property CellIndex: Integer read FCellIndex write FCellIndex;
end;
{ the class representing column items }
TfrxCrossColumns = class(TfrxIndexCollection)
private
function GetItems(Index: Integer): TfrxCrossColumn;
public
constructor Create;
function Column(const Indexes: array of Variant): TfrxCrossColumn;
function InsertItem(Index: Integer; const Indexes: array of Variant): TfrxIndexItem; override;
property Items[Index: Integer]: TfrxCrossColumn read GetItems; default;
end;
{ TfrxCrossHeader represents one cell of a cross header. The cell has a value,
position, size and list of subcells }
TfrxCrossHeader = class(TObject)
private
FBounds: TfrxRect; { bounds of the cell }
FMemos: TList;
FTotalMemos: TList;
FCounts: TfrxVariantArray;
FCellIndex: Integer; { help to determine cell index for cell header }
FCellLevels: Integer;
FFuncValues: TfrxVariantArray;
FHasCellHeaders: Boolean; { top level item only }
FIndex: Integer; { index of the item }
FIsCellHeader: Boolean;
FIsIndex: Boolean; { used in IndexItems to determine if item is index }
FIsTotal: Boolean; { is this cell a total cell }
FItems: TList; { subcells }
FLevelsCount: Integer; { number of header levels }
FMemo: TfrxCustomMemoView; { memo for this cell }
FNoLevels: Boolean; { true if no items in row/column header }
FParent: TfrxCrossHeader; { parent of the cell }
FSize: TfrxPoint;
FTotalIndex: Integer; { will help to choose which header memo to use }
FValue: Variant; { value (text) of the cell }
FVisible: Boolean; { visibility of the cell }
FDefaultHeight: Integer; { can be used to synchonize with other headers }
FRecalcSizes: Boolean; { used when Column width was decreased during cross building }
function AddCellHeader(Memos: TList; Index, CellIndex: Integer): TfrxCrossHeader;
function AddChild(Memo: TfrxCustomMemoView): TfrxCrossHeader;
procedure AddFuncValues(const Values, Counts: array of Variant;
const CellFunctions: array of TfrxCrossFunction);
procedure AddValues(const Values: array of Variant; Unsorted: Boolean);
procedure Reset(const CellFunctions: array of TfrxCrossFunction);
function GetCount: Integer;
function GetItems(Index: Integer): TfrxCrossHeader;
function GetLevel: Integer;
function GetHeight: Extended;
function GetWidth: Extended;
public
constructor Create(CellLevels: Integer);
destructor Destroy; override;
procedure CalcBounds; virtual; abstract;
procedure CalcSizes(MaxWidth, MinWidth: Integer; AutoSize: Boolean); virtual; abstract;
function AllItems: TList;
function Find(Value: Variant): Integer;
function GetIndexes: Variant;
function GetValues: Variant;
function TerminalItems: TList;
function IndexItems: TList;
property Bounds: TfrxRect read FBounds write FBounds;
property Count: Integer read GetCount;
property HasCellHeaders: Boolean read FHasCellHeaders write FHasCellHeaders;
property Height: Extended read GetHeight;
property IsTotal: Boolean read FIsTotal;
property Items[Index: Integer]: TfrxCrossHeader read GetItems; default;
property Level: Integer read GetLevel;
property Memo: TfrxCustomMemoView read FMemo;
property Parent: TfrxCrossHeader read FParent;
property Value: Variant read FValue write FValue;
property Visible: Boolean read FVisible write FVisible;
property Width: Extended read GetWidth;
property DefaultHeight: Integer read FDefaultHeight write FDefaultHeight;
end;
{ the cross columns }
TfrxCrossColumnHeader = class(TfrxCrossHeader)
private
FCorner: TfrxCrossHeader;
public
procedure CalcBounds; override;
procedure CalcSizes(MaxWidth, MinWidth: Integer; AutoSize: Boolean); override;
end;
{ the cross rows }
TfrxCrossRowHeader = class(TfrxCrossHeader)
private
FCorner: TfrxCrossHeader;
public
procedure CalcBounds; override;
procedure CalcSizes(MaxWidth, MinWidth: Integer; AutoSize: Boolean); override;
end;
{ the cross corner }
TfrxCrossCorner = class(TfrxCrossColumnHeader)
end;
{ cutted bands }
TfrxCutBandItem = class(TCollectionItem)
public
Band: TfrxBand;
FromIndex: Integer;
ToIndex: Integer;
destructor Destroy; override;
end;
TfrxCutBands = class(TCollection)
private
function GetItems(Index: Integer): TfrxCutBandItem;
public
constructor Create;
procedure Add(ABand: TfrxBand; AFromIndex, AToIndex: Integer);
property Items[Index: Integer]: TfrxCutBandItem read GetItems; default;
end;
{ design-time grid resize support }
TfrxGridLineItem = class(TCollectionItem)
public
Coord: Extended;
Objects: TList;
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
end;
TfrxGridLines = class(TCollection)
private
function GetItems(Index: Integer): TfrxGridLineItem;
public
constructor Create;
procedure Add(AObj: TObject; ACoord: Extended);
property Items[Index: Integer]: TfrxGridLineItem read GetItems; default;
end;
{ custom cross object }
{$IFDEF FR_COM}
TfrxCustomCrossView = class(TfrxView, IfrxCustomCrossView)
{$ELSE}
/// <summary>
/// The TfrxCustomCrossView component is the base class for TfrxCrossView,
/// TfrxDBCrossView components.
/// </summary>
TfrxCustomCrossView = class(TfrxView)
{$ENDIF}
private
FAddHeight: Extended;
FAddWidth: Extended;
FAllowDuplicates: Boolean;
FAutoSize: Boolean;
FBorder: Boolean;
FCellFields: TStrings;
FCellFunctions: array[0..63] of TfrxCrossFunction;
FCellLevels: Integer;
FClearBeforePrint: Boolean;
FColumnBands: TfrxCutBands;
FColumnFields: TStrings;
FColumnHeader: TfrxCrossColumnHeader;
FColumnLevels: Integer;
FColumns: TfrxCrossColumns;
FColumnSort: TfrxSortArray;
FCorner: TfrxCrossCorner;
FDefHeight: Integer;
FDotMatrix: Boolean;
FDownThenAcross: Boolean;
FFirstMousePos: TPoint;
FGapX: Integer;
FGapY: Integer;
FGridUsed: TfrxGridLines;
FGridX: TfrxGridLines;
FGridY: TfrxGridLines;
FJoinEqualCells: Boolean;
FKeepTogether: Boolean;
FLastMousePos: TPoint;
FMaxWidth: Integer;
FMinWidth: Integer;
FMouseDown: Boolean;
FMovingObjects: Integer;
FNextCross: TfrxCustomCrossView;
FNextCrossGap: Extended;
FNoColumns: Boolean;
FNoRows: Boolean;
FPlainCells: Boolean;
FRepeatHeaders: Boolean;
FRowBands: TfrxCutBands;
FRowFields: TStrings;
FRowHeader: TfrxCrossRowHeader;
FRowLevels: Integer;
FRows: TfrxCrossRows;
FRowSort: TfrxSortArray;
FShowColumnHeader: Boolean;
FShowColumnTotal: Boolean;
FShowCorner: Boolean;
FShowRowHeader: Boolean;
FShowRowTotal: Boolean;
FShowTitle: Boolean;
FSuppressNullRecords: Boolean;
FKeepRowsTogether: Boolean;
FDragActive: Boolean;
FShowMoveArrow: Boolean;
FPrevCrossLastPage: Integer;
FShowCellBreak: Boolean;
FAllMemos: TList;
FCellMemos: TList;
FCellHeaderMemos: TList;
FColumnMemos: TList;
FColumnTotalMemos: TList;
FCornerMemos: TList;
FRowMemos: TList;
FRowTotalMemos: TList;
FOnCalcHeight: TfrxCalcHeightEvent; { script event }
FOnCalcWidth: TfrxCalcWidthEvent; { script event }
FOnPrintCell: TfrxPrintCellEvent; { script event }
FOnPrintColumnHeader: TfrxPrintHeaderEvent; { script event }
FOnPrintRowHeader: TfrxPrintHeaderEvent; { script event }
FOnBeforeCalcHeight: TfrxOnCalcHeightEvent; { Delphi event }
FOnBeforeCalcWidth: TfrxOnCalcWidthEvent; { Delphi event }
FOnBeforePrintCell: TfrxOnPrintCellEvent; { Delphi event }
FOnBeforePrintColumnHeader: TfrxOnPrintHeaderEvent; { Delphi event }
FOnBeforePrintRowHeader: TfrxOnPrintHeaderEvent; { Delphi event }
procedure CalcBounds(addWidth, addHeight: Extended);
procedure CalcTotal(Header: TfrxCrossHeader; Source: TfrxIndexCollection);
procedure CalcTotals;
procedure CreateHeader(Header: TfrxCrossHeader; Source: TfrxIndexCollection;
Totals: TList; TotalVisible: Boolean);
procedure CreateHeaders;
procedure BuildColumnBands;
procedure BuildRowBands;
procedure ClearMatrix;
procedure ClearMemos;
procedure CreateCellHeaderMemos(NewCount: Integer);
procedure CreateCellMemos(NewCount: Integer);
procedure CreateColumnMemos(NewCount: Integer);
procedure CreateCornerMemos(NewCount: Integer);
procedure CreateRowMemos(NewCount: Integer);
procedure CorrectDMPBounds(Memo: TfrxCustomMemoView);
procedure DoCalcHeight(Row: Integer; var Height: Extended);
procedure DoCalcWidth(Column: Integer; var Width: Extended);
procedure DoOnCell(Memo: TfrxCustomMemoView; Row, Column, Cell: Integer;
const Value: Variant);
procedure DoOnColumnHeader(Memo: TfrxCustomMemoView; Header: TfrxCrossHeader);
procedure DoOnRowHeader(Memo: TfrxCustomMemoView; Header: TfrxCrossHeader);
procedure InitMatrix;
procedure InitMemos(AddToScript: Boolean);
procedure ReadMemos(Stream: TStream);
procedure RenderMatrix;
procedure SetCellFields(const Value: TStrings);
procedure SetCellFunctions(Index: Integer; const Value: TfrxCrossFunction);
procedure SetColumnFields(const Value: TStrings);
procedure SetColumnSort(Index: Integer; Value: TfrxCrossSortOrder);
procedure SetDotMatrix(const Value: Boolean);
procedure SetRowFields(const Value: TStrings);
procedure SetRowSort(Index: Integer; Value: TfrxCrossSortOrder);
procedure SetupOriginalComponent(Obj1, Obj2: TfrxComponent);
procedure UpdateVisibility;
procedure WriteMemos(Stream: TStream);
function CreateMemo(Parent: TfrxComponent): TfrxCustomMemoView;
function GetCellFunctions(Index: Integer): TfrxCrossFunction;
function GetCellHeaderMemos(Index: Integer): TfrxCustomMemoView;
function GetCellMemos(Index: Integer): TfrxCustomMemoView;
function GetColumnMemos(Index: Integer): TfrxCustomMemoView;
function GetColumnSort(Index: Integer): TfrxCrossSortOrder;
function GetColumnTotalMemos(Index: Integer): TfrxCustomMemoView;
function GetCornerMemos(Index: Integer): TfrxCustomMemoView;
function GetNestedObjects: TList;
function GetRowMemos(Index: Integer): TfrxCustomMemoView;
function GetRowSort(Index: Integer): TfrxCrossSortOrder;
function GetRowTotalMemos(Index: Integer): TfrxCustomMemoView;
protected
procedure DefineProperties(Filer: TFiler); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetCellLevels(const Value: Integer); virtual;
procedure SetColumnLevels(const Value: Integer); virtual;
procedure SetRowLevels(const Value: Integer); virtual;
function GetContainerObjects: TList; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Draw(Canvas: TCanvas; ScaleX, ScaleY, OffsetX, OffsetY: Extended); override;
procedure BeforePrint; override;
procedure BeforeStartReport; override;
procedure GetData; override;
procedure AddSourceObjects; override;
function ContainerAdd(Obj: TfrxComponent): Boolean; override;
function IsContain(X, Y: Extended): Boolean; override;
function IsAcceptAsChild(aParent: TfrxComponent): Boolean; override;
function GetContainedComponent(X, Y: Extended; IsCanContain: TfrxComponent = nil): TfrxComponent; override;
procedure DoMouseMove(X, Y: Integer;
Shift: TShiftState; var EventParams: TfrxInteractiveEventsParams); override;
procedure DoMouseUp(X, Y: Integer; Button: TMouseButton; Shift: TShiftState;
var EventParams: TfrxInteractiveEventsParams); override;
function DoMouseDown(X, Y: Integer; Button: TMouseButton; Shift: TShiftState;
var EventParams: TfrxInteractiveEventsParams): Boolean; override;
procedure DoMouseLeave(aNextObject: TfrxComponent; var EventParams: TfrxInteractiveEventsParams); override;
procedure DoMouseEnter(aPreviousObject: TfrxComponent; var EventParams: TfrxInteractiveEventsParams); override;
function DoDragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean; var EventParams: TfrxInteractiveEventsParams): Boolean; override;
function DoDragDrop(Source: TObject; X, Y: Integer; var EventParams: TfrxInteractiveEventsParams): Boolean; override;
function DoMouseWheel(Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint;
var EventParams: TfrxInteractiveEventsParams): Boolean; override;
/// <summary>
/// Method adds a value into cross-table. Use this method to fill in the
/// TfrxCrossView object.
/// </summary>
procedure AddValue(const Rows, Columns, Cells: array of Variant);
procedure ApplyStyle(Style: TfrxStyles);
procedure BeginMatrix;
procedure EndMatrix;
procedure FillMatrix; virtual;
procedure GetStyle(Style: TfrxStyles);
/// <summary>
/// Returns a number of columns in the cross.
/// </summary>
function ColCount: Integer;
function DrawCross(Canvas: TCanvas; ScaleX, ScaleY, OffsetX, OffsetY: Extended): TfrxPoint;
function GetColumnIndexes(AColumn: Integer): Variant;
function GetRowIndexes(ARow: Integer): Variant;
function GetValue(ARow, AColumn, ACell: Integer): Variant;
function IsCrossValid: Boolean; virtual;
/// <summary>
/// Returns True if specified column is grand total column.
/// </summary>
function IsGrandTotalColumn(Index: Integer): Boolean;
/// <summary>
/// Returns True if specified row is grand total row.
/// </summary>
function IsGrandTotalRow(Index: Integer): Boolean;
/// <summary>
/// Returns True if specified column is total column.
/// </summary>
function IsTotalColumn(Index: Integer): Boolean;
/// <summary>
/// Returns True if specified row is total row.
/// </summary>
function IsTotalRow(Index: Integer): Boolean;
/// <summary>
/// Returns a number of rows in the cross.
/// </summary>
function RowCount: Integer;
function RowHeaderWidth: Extended;
function ColumnHeaderHeight: Extended;
property ColumnHeader: TfrxCrossColumnHeader read FColumnHeader;
property RowHeader: TfrxCrossRowHeader read FRowHeader;
property Corner: TfrxCrossCorner read FCorner;
property NoColumns: Boolean read FNoColumns;
property NoRows: Boolean read FNoRows;
property CellFields: TStrings read FCellFields write SetCellFields;
/// <summary>
/// Aggregate functions for each cell level.
/// </summary>
property CellFunctions[Index: Integer]: TfrxCrossFunction read GetCellFunctions
write SetCellFunctions;
/// <summary>
/// Array of Text objects that represents each level of cell. First array
/// element has 0 index.
/// </summary>
property CellMemos[Index: Integer]: TfrxCustomMemoView read GetCellMemos;
property CellHeaderMemos[Index: Integer]: TfrxCustomMemoView read GetCellHeaderMemos;
property ClearBeforePrint: Boolean read FClearBeforePrint write FClearBeforePrint;
property ColumnFields: TStrings read FColumnFields write SetColumnFields;
/// <summary>
/// Array of Text objects that represents each level of column header.
/// First array element has 0 index.
/// </summary>
property ColumnMemos[Index: Integer]: TfrxCustomMemoView read GetColumnMemos;
/// <summary>
/// Sort order for each level of column header.
/// </summary>
property ColumnSort[Index: Integer]: TfrxCrossSortOrder read GetColumnSort
write SetColumnSort;
/// <summary>
/// Array of Text objects that represents each total cell of column
/// header. First array element has 0 index and is represents the grand
/// total.
/// </summary>
property ColumnTotalMemos[Index: Integer]: TfrxCustomMemoView read GetColumnTotalMemos;
/// <summary>
/// Array of Text objects that represents the table header. The first
/// array element has 0 index and is represents the top left corner, the
/// second represents all columns header, CornerMemos[2..RowLevels+2] are
/// row levels headers.
/// </summary>
property CornerMemos[Index: Integer]: TfrxCustomMemoView read GetCornerMemos;
property DotMatrix: Boolean read FDotMatrix;
property RowFields: TStrings read FRowFields write SetRowFields;
/// <summary>
/// Array of Text objects that represents each level of row header. First
/// array element has 0 index.
/// </summary>
property RowMemos[Index: Integer]: TfrxCustomMemoView read GetRowMemos;
/// <summary>
/// Sort order for each level of row header.
/// </summary>
property RowSort[Index: Integer]: TfrxCrossSortOrder read GetRowSort
write SetRowSort;
/// <summary>
/// Array of Text objects that represents each total cell of row header.
/// First array element has 0 index and is represents the grand total.
/// </summary>
property RowTotalMemos[Index: Integer]: TfrxCustomMemoView read GetRowTotalMemos;
property OnBeforeCalcHeight: TfrxOnCalcHeightEvent
read FOnBeforeCalcHeight write FOnBeforeCalcHeight;
property OnBeforeCalcWidth: TfrxOnCalcWidthEvent
read FOnBeforeCalcWidth write FOnBeforeCalcWidth;
property OnBeforePrintCell: TfrxOnPrintCellEvent
read FOnBeforePrintCell write FOnBeforePrintCell;
property OnBeforePrintColumnHeader: TfrxOnPrintHeaderEvent
read FOnBeforePrintColumnHeader write FOnBeforePrintColumnHeader;
property OnBeforePrintRowHeader: TfrxOnPrintHeaderEvent
read FOnBeforePrintRowHeader write FOnBeforePrintRowHeader;
published
property AddHeight: Extended read FAddHeight write FAddHeight;
property AddWidth: Extended read FAddWidth write FAddWidth;
property AllowDuplicates: Boolean read FAllowDuplicates write FAllowDuplicates default True;
property AutoSize: Boolean read FAutoSize write FAutoSize default True;
/// <summary>
/// Determines whether the cross-table should have an outer border.
/// Default value is True.
/// </summary>
property Border: Boolean read FBorder write FBorder default True;
/// <summary>
/// Number of levels of cross-tab cell.
/// </summary>
property CellLevels: Integer read FCellLevels write SetCellLevels default 1;
/// <summary>
/// Number of levels in the column header.
/// </summary>
property ColumnLevels: Integer read FColumnLevels write SetColumnLevels default 1;
/// <summary>
/// Default height of each row. If this property is 0 (default), the
/// height of each row is calculated automatically.
/// </summary>
property DefHeight: Integer read FDefHeight write FDefHeight default 0;
/// <summary>
/// Determines how the big table will break across pages. If this
/// property is False (default), the table will break across then down.
/// </summary>
property DownThenAcross: Boolean read FDownThenAcross write FDownThenAcross;
/// <summary>
/// The left indent of the text in the cross-tab cells.
/// </summary>
property GapX: Integer read FGapX write FGapX default 3;
/// <summary>
/// The top indent of the text in the cross-tab cells.
/// </summary>
property GapY: Integer read FGapY write FGapY default 3;
/// <summary>
/// Determines whether to join several side-by-side cells with equal
/// values into one wide cell.
/// </summary>
property JoinEqualCells: Boolean read FJoinEqualCells write FJoinEqualCells default False;
property KeepTogether: Boolean read FKeepTogether write FKeepTogether default False;
property KeepRowsTogether: Boolean read FKeepRowsTogether write FKeepRowsTogether default False;
/// <summary>
/// The maximum width of the cross-tab column. If this property is 0
/// (default), the width is calculated automatically.
/// </summary>
property MaxWidth: Integer read FMaxWidth write FMaxWidth default 200;
/// <summary>
/// The minimum width of the cross-tab column. If this property is 0
/// (default), the width is calculated automatically.
/// </summary>
property MinWidth: Integer read FMinWidth write FMinWidth default 0;
/// <summary>
/// The next cross-tab object that will be rendered side-by-side.
/// </summary>
property NextCross: TfrxCustomCrossView read FNextCross write FNextCross;
/// <summary>
/// The gap between two side-by-side crosses (see NextCross).
/// </summary>
property NextCrossGap: Extended read FNextCrossGap write FNextCrossGap;
/// <summary>
/// Determines whether to print cell levels stacked (default) or
/// side-by-side.
/// </summary>
property PlainCells: Boolean read FPlainCells write FPlainCells default False;
/// <summary>
/// Determines whether cross-tab should repeat column and row headers on
/// each new page. Default value is True.
/// </summary>
property RepeatHeaders: Boolean read FRepeatHeaders write FRepeatHeaders default True;
/// <summary>
/// Number of levels in the row header.
/// </summary>
property RowLevels: Integer read FRowLevels write SetRowLevels default 1;
/// <summary>
/// Determines whether to show column header or not. Default value is
/// True.
/// </summary>
property ShowColumnHeader: Boolean read FShowColumnHeader write FShowColumnHeader default True;
/// <summary>
/// Determines if column total is shown. Default value is True.
/// </summary>
property ShowColumnTotal: Boolean read FShowColumnTotal write FShowColumnTotal default True;
/// <summary>
/// Determines whether to show top-left corner elements (table headers).
/// </summary>
property ShowCorner: Boolean read FShowCorner write FShowCorner default True;
property ShowCellBreak: Boolean read FShowCellBreak write FShowCellBreak default True;
/// <summary>
/// Determines if row header is shown. Default value is True.
/// </summary>
property ShowRowHeader: Boolean read FShowRowHeader write FShowRowHeader default True;
/// <summary>
/// Determines if row total is shown. Default value is True.
/// </summary>
property ShowRowTotal: Boolean read FShowRowTotal write FShowRowTotal default True;
property ShowTitle: Boolean read FShowTitle write FShowTitle default True;
property SuppressNullRecords: Boolean read FSuppressNullRecords write FSuppressNullRecords default True;
/// <summary>
/// Name of the script procedure that will run when calculating a height
/// of each row.
/// </summary>
property OnCalcHeight: TfrxCalcHeightEvent read FOnCalcHeight write FOnCalcHeight;
/// <summary>
/// Name of the script procedure that will run when calculating a width
/// of each column.
/// </summary>
property OnCalcWidth: TfrxCalcWidthEvent read FOnCalcWidth write FOnCalcWidth;
/// <summary>
/// Name of the script procedure that will run before printing each cell
/// of the cross-table.
/// </summary>
property OnPrintCell: TfrxPrintCellEvent read FOnPrintCell write FOnPrintCell;
/// <summary>
/// Name of the script procedure that will run before printing
/// eachelement of the column header.
/// </summary>
property OnPrintColumnHeader: TfrxPrintHeaderEvent
read FOnPrintColumnHeader write FOnPrintColumnHeader;
/// <summary>
/// Name of the script procedure that will run before printing each
/// element of the row header.
/// </summary>
property OnPrintRowHeader: TfrxPrintHeaderEvent
read FOnPrintRowHeader write FOnPrintRowHeader;
end;
{$IFDEF FR_COM}
TfrxCrossView = class(TfrxCustomCrossView, IfrxCrossView)
{$ELSE}
/// <summary>
/// The TfrxCrossView component shows a cross-table. The component can show
/// non-DB data, you should fill it using AddValue method in the
/// OnBeforePrint event.
/// </summary>
TfrxCrossView = class(TfrxCustomCrossView)
{$ENDIF}
protected
procedure SetCellLevels(const Value: Integer); override;
procedure SetColumnLevels(const Value: Integer); override;
procedure SetRowLevels(const Value: Integer); override;
public
class function GetDescription: String; override;
function IsCrossValid: Boolean; override;
published
end;
{$IFDEF FR_COM}
TfrxDBCrossView = class(TfrxCustomCrossView, IfrxDBCrossView)
{$ELSE}
/// <summary>
/// The TfrxDBCrossView component shows a cross-table from the DB data. You
/// should connect it to the dataset (DataSet property) and fill in the
/// CellFields, ColumnFields, RowFields properties.
/// </summary>
TfrxDBCrossView = class(TfrxCustomCrossView)
{$ENDIF}
private
public
class function GetDescription: String; override;
function IsCrossValid: Boolean; override;
procedure FillMatrix; override;
published
/// <summary>
/// List of DB fields representing each level of the cell.
/// </summary>
property CellFields;
/// <summary>
/// List of DB fields representing each level of the column header.
/// </summary>
property ColumnFields;
property DataSet;
property DataSetName;
property RowFields;
end;
{$IFDEF FPC}
// procedure Register;
{$ENDIF}
implementation
uses
{$IFNDEF NO_EDITORS}
frxCrossEditor,
{$ENDIF}
{$IFNDEF NO_INLINE_EDITORS}
frxCrossInPlaceEditor,
{$ENDIF}
frxCrossRTTI, frxDsgnIntf, frxXML, frxUtils, frxXMLSerializer, frxRes,
frxDMPClass, frxVariables, frxUnicodeUtils, Math;
type
THackComponent = class(TfrxComponent);
THackReportComponent = class(TfrxReportComponent);
THackMemoView = class(TfrxCustomMemoView);
function CalcSize(m: TfrxCustomMemoView): TfrxPoint;
var
e, SaveHeight: Extended;
r: integer;
begin
SaveHeight := m.Height;
m.Height := 10000;
r := m.Rotation;
m.Rotation := 0;
try
Result.X := m.CalcWidth;
Result.Y := m.CalcHeight;
finally
m.Rotation := r;
end;
if m is TfrxDMPMemoView then
begin
Result.X := Result.X + fr1CharX;
Result.Y := Result.Y + fr1CharY;
end;
if (m.Rotation = 90) or (m.Rotation = 270) then
begin
e := Result.X;
Result.X := Result.Y;
Result.Y := e;
end;
m.Height := SaveHeight;
end;
{ TfrxIndexItem }
destructor TfrxIndexItem.Destroy;
begin
FIndexes := nil;
inherited;
end;
{ TfrxIndexCollection }
function TfrxIndexCollection.GetItems(Index: Integer): TfrxIndexItem;
begin
Result := TfrxIndexItem(inherited Items[Index]);
end;
function TfrxIndexCollection.Find(const Indexes: array of Variant;
var Index: Integer): Boolean;
var
i, i0, i1, c: Integer;
Item: TfrxIndexItem;
{compare function returns > 2 to keep equal columns/rows together without sorting}
function Compare: Integer;
var
i: Integer;
begin
Result := 1;
for i := 0 to FIndexesCount - 1 do
if Item.Indexes[i] = Indexes[i] then
begin
if (VarType(Indexes[i]) = varString) or (VarType(Indexes[i]) = varOleStr)
{$IFDEF Delphi12}or (VarType(Indexes[i]) = varUString){$ENDIF} then
if VarToWideStr(Item.Indexes[i]) = VarToWideStr(Indexes[i]) then
Result := 0
else
begin
Result := -1;
break;
end
else
Result := 0;
end
else if (Result = 0) and (FSortOrder[0] = {soNone} soGrouping) then
begin
Result := i + 2;
break;
end
else if VarIsNull(Indexes[i]) then
begin
if FSortOrder[i] = soAscending then
Result := 1 else
Result := -1;
break;
end
else if VarIsNull(Item.Indexes[i]) then
begin
if FSortOrder[i] = soAscending then
Result := -1 else
Result := 1;
break;
end
else if Item.Indexes[i] > Indexes[i] then
begin
if FSortOrder[i] = soAscending then
Result := 1 else
Result := -1;
break;
end
else if Item.Indexes[i] < Indexes[i] then
begin
if FSortOrder[i] = soAscending then
Result := -1 else
Result := 1;
break;
end;
end;
begin
Result := False;
if FSortOrder[0] in [soNone, soGrouping] then
begin
i0 := 0;
Index := Count;
for i := 0 to Count - 1 do
begin
Item := TfrxIndexItem(Items[i]);
c := Compare;
if c = 0 then
begin
Result := True;
Index := i;
Exit;
end else if (c > 2) and (i0 <= c) then {place same colums together}
begin
Index := i + 1;
i0 := c;
end;
end;
Exit;
end;
{ quick find }
i0 := 0;
i1 := Count - 1;
while i0 <= i1 do
begin
i := (i0 + i1) div 2;
Item := TfrxIndexItem(Items[i]);
c := Compare;
if c < 0 then
i0 := i + 1
else
begin
i1 := i - 1;
if c = 0 then
begin
Result := True;
i0 := i;
end;
end;
end;
Index := i0;
end;
function TfrxIndexCollection.InsertItem(Index: Integer;
const Indexes: array of Variant): TfrxIndexItem;
var
i: Integer;
begin
if Index < Count then
Result := TfrxIndexItem(Insert(Index)) else
Result := TfrxIndexItem(Add);
SetLength(Result.FIndexes, FIndexesCount);
for i := 0 to FIndexesCount - 1 do
Result.FIndexes[i] := Indexes[i];
end;
{ TfrxCrossRow }
constructor TfrxCrossRow.Create;
begin
inherited;
FCells := TList.Create;
end;
destructor TfrxCrossRow.Destroy;
var
i: Integer;
c, c1: PfrCrossCell;
begin
for i := 0 to FCells.Count - 1 do
begin
c := FCells[i];
while c <> nil do
begin
c1 := c;
c := c.Next;
VarClear(c1.Value);
Dispose(c1);
end;
end;
FCells.Free;
inherited;
end;
procedure TfrxCrossRow.CreateCell(Index: Integer);
var
i: Integer;
c, c1: PfrCrossCell;
begin
while Index >= FCells.Count do
begin
c1 := nil;
for i := 0 to FCellLevels - 1 do
begin
New(c);
c.Value := Null;
c.Count := 1;
c.Next := nil;
if c1 <> nil then
c1.Next := c else
FCells.Add(c);
c1 := c;
end;
end;
end;
function TfrxCrossRow.GetCellValue(Index1, Index2: Integer): Variant;
var
c: PfrCrossCell;
begin
Result := Null;
if (Index1 < 0) or (Index1 >= FCells.Count) then Exit;
c := FCells[Index1];
while (c <> nil) and (Index2 > 0) do
begin
c := c.Next;
Dec(Index2);
end;
if c <> nil then
Result := c.Value;
end;
procedure TfrxCrossRow.SetCellValue(Index1, Index2: Integer; const Value: Variant);
var
c: PfrCrossCell;
begin
if Index1 < 0 then Exit;
if Index1 >= FCells.Count then
CreateCell(Index1);
c := FCells[Index1];
while (c <> nil) and (Index2 > 0) do
begin
c := c.Next;
Dec(Index2);
end;
if c <> nil then
if c.Value = Null then
c.Value := Value else
c.Value := c.Value + Value;
end;
function TfrxCrossRow.GetCell(Index: Integer): PfrCrossCell;
begin
Result := nil;
if Index < 0 then Exit;
if Index >= FCells.Count then
CreateCell(Index);
Result := FCells[Index];
end;
{ TfrxCrossRows }
constructor TfrxCrossRows.Create;
begin
inherited Create(TfrxCrossRow);
end;
function TfrxCrossRows.GetItems(Index: Integer): TfrxCrossRow;
begin
Result := TfrxCrossRow(inherited Items[Index]);
end;
function TfrxCrossRows.InsertItem(Index: Integer;
const Indexes: array of Variant): TfrxIndexItem;
begin
Result := inherited InsertItem(Index, Indexes);
TfrxCrossRow(Result).FCellLevels := FCellLevels;
end;
function TfrxCrossRows.Row(const Indexes: array of Variant): TfrxCrossRow;
var
i: Integer;
begin
if Find(Indexes, i) then
Result := Items[i] else
Result := TfrxCrossRow(InsertItem(i, Indexes));
end;
{ TfrxCrossColumns }
constructor TfrxCrossColumns.Create;
begin
inherited Create(TfrxCrossColumn);
end;
function TfrxCrossColumns.GetItems(Index: Integer): TfrxCrossColumn;
begin
Result := TfrxCrossColumn(inherited Items[Index]);
end;
function TfrxCrossColumns.Column(const Indexes: array of Variant): TfrxCrossColumn;
var
i: Integer;
begin
if Find(Indexes, i) then
Result := Items[i] else
Result := TfrxCrossColumn(InsertItem(i, Indexes));
end;
function TfrxCrossColumns.InsertItem(Index: Integer;
const Indexes: array of Variant): TfrxIndexItem;
begin
Result := inherited InsertItem(Index, Indexes);
TfrxCrossColumn(Result).FCellIndex := Count - 1;
end;
{ TfrxCrossHeader }
constructor TfrxCrossHeader.Create(CellLevels: Integer);
begin
FItems := TList.Create;
FCellLevels := CellLevels;
FValue := Null;
FVisible := True;
SetLength(FFuncValues, FCellLevels);
SetLength(FCounts, FCellLevels);
FDefaultHeight := 0;
end;
destructor TfrxCrossHeader.Destroy;
begin
FFuncValues := nil;
FCounts := nil;
while FItems.Count > 0 do
begin
TfrxCrossHeader(FItems[0]).Free;
FItems.Delete(0);
end;
FItems.Free;
inherited;
end;
function TfrxCrossHeader.GetItems(Index: Integer): TfrxCrossHeader;
begin
Result := TfrxCrossHeader(FItems[Index]);
end;
function TfrxCrossHeader.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TfrxCrossHeader.GetLevel: Integer;
var
h: TfrxCrossHeader;
begin
Result := -2;
h := Self;
while h <> nil do
begin
h := h.Parent;
Inc(Result);
end;
end;
function TfrxCrossHeader.Find(Value: Variant): Integer;
var
i: Integer;
begin
{ find the cell containing the given value }
Result := -1;
for i := 0 to Count - 1 do
if VarToWideStr(Items[i].Value) = VarToWideStr(Value) then
begin
Result := i;
Exit;
end;
end;
function TfrxCrossHeader.AddChild(Memo: TfrxCustomMemoView): TfrxCrossHeader;
begin
Result := TfrxCrossHeader(NewInstance);
Result.Create(FCellLevels);
{ link it to the parent }
FItems.Add(Result);
Result.FParent := Self;
Result.FLevelsCount := FLevelsCount;
Result.FMemo := Memo;
Result.FValue := Memo.Text;
end;
function TfrxCrossHeader.AddCellHeader(Memos: TList; Index, CellIndex: Integer): TfrxCrossHeader;
begin
Result := TfrxCrossHeader(NewInstance);
Result.Create(FCellLevels);
{ link it to the parent }
FItems.Add(Result);
Result.FParent := Self;
Result.FIndex := Index;
Result.FCellIndex := CellIndex;
Result.FLevelsCount := FLevelsCount;
Result.FIsTotal := FIsTotal;
Result.FTotalIndex := FTotalIndex;
Result.FMemo := Memos[FTotalIndex * FCellLevels + CellIndex];
Result.FValue := Result.FMemo.Text;
Result.FIsCellHeader := True;
end;
procedure TfrxCrossHeader.AddValues(const Values: array of Variant; Unsorted: Boolean);
var
i, j: Integer;
Header, Header1: TfrxCrossHeader;
v: Variant;
s: String;
begin
{ create the header tree. For example, subsequent calls
AddValues([1998,1]);
AddValues([1998,2]);
AddValues([1999,1]);
will create the header
1998 | 1999
--+--+-----
1 |2 | 1 }
Header := Self;
for i := Low(Values) to High(Values) do
begin
{unsorted mode join same cells only for consecutive values}
if Unsorted then
begin
j := Header.Count;
if (j > 0) and
(VarToWideStr(Header.Items[Header.Count - 1].Value) = VarToWideStr(Values[i])) then
dec(j)
else j := -1;
end
else j := Header.Find(Values[i]);
if j <> -1 then
Header := Header.Items[j] { find existing item... }
else
begin
{ ...or create new one }
Header1 := TfrxCrossHeader(NewInstance);
Header1.Create(FCellLevels);
Header1.FLevelsCount := FLevelsCount;
{ link it to the parent }
Header.FItems.Add(Header1);
Header1.FParent := Header;
v := Values[i];
s := VarToStr(v);
{ this is subtotal item }
if Pos('@@@', s) = 1 then
begin
{ remove @@@ }
s := Copy(s, 4, Length(s) - {$IFDEF NONWINFPC}4{$ELSE}5{$ENDIF});
v := s;
Header1.FIsTotal := True;
Header1.FMemo := FTotalMemos[i];
Header1.FTotalIndex := FLevelsCount - i;
end
else
Header1.FMemo := FMemos[i];
Header1.FValue := v;
Header := Header1;
if Header.FIsTotal then break;
end;
end;
end;
procedure TfrxCrossHeader.Reset(const CellFunctions: array of TfrxCrossFunction);
var
i: Integer;
h: TfrxCrossHeader;
begin
{ reset aggregate values for this cell and all its parent cells }
h := Self;
while h <> nil do
begin
for i := 0 to FCellLevels - 1 do
begin
case CellFunctions[i] of
cfNone, cfMin, cfMax:
h.FFuncValues[i] := Null;
cfSum, cfAvg, cfCount:
h.FFuncValues[i] := 0;
end;
h.FCounts[i] := 0;
end;
h := h.Parent;
end;
end;
procedure TfrxCrossHeader.AddFuncValues(const Values, Counts: array of Variant;
const CellFunctions: array of TfrxCrossFunction);
var
i: Integer;
h: TfrxCrossHeader;
begin
{ add aggregate values for this cell and all its parent cells }
h := Self;
while h <> nil do
begin
for i := 0 to FCellLevels - 1 do
if Values[i] <> Null then
case CellFunctions[i] of
cfNone:;
cfSum:
h.FFuncValues[i] := h.FFuncValues[i] + Values[i];
cfMin:
if (h.FFuncValues[i] = Null) or (Values[i] < h.FFuncValues[i]) then
h.FFuncValues[i] := Values[i];
cfMax:
if (h.FFuncValues[i] = Null) or (Values[i] > h.FFuncValues[i]) then
h.FFuncValues[i] := Values[i];
cfAvg:
begin
h.FFuncValues[i] := h.FFuncValues[i] + Values[i];
h.FCounts[i] := h.FCounts[i] + Counts[i];
end;
cfCount:
h.FFuncValues[i] := h.FFuncValues[i] + Values[i];// + Counts[i];
end;
h := h.Parent;
end;
end;
function TfrxCrossHeader.AllItems: TList;
procedure EnumItems(Item: TfrxCrossHeader);
var
i: Integer;
begin
if Item.Memo <> nil then
Result.Add(Item);
for i := 0 to Item.Count - 1 do
EnumItems(Item[i]);
end;
begin
{ list all items in the header }
Result := TList.Create;
EnumItems(Self);
end;
function TfrxCrossHeader.TerminalItems: TList;
var
i: Integer;
Item: TfrxCrossHeader;
begin
{ list all terminal items in the header }
Result := AllItems;
i := 0;
while i < Result.Count do
begin
Item := Result[i];
if Item.Count = 0 then
Inc(i)
else
Result.Delete(i);
end;
end;
function TfrxCrossHeader.IndexItems: TList;
var
i: Integer;
Item: TfrxCrossHeader;
begin
{ list all terminal items in the header }
Result := AllItems;
i := 0;
while i < Result.Count do
begin
Item := Result[i];
if Item.FIsIndex then
Inc(i)
else
Result.Delete(i);
end;
end;
function TfrxCrossHeader.GetIndexes: Variant;
var
ar: array of Variant;
i, n: Integer;
h, h1: TfrxCrossHeader;
begin
SetLength(ar, FLevelsCount + 1);
n := 0;
h := Parent;
h1 := Self;
while h <> nil do
begin
ar[n] := h.FItems.IndexOf(h1);
Inc(n);
h1 := h;
h := h.Parent;
end;
Result := VarArrayCreate([0, FLevelsCount - 1], varVariant);
for i := 0 to FLevelsCount - 1 do
if i < n then
Result[i] := ar[n - i - 1] else
Result[i] := Null;
ar := nil;
end;
function TfrxCrossHeader.GetValues: Variant;
var
ar: array of Variant;
i, n: Integer;
h: TfrxCrossHeader;
begin
SetLength(ar, FLevelsCount + 1);
n := 0;
h := Self;
while h.Parent <> nil do
begin
ar[n] := h.Value;
Inc(n);
h := h.Parent;
end;
Result := VarArrayCreate([0, FLevelsCount - 1], varVariant);
for i := 0 to FLevelsCount - 1 do
if i < n then
Result[i] := ar[n - i - 1] else
Result[i] := Null;
ar := nil;
end;
function TfrxCrossHeader.GetHeight: Extended;
var
Items: TList;
begin
Items := TerminalItems;
if (Items.Count > 0) and FVisible then
Result := TfrxCrossHeader(Items[Items.Count - 1]).Bounds.Top +
TfrxCrossHeader(Items[Items.Count - 1]).Bounds.Bottom
else
Result := 0;
Items.Free;
end;
function TfrxCrossHeader.GetWidth: Extended;
var
Items: TList;
begin
Items := TerminalItems;
if (Items.Count > 0) and FVisible then
Result := TfrxCrossHeader(Items[Items.Count - 1]).Bounds.Left +
TfrxCrossHeader(Items[Items.Count - 1]).Bounds.Right
else
Result := 0;
Items.Free;
end;
{ TfrxCrossColumnHeader }
procedure TfrxCrossColumnHeader.CalcBounds;
var
i, j, l: Integer;
h, hAvg: Extended;
Items: TList;
Item: TfrxCrossHeader;
LevelHeights: array of Extended;
function DoAdjust(Item: TfrxCrossHeader): Extended;
var
i: Integer;
Width: Extended;
begin
if Item.Count = 0 then
begin
Result := Item.FSize.X;
Exit;
end;
Width := 0;
for i := 0 to Item.Count - 1 do
Width := Width + DoAdjust(Item[i]);
if Item.FSize.X < Width then
Item.FSize.X := Width
else
begin
Item[Item.Count - 1].FSize.X := Item[Item.Count - 1].FSize.X + Item.FSize.X - Width;
DoAdjust(Item[Item.Count - 1]);
end;
Result := Item.FSize.X;
end;
procedure FillBounds(Item: TfrxCrossHeader; Offset: TfrxPoint);
var
i, j, l: Integer;
h: Extended;
begin
l := Item.Level;
if l <> -1 then
h := LevelHeights[l] else
h := Item.FSize.Y;
if Item.FIsCellHeader then
h := LevelHeights[FLevelsCount]
else if Item.IsTotal then
for j := l + 1 to FLevelsCount - 1 do
h := h + LevelHeights[j];
Item.FBounds := frxRect(Offset.X, Offset.Y, Item.FSize.X, h);
Offset.Y := Offset.Y + h;
for i := 0 to Item.Count - 1 do
begin
FillBounds(Item[i], Offset);
Offset.X := Offset.X + Item[i].FSize.X;
end;
end;
begin
DoAdjust(Self);
SetLength(LevelHeights, FLevelsCount + 1);
Items := AllItems;
// calculate height of each row
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
l := Item.Level;
// cell headers always adjust the last level height
if Item.FIsCellHeader then
l := FLevelsCount
// don't count total elemens unless they are on last level.
// such elements will be adjusted later
else if Item.IsTotal then
if l <> FLevelsCount - 1 then continue;
if l >= 0 then
if Item.FSize.Y > LevelHeights[l] then
LevelHeights[l] := Item.FSize.Y;
end;
if FNoLevels then
LevelHeights[0] := 0;
// adjust level height - count totals that not on the last level
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
l := Item.Level;
if Item.IsTotal and (l < FLevelsCount - 1) then
begin
h := 0;
for j := l to FLevelsCount - 1 do
h := h + LevelHeights[j];
if Item.FSize.Y > h then
LevelHeights[FLevelsCount - 1] := LevelHeights[FLevelsCount - 1] + Item.FSize.Y - h;
end;
end;
{ syncronize height of CornerMemos[0] and [1] }
if FCorner <> nil then
begin
if not FMemo.Visible then
FSize.Y := 0;
if not FCorner.FMemo.Visible then
FCorner.FSize.Y := 0;
h := FSize.Y;
if FCorner.FSize.Y > h then
h := FCorner.FSize.Y;
FSize.Y := h;
if not FNoLevels then
FCorner.FSize.Y := h;
end;
//FillBounds(Self, frxPoint(0, 0));
{ update height of CornerMemos[2..n] }
if FCorner <> nil then
begin
h := 0;
l := FLevelsCount - 1;
if HasCellHeaders then
Inc(l);
for i := 0 to l do
h := h + LevelHeights[i];
if FNoLevels then
h := h + FSize.Y;
for i := 0 to FCorner.Count - 1 do
if FCorner[i].FSize.Y > h then
begin
if l = 0 then
if TfrxCrossHeader(Items[0]).Memo.Visible then
TfrxCrossHeader(Items[0]).FSize.Y := FCorner[i].FSize.Y
else
begin
hAvg := (FCorner[i].FSize.Y - h) / (l + 1);
for j := 0 to l do
LevelHeights[j] := LevelHeights[j] + hAvg; {normalize columns height}
end;
end
else FCorner[i].FSize.Y := h;
end;
FillBounds(Self, frxPoint(0, 0));
Items.Free;
LevelHeights := nil;
end;
procedure TfrxCrossColumnHeader.CalcSizes(MaxWidth, MinWidth: Integer; AutoSize: Boolean);
var
i: Integer;
Items: TList;
Item: TfrxCrossHeader;
s: WideString;
m: TfrxCustomMemoView;
DefHeaderSize: Extended;
aMaxWidth, aMinWidth: Integer;
begin
Items := AllItems;
DefHeaderSize := 0;
aMaxWidth := MaxWidth;
aMinWidth := MinWidth;
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
if (MaxWidth = -1) then
begin
if not Item.FRecalcSizes then Continue;
Item.FRecalcSizes := False;
aMaxWidth := Round(Item.FSize.X);
aMinWidth := Round(Item.FSize.X);
end;
m := Item.FMemo;
if m <> nil then
begin
if AutoSize or (((m.Width = 0) or (m.Height = 0)) and m.Visible) then
begin
m.Width := aMaxWidth;
s := m.Text;
m.Text := m.FormatData(Item.Value);
if m.Lines.Count = 0 then
m.Text := ' ';
Item.FSize := CalcSize(m);
m.Text := s;
end
else
begin
if (Item.Count = 0) or (Item.Count = 1) then
Item.FSize.X := m.Width;
if not Item.IsTotal then
Item.FSize.Y := m.Height;
end;
if Item.FSize.X < aMinWidth then
Item.FSize.X := aMinWidth;
if Item.FSize.X > aMaxWidth then
Item.FSize.X := aMaxWidth;
if Item.DefaultHeight > 0 then
DefHeaderSize := Item.DefaultHeight;
if(DefHeaderSize > 0) then
if i < Items.Count - 1 then
DefHeaderSize := DefHeaderSize - Item.FSize.Y
else if (i = Items.Count - 1) then
begin
if Item.FSize.Y < DefHeaderSize then
Item.FSize.Y := DefHeaderSize
end;
end;
end;
Items.Free;
end;
{ TfrxCrossRowHeader }
procedure TfrxCrossRowHeader.CalcBounds;
var
i, j, l: Integer;
w: Extended;
Items: TList;
Item: TfrxCrossHeader;
LevelWidths: array of Extended;
function DoAdjust(Item: TfrxCrossHeader; HideNested: Boolean = false): Extended;
var
i: Integer;
Height: Extended;
begin
if Item.Count = 0 then
begin
if HideNested then
Item.FSize.Y := 0;
Result := Item.FSize.Y;
Exit;
end;
Height := 0;
for i := 0 to Item.Count - 1 do
Height := Height + DoAdjust(Item[i], ((not Item.Visible) and (Item.Parent <> nil)) or HideNested);
if Item.FSize.Y < Height then
Item.FSize.Y := Height
else
begin
Item[Item.Count - 1].FSize.Y := Item[Item.Count - 1].FSize.Y + Item.FSize.Y - Height;
DoAdjust(Item[Item.Count - 1]);
end;
Result := Item.FSize.Y;
end;
procedure FillBounds(Item: TfrxCrossHeader; Offset: TfrxPoint);
var
i, j, l: Integer;
w: Extended;
begin
l := Item.Level;
if l <> -1 then
w := LevelWidths[l] else
w := Item.FSize.X;
if Item.FIsCellHeader then
w := LevelWidths[FLevelsCount]
else if Item.IsTotal then
for j := l + 1 to FLevelsCount - 1 do
w := w + LevelWidths[j];
Item.FBounds := frxRect(Offset.X, Offset.Y, w, Item.FSize.Y);
Offset.X := Offset.X + w;
for i := 0 to Item.Count - 1 do
begin
FillBounds(Item[i], Offset);
Offset.Y := Offset.Y + Item[i].FSize.Y;
end;
end;
begin
DoAdjust(Self);
SetLength(LevelWidths, FLevelsCount + 1);
Items := AllItems;
// calculate maxwidth of each row
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
l := Item.Level;
// cell headers always adjust the last level width
if Item.FIsCellHeader then
l := FLevelsCount
// don't count total elemens unless they are on last level.
// such elements will be adjusted later
else if Item.IsTotal then
if l <> FLevelsCount - 1 then continue;
if l >= 0 then
if Item.FSize.X > LevelWidths[l] then
LevelWidths[l] := Item.FSize.X;
end;
// adjust totals
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
l := Item.Level;
if Item.IsTotal and (l < FLevelsCount - 1) then
begin
w := 0;
for j := l to FLevelsCount - 1 do
w := w + LevelWidths[j];
if Item.FSize.X > w then
LevelWidths[FLevelsCount - 1] := LevelWidths[FLevelsCount - 1] + Item.FSize.X - w;
end;
end;
// adjust corner
for i := 0 to FCorner.Count - 1 do
if FCorner[i].FSize.X > LevelWidths[i] then
LevelWidths[i] := FCorner[i].FSize.X
else
FCorner[i].FSize.X := LevelWidths[i];
FillBounds(Self, frxPoint(0, 0));
Items.Free;
LevelWidths := nil;
end;
procedure TfrxCrossRowHeader.CalcSizes(MaxWidth, MinWidth: Integer; AutoSize: Boolean);
var
i: Integer;
Items: TList;
Item: TfrxCrossHeader;
s: WideString;
m: TfrxCustomMemoView;
begin
Items := AllItems;
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
m := Item.FMemo;
if m <> nil then
begin
if AutoSize or (((m.Width = 0) or (m.Height = 0)) and m.Visible) then
begin
m.Width := MaxWidth;
s := m.Text;
m.Text := m.FormatData(Item.Value);
if m.Lines.Count = 0 then
m.Text := ' ';
Item.FSize := CalcSize(m);
m.Text := s;
end
else
begin
if Item.Count = 0 then
Item.FSize.Y := m.Height;
if not Item.IsTotal then
Item.FSize.X := m.Width;
end;
if Item.FSize.X < MinWidth then
Item.FSize.X := MinWidth;
if Item.FSize.X > MaxWidth then
Item.FSize.X := MaxWidth;
end;
end;
Items.Free;
end;
{ TfrxCutBandItem }
destructor TfrxCutBandItem.Destroy;
begin
Band.Free;
inherited;
end;
{ TfrxCutBands }
constructor TfrxCutBands.Create;
begin
inherited Create(TfrxCutBandItem);
end;
procedure TfrxCutBands.Add(ABand: TfrxBand; AFromIndex, AToIndex: Integer);
begin
with TfrxCutBandItem(inherited Add) do
begin
Band := ABand;
FromIndex := AFromIndex;
ToIndex := AToIndex;
end;
end;
function TfrxCutBands.GetItems(Index: Integer): TfrxCutBandItem;
begin
Result := TfrxCutBandItem(inherited Items[Index]);
end;
{ TfrxGridLineItem }
constructor TfrxGridLineItem.Create(Collection: TCollection);
begin
inherited;
Objects := TList.Create;
end;
destructor TfrxGridLineItem.Destroy;
begin
Objects.Free;
inherited;
end;
{ TfrxGridLines }
constructor TfrxGridLines.Create;
begin
inherited Create(TfrxGridLineItem);
end;
procedure TfrxGridLines.Add(AObj: TObject; ACoord: Extended);
var
i: Integer;
Item: TfrxGridLineItem;
begin
Item := nil;
for i := 0 to Count - 1 do
if Abs(Items[i].Coord - ACoord) < 1 then
begin
Item := Items[i];
break;
end;
if Item = nil then
Item := TfrxGridLineItem(inherited Add);
Item.Coord := ACoord;
Item.Objects.Add(AObj);
end;
function TfrxGridLines.GetItems(Index: Integer): TfrxGridLineItem;
begin
Result := TfrxGridLineItem(inherited Items[Index]);
end;
{ TfrxCustomCrossView }
constructor TfrxCustomCrossView.Create(AOwner: TComponent);
var
i: Integer;
begin
inherited;
Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
Color := clWhite;
frComponentStyle := frComponentStyle - [csPreviewVisible] + [csContainer];
FAllMemos := TList.Create;
FCellMemos := TList.Create;
FCellHeaderMemos := TList.Create;
FColumnMemos := TList.Create;
FColumnTotalMemos := TList.Create;
FCornerMemos := TList.Create;
FRowMemos := TList.Create;
FRowTotalMemos := TList.Create;
FCellFields := TStringList.Create;
FColumnFields := TStringList.Create;
FRowFields := TStringList.Create;
FColumnBands := TfrxCutBands.Create;
FRowBands := TfrxCutBands.Create;
FGridX := TfrxGridLines.Create;
FGridY := TfrxGridLines.Create;
FAutoSize := True;
FBorder := True;
FGapX := 3;
FGapY := 3;
FMaxWidth := 200;
FRepeatHeaders := True;
FShowColumnHeader := True;
FShowColumnTotal := True;
FShowRowHeader := True;
FShowRowTotal := True;
FShowCorner := True;
FShowTitle := True;
FAllowDuplicates := True;
FClearBeforePrint := True;
FSuppressNullRecords := True;
FShowMoveArrow := False;
FShowCellBreak := True;
SetDotMatrix(Page is TfrxDMPPage);
CreateCornerMemos(3);
CellLevels := 1;
ColumnLevels := 1;
RowLevels := 1;
FPrevCrossLastPage := -1;
for i := 0 to 63 do
begin
FCellFunctions[i] := cfSum;
FColumnSort[i] := soAscending;
FRowSort[i] := soAscending;
end;
end;
destructor TfrxCustomCrossView.Destroy;
begin
ClearMemos;
FAllMemos.Free;
FCellMemos.Free;
FCellHeaderMemos.Free;
FColumnMemos.Free;
FColumnTotalMemos.Free;
FCornerMemos.Free;
FRowMemos.Free;
FRowTotalMemos.Free;
FCellFields.Free;
FColumnFields.Free;
FRowFields.Free;
FColumnBands.Free;
FRowBands.Free;
FGridX.Free;
FGridY.Free;
ClearMatrix;
inherited;
end;
function TfrxCustomCrossView.GetCellFunctions(Index: Integer): TfrxCrossFunction;
begin
Result := FCellFunctions[Index];
end;
function TfrxCustomCrossView.GetCellMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FCellMemos[Index];
end;
function TfrxCustomCrossView.GetCellHeaderMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FCellHeaderMemos[Index];
end;
function TfrxCustomCrossView.GetColumnMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FColumnMemos[Index];
end;
function TfrxCustomCrossView.GetColumnTotalMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FColumnTotalMemos[Index];
end;
function TfrxCustomCrossView.GetCornerMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FCornerMemos[Index];
end;
function TfrxCustomCrossView.GetRowMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FRowMemos[Index];
end;
function TfrxCustomCrossView.GetRowTotalMemos(Index: Integer): TfrxCustomMemoView;
begin
Result := FRowTotalMemos[Index];
end;
function TfrxCustomCrossView.GetColumnSort(Index: Integer): TfrxCrossSortOrder;
begin
Result := FColumnSort[Index];
end;
function TfrxCustomCrossView.GetRowSort(Index: Integer): TfrxCrossSortOrder;
begin
Result := FRowSort[Index];
end;
procedure TfrxCustomCrossView.SetCellFunctions(Index: Integer;
const Value: TfrxCrossFunction);
begin
FCellFunctions[Index] := Value;
end;
procedure TfrxCustomCrossView.SetColumnSort(Index: Integer; Value: TfrxCrossSortOrder);
begin
FColumnSort[Index] := Value;
end;
procedure TfrxCustomCrossView.SetRowSort(Index: Integer; Value: TfrxCrossSortOrder);
begin
FRowSort[Index] := Value;
end;
function TfrxCustomCrossView.ColCount: Integer;
begin
Result := FColumns.Count;
end;
function TfrxCustomCrossView.RowCount: Integer;
begin
Result := FRows.Count;
end;
function TfrxCustomCrossView.IsGrandTotalColumn(Index: Integer): Boolean;
begin
Result := Index = FColumns.Count - 1;
end;
function TfrxCustomCrossView.IsGrandTotalRow(Index: Integer): Boolean;
begin
Result := Index = FRows.Count - 1;
end;
function TfrxCustomCrossView.IsTotalColumn(Index: Integer): Boolean;
var
i: Integer;
begin
Result := False;
for i := 0 to FColumns.FIndexesCount - 1 do
if VarToStr(FColumns[Index].Indexes[i]) = '@@@' then
Result := True;
end;
function TfrxCustomCrossView.IsTotalRow(Index: Integer): Boolean;
var
i: Integer;
begin
Result := False;
for i := 0 to FRows.FIndexesCount - 1 do
if VarToStr(FRows[Index].Indexes[i]) = '@@@' then
Result := True;
end;
function TfrxCustomCrossView.GetColumnIndexes(AColumn: Integer): Variant;
begin
Result := FColumns[AColumn].Indexes;
end;
function TfrxCustomCrossView.GetRowIndexes(ARow: Integer): Variant;
begin
Result := FRows[ARow].Indexes;
end;
procedure TfrxCustomCrossView.SetCellFields(const Value: TStrings);
begin
FCellFields.Assign(Value);
end;
procedure TfrxCustomCrossView.SetColumnFields(const Value: TStrings);
begin
FColumnFields.Assign(Value);
end;
procedure TfrxCustomCrossView.SetRowFields(const Value: TStrings);
begin
FRowFields.Assign(Value);
end;
procedure TfrxCustomCrossView.SetCellLevels(const Value: Integer);
var
max: Integer;
begin
if Value > 64 then exit;
FCellLevels := Value;
CreateCellMemos(FCellLevels * (FRowLevels + 1) * (FColumnLevels + 1));
max := FRowLevels;
if FColumnLevels > max then
max := FColumnLevels;
CreateCellHeaderMemos(FCellLevels * (max + 1));
end;
procedure TfrxCustomCrossView.SetColumnLevels(const Value: Integer);
var
max, lvl: Integer;
begin
if Value > 64 then exit;
FColumnLevels := Value;
lvl := FColumnLevels;
if lvl = 0 then
lvl := 1;
CreateColumnMemos(lvl);
CreateCellMemos(FCellLevels * (FRowLevels + 1) * (FColumnLevels + 1));
max := FRowLevels;
if FColumnLevels > max then
max := FColumnLevels;
CreateCellHeaderMemos(FCellLevels * (max + 1));
end;
procedure TfrxCustomCrossView.SetRowLevels(const Value: Integer);
var
max, lvl: Integer;
begin
if Value > 64 then exit;
FRowLevels := Value;
lvl := FRowLevels;
if lvl = 0 then
lvl := 1;
CreateRowMemos(lvl);
CreateCornerMemos(FRowLevels + 3);
CreateCellMemos(FCellLevels * (FRowLevels + 1) * (FColumnLevels + 1));
max := FRowLevels;
if FColumnLevels > max then
max := FColumnLevels;
CreateCellHeaderMemos(FCellLevels * (max + 1));
end;
procedure TfrxCustomCrossView.SetDotMatrix(const Value: Boolean);
begin
FDotMatrix := Value;
if FDotMatrix then
begin
FGapX := 0;
FGapY := 0;
end;
end;
function TfrxCustomCrossView.IsAcceptAsChild(aParent: TfrxComponent): Boolean;
begin
Result := (aParent is TfrxBand) or (aParent is TfrxPage);
end;
function TfrxCustomCrossView.IsContain(X, Y: Extended): Boolean;
begin
if FShowMoveArrow and (AbsLeft <= X) and (AbsLeft + 16 >= X) and (AbsTop - 20 <= Y) and (AbsTop + 2 >= Y) then
begin
Result := True;
Exit;
end;
Result := Inherited IsContain(X, Y);
end;
function TfrxCustomCrossView.IsCrossValid: Boolean;
begin
Result := True;
end;
function TfrxCustomCrossView.ColumnHeaderHeight: Extended;
begin
Result := ColumnHeader.Height;
end;
function TfrxCustomCrossView.RowHeaderWidth: Extended;
begin
Result := RowHeader.Width;
if FNoRows then
Result := 0;
end;
procedure TfrxCustomCrossView.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = FNextCross) then
FNextCross := nil;
end;
procedure TfrxCustomCrossView.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineBinaryProperty('Memos', ReadMemos, WriteMemos, True);
end;
procedure TfrxCustomCrossView.ReadMemos(Stream: TStream);
var
x: TfrxXMLDocument;
i: Integer;
procedure GetItem(m: TfrxCustomMemoView; const Name: String; Index: Integer);
var
xs: TfrxXMLSerializer;
Item: TfrxXMLItem;
begin
Item := x.Root.FindItem(Name);
if Index >= Item.Count then Exit;
Item := Item[Index];
xs := TfrxXMLSerializer.Create(nil);
xs.OldFormat := x.OldVersion;
m.Color := clNone;
m.Frame.Color := clBlack;
m.Frame.Width := 1;
m.Frame.Typ := [];
m.Font.Style := [];
m.HAlign := haLeft;
m.VAlign := vaTop;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
xs.ReadRootComponent(m, Item);
xs.Free;
end;
function GetItem1(const Name: String; Index: Integer): TfrxCrossFunction;
var
Item: TfrxXMLItem;
begin
Result := cfNone;
Item := x.Root.FindItem(Name);
if Index >= Item.Count then Exit;
Item := Item[Index];
Result := TfrxCrossFunction(StrToInt(Item.Text));
end;
function GetItem2(const Name: String; Index: Integer): TfrxCrossSortOrder;
var
Item: TfrxXMLItem;
begin
Result := soAscending;
Item := x.Root.FindItem(Name);
if Index >= Item.Count then Exit;
Item := Item[Index];
Result := TfrxCrossSortOrder(StrToInt(Item.Text));
end;
begin
x := TfrxXMLDocument.Create;
try
x.LoadFromStream(Stream);
for i := 0 to FCellLevels - 1 do
CellFunctions[i] := GetItem1('cellfunctions', i);
for i := 0 to FCellHeaderMemos.Count - 1 do
GetItem(CellHeaderMemos[i], 'cellheadermemos', i);
for i := 0 to FCellMemos.Count - 1 do
GetItem(CellMemos[i], 'cellmemos', i);
for i := 0 to FColumnMemos.Count - 1 do
begin
GetItem(ColumnMemos[i], 'columnmemos', i);
GetItem(ColumnTotalMemos[i], 'columntotalmemos', i);
ColumnSort[i] := GetItem2('columnsort', i);
end;
for i := 0 to FRowMemos.Count - 1 do
begin
GetItem(RowMemos[i], 'rowmemos', i);
GetItem(RowTotalMemos[i], 'rowtotalmemos', i);
RowSort[i] := GetItem2('rowsort', i);
end;
for i := 0 to FCornerMemos.Count - 1 do
GetItem(CornerMemos[i], 'cornermemos', i);
finally
x.Free;
end;
end;
procedure TfrxCustomCrossView.WriteMemos(Stream: TStream);
var
x: TfrxXMLDocument;
i: Integer;
procedure AddItem(m: TfrxCustomMemoView; const Name: String);
var
xs: TfrxXMLSerializer;
begin
xs := TfrxXMLSerializer.Create(nil);
xs.WriteRootComponent(m, True, x.Root.FindItem(Name).Add);
xs.Free;
end;
procedure AddItem1(f: TfrxCrossFunction; const Name: String);
var
Item: TfrxXMLItem;
begin
Item := x.Root.FindItem(Name);
Item := Item.Add;
Item.Name := 'item';
Item.Text := IntToStr(Integer(f));
end;
procedure AddItem2(f: TfrxCrossSortOrder; const Name: String);
var
Item: TfrxXMLItem;
begin
Item := x.Root.FindItem(Name);
Item := Item.Add;
Item.Name := 'item';
Item.Text := IntToStr(Integer(f));
end;
begin
x := TfrxXMLDocument.Create;
x.Root.Name := 'cross';
try
x.Root.Add.Name := 'cellmemos';
x.Root.Add.Name := 'cellheadermemos';
x.Root.Add.Name := 'columnmemos';
x.Root.Add.Name := 'columntotalmemos';
x.Root.Add.Name := 'cornermemos';
x.Root.Add.Name := 'rowmemos';
x.Root.Add.Name := 'rowtotalmemos';
x.Root.Add.Name := 'cellfunctions';
x.Root.Add.Name := 'columnsort';
x.Root.Add.Name := 'rowsort';
for i := 0 to FCellLevels - 1 do
AddItem1(CellFunctions[i], 'cellfunctions');
for i := 0 to FCellHeaderMemos.Count - 1 do
AddItem(CellHeaderMemos[i], 'cellheadermemos');
for i := 0 to FCellMemos.Count - 1 do
AddItem(CellMemos[i], 'cellmemos');
for i := 0 to FColumnMemos.Count - 1 {FColumnLevels - 1} do
begin
AddItem(ColumnMemos[i], 'columnmemos');
AddItem(ColumnTotalMemos[i], 'columntotalmemos');
AddItem2(ColumnSort[i], 'columnsort');
end;
for i := 0 to FRowMemos.Count - 1 {FRowLevels - 1} do
begin
AddItem(RowMemos[i], 'rowmemos');
AddItem(RowTotalMemos[i], 'rowtotalmemos');
AddItem2(RowSort[i], 'rowsort');
end;
for i := 0 to FCornerMemos.Count - 1 do
AddItem(CornerMemos[i], 'cornermemos');
x.SaveToStream(Stream);
finally
x.Free;
end;
end;
procedure TfrxCustomCrossView.CreateCellHeaderMemos(NewCount: Integer);
var
i: Integer;
m: TfrxCustomMemoView;
begin
for i := FCellHeaderMemos.Count to NewCount - 1 do
begin
m := CreateMemo(nil);
FCellHeaderMemos.Add(m);
m.Restrictions := [rfDontDelete];
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
m.AllowExpressions := False;
end;
end;
procedure TfrxCustomCrossView.CreateCellMemos(NewCount: Integer);
var
i: Integer;
m: TfrxCustomMemoView;
begin
for i := FCellMemos.Count to NewCount - 1 do
begin
m := CreateMemo(nil);
FCellMemos.Add(m);
m.Restrictions := [rfDontDelete];
m.HAlign := haRight;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
m.AllowExpressions := False;
end;
end;
procedure TfrxCustomCrossView.CreateColumnMemos(NewCount: Integer);
var
i: Integer;
m: TfrxCustomMemoView;
begin
for i := FColumnMemos.Count to NewCount - 1 do
begin
m := CreateMemo(nil);
FColumnMemos.Add(m);
m.Restrictions := [rfDontDelete, rfDontEdit];
m.HAlign := haCenter;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
m.AllowExpressions := False;
m := CreateMemo(nil);
FColumnTotalMemos.Add(m);
m.Restrictions := [rfDontDelete];
if i = 0 then
m.Text := 'Grand Total'
else
m.Text := 'Total';
m.Font.Style := [fsBold];
m.HAlign := haCenter;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
end;
end;
procedure TfrxCustomCrossView.CreateRowMemos(NewCount: Integer);
var
i: Integer;
m: TfrxCustomMemoView;
begin
for i := FRowMemos.Count to NewCount - 1 do
begin
m := CreateMemo(nil);
FRowMemos.Add(m);
m.Restrictions := [rfDontDelete, rfDontEdit];
m.HAlign := haCenter;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
m.AllowExpressions := False;
m := CreateMemo(nil);
FRowTotalMemos.Add(m);
m.Restrictions := [rfDontDelete];
if i = 0 then
m.Text := 'Grand Total'
else
m.Text := 'Total';
m.Font.Style := [fsBold];
m.HAlign := haCenter;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
end;
end;
procedure TfrxCustomCrossView.CreateCornerMemos(NewCount: Integer);
var
i: Integer;
m: TfrxCustomMemoView;
begin
for i := FCornerMemos.Count to NewCount - 1 do
begin
m := CreateMemo(nil);
FCornerMemos.Add(m);
m.Restrictions := [rfDontDelete];
m.HAlign := haCenter;
m.VAlign := vaCenter;
m.Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
m.AllowExpressions := False;
end;
end;
procedure TfrxCustomCrossView.ClearMemos;
begin
while FCellHeaderMemos.Count > 0 do
begin
CellHeaderMemos[0].Free;
FCellHeaderMemos.Delete(0);
end;
while FCellMemos.Count > 0 do
begin
CellMemos[0].Free;
FCellMemos.Delete(0);
end;
while FColumnMemos.Count > 0 do
begin
ColumnMemos[0].Free;
FColumnMemos.Delete(0);
ColumnTotalMemos[0].Free;
FColumnTotalMemos.Delete(0);
end;
while FRowMemos.Count > 0 do
begin
RowMemos[0].Free;
FRowMemos.Delete(0);
RowTotalMemos[0].Free;
FRowTotalMemos.Delete(0);
end;
while FCornerMemos.Count > 0 do
begin
CornerMemos[0].Free;
FCornerMemos.Delete(0);
end;
end;
procedure TfrxCustomCrossView.InitMatrix;
var
ColL, RowL: Integer;
begin
ClearMatrix;
RowL := FRowLevels;
FNoRows := FRowLevels = 0;
if FNoRows then
RowL := 1;
ColL := FColumnLevels;
FNoColumns := FColumnLevels = 0;
if FNoColumns then
ColL := 1;
FRows := TfrxCrossRows.Create;
FRows.FIndexesCount := RowL;
FRows.FSortOrder := FRowSort;
FRows.FCellLevels := FCellLevels;
FColumns := TfrxCrossColumns.Create;
FColumns.FIndexesCount := ColL;
FColumns.FSortOrder := FColumnSort;
FCorner := TfrxCrossCorner.Create(1);
FCorner.FMemo := CornerMemos[0];
FCorner.Value := CornerMemos[0].Text;
FCorner.FLevelsCount := 1;
FRowHeader := TfrxCrossRowHeader.Create(FCellLevels);
FRowHeader.FMemos := FRowMemos;
FRowHeader.FTotalMemos := FRowTotalMemos;
FRowHeader.FLevelsCount := RowL;
FRowHeader.HasCellHeaders := (FCellLevels > 1) and not FPlainCells;
FRowHeader.FCorner := FCorner;
FRowHeader.FNoLevels := FNoRows;
FColumnHeader := TfrxCrossColumnHeader.Create(FCellLevels);
FColumnHeader.FMemos := FColumnMemos;
FColumnHeader.FTotalMemos := FColumnTotalMemos;
FColumnHeader.FMemo := CornerMemos[1];
FColumnHeader.Value := CornerMemos[1].Text;
FColumnHeader.FLevelsCount := ColL;
FColumnHeader.HasCellHeaders := (FCellLevels > 1) and FPlainCells;
FColumnHeader.FCorner := FCorner;
FColumnHeader.FNoLevels := FNoColumns;
end;
function TfrxCustomCrossView.GetNestedObjects: TList;
var
i: Integer;
NestedObjects: TList;
procedure DoNested(Memo: TfrxCustomMemoView);
var
i: Integer;
c: TfrxComponent;
begin
for i := 0 to Memo.Objects.Count - 1 do
begin
c := Memo.Objects[i];
NestedObjects.Add(c);
end;
end;
begin
NestedObjects := TList.Create;
for i := 0 to FCellHeaderMemos.Count - 1 do
DoNested(CellHeaderMemos[i]);
for i := 0 to FCellMemos.Count - 1 do
DoNested(CellMemos[i]);
for i := 0 to FColumnMemos.Count - 1 do
begin
DoNested(ColumnMemos[i]);
DoNested(ColumnTotalMemos[i]);
end;
for i := 0 to FRowMemos.Count - 1 do
begin
DoNested(RowMemos[i]);
DoNested(RowTotalMemos[i]);
end;
for i := 0 to FCornerMemos.Count - 1 do
DoNested(CornerMemos[i]);
Result := NestedObjects;
end;
procedure TfrxCustomCrossView.InitMemos(AddToScript: Boolean);
var
i: Integer;
m: TfrxCustomMemoView;
NestedObjects: TList;
begin
for i := 0 to FCellHeaderMemos.Count - 1 do
begin
m := CellHeaderMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.Visible := True;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
m.Name := Name + 'CellHeader' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
for i := 0 to FCellMemos.Count - 1 do
begin
m := CellMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
m.Name := Name + 'Cell' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
for i := 0 to FColumnMemos.Count - 1 do
begin
m := ColumnMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.Visible := True;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
m.Name := Name + 'Column' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
m := ColumnTotalMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.AllowExpressions := False;
m.Name := Name + 'ColumnTotal' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
for i := 0 to FRowMemos.Count - 1 do
begin
m := RowMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.Visible := true;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
m.Name := Name + 'Row' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
m := RowTotalMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.AllowExpressions := False;
m.Name := Name + 'RowTotal' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
for i := 0 to FCornerMemos.Count - 1 do
begin
m := CornerMemos[i];
m.GapX := FGapX;
m.GapY := FGapY;
m.AllowMirrorMode := AllowMirrorMode;
m.Restrictions := m.Restrictions - [rfDontMove, rfDontSize];
if i > 2 then
m.Visible := True;
m.Name := Name + 'Corner' + IntToStr(i);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
NestedObjects := GetNestedObjects;
for i := 0 to NestedObjects.Count - 1 do
begin
m := NestedObjects[i];
m.Name := Name + 'Object' + IntToStr(m.Tag);
if AddToScript then
Report.Script.AddObject(m.Name, m);
end;
NestedObjects.Free;
end;
procedure TfrxCustomCrossView.ClearMatrix;
begin
FRows.Free;
FRows := nil;
FColumns.Free;
FColumns := nil;
FCorner.Free;
FCorner := nil;
FRowHeader.Free;
FRowHeader := nil;
FColumnHeader.Free;
FColumnHeader := nil;
end;
procedure TfrxCustomCrossView.AddValue(const Rows, Columns, Cells: array of Variant);
var
i: Integer;
Row: TfrxCrossRow;
Column: TfrxCrossColumn;
Cell: PfrCrossCell;
Value, v: Variant;
isNull: Boolean;
begin
if not IsCrossValid then
raise Exception.Create('Cross-tab is not valid');
if FRows = nil then Exit;
{ check for all nulls }
isNull := True;
for i := Low(Rows) to High(Rows) do
if not VarIsNull(Rows[i]) then
isNull := False;
if isNull then
begin
for i := Low(Columns) to High(Columns) do
if not VarIsNull(Columns[i]) then
isNull := False;
if isNull then
begin
for i := Low(Cells) to High(Cells) do
if not VarIsNull(Cells[i]) then
isNull := False;
end;
end;
if isNull and FSuppressNullRecords then Exit;
if FNoColumns then
Column := FColumns.Column([Null]) else
Column := FColumns.Column(Columns);
if FNoRows then
Row := FRows.Row([Null]) else
Row := FRows.Row(Rows);
Cell := Row.GetCell(Column.CellIndex);
for i := 0 to FCellLevels - 1 do
begin
Value := Cell.Value;
v := Cells[i];
if FCellFunctions[i] = cfCount then
begin
if v = Null then
v := 0
else
v := 1;
end;
if Value = Null then
Cell.Value := v
else if (TVarData(Value).VType = varString) or (TVarData(Value).VType = varOleStr){$IFDEF Delphi12} or (TVarData(Value).VType = varUString){$ENDIF} then
begin
if FAllowDuplicates or
(Pos(#13#10 + v + #13#10, #13#10 + Cell.Value + #13#10) = 0) then
Cell.Value := Value + #13#10 + v
end
else
Cell.Value := Value + v;
Cell := Cell.Next;
end;
end;
function TfrxCustomCrossView.GetValue(ARow, AColumn, ACell: Integer): Variant;
var
Row: TfrxCrossRow;
Column: TfrxCrossColumn;
Cell: PfrCrossCell;
begin
Result := Null;
Column := FColumns[AColumn];
Row := FRows[ARow];
Cell := Row.GetCell(Column.CellIndex);
while (Cell <> nil) and (ACell > 0) do
begin
Cell := Cell.Next;
Dec(ACell);
end;
if Cell <> nil then
Result := Cell.Value;
end;
procedure TfrxCustomCrossView.CreateHeader(Header: TfrxCrossHeader;
Source: TfrxIndexCollection; Totals: TList; TotalVisible: Boolean);
var
i, j, IndexesCount: Integer;
LastValues, CurValues: TfrxVariantArray;
Unsorted: Boolean;
function ExpandVariable(s: String; const Value: Variant): String;
var
i: Integer;
begin
{ expand the [Value] macro if any (eg. if total memo contains
the text: 'Total of [Value]' }
i := Pos('[VALUE]', AnsiUppercase(s));
if i <> 0 then
begin
Delete(s, i, 7);
Insert(VarToStr(Value), s, i);
end;
Result := s;
end;
procedure AddTotals;
var
j, k: Integer;
begin
for j := 0 to IndexesCount - 1 do
{ if value changed... }
if LastValues[j] <> CurValues[j] then
begin
{ ...create subtotals for all down-level values }
for k := IndexesCount - 1 downto j + 1 do
if TfrxCustomMemoView(Totals[k]).Visible then
begin
{ '@@@' means that this is subtotal cell }
LastValues[k] := '@@@' +
ExpandVariable(TfrxCustomMemoView(Totals[k]).Text, LastValues[k - 1]);
{ create header cells... }
Header.AddValues(LastValues, Unsorted);
LastValues[k] := '@@@';
{ ...and row/column item }
Source.InsertItem(i, LastValues);
Inc(i);
end;
break;
end;
end;
begin
if Source.Count = 0 then Exit;
Unsorted := (Source.FSortOrder[0] = soNone);
IndexesCount := Source.FIndexesCount;
{ copy first indexes to lastvalues }
LastValues := Copy(Source.Items[0].Indexes, 0, IndexesCount);
i := 0;
while i < Source.Count do
begin
{ copy current indexes to curvalues }
CurValues := Copy(Source.Items[i].Indexes, 0, IndexesCount);
{ if lastvalues <> curvalues, make a subtotal item }
AddTotals;
{ add header cells }
Header.AddValues(CurValues, Unsorted);
LastValues := CurValues;
Inc(i);
end;
{ create last subtotal item }
CurValues := Copy(Source.Items[0].Indexes, 0, IndexesCount);
for j := 0 to IndexesCount - 1 do
CurValues[j] := Null;
AddTotals;
{ create grand total }
if TotalVisible and TfrxCustomMemoView(Totals[0]).Visible then
begin
LastValues[0] := '@@@' + TfrxCustomMemoView(Totals[0]).Text;
Header.AddValues(LastValues, Unsorted);
LastValues[0] := '@@@';
Source.InsertItem(i, LastValues);
end;
end;
procedure TfrxCustomCrossView.CreateHeaders;
var
i: Integer;
begin
CreateHeader(FColumnHeader, FColumns, FColumnTotalMemos, not FNoColumns);
CreateHeader(FRowHeader, FRows, FRowTotalMemos, not FNoRows);
{ add corner elements }
for i := 0 to FRowLevels - 1 do
FCorner.AddChild(FCornerMemos[3 + i]);
if FRowHeader.HasCellHeaders then
FCorner.AddChild(FCornerMemos[2]);
end;
procedure TfrxCustomCrossView.CalcTotal(Header: TfrxCrossHeader;
Source: TfrxIndexCollection);
var
i, j: Integer;
Items: TList;
Values, Counts: TfrxVariantArray;
Item: TfrxCrossHeader;
p: PfrCrossCell;
FinalPass: Boolean;
procedure CellToArrays(p: PfrCrossCell);
var
i: Integer;
begin
for i := 0 to FCellLevels - 1 do
begin
Values[i] := p.Value;
Counts[i] := p.Count;
if (FCellFunctions[i] = cfAvg) and FinalPass and (p.Count <> 0) then
p.Value := p.Value / p.Count;
p := p.Next;
end;
end;
procedure ArraysToCell(p: PfrCrossCell);
var
i: Integer;
begin
for i := 0 to FCellLevels - 1 do
begin
p.Value := Item.FFuncValues[i];
p.Count := Item.FCounts[i];
if (FCellFunctions[i] = cfAvg) and FinalPass then
if p.Count <> 0 then
p.Value := p.Value / p.Count else
p.Value := 0;
if (FCellFunctions[i] = cfCount) and not FinalPass then
p.Count := p.Value;
p := p.Next;
end;
end;
begin
Items := Header.TerminalItems;
SetLength(Values, FCellLevels);
SetLength(Counts, FCellLevels);
FinalPass := Source = FColumns;
{ scan the matrix }
for i := 0 to Source.Count - 1 do
begin
for j := 0 to Items.Count - 1 do
TfrxCrossHeader(Items[j]).Reset(FCellFunctions);
for j := 0 to Items.Count - 1 do
begin
Item := Items[j];
if Source = FRows then
p := FRows[i].GetCell(FColumns[j].CellIndex) else
p := FRows[j].GetCell(FColumns[i].CellIndex);
if not Item.IsTotal then
begin
{ convert cell values to Values and Counts arrays }
CellToArrays(p);
{ accumulate values in the header items }
Item.AddFuncValues(Values, Counts, FCellFunctions);
end
else
begin
{ get the accumulated values from the item's parent }
Item := Item.Parent;
{ and convert it to the cell }
ArraysToCell(p);
end;
end;
end;
Items.Free;
Values := nil;
Counts := nil;
end;
procedure TfrxCustomCrossView.CalcTotals;
begin
{ scan the matrix from left to right, then from top to bottom }
CalcTotal(FColumnHeader, FRows);
{ final pass, scan the matrix from top to bottom, then from left to right }
CalcTotal(FRowHeader, FColumns);
end;
procedure TfrxCustomCrossView.CalcBounds;
var
i, j, k: Integer;
ColumnItems, RowItems, CornerItems, HeaderItems: TList;
ColumnItem, RowItem: TfrxCrossHeader;
Cell: PfrCrossCell;
m: TfrxCustomMemoView;
NewHeight: Extended;
Size: TfrxPoint;
function CalcRowHeight(aRowItem: TfrxCrossHeader): Extended;
var
idx: Integer;
begin
Result := 0;
for idx := 0 to aRowItem.FItems.Count - 1 do
Result := Result + TfrxCrossHeader(aRowItem.FItems[idx]).FSize.Y;
end;
procedure DoCalc(const Value: Variant);
var
i, r: Integer;
s: WideString;
Width, NewWidth: Extended;
WidthChanged: Boolean;
begin
if FAutoSize then
begin
s := m.Text;
m.Text := m.FormatData(Value, CellMemos[k].DisplayFormat);
r := m.Rotation;
m.Rotation := 0;
Width := FMaxWidth;
NewWidth := -1;
DoCalcWidth(j, NewWidth);
WidthChanged := NewWidth <> -1;
if not WidthChanged then
NewWidth := Width;
m.Width := NewWidth;
Size := CalcSize(m);
Size.X := Size.X + FAddWidth;
Size.Y := Size.Y + FAddHeight;
if Size.X > FMaxWidth then
Size.X := FMaxWidth;
if Size.X < FMinWidth then
Size.X := FMinWidth;
m.Rotation := r;
m.Text := s;
end
else
begin
NewWidth := m.Width;
DoCalcWidth(j, NewWidth);
WidthChanged := NewWidth <> m.Width;
m.Width := NewWidth;
end;
if WidthChanged then
begin
Size.X := NewWidth;
ColumnItem.FSize.X := Size.X;
ColumnItem.FRecalcSizes := True;
for i := 0 to ColumnItem.Count - 1 do
begin
ColumnItem[i].FSize.X := NewWidth;
ColumnItem[i].FRecalcSizes := True;
end;
end;
if FDefHeight <> 0 then
Size.Y := FDefHeight;
if NewWidth = 0 then
Size.Y := 0;
end;
begin
ColumnItems := FColumnHeader.TerminalItems;
RowItems := FRowHeader.TerminalItems;
{ create cell headers }
if FCellLevels > 1 then
if FPlainCells then
begin
for i := 0 to ColumnItems.Count - 1 do
begin
ColumnItem := ColumnItems[i];
for j := 0 to FCellLevels - 1 do
ColumnItem.AddCellHeader(FCellHeaderMemos, i, j);
end;
end
else
begin
for i := 0 to RowItems.Count - 1 do
begin
RowItem := RowItems[i];
for j := 0 to FCellLevels - 1 do
RowItem.AddCellHeader(FCellHeaderMemos, i, j);
end;
end;
{ calculate the widths of columns and the heights of rows }
if Corner.Visible then
FCorner.CalcSizes(FMaxWidth, FMinWidth, FAutoSize);
{ correction when title text height is greater than column }
if FColumnHeader.Visible or not FAutoSize then
begin
CornerItems := FCorner.AllItems;
HeaderItems := FColumnHeader.AllItems;
if (CornerItems.Count > 1) and (HeaderItems.Count > 1) then
if (TfrxCrossCorner(CornerItems[1]).Visible) and (TfrxCrossCorner(HeaderItems[1]).DefaultHeight = 0) then
TfrxCrossCorner(HeaderItems[1]).DefaultHeight := Round(TfrxCrossCorner(CornerItems[1]).FSize.Y);
CornerItems.Free;
HeaderItems.Free;
FColumnHeader.CalcSizes(FMaxWidth, FMinWidth, FAutoSize);
end;
FRowHeader.CalcSizes(FMaxWidth, FMinWidth, FAutoSize);
{ scanning the matrix cells and update calculated widths and heights }
for i := 0 to RowItems.Count - 1 do
begin
RowItem := RowItems[i];
RowItem.FIsIndex := True;
RowItem.FIndex := i;
for j := 0 to ColumnItems.Count - 1 do
begin
ColumnItem := ColumnItems[j];
ColumnItem.FIsIndex := True;
ColumnItem.FIndex := j;
if not FAutoSize then
begin
if ((FOnCalcWidth = '') or IsDesigning) then continue;
Size := frxPoint(0 , 0);
end;
Cell := FRows[i].GetCell(FColumns[j].CellIndex);
for k := 0 to FCellLevels - 1 do
begin
m := CellMemos[ColumnItem.FTotalIndex * ((FRowLevels + 1) * FCellLevels) +
RowItem.FTotalIndex * FCellLevels + k];
DoCalc(Cell.Value);
if FCellLevels > 1 then
if FPlainCells then
begin
if ColumnItem[k].FSize.X < Size.X then
ColumnItem[k].FSize.X := Size.X;
if RowItem.FSize.Y < Size.Y then
RowItem.FSize.Y := Size.Y;
end
else
begin
if RowItem[k].FSize.Y < Size.Y then
RowItem[k].FSize.Y := Size.Y;
if ColumnItem.FSize.X < Size.X then
ColumnItem.FSize.X := Size.X;
end
else
begin
if RowItem.FSize.Y < Size.Y then
RowItem.FSize.Y := Size.Y;
if ColumnItem.FSize.X < Size.X then
ColumnItem.FSize.X := Size.X;
end;
Cell := Cell.Next;
end;
end;
if (RowItem.FSize.Y = 0) and (RowItem.FItems.Count > 0) and not FPlainCells then
RowItem.FSize.Y := CalcRowHeight(RowItem);
NewHeight := RowItem.FSize.Y;
DoCalcHeight(i, NewHeight);
RowItem.FSize.Y := NewHeight;
if NewHeight = 0 then
RowItem.Visible := False;
end;
{ calculate the positions and sizes of the header cells }
FColumnHeader.CalcSizes(-1, -1, FAutoSize);
FCorner.CalcBounds;
FColumnHeader.CalcBounds;
FRowHeader.CalcBounds;
{ recalc corner again - it may be adjusted in rowheader }
FCorner.CalcBounds;
ColumnItems.Free;
RowItems.Free;
end;
function TfrxCustomCrossView.CreateMemo(Parent: TfrxComponent): TfrxCustomMemoView;
begin
if FDotMatrix then
Result := TfrxDMPMemoView.Create(Parent)
else
Result := TfrxMemoView.Create(Parent);
Result.frComponentStyle := Result.frComponentStyle + [csContained];
end;
procedure TfrxCustomCrossView.CorrectDMPBounds(Memo: TfrxCustomMemoView);
begin
if Memo is TfrxDMPMemoView then
begin
Memo.Left := Memo.Left + fr1CharX;
Memo.Top := Memo.Top + fr1CharY;
Memo.Width := Memo.Width - fr1CharX;
Memo.Height := Memo.Height - fr1CharY;
end;
end;
function TfrxCustomCrossView.GetContainedComponent(X, Y: Extended;
IsCanContain: TfrxComponent): TfrxComponent;
//var
// i: Integer;
// c: TfrxComponent;
begin
Result := Inherited GetContainedComponent(X, Y, IsCanContain);
//TODO : Make proper GetContainedComponent
// for i := 0 to FAllMemos.Count - 1 do
// begin
// c := FAllMemos[i];
// if c.IsContain(X, Y) then
// begin
// Result := c.GetContainedComponent(X, Y, IsCanContain);
// if (Result = nil) or (IsCanContain = Self) then
// Result := c;
// break;
// end;
// end;
// if (Result = Self) and (IsCanContain = Self){ or (not IsAcceptControl(IsCanContain)))} then
// Result := nil;
end;
function TfrxCustomCrossView.GetContainerObjects: TList;
begin
Result := FAllMemos;
end;
function TfrxCustomCrossView.ContainerAdd(Obj: TfrxComponent): Boolean;
var
i, j, n: Integer;
c: TfrxComponent;
NestedObjects: TList;
Found: Boolean;
begin
Result := False;
if (Obj is TfrxCustomCrossView) or (Obj is TfrxSubreport) then Exit;
{ call DrawCross to calc visible memos and their bounds }
DrawCross(nil, FScaleX, FScaleY, AbsLeft, AbsTop);
{ find parent memo for added object }
for i := 0 to FAllMemos.Count - 1 do
begin
c := FAllMemos[i];
if (Obj.Left >= c.Left) and (Obj.Top >= c.Top) and
(Obj.Left <= c.Left + c.Width) and
(Obj.Top <= c.Top + c.Height) then
begin
Obj.Left := Obj.Left - c.Left;
Obj.Top := Obj.Top - c.Top;
Obj.Owner.RemoveComponent(Obj);
Obj.Parent := c;
{ create unique tag for it - it will be used for name creation }
NestedObjects := GetNestedObjects;
n := 0;
while True do
begin
Inc(n);
Found := False;
for j := 0 to NestedObjects.Count - 1 do
if TfrxComponent(NestedObjects[j]).Tag = n then
begin
Found := True;
break;
end;
if not Found then
begin
Obj.Tag := n;
Obj.Name := Name + 'Object' + IntToStr(n);
break;
end;
end;
NestedObjects.Free;
Result := True;
break;
end;
end;
end;
function TfrxCustomCrossView.DoMouseDown(X, Y: Integer;
Button: TMouseButton; Shift: TShiftState; var EventParams: TfrxInteractiveEventsParams): Boolean;
var
i, j: Integer;
c: TfrxComponent;
begin
DrawCross(nil, FScaleX, FScaleY, AbsLeft, AbsTop);
FGridUsed := nil;
FFirstMousePos := Point(X, Y);
FLastMousePos := Point(X, Y);
for i := 0 to FGridX.Count - 1 do
for j := 0 to FGridX[i].Objects.Count - 1 do
begin
c := FGridX[i].Objects[j];
if (Abs(c.AbsLeft + c.Width - X / FScaleX) < 2) and
(Y / FScaleY >= c.AbsTop) and (Y / FScaleY <= c.AbsTop + c.Height) then
begin
FGridUsed := FGridX;
FMovingObjects := i;
FMouseDown := True;
break;
end;
end;
for i := 0 to FGridY.Count - 1 do
for j := 0 to FGridY[i].Objects.Count - 1 do
begin
c := FGridY[i].Objects[j];
if (Abs(c.AbsTop + c.Height - Y / FScaleY) < 2) and
(X / FScaleX >= c.AbsLeft) and (X / FScaleX <= c.AbsLeft + c.Width) then
begin
FGridUsed := FGridY;
FMovingObjects := i;
FMouseDown := True;
break;
end;
end;
Result := FMouseDown;
// if not ((AbsLeft <= X) and (AbsLeft + 16 >= X) and (AbsTop - 20 <= Y) and (AbsTop + 2 >= Y)) then
// Result := False;
end;
procedure TfrxCustomCrossView.DoMouseEnter(aPreviousObject: TfrxComponent;
var EventParams: TfrxInteractiveEventsParams);
begin
inherited;
FShowMoveArrow := True;
EventParams.Refresh := True;
end;
procedure TfrxCustomCrossView.DoMouseLeave(aNextObject: TfrxComponent;
var EventParams: TfrxInteractiveEventsParams);
begin
inherited;
if FShowMoveArrow then
EventParams.Refresh := True;
FDragActive := False;
FShowMoveArrow := False;
end;
procedure TfrxCustomCrossView.DoMouseMove(X, Y: Integer;
Shift: TShiftState; var EventParams: TfrxInteractiveEventsParams);
var
i, j: Integer;
c: TfrxComponent;
begin
if (FScaleX = 0) or (FScaleY = 0) then Exit;
if not FMouseDown then
begin
DrawCross(nil, FScaleX, FScaleY, AbsLeft, AbsTop);
for i := 0 to FGridX.Count - 1 do
for j := 0 to FGridX[i].Objects.Count - 1 do
begin
c := FGridX[i].Objects[j];
if (Abs(c.AbsLeft + c.Width - X / FScaleX) < 2) and
(Y / FScaleY >= c.AbsTop) and (Y / FScaleY <= c.AbsTop + c.Height) then
begin
TWinControl(EventParams.Sender).Cursor := crHSplit;
break;
end;
end;
for i := 0 to FGridY.Count - 1 do
for j := 0 to FGridY[i].Objects.Count - 1 do
begin
c := FGridY[i].Objects[j];
if (Abs(c.AbsTop + c.Height - Y / FScaleY) < 2) and
(X / FScaleX >= c.AbsLeft) and (X / FScaleX <= c.AbsLeft + c.Width) then
begin
TWinControl(EventParams.Sender).Cursor := crVSplit;
break;
end;
end;
end
else
begin
if FGridUsed = FGridX then
begin
for i := 0 to FGridX[FMovingObjects].Objects.Count - 1 do
begin
c := FGridX[FMovingObjects].Objects[i];
c.Width := c.Width + (X - FLastMousePos.X);
end;
end
else if FGridUsed = FGridY then
begin
for i := 0 to FGridY[FMovingObjects].Objects.Count - 1 do
begin
c := FGridY[FMovingObjects].Objects[i];
c.Height := c.Height + (Y - FLastMousePos.Y);
end;
end;
FLastMousePos := Point(X, Y);
EventParams.Refresh := True;
end;
end;
procedure TfrxCustomCrossView.DoMouseUp(X, Y: Integer; Button: TMouseButton;
Shift: TShiftState; var EventParams: TfrxInteractiveEventsParams);
var
i: Integer;
begin
if (FScaleX = 0) or (FScaleY = 0) then Exit;
for i := 0 to FAllMemos.Count - 1 do
if TfrxComponent(FAllMemos[i]).IsContain(X / FScaleX, Y / FScaleY) then
begin
TfrxComponent(FAllMemos[i]).MouseUp(X, Y, Button, Shift, EventParams);
break;
end;
if not FMouseDown then Exit;
FMouseDown := False;
if FAutoSize and ((Abs(X - FFirstMousePos.X) > 5) or (Abs(Y - FFirstMousePos.Y) > 5)) then
frxInfoMsg(frxResources.Get('crResize'));
if not FAutoSize and ((Abs(X - FFirstMousePos.X) > 1) or (Abs(Y - FFirstMousePos.Y) > 1)) then
EventParams.Modified := True;
end;
function TfrxCustomCrossView.DoMouseWheel(Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint;
var EventParams: TfrxInteractiveEventsParams): Boolean;
begin
Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos, EventParams);
{ just for testing event - remove }
// MaxWidth := MaxWidth + WheelDelta;
// Result := False;
// EventParams.Refresh := True;
// EventParams.Modified := True;
end;
function TfrxCustomCrossView.DrawCross(Canvas: TCanvas; ScaleX, ScaleY,
OffsetX, OffsetY: Extended): TfrxPoint;
procedure FillMatrix;
var
i: Integer;
RowValues, ColumnValues, CellValues: array of Variant;
begin
BeginMatrix;
InitMemos(False);
SetLength(RowValues, RowLevels);
SetLength(ColumnValues, ColumnLevels);
SetLength(CellValues, CellLevels);
for i := 0 to RowLevels - 1 do
RowValues[i] := '[' + RowFields[i] + ']';
for i := 0 to ColumnLevels - 1 do
ColumnValues[i] := '[' + ColumnFields[i] + ']';
for i := 0 to CellLevels - 1 do
CellValues[i] := 0;
AddValue(RowValues, ColumnValues, CellValues);
RowValues := nil;
ColumnValues := nil;
CellValues := nil;
EndMatrix;
end;
procedure DrawLine(x, y, dx, dy: Extended);
begin
Canvas.MoveTo(Round(x * ScaleX), Round(y * ScaleY));
Canvas.LineTo(Round((x + dx) * ScaleX), Round((y + dy) * ScaleY));
end;
procedure DrawScriptSign(c: TfrxReportComponent);
begin
if (Canvas <> nil) and (c.OnBeforePrint <> '') then
with c, Canvas do
begin
Pen.Style := psSolid;
Pen.Color := clRed;
Pen.Width := 1;
DrawLine(AbsLeft + 2, AbsTop + 1, 0, 7);
DrawLine(AbsLeft + 3, AbsTop + 2, 0, 5);
DrawLine(AbsLeft + 4, AbsTop + 3, 0, 3);
DrawLine(AbsLeft + 5, AbsTop + 4, 0, 1);
end;
end;
procedure DrawObj(Obj: TfrxReportComponent; Child: Boolean = False);
var
i: Integer;
begin
{ don't let a child move outside parent }
if Child then
begin
if Obj.Left < 0 then
Obj.Left := 0;
if Obj.Left + Obj.Width > Obj.Parent.Width then
Obj.Left := Obj.Parent.Width - Obj.Width;
if Obj.Top < 0 then
Obj.Top := 0;
if Obj.Top + Obj.Height > Obj.Parent.Height then
Obj.Top := Obj.Parent.Height - Obj.Height;
end;
Obj.IsDesigning := IsDesigning;
if Canvas <> nil then
Obj.InteractiveDraw(Canvas, ScaleX, ScaleY, 0, 0);
DrawScriptSign(Obj);
if not Child then
begin
FGridX.Add(Obj, Obj.AbsLeft + Obj.Width);
FGridY.Add(Obj, Obj.AbsTop + Obj.Height);
end;
FAllMemos.Add(Obj);
for i := 0 to Obj.Objects.Count - 1 do
DrawObj(Obj.Objects[i], True);
end;
procedure DrawHeader(Header: TfrxCrossHeader; p: TfrxPoint; hVisible: Boolean = true);
var
i: Integer;
Items: TList;
Item: TfrxCrossHeader;
r: TfrxRect;
m: TfrxCustomMemoView;
SaveWidth, SaveHeight: Extended; // for dot-matrix
s: WideString;
begin
Items := Header.AllItems;
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
m := Item.Memo;
r := Item.Bounds;
s := m.Text;
m.Text := VarToWideStr(Item.Value);
m.SetBounds(r.Left + p.X, r.Top + p.Y, r.Right, r.Bottom);
SaveWidth := m.Width;
SaveHeight := m.Height;
CorrectDMPBounds(m);
if m.Left + m.Width > Result.X then
Result.X := m.Left + m.Width;
if m.Top + m.Height > Result.Y then
Result.Y := m.Top + m.Height;
if m.Visible and hVisible then
DrawObj(m)
else
begin
FGridX.Add(m, m.AbsLeft + m.Width);
FGridY.Add(m, m.AbsTop + m.Height);
end;
m.Text := s;
if m is TfrxDMPMemoView then
TfrxDMPMemoView(m).SetBoundsDirect(m.Left - fr1CharX / 2,
m.Top - fr1CharY / 2, SaveWidth, SaveHeight);
end;
Items.Free;
end;
procedure DrawCell(p: TfrxPoint);
var
i, j, CellIndex: Integer;
Cell: Variant;
ColumnItems, RowItems: TList;
ColumnItem, RowItem: TfrxCrossHeader;
m: TfrxCustomMemoView;
SaveWidth, SaveHeight: Extended; // for dot-matrix
begin
ColumnItems := ColumnHeader.TerminalItems;
RowItems := RowHeader.TerminalItems;
for i := 0 to RowItems.Count - 1 do
begin
RowItem := RowItems[i];
for j := 0 to ColumnItems.Count - 1 do
begin
ColumnItem := ColumnItems[j];
if FCellLevels > 1 then
if FPlainCells then
begin
CellIndex := ColumnItem.FCellIndex;
Cell := GetValue(i, ColumnItem.FIndex, CellIndex);
end
else
begin
CellIndex := RowItem.FCellIndex;
Cell := GetValue(RowItem.FIndex, j, CellIndex);
end
else
begin
CellIndex := 0;
Cell := GetValue(i, j, 0);
end;
m := CellMemos[ColumnItem.FTotalIndex * ((FRowLevels + 1) * FCellLevels) +
RowItem.FTotalIndex * FCellLevels + CellIndex];
m.Visible := True;
m.Restrictions := [rfDontDelete, rfDontEdit];
m.Text := m.FormatData(Cell, CellMemos[CellIndex].DisplayFormat);
m.SetBounds(ColumnItem.Bounds.Left, RowItem.Bounds.Top,
ColumnItem.Bounds.Right, RowItem.Bounds.Bottom);
m.Left := m.Left + p.X;
m.Top := m.Top + p.Y;
SaveWidth := m.Width;
SaveHeight := m.Height;
CorrectDMPBounds(m);
if m.Left + m.Width > Result.X then
Result.X := m.Left + m.Width;
if m.Top + m.Height > Result.Y then
Result.Y := m.Top + m.Height;
DrawObj(m);
if m is TfrxDMPMemoView then
TfrxDMPMemoView(m).SetBoundsDirect(m.Left - fr1CharX / 2,
m.Top - fr1CharY / 2, SaveWidth, SaveHeight);
end;
end;
ColumnItems.Free;
RowItems.Free;
end;
begin
Result := frxPoint(0, 0);
FGridX.Clear;
FGridY.Clear;
FAllMemos.Clear;
if IsCrossValid and not FDragActive then
begin
FillMatrix;
if Corner.Visible then
DrawHeader(Corner, frxPoint(OffsetX, OffsetY));
if ColumnHeader.Visible or not FAutoSize then
DrawHeader(ColumnHeader, frxPoint(OffsetX + RowHeaderWidth, OffsetY), ColumnHeader.Visible);
if RowHeader.Visible then
DrawHeader(RowHeader, frxPoint(OffsetX, OffsetY + ColumnHeaderHeight));
DrawCell(frxPoint(OffsetX + RowHeaderWidth, OffsetY + ColumnHeaderHeight));
end;
Result.X := Result.X - OffsetX;
Result.Y := Result.Y - OffsetY;
end;
procedure TfrxCustomCrossView.Draw(Canvas: TCanvas; ScaleX, ScaleY,
OffsetX, OffsetY: Extended);
var
size: TfrxPoint;
begin
size := DrawCross(nil, ScaleX, ScaleY, AbsLeft, AbsTop);
if (size.X > 0) and (size.Y > 0) then
begin
Width := size.X;
Height := size.Y;
end;
FillType := ftBrush;
TfrxBrushFill(Fill).BackColor := clWhite;
Frame.Style := fsDot;
if FDotMatrix then
begin
Width := Width + fr1CharX;
Height := Height + fr1CharY;
end
else
Frame.Typ := [];
inherited;
DrawCross(Canvas, ScaleX, ScaleY, AbsLeft, AbsTop);
if FShowMoveArrow then
frxResources.MainButtonImages.Draw(Canvas, FX, FY - 20, 110);
if Assigned(FComponentEditors) then
FComponentEditors.DrawCustomEditor(Canvas, Rect(FX, FY, FX1, FY1));
end;
procedure TfrxCustomCrossView.ApplyStyle(Style: TfrxStyles);
var
i: Integer;
s: String;
begin
for i := 0 to FCellHeaderMemos.Count - 1 do
CellHeaderMemos[i].ApplyStyle(Style.Find('cellheader'));
for i := 0 to FCellMemos.Count - 1 do
CellMemos[i].ApplyStyle(Style.Find('cell'));
for i := 0 to FColumnMemos.Count - 1 do
begin
ColumnMemos[i].ApplyStyle(Style.Find('column'));
if i = 0 then
s := 'colgrand'
else
s := 'coltotal';
ColumnTotalMemos[i].ApplyStyle(Style.Find(s));
end;
for i := 0 to FRowMemos.Count - 1 do
begin
RowMemos[i].ApplyStyle(Style.Find('row'));
if i = 0 then
s := 'rowgrand'
else
s := 'rowtotal';
RowTotalMemos[i].ApplyStyle(Style.Find(s));
end;
for i := 0 to FCornerMemos.Count - 1 do
CornerMemos[i].ApplyStyle(Style.Find('corner'));
end;
procedure TfrxCustomCrossView.GetStyle(Style: TfrxStyles);
procedure DoStyle(m: TfrxCustomMemoView; const s: String);
var
stItem: TfrxStyleItem;
begin
stItem := Style.Find(s);
if stItem = nil then
stItem := Style.Add;
stItem.Name := s;
stItem.Color := m.Color;
stItem.Font := m.Font;
stItem.Frame := m.Frame;
end;
begin
if FCellHeaderMemos.Count > 0 then
DoStyle(CellHeaderMemos[0], 'cellheader');
if FCellMemos.Count > 0 then
DoStyle(CellMemos[0], 'cell');
if FColumnMemos.Count > 0 then
begin
DoStyle(ColumnMemos[0], 'column');
DoStyle(ColumnTotalMemos[0], 'colgrand');
if FColumnTotalMemos.Count > 1 then
DoStyle(ColumnTotalMemos[1], 'coltotal');
end;
if FRowMemos.Count > 0 then
begin
DoStyle(RowMemos[0], 'row');
DoStyle(RowTotalMemos[0], 'rowgrand');
if FRowTotalMemos.Count > 1 then
DoStyle(RowTotalMemos[1], 'rowtotal');
end;
if FCornerMemos.Count > 0 then
DoStyle(CornerMemos[0], 'corner');
end;
procedure TfrxCustomCrossView.UpdateVisibility;
begin
Corner.Visible := FShowCorner and not FNoRows
and FShowColumnHeader and FShowRowHeader;
CornerMemos[0].Visible := Corner.Visible and not FNoColumns and FShowTitle;
CornerMemos[2].Visible := Corner.Visible and (FCellLevels > 1) and not FPlainCells;
ColumnHeader.Visible := FShowColumnHeader;
if FColumnTotalMemos.Count > 0 then
ColumnTotalMemos[0].Visible := FShowColumnTotal and not FNoColumns;
CornerMemos[1].Visible := FShowTitle and ColumnHeader.Visible;
ColumnMemos[0].Visible := ColumnHeader.Visible and not FNoColumns;
RowHeader.Visible := not FNoRows and FShowRowHeader;
if FRowTotalMemos.Count > 0 then
RowTotalMemos[0].Visible := FShowRowTotal and not FNoRows;
end;
procedure TfrxCustomCrossView.BeginMatrix;
begin
InitMatrix;
UpdateVisibility;
end;
procedure TfrxCustomCrossView.EndMatrix;
begin
CreateHeaders;
CalcTotals;
CalcBounds(FAddWidth, FAddHeight);
end;
procedure TfrxCustomCrossView.FillMatrix;
begin
end;
procedure TfrxCustomCrossView.DoCalcHeight(Row: Integer; var Height: Extended);
var
v: Variant;
begin
if FOnCalcHeight <> '' then
begin
v := VarArrayOf([Row, GetRowIndexes(Row), Height]);
if Report <> nil then
Report.DoParamEvent(FOnCalcHeight, v);
Height := v[2];
end;
if Assigned(FOnBeforeCalcHeight) then
FOnBeforeCalcHeight(Row, GetRowIndexes(Row), Height);
end;
procedure TfrxCustomCrossView.DoCalcWidth(Column: Integer; var Width: Extended);
var
v: Variant;
begin
if FOnCalcWidth <> '' then
begin
v := VarArrayOf([Column, GetColumnIndexes(Column), Width]);
if Report <> nil then
Report.DoParamEvent(FOnCalcWidth, v);
Width := v[2];
end;
if Assigned(FOnBeforeCalcWidth) then
FOnBeforeCalcWidth(Column, GetColumnIndexes(Column), Width);
end;
function TfrxCustomCrossView.DoDragDrop(Source: TObject; X, Y: Integer;
var EventParams: TfrxInteractiveEventsParams): Boolean;
begin
Result := False;
// Result := False; // do not precess default enents
FDragActive := False;
// if Assigned(FComponentEditors) then
// if FComponentEditors.DoCustomDragDrop(Source, Round(X / FScaleX), Round(Y / FScaleY), EventParams) then
// begin
// EventParams.Modified := True;
// EventParams.Refresh := True;
// end;
end;
function TfrxCustomCrossView.DoDragOver(Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean;
var EventParams: TfrxInteractiveEventsParams): Boolean;
begin
Result := False;
// if Assigned(FComponentEditors) then
// FComponentEditors.DoCustomDragOver(Source, Round(X / FScaleX), Round(Y / FScaleY), State, Accept, EventParams);
if Accept then
FDragActive := True;
end;
procedure TfrxCustomCrossView.DoOnCell(Memo: TfrxCustomMemoView;
Row, Column, Cell: Integer; const Value: Variant);
var
v: Variant;
begin
if FOnPrintCell <> '' then
begin
v := VarArrayOf([frxInteger(Memo), Row, Column, Cell, GetRowIndexes(Row),
GetColumnIndexes(Column), Value]);
if Report <> nil then
Report.DoParamEvent(FOnPrintCell, v);
end;
if Assigned(FOnBeforePrintCell) then
FOnBeforePrintCell(Memo, Row, Column, Cell, GetRowIndexes(Row),
GetColumnIndexes(Column), Value);
end;
procedure TfrxCustomCrossView.DoOnColumnHeader(Memo: TfrxCustomMemoView;
Header: TfrxCrossHeader);
var
v: Variant;
begin
if FOnPrintColumnHeader <> '' then
begin
v := VarArrayOf([frxInteger(Memo), Header.GetIndexes, Header.GetValues, Header.Value]);
if Report <> nil then
Report.DoParamEvent(FOnPrintColumnHeader, v);
end;
if Assigned(FOnBeforePrintColumnHeader) then
FOnBeforePrintColumnHeader(Memo, Header.GetIndexes, Header.GetValues, Header.Value);
end;
procedure TfrxCustomCrossView.DoOnRowHeader(Memo: TfrxCustomMemoView;
Header: TfrxCrossHeader);
var
v: Variant;
begin
if FOnPrintRowHeader <> '' then
begin
v := VarArrayOf([frxInteger(Memo), Header.GetIndexes, Header.GetValues, Header.Value]);
if Report <> nil then
Report.DoParamEvent(FOnPrintRowHeader, v);
end;
if Assigned(FOnBeforePrintRowHeader) then
FOnBeforePrintRowHeader(Memo, Header.GetIndexes, Header.GetValues, Header.Value);
end;
procedure TfrxCustomCrossView.BeforeStartReport;
begin
inherited;
InitMemos(True);
end;
procedure TfrxCustomCrossView.BeforePrint;
begin
inherited;
if FClearBeforePrint then
BeginMatrix;
end;
procedure TfrxCustomCrossView.GetData;
begin
inherited;
Report.SetProgressMessage(frxResources.Get('crFillMx'));
if IsCrossValid then
FillMatrix;
Report.SetProgressMessage(frxResources.Get('crBuildMx'));
EndMatrix;
RenderMatrix;
end;
procedure TfrxCustomCrossView.RenderMatrix;
var
i, j, Page, SavePage, cIndex: Integer;
CurY, FirstCurY, SaveCurY, AddWidth, MaxX: Extended;
Band: TfrxBand;
ColumnItems: TList;
RowItems: TList;
VarRowIndex, VarColumnIndex: TfrxVariable;
aReport: TfrxReport;
function GetCellBand(RowIndex, ColumnIndex: Integer): TfrxBand;
var
i, iFrom, iTo, j: Integer;
Cell: Variant;
CellIndex: Integer;
ColumnItem, RowItem: TfrxCrossHeader;
m, Memo: TfrxCustomMemoView;
LeftMargin, TopMargin: Extended;
SameMemos: array[0..63] of TfrxCustomMemoView;
c, c1: TfrxReportComponent;
begin
RowItem := RowItems[RowIndex];
Result := TfrxNullBand.Create(aReport);
Result.AllowMirrorMode := AllowMirrorMode;
Result.ShiftEngine := seDontShift;
Result.Height := RowItem.Bounds.Bottom;
iFrom := FColumnBands[ColumnIndex].FromIndex;
iTo := FColumnBands[ColumnIndex].ToIndex;
LeftMargin := TfrxCrossHeader(ColumnItems[iFrom]).Bounds.Left;
TopMargin := RowItem.Bounds.Top;
for i := 0 to CellLevels - 1 do
SameMemos[i] := nil;
for i := iFrom to iTo do
begin
ColumnItem := ColumnItems[i];
if FCellLevels > 1 then
if FPlainCells then
begin
CellIndex := ColumnItem.FCellIndex;
Cell := GetValue(RowIndex, ColumnItem.FIndex, CellIndex);
end
else
begin
CellIndex := RowItem.FCellIndex;
Cell := GetValue(RowItem.FIndex, i, CellIndex);
end
else
begin
CellIndex := 0;
Cell := GetValue(RowIndex, i, 0);
end;
m := CellMemos[ColumnItem.FTotalIndex * ((FRowLevels + 1) * FCellLevels) +
RowItem.FTotalIndex * FCellLevels + CellIndex];
Memo := CreateMemo(Result);
Memo.Assign(m);
SetupOriginalComponent(Memo, m);
if Cell <> Null then
THackMemoView(Memo).Value := Cell
else
THackMemoView(Memo).Value := 0;
if THackMemoView(Memo).HideZeros and (not VarIsNull(THackMemoView(Memo).Value)) and (TVarData(THackMemoView(Memo).Value).VType <> varString)
{$IFDEF Delphi12}and (TVarData(THackMemoView(Memo).Value).VType <> varUString){$ENDIF} and
(TVarData(THackMemoView(Memo).Value).VType <> varOleStr) and SameValue(THackMemoView(Memo).Value, 0, aReport.EngineOptions.ZeroPrecisionValue) then
Memo.Text := ''
else
Memo.Text := Memo.FormatData(Cell, CellMemos[CellIndex].DisplayFormat);
//Memo.Rotation := 0;
Memo.SetBounds(ColumnItem.Bounds.Left - LeftMargin + AddWidth,
RowItem.Bounds.Top - TopMargin,
ColumnItem.Bounds.Right,
RowItem.Bounds.Bottom);
CorrectDMPBounds(Memo);
if Memo.AbsLeft + Memo.Width > MaxX then
MaxX := Memo.AbsLeft + Memo.Width;
Memo.Visible := (Memo.Width <> 0) and (Memo.Height <> 0);
DoOnCell(Memo, RowItem.FIndex, ColumnItem.FIndex, CellIndex, Cell);
if FBorder then
begin
if FPlainCells then
begin
if RowIndex = 0 then
Memo.Frame.Typ := Memo.Frame.Typ + [ftTop];
if (i = 0) and (CellIndex = 0) then
Memo.Frame.Typ := Memo.Frame.Typ + [ftLeft];
if (i = ColumnItems.Count - 1) and (CellIndex = CellLevels - 1) then
Memo.Frame.Typ := Memo.Frame.Typ + [ftRight];
if RowIndex = RowItems.Count - 1 then
Memo.Frame.Typ := Memo.Frame.Typ + [ftBottom];
end
else
begin
if (RowIndex = 0) and (CellIndex = 0) then
Memo.Frame.Typ := Memo.Frame.Typ + [ftTop];
if i = 0 then
Memo.Frame.Typ := Memo.Frame.Typ + [ftLeft];
if i = ColumnItems.Count - 1 then
Memo.Frame.Typ := Memo.Frame.Typ + [ftRight];
if (RowIndex = RowItems.Count - 1) and (CellIndex = CellLevels - 1) then
Memo.Frame.Typ := Memo.Frame.Typ + [ftBottom];
end;
end;
{ check if previous memo has the same value and JoinEqualCells is True }
cIndex := CellIndex;
{ for PlainCells every next cell follows previous }
if PlainCells then
cIndex := 0;
if JoinEqualCells then
if RowItem.IsTotal or ColumnItem.IsTotal then
SameMemos[cIndex] := nil
else if (SameMemos[cIndex] = nil) or RowItem.IsTotal or ColumnItem.IsTotal or
(TVarData(THackMemoView(Memo).Value).VType <> TVarData(THackMemoView(SameMemos[cIndex]).Value).VType) or
(THackMemoView(SameMemos[cIndex]).Value <> THackMemoView(Memo).Value) then
SameMemos[cIndex] := Memo
else
begin
SameMemos[cIndex].Width := SameMemos[cIndex].Width + Memo.Width;
SameMemos[cIndex].HAlign := haCenter;
Memo.Free;
Memo := SameMemos[cIndex];
end;
VarRowIndex.Value := RowIndex;
VarColumnIndex.Value := i;
aReport.LocalValue := THackMemoView(Memo).Value;
aReport.CurObject := Memo.Name;
aReport.DoBeforePrint(Memo);
{ process memo children if any }
for j := 0 to m.Objects.Count - 1 do
begin
c := m.Objects[j];
c1 := TfrxReportComponent(c.NewInstance);
c1.Create(Result);
c1.Assign(c);
c1.Left := c1.Left + Memo.Left;
c1.Top := c1.Top + Memo.Top;
aReport.CurObject := c.Name;
aReport.DoBeforePrint(c1);
end;
end;
end;
procedure DrawCorner(Offset: TfrxPoint);
var
i: Integer;
Items: TList;
Item: TfrxCrossHeader;
r: TfrxRect;
m: TfrxCustomMemoView;
begin
if not FShowRowHeader or not FShowColumnHeader or FNoRows or not FShowCorner then Exit;
Items := Corner.AllItems;
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
m := Item.Memo;
r := Item.Bounds;
m.BeforePrint;
m.Text := VarToWideStr(Item.Value);
if Item.Value <> Null then
THackMemoView(m).Value := Item.Value
else
THackMemoView(m).Value := 0;
m.SetBounds(r.Left + Offset.X, r.Top + Offset.Y, r.Right, r.Bottom);
CorrectDMPBounds(m);
aReport.PreviewPages.AddObject(m);
m.AfterPrint;
end;
Items.Free;
end;
procedure DoPagination(i, j: Integer);
var
k, kFrom, kTo: Integer;
begin
if ShowColumnHeader and (FRepeatHeaders or (i = 0)) then
begin
Band := FColumnBands[j].Band;
Band.Top := CurY;
aReport.Engine.ShowBand(Band);
end;
if ShowRowHeader and (FRepeatHeaders or (j = 0)) and not FNoRows then
begin
Band := FRowBands[i].Band;
if j = 0 then
Band.Left := Left
else
Band.Left := 0;
Band.Top := Band.Top + CurY;
aReport.Engine.ShowBand(Band);
Band.Top := Band.Top - CurY;
if ShowColumnHeader and (FRepeatHeaders or (i = 0)) then
DrawCorner(frxPoint(Band.Left, Band.Top + CurY - ColumnHeaderHeight));
end;
if FRepeatHeaders or (i = 0) then
aReport.Engine.CurY := CurY + ColumnHeaderHeight else
aReport.Engine.CurY := CurY;
if FRepeatHeaders or (j = 0) then
begin
AddWidth := RowHeaderWidth;
if j = 0 then
AddWidth := AddWidth + Left;
end
else
AddWidth := 0;
kFrom := FRowBands[i].FromIndex;
kTo := FRowBands[i].ToIndex;
for k := kFrom to kTo do
begin
Band := GetCellBand(k, j);
Band.Top := aReport.Engine.CurY;
aReport.Engine.ShowBand(Band);
Band.Free;
end;
end;
begin
aReport := Report;
{ chek if cross header doesn't fit on the free space}
if ColumnHeaderHeight >= aReport.Engine.FreeSpace then
aReport.Engine.NewPage;
BuildColumnBands;
BuildRowBands;
ColumnItems := ColumnHeader.TerminalItems;
RowItems := RowHeader.TerminalItems;
SavePage := aReport.PreviewPages.CurPage;
Page := SavePage;
SaveCurY := aReport.Engine.CurY;
CurY := SaveCurY;
MaxX := 0;
frxGlobalVariables['RowIndex'] := 0;
frxGlobalVariables['ColumnIndex'] := 0;
VarRowIndex := frxGlobalVariables.Items[frxGlobalVariables.IndexOf('RowIndex')];
VarColumnIndex := frxGlobalVariables.Items[frxGlobalVariables.IndexOf('ColumnIndex')];
if FDownThenAcross then
begin
FirstCurY := aReport.Engine.CurY;
for i := 0 to FColumnBands.Count - 1 do
begin
for j := 0 to FRowBands.Count - 1 do
begin
aReport.PreviewPages.CurPage := Page + j;
MaxX := 0;
DoPagination(j, i);
if j <> FRowBands.Count - 1 then
begin
aReport.Engine.BreakAllKeep;
aReport.Engine.NewPage;
CurY := aReport.Engine.CurY;
end
else if (NextCross <> nil) and ((FColumnBands.Count = 1) or (i > 0)) then
NextCross.Left := MaxX + NextCrossGap;
end;
aReport.PreviewPages.AddPageAction := apAdd;
if i <> FColumnBands.Count - 1 then
aReport.Engine.NewPage;
CurY := FirstCurY;
Inc(Page, FRowBands.Count);
{ assigned from previous cross , skip all previous column bands }
if FPrevCrossLastPage <> -1 then
Page := FPrevCrossLastPage;
FPrevCrossLastPage := -1;
if not aReport.EngineOptions.EnableThreadSafe then
Application.ProcessMessages;
if aReport.Terminated then break;
end;
CurY := aReport.Engine.CurY;
end
else
for i := 0 to FRowBands.Count - 1 do
begin
for j := 0 to FColumnBands.Count - 1 do
begin
aReport.PreviewPages.CurPage := Page + j;
MaxX := 0;
DoPagination(i, j);
if j <> FColumnBands.Count - 1 then
begin
aReport.PreviewPages.AddPageAction := apWriteOver;
aReport.Engine.BreakAllKeep;
aReport.Engine.NewPage;
end
else if NextCross <> nil then
NextCross.Left := MaxX + NextCrossGap;
end;
if i <> FRowBands.Count - 1 then
begin
aReport.PreviewPages.AddPageAction := apAdd;
aReport.Engine.BreakAllKeep;
aReport.Engine.NewPage;
Page := aReport.PreviewPages.CurPage;
end
else
Inc(Page, FColumnBands.Count);
CurY := aReport.Engine.CurY;
if not aReport.EngineOptions.EnableThreadSafe then
Application.ProcessMessages;
if aReport.Terminated then break;
end;
if Parent is TfrxBand then
CurY := CurY - Height;
{ print last page footers }
if FColumnBands.Count > 1 then
aReport.Engine.EndPage;
if NextCross <> nil then
begin
{ position to last column, first row page }
aReport.PreviewPages.CurPage := SavePage + FColumnBands.Count - 1;
{ for DownThenAcross we can try to print crosses side by side even when cross split to different pages }
if DownThenAcross and (FColumnBands.Count >= 1) then
begin
aReport.PreviewPages.CurPage := SavePage + (FColumnBands.Count - 1) * FRowBands.Count;
NextCross.FPrevCrossLastPage := SavePage + FColumnBands.Count * FRowBands.Count;
aReport.PreviewPages.AddPageAction := apWriteOver;
end
else
aReport.PreviewPages.AddPageAction := apAdd;
aReport.Engine.CurY := SaveCurY;
end
else
begin
{ position to last row, first column page }
aReport.PreviewPages.CurPage := Page - FColumnBands.Count;
aReport.PreviewPages.AddPageAction := apAdd;
aReport.Engine.CurY := CurY;
end;
FPrevCrossLastPage := -1;
ColumnItems.Free;
RowItems.Free;
FColumnBands.Clear;
FRowBands.Clear;
end;
procedure TfrxCustomCrossView.AddSourceObjects;
var
i: Integer;
begin
for i := 0 to FCellHeaderMemos.Count - 1 do
Report.PreviewPages.AddToSourcePage(CellHeaderMemos[i]);
for i := 0 to FCellMemos.Count - 1 do
Report.PreviewPages.AddToSourcePage(CellMemos[i]);
for i := 0 to FColumnMemos.Count - 1 do
begin
Report.PreviewPages.AddToSourcePage(ColumnMemos[i]);
Report.PreviewPages.AddToSourcePage(ColumnTotalMemos[i]);
end;
for i := 0 to FCornerMemos.Count - 1 do
Report.PreviewPages.AddToSourcePage(CornerMemos[i]);
for i := 0 to FRowMemos.Count - 1 do
begin
Report.PreviewPages.AddToSourcePage(RowMemos[i]);
Report.PreviewPages.AddToSourcePage(RowTotalMemos[i]);
end;
end;
procedure TfrxCustomCrossView.SetupOriginalComponent(Obj1, Obj2: TfrxComponent);
begin
THackComponent(Obj1).FOriginalComponent := THackComponent(Obj2).FOriginalComponent;
THackComponent(Obj1).FAliasName := THackComponent(Obj2).FAliasName;
end;
procedure TfrxCustomCrossView.BuildColumnBands;
var
i, j, LeftIndex, RightIndex: Integer;
Items: TList;
Item: TfrxCrossHeader;
Memo: TfrxCustomMemoView;
LargeBand: TfrxNullBand;
CurWidth, AddWidth, LeftMargin, RightMargin: Extended;
c: TfrxReportComponent;
procedure CreateBand;
var
i: Integer;
Band: TfrxNullBand;
Memo, CutMemo: TfrxCustomMemoView;
CutSize: Extended;
begin
Band := TfrxNullBand.Create(Report);
Band.AllowMirrorMode := AllowMirrorMode;
Band.ShiftEngine := seDontShift;
Band.Left := AddWidth;
{ move in-bounds memos to the new band }
i := 0;
while i < LargeBand.Objects.Count do
begin
Memo := LargeBand.Objects[i];
if Memo.Left < RightMargin then
begin
if Memo.Left + Memo.Width <= RightMargin + 5 then
begin
Memo.Parent := Band;
Memo.Visible := Memo.Width > 0;
Dec(i);
end
else { cut off the memo }
begin
CutSize := RightMargin - Memo.Left;
CutMemo := CreateMemo(Band);
CutMemo.AssignAll(Memo);
CutMemo.Width := CutSize;
//if CutMemo.CalcWidth > CutSize then
//CutMemo.Text := '';
SetupOriginalComponent(CutMemo, Memo);
Memo.Width := Memo.Width - CutSize;
Memo.Left := Memo.Left + CutSize;
if Memo is TfrxDMPMemoView then
begin
Memo.Left := Memo.Left + fr1CharX;
Memo.Width := Memo.Width - fr1CharX;
end;
if FShowCellBreak then
begin
CutMemo.Frame.Typ := CutMemo.Frame.Typ - [ftRight];
Memo.Frame.Typ := Memo.Frame.Typ - [ftLeft];
end;
//if Memo.CalcWidth > Memo.Width then
//Memo.Text := '';
Memo := CutMemo;
end;
Memo.Left := Memo.Left - LeftMargin;
end;
Inc(i);
end;
FColumnBands.Add(Band, LeftIndex, RightIndex);
end;
begin
FColumnBands.Clear;
{ create one large band }
LargeBand := TfrxNullBand.Create(nil);
LargeBand.ShiftEngine := seDontShift;
LargeBand.AllowMirrorMode := AllowMirrorMode;
Items := ColumnHeader.AllItems;
{ add memos to band }
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
if (i = 0) and not FShowTitle then continue;
Memo := CreateMemo(LargeBand);
Memo.AssignAll(Item.Memo);
SetupOriginalComponent(Memo, Item.Memo);
Memo.Text := Memo.FormatData(Item.Value);
if Item.Value <> Null then
THackMemoView(Memo).Value := Item.Value
else
THackMemoView(Memo).Value := 0;
Memo.Highlight.Condition := '';
with Item.Bounds do
Memo.SetBounds(Left, Top, Right, Bottom);
CorrectDMPBounds(Memo);
Memo.Visible := (Memo.Width <> 0) and (Memo.Height <> 0);
DoOnColumnHeader(Memo, Item);
Report.LocalValue := Item.Value;
Report.CurObject := Memo.Name;
Report.DoBeforePrint(Memo);
{ process memo children if any }
for j := 0 to Memo.Objects.Count - 1 do
begin
c := Memo.Objects[j];
Report.CurObject := c.Name;
Report.DoBeforePrint(c);
end;
end;
Items.Free;
{ cut it to small bands for each page }
Items := ColumnHeader.TerminalItems;
AddWidth := RowHeaderWidth;
CurWidth := Report.Engine.PageWidth - AddWidth;
LeftMargin := -Left;
RightMargin := LeftMargin + CurWidth;
LeftIndex := 0;
RightIndex := Items.Count - 1;
if not TfrxReportPage(Page).EndlessWidth then
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
{ find right terminal item }
if Item.Bounds.Left + Item.Bounds.Right - LeftMargin > CurWidth then
begin
RightMargin := Item.Bounds.Left;
RightIndex := i - 1;
CreateBand;
LeftMargin := RightMargin;
if FRepeatHeaders then
AddWidth := RowHeaderWidth else
AddWidth := 0;
CurWidth := Report.Engine.PageWidth - AddWidth;
RightMargin := LeftMargin + CurWidth;
LeftIndex := RightIndex + 1;
RightIndex := Items.Count - 1;
end;
end;
if TfrxReportPage(Page).EndlessWidth then
begin
Item := Items[Items.Count - 1];
CurWidth := Item.Bounds.Left + Item.Bounds.Right - LeftMargin + AddWidth;
if Report.Engine.PageWidth < CurWidth then
Report.Engine.PageWidth := CurWidth;
RightMargin := 1e+6;
end;
{ add last band }
CreateBand;
LargeBand.Free;
Items.Free;
end;
procedure TfrxCustomCrossView.BuildRowBands;
var
i, j, TopIndex, BottomIndex: Integer;
Items: TList;
Item, HParent: TfrxCrossHeader;
Memo: TfrxCustomMemoView;
LargeBand: TfrxNullBand;
MaxHeight, CurHeight, AddHeight, TopMargin, BottomMargin: Extended;
c: TfrxReportComponent;
procedure CreateBand;
var
i: Integer;
Band: TfrxNullBand;
Memo, CutMemo: TfrxCustomMemoView;
CutSize: Extended;
begin
Band := TfrxNullBand.Create(Report);
Band.ShiftEngine := seDontShift;
Band.AllowMirrorMode := AllowMirrorMode;
Band.Top := AddHeight;
{ move in-bounds memos to the new band }
i := 0;
while i < LargeBand.Objects.Count do
begin
Memo := LargeBand.Objects[i];
if Memo.Top < BottomMargin then
begin
if Memo.Top + Memo.Height <= BottomMargin + 5 then
begin
Memo.Parent := Band;
Dec(i);
end
else { cut off the memo }
begin
CutSize := BottomMargin - Memo.Top;
CutMemo := CreateMemo(Band);
CutMemo.AssignAll(Memo);
CutMemo.Height := CutSize;
SetupOriginalComponent(CutMemo, Memo);
Memo.Height := Memo.Height - CutSize;
Memo.Top := Memo.Top + CutSize;
if Memo is TfrxDMPMemoView then
begin
Memo.Top := Memo.Top + fr1CharY;
Memo.Height := Memo.Height - fr1CharY;
end;
if FShowCellBreak then
begin
CutMemo.Frame.Typ := CutMemo.Frame.Typ - [ftBottom];
Memo.Frame.Typ := Memo.Frame.Typ - [ftTop];
end;
Memo := CutMemo;
end;
Memo.Top := Memo.Top - TopMargin;
end;
Inc(i);
end;
FRowBands.Add(Band, TopIndex, BottomIndex);
end;
begin
FRowBands.Clear;
LargeBand := TfrxNullBand.Create(nil);
LargeBand.ShiftEngine := seDontShift;
LargeBand.AllowMirrorMode := AllowMirrorMode;
Items := RowHeader.AllItems;
MaxHeight := 0;
HParent := nil;
{ create one large band }
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
Memo := CreateMemo(LargeBand);
Memo.AssignAll(Item.Memo);
SetupOriginalComponent(Memo, Item.Memo);
if Item.Value <> Null then
THackMemoView(Memo).Value := Item.Value
else
THackMemoView(Memo).Value := 0;
Memo.Text := Memo.FormatData(Item.Value);
Memo.Highlight.Condition := '';
with Item.Bounds do
Memo.SetBounds(Left, Top, Right, Bottom);
CorrectDMPBounds(Memo);
Memo.Visible := (Memo.Width <> 0) and (Memo.Height <> 0);
DoOnRowHeader(Memo, Item);
if Item.Bounds.Top + Item.Bounds.Bottom > MaxHeight then
MaxHeight := Item.Bounds.Top + Item.Bounds.Bottom;
Report.LocalValue := Item.Value;
Report.CurObject := Memo.Name;
Report.DoBeforePrint(Memo);
{ process memo children if any }
for j := 0 to Memo.Objects.Count - 1 do
begin
c := Memo.Objects[j];
Report.CurObject := c.Name;
Report.DoBeforePrint(c);
end;
end;
Items.Free;
{ cut it to small bands for each page }
Items := RowHeader.TerminalItems;
AddHeight := ColumnHeaderHeight;
CurHeight := Report.Engine.FreeSpace - AddHeight;
if (MaxHeight > CurHeight) and KeepTogether then
begin
Report.Engine.NewPage;
AddHeight := ColumnHeaderHeight;
CurHeight := Report.Engine.FreeSpace - AddHeight;
end;
TopMargin := 0;
BottomMargin := TopMargin + CurHeight;
TopIndex := 0;
BottomIndex := Items.Count - 1;
for i := 0 to Items.Count - 1 do
begin
Item := Items[i];
{ find right terminal item }
{ keep rows by top parent }
if FKeepRowsTogether then
begin
HParent := Item.Parent;
while (HParent.Parent <> nil) do
begin
if (HParent.Parent.Parent = nil) then
Break;
HParent := HParent.Parent;
end;
end;
if ((HParent <> nil) and (CurHeight > HParent.Bounds.Bottom)
and (CurHeight - (HParent.Bounds.Top - TopMargin) < HParent.Bounds.Bottom))
or (Item.Bounds.Top + Item.Bounds.Bottom - TopMargin > CurHeight) then
begin
BottomMargin := Item.Bounds.Top;
BottomIndex := i - 1;
CreateBand;
TopMargin := BottomMargin;
if FRepeatHeaders then
AddHeight := ColumnHeaderHeight else
AddHeight := 0;
if (Parent is TfrxDataBand) and (TfrxDataBand(Parent).FHeader is TfrxHeader)
and TfrxHeader(TfrxDataBand(Parent).FHeader).ReprintOnNewPage then
CurHeight := Report.Engine.PageHeight - Report.Engine.CurY -
Report.Engine.FooterHeight - AddHeight
else
CurHeight := Report.Engine.PageHeight - Report.Engine.HeaderHeight(True) -
Report.Engine.FooterHeight - AddHeight;
BottomMargin := TopMargin + CurHeight;
TopIndex := BottomIndex + 1;
BottomIndex := Items.Count - 1;
end;
end;
CreateBand;
LargeBand.Free;
Items.Free;
end;
{$IFDEF FR_COM}
function TfrxCustomCrossView.Get_CellFields(out Value: WideString): HResult; stdcall;
begin
Value := WideString(String(CellFields.GetText));
Result := S_OK;
end;
function TfrxCustomCrossView.Set_CellFields(const Value: WideString): HResult; stdcall;
begin
CellFields.SetText( PChar(Value) );
CellLevels := CellFields.Count;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_CellFunctions(Index: Integer; out Value: frxCrossFunction): HResult; stdcall;
begin
Value := frxCrossFunction(CellFunctions[Index]);
Result := S_OK;
end;
function TfrxCustomCrossView.Set_CellFunctions(Index: Integer; Value: frxCrossFunction): HResult; stdcall;
begin
CellFunctions[Index] := TfrxCrossFunction(Value);
Result := S_OK;
end;
function TfrxCustomCrossView.Get_CellMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
begin
Value := CellMemos[Index];
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ColumnFields(out Value: WideString): HResult; stdcall;
begin
Value := WideString(String(ColumnFields.GetText));
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ColumnFields(const Value: WideString): HResult; stdcall;
begin
ColumnFields.SetText( PChar(Value) );
ColumnLevels := ColumnFields.Count;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ColumnMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
begin
Value := ColumnMemos[Index];
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ColumnSort(Index: Integer; out Value: frxCrossSortOrder): HResult; stdcall;
begin
Value := frxCrossSortOrder(ColumnSort[Index]);
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ColumnSort(Index: Integer; Value: frxCrossSortOrder): HResult; stdcall;
begin
ColumnSort[Index] := TfrxCrossSortOrder(Value);
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ColumnTotalMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
begin
Value := ColumnTotalMemos[Index];
Result := S_OK;
end;
function TfrxCustomCrossView.Get_RowFields(out Value: WideString): HResult; stdcall;
begin
Value := WideString(String(RowFields.GetText));
Result := S_OK;
end;
function TfrxCustomCrossView.Set_RowFields(const Value: WideString): HResult; stdcall;
begin
RowFields.SetText( PChar(Value) );
RowLevels := RowFields.Count;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_RowMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
begin
Value := RowMemos[Index];
Result := S_OK;
end;
function TfrxCustomCrossView.Get_RowSort(Index: Integer; out Value: frxCrossSortOrder): HResult; stdcall;
begin
Value := frxCrossSortOrder( RowSort[Index] );
Result := S_OK;
end;
function TfrxCustomCrossView.Set_RowSort(Index: Integer; Value: frxCrossSortOrder): HResult; stdcall;
begin
RowSort[Index] := TfrxCrossSortOrder( Value );
Result := S_OK;
end;
function TfrxCustomCrossView.Get_RowTotalMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
begin
Value := RowTotalMemos[Index];
Result := S_OK;
end;
function TfrxCustomCrossView.Get_MaxWidth(out Value: Integer): HResult; stdcall;
begin
Value := MaxWidth;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_MaxWidth(Value: Integer): HResult; stdcall;
begin
MaxWidth := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_MinWidth(out Value: Integer): HResult; stdcall;
begin
Value := MinWidth;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_MinWidth(Value: Integer): HResult; stdcall;
begin
MinWidth := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.AddValues(Rows: PSafeArray; Columns: PSafeArray; Cells: PSafeArray): HResult; stdcall;
type
VariantArray = array of Variant;
var
ArrayData: Pointer;
R: VariantArray;
C: VariantArray;
V: VariantArray;
begin
SafeArrayAccessData( Rows, ArrayData);
R := VariantArray(ArrayData);
SetLength(R,Rows.cDims);
SafeArrayUnAccessData( Rows );
SafeArrayAccessData( Columns, ArrayData );
C := VariantArray(ArrayData);
SetLength(C,Rows.cDims);
SafeArrayUnAccessData( Columns );
SafeArrayAccessData( Cells, ArrayData );
V := VariantArray(ArrayData);
SetLength(V,Rows.cDims);
SafeArrayUnAccessData( Cells );
AddValue( R, C, V );
Result := S_OK;
end;
function TfrxCustomCrossView.AddValuesVB6(Rows: OleVariant; Columns: OleVariant; Cells: OleVariant): HResult; stdcall;
var
r: PSafeArray;
c: PSafeArray;
v: PSafeArray;
begin
Result := E_HANDLE;
repeat
if not VarIsArray(Rows) then break;
if not VarIsArray(Columns) then break;
if not VarIsArray(Cells) then break;
r := VarArrayLock(Rows);
c := VarArrayLock(Columns);
v := VarArrayLock(Cells);
Result := AddValues(r, c, v);
VarArrayUnlock(Cells);
VarArrayUnlock(Columns);
VarArrayUnlock(Rows);
until True;
end;
function TfrxCustomCrossView.Get_GapX(out Value: Integer): HResult; stdcall;
begin
Value := GapX;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_GapX(Value: Integer): HResult; stdcall;
begin
GapX := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_GapY(out Value: Integer): HResult; stdcall;
begin
Value := GapY;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_GapY(Value: Integer): HResult; stdcall;
begin
GapY := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_PlainCells(out Value: WordBool): HResult; stdcall;
begin
Value := PlainCells;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_PlainCells(Value: WordBool): HResult; stdcall;
begin
PlainCells := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_DownThenAcross(out Value: WordBool): HResult; stdcall;
begin
Value := DownThenAcross;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_DownThenAcross(Value: WordBool): HResult; stdcall;
begin
DownThenAcross := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_RepeatHeaders(out Value: WordBool): HResult; stdcall;
begin
Value := RepeatHeaders;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_RepeatHeaders(Value: WordBool): HResult; stdcall;
begin
RepeatHeaders := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ShowColumnHeader(out Value: WordBool): HResult; stdcall;
begin
Value := ShowColumnHeader;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ShowColumnHeader(Value: WordBool): HResult; stdcall;
begin
ShowColumnHeader := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ShowColumnTotal(out Value: WordBool): HResult; stdcall;
begin
Value := ShowColumnTotal;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ShowColumnTotal(Value: WordBool): HResult; stdcall;
begin
ShowColumnTotal := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ShowRowHeader(out Value: WordBool): HResult; stdcall;
begin
Value := ShowRowHeader;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ShowRowHeader(Value: WordBool): HResult; stdcall;
begin
ShowRowHeader := Value;
Result := S_OK;
end;
function TfrxCustomCrossView.Get_ShowRowTotal(out Value: WordBool): HResult; stdcall;
begin
Value := ShowRowTotal;
Result := S_OK;
end;
function TfrxCustomCrossView.Set_ShowRowTotal(Value: WordBool): HResult; stdcall;
begin
ShowRowTotal := Value;
Result := S_OK;
end;
{$ENDIF}
{ TfrxCrossView }
class function TfrxCrossView.GetDescription: String;
begin
Result := frxResources.Get('obCross');
end;
function TfrxCrossView.IsCrossValid: Boolean;
begin
Result := (FCellLevels > 0) and (FRowLevels >= 0) and (FColumnLevels >= 0);
end;
procedure TfrxCrossView.SetCellLevels(const Value: Integer);
var
i: Integer;
begin
inherited;
FCellFields.Clear;
if Value = 1 then
FCellFields.Add('Cell')
else
for i := 0 to Value - 1 do
FCellFields.Add('Cell' + IntToStr(i + 1));
end;
procedure TfrxCrossView.SetColumnLevels(const Value: Integer);
var
i: Integer;
begin
inherited;
FColumnFields.Clear;
if Value = 1 then
FColumnFields.Add('Column')
else
for i := 0 to Value - 1 do
FColumnFields.Add('Column' + IntToStr(i + 1));
end;
procedure TfrxCrossView.SetRowLevels(const Value: Integer);
var
i: Integer;
begin
inherited;
FRowFields.Clear;
if Value = 1 then
FRowFields.Add('Row')
else
for i := 0 to Value - 1 do
FRowFields.Add('Row' + IntToStr(i + 1));
end;
{ TfrxDBCrossView }
class function TfrxDBCrossView.GetDescription: String;
begin
Result := frxResources.Get('obDBCross');
end;
function TfrxDBCrossView.IsCrossValid: Boolean;
begin
Result := (DataSet <> nil) and (FCellLevels > 0) and
(FRowFields.Count = FRowLevels) and (FColumnFields.Count = FColumnLevels) and
(FCellFields.Count = FCellLevels);
end;
procedure TfrxDBCrossView.FillMatrix;
var
i: Integer;
RowValues, ColumnValues, CellValues: array of Variant;
sl: TStringList;
begin
SetLength(RowValues, FRowLevels);
SetLength(ColumnValues, FColumnLevels);
SetLength(CellValues, FCellLevels);
sl := TStringList.Create;
try
DataSet.Open;
DataSet.GetFieldList(sl);
sl.Sorted := True;
DataSet.First;
while not DataSet.Eof do
begin
{ fix for preview on timer }
if not Report.EngineOptions.EnableThreadSafe then
Application.ProcessMessages;
if Report.Terminated then Exit;
for i := 0 to FRowLevels - 1 do
begin
if sl.IndexOf(FRowFields[i]) <> -1 then
RowValues[i] := DataSet.Value[FRowFields[i]]
else
RowValues[i] := Report.Calc(FRowFields[i])
end;
for i := 0 to FColumnLevels - 1 do
begin
if sl.IndexOf(FColumnFields[i]) <> -1 then
ColumnValues[i] := DataSet.Value[FColumnFields[i]]
else
ColumnValues[i] := Report.Calc(FColumnFields[i])
end;
for i := 0 to FCellLevels - 1 do
begin
if sl.IndexOf(FCellFields[i]) <> -1 then
CellValues[i] := DataSet.Value[FCellFields[i]]
else
CellValues[i] := Report.Calc(FCellFields[i])
end;
AddValue(RowValues, ColumnValues, CellValues);
DataSet.Next;
end;
finally
sl.Free;
end;
RowValues := nil;
ColumnValues := nil;
CellValues := nil;
end;
{$IFDEF FPC}
{procedure RegisterUnitfrxCross;
begin
RegisterComponents('Fast Report 6',[TfrxCrossObject]);
RegisterNoIcon([TfrxCrossView, TfrxDBCrossView]);
end;
procedure Register;
begin
RegisterUnit('frxCross',@RegisterUnitfrxCross);
end; }
{$ENDIF}
{$IFNDEF RAD_ED}
initialization
{$IFDEF DELPHI16}
StartClassGroup(TControl);
ActivateClassGroup(TControl);
GroupDescendentsWith(TfrxCrossObject, TControl);
{$ENDIF}
frxObjects.RegisterObject1(TfrxCrossView, nil, '', '', 0, 42, [ctReport, ctDMP]);
frxObjects.RegisterObject1(TfrxDBCrossView, nil, '', '', 0, 49, [ctReport, ctDMP]);
frxResources.Add('TfrxPrintCellEvent',
'PascalScript=(Memo: TfrxMemoView; RowIndex, ColumnIndex, CellIndex: Integer; RowValues, ColumnValues, Value: Variant);' + #13#10 +
'C++Script=(TfrxMemoView Memo, int RowIndex, int ColumnIndex, int CellIndex, variant RowValues, variant ColumnValues, variant Value)' + #13#10 +
'BasicScript=(Memo, RowIndex, ColumnIndex, CellIndex, RowValues, ColumnValues, Value)' + #13#10 +
'JScript=(Memo, RowIndex, ColumnIndex, CellIndex, RowValues, ColumnValues, Value)');
frxResources.Add('TfrxPrintHeaderEvent',
'PascalScript=(Memo: TfrxMemoView; HeaderIndexes, HeaderValues, Value: Variant);' + #13#10 +
'C++Script=(TfrxMemoView Memo, variant HeaderIndexes, variant HeaderValues, variant Value)' + #13#10 +
'BasicScript=(Memo, HeaderIndexes, HeaderValues, Value)' + #13#10 +
'JScript=(Memo, HeaderIndexes, HeaderValues, Value)');
frxResources.Add('TfrxCalcWidthEvent',
'PascalScript=(ColumnIndex: Integer; ColumnValues: Variant; var Width: Extended);' + #13#10 +
'C++Script=(int ColumnIndex, variant ColumnValues, float &Width)' + #13#10 +
'BasicScript=(ColumnIndex, ColumnValues, byref Width)' + #13#10 +
'JScript=(ColumnIndex, ColumnValues, &Width)');
frxResources.Add('TfrxCalcHeightEvent',
'PascalScript=(RowIndex: Integer; RowValues: Variant; var Height: Extended);' + #13#10 +
'C++Script=(int RowIndex, variant RowValues, float &Height)' + #13#10 +
'BasicScript=(RowIndex, RowValues, byref Height)' + #13#10 +
'JScript=(RowIndex, RowValues, &Height)');
finalization
frxObjects.UnRegister(TfrxCrossView);
frxObjects.UnRegister(TfrxDBCrossView);
{$ENDIF}
end.