
{******************************************}
{                                          }
{             FastReport v3.0              }
{              Cross object                }
{                                          }
{         Copyright (c) 1998-2006          }
{         by Alexander Tzyganenko,         }
{            Fast Reports Inc.             }
{                                          }
{******************************************}

unit frxCross;

interface

{$I frx.inc}

uses
  Windows, SysUtils, Classes, Controls, Graphics, Forms, frxCrossMatrix,
  frxClass
{$IFDEF Delphi6}
, Variants
{$ENDIF}
{$IFDEF FR_COM}
, FastReport_TLB
, ActiveX
{$ENDIF};

type
  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;

{$IFDEF FR_COM}
  TfrxCustomCrossView = class(TfrxView, IfrxCustomCrossView)
{$ELSE}
  TfrxCustomCrossView = class(TfrxView)
{$ENDIF}
  private
    FMatrix: TfrxCrossMatrix;
    FBorder: Boolean;
    FCellFields: TStrings;
    FCellLevels: Integer;
    FClearBeforePrint: Boolean;
    FColumnBands: TList;
    FColumnFields: TStrings;
    FColumnLevels: Integer;
    FDefHeight: Integer;
    FDotMatrix: Boolean;
    FDownThenAcross: Boolean;
    FGapX: Integer;
    FGapY: Integer;
    FMaxWidth: Integer;
    FMinWidth: Integer;
    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 }
    FPlainCells: Boolean;
    FRowBands: TList;
    FRowFields: TStrings;
    FRowLevels: Integer;
    FRepeatHeaders: Boolean;
    FShowColumnHeader: Boolean;
    FShowRowHeader: Boolean;
    procedure AddSourceObjects;
    procedure BuildColumnBands;
    procedure BuildRowBands;
    procedure ClearColumnBands;
    procedure ClearRowBands;
    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 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 SetRowFields(const Value: TStrings);
    procedure SetRowSort(Index: Integer; Value: TfrxCrossSortOrder);
    procedure SetShowColumnTotal(const Value: Boolean);
    procedure SetShowRowTotal(const Value: Boolean);
    procedure SetupOriginalComponent(Obj1, Obj2: TfrxComponent);
    procedure WriteMemos(Stream: TStream);
    function CreateMemo(Parent: TfrxComponent): TfrxCustomMemoView;
    function GetCellFunctions(Index: Integer): TfrxCrossFunction;
    function GetCellMemos(Index: Integer): TfrxCustomMemoView;
    function GetColumnMemos(Index: Integer): TfrxCustomMemoView;
    function GetColumnSort(Index: Integer): TfrxCrossSortOrder;
    function GetColumnTotalMemos(Index: Integer): TfrxCustomMemoView;
    function GetRowMemos(Index: Integer): TfrxCustomMemoView;
    function GetRowSort(Index: Integer): TfrxCrossSortOrder;
    function GetRowTotalMemos(Index: Integer): TfrxCustomMemoView;
    function GetShowColumnTotal: Boolean;
    function GetShowRowTotal: Boolean;
    procedure SetDotMatrix(const Value: Boolean);
    procedure SetPlainCells(const Value: Boolean);
  protected
    procedure DefineProperties(Filer: TFiler); override;
    procedure SetCellLevels(const Value: Integer); virtual;
    procedure SetColumnLevels(const Value: Integer); virtual;
    procedure SetRowLevels(const Value: Integer); virtual;
{$IFDEF FR_COM}
    function Get_CellFields(out Value: WideString): HResult; stdcall;
    function Set_CellFields(const Value: WideString): HResult; stdcall;
    function Get_CellFunctions(Index: Integer; out Value: frxCrossFunction): HResult; stdcall;
    function Set_CellFunctions(Index: Integer; Value: frxCrossFunction): HResult; stdcall;
    function Get_CellMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
    function Get_ColumnFields(out Value: WideString): HResult; stdcall;
    function Set_ColumnFields(const Value: WideString): HResult; stdcall;
    function Get_ColumnMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
    function Get_ColumnSort(Index: Integer; out Value: frxCrossSortOrder): HResult; stdcall;
    function Set_ColumnSort(Index: Integer; Value: frxCrossSortOrder): HResult; stdcall;
    function Get_ColumnTotalMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
    function Get_RowFields(out Value: WideString): HResult; stdcall;
    function Set_RowFields(const Value: WideString): HResult; stdcall;
    function Get_RowMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
    function Get_RowSort(Index: Integer; out Value: frxCrossSortOrder): HResult; stdcall;
    function Set_RowSort(Index: Integer; Value: frxCrossSortOrder): HResult; stdcall;
    function Get_RowTotalMemos(Index: Integer; out Value: IfrxCustomMemoView): HResult; stdcall;
    function Get_MaxWidth(out Value: Integer): HResult; stdcall;
    function Set_MaxWidth(Value: Integer): HResult; stdcall;
    function Get_MinWidth(out Value: Integer): HResult; stdcall;
    function Set_MinWidth(Value: Integer): HResult; stdcall;
    function AddValues(Rows: PSafeArray; Columns: PSafeArray; Cells: PSafeArray): HResult; stdcall;
    function Get_GapX(out Value: Integer): HResult; stdcall;
    function Set_GapX(Value: Integer): HResult; stdcall;
    function Get_GapY(out Value: Integer): HResult; stdcall;
    function Set_GapY(Value: Integer): HResult; stdcall;
    function Get_PlainCells(out Value: WordBool): HResult; stdcall;
    function Set_PlainCells(Value: WordBool): HResult; stdcall;
    function Get_DownThenAcross(out Value: WordBool): HResult; stdcall;
    function Set_DownThenAcross(Value: WordBool): HResult; stdcall;
    function Get_RepeatHeaders(out Value: WordBool): HResult; stdcall;
    function Set_RepeatHeaders(Value: WordBool): HResult; stdcall;
    function Get_ShowColumnHeader(out Value: WordBool): HResult; stdcall;
    function Set_ShowColumnHeader(Value: WordBool): HResult; stdcall;
    function Get_ShowColumnTotal(out Value: WordBool): HResult; stdcall;
    function Set_ShowColumnTotal(Value: WordBool): HResult; stdcall;
    function Get_ShowRowHeader(out Value: WordBool): HResult; stdcall;
    function Set_ShowRowHeader(Value: WordBool): HResult; stdcall;
    function Get_ShowRowTotal(out Value: WordBool): HResult; stdcall;
    function Set_ShowRowTotal(Value: WordBool): HResult; stdcall;
    function AddValuesVB6(Rows: OleVariant; Columns: OleVariant; Cells: OleVariant): HResult; stdcall;
{$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Draw(Canvas: TCanvas; ScaleX, ScaleY, OffsetX, OffsetY: Extended); override;
    procedure BeforePrint; override;
    procedure GetData; override;
    procedure BeginMatrix;
    procedure EndMatrix;
    procedure FillMatrix; virtual;
    procedure AddValue(const Rows, Columns, Cells: array of Variant);

    function ColCount: Integer;
    function RowCount: Integer;
    function IsCrossValid: Boolean; virtual;
    function IsGrandTotalColumn(Index: Integer): Boolean;
    function IsGrandTotalRow(Index: Integer): Boolean;
    function IsTotalColumn(Index: Integer): Boolean;
    function IsTotalRow(Index: Integer): Boolean;
    function RowHeaderWidth: Extended;
    function ColumnHeaderHeight: Extended;

    property Matrix: TfrxCrossMatrix read FMatrix;
    property CellFields: TStrings read FCellFields write SetCellFields;
    property CellFunctions[Index: Integer]: TfrxCrossFunction read GetCellFunctions
      write SetCellFunctions;
    property CellMemos[Index: Integer]: TfrxCustomMemoView read GetCellMemos;
    property ColumnFields: TStrings read FColumnFields write SetColumnFields;
    property ColumnMemos[Index: Integer]: TfrxCustomMemoView read GetColumnMemos;
    property ColumnSort[Index: Integer]: TfrxCrossSortOrder read GetColumnSort
      write SetColumnSort;
    property ColumnTotalMemos[Index: Integer]: TfrxCustomMemoView read GetColumnTotalMemos;
    property ClearBeforePrint: Boolean read FClearBeforePrint write FClearBeforePrint;
    property DotMatrix: Boolean read FDotMatrix;
    property RowFields: TStrings read FRowFields write SetRowFields;
    property RowMemos[Index: Integer]: TfrxCustomMemoView read GetRowMemos;
    property RowSort[Index: Integer]: TfrxCrossSortOrder read GetRowSort
      write SetRowSort;
    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 Border: Boolean read FBorder write FBorder default True;
    property CellLevels: Integer read FCellLevels write SetCellLevels default 1;
    property ColumnLevels: Integer read FColumnLevels write SetColumnLevels default 1;
    property DefHeight: Integer read FDefHeight write FDefHeight default 0;
    property DownThenAcross: Boolean read FDownThenAcross write FDownThenAcross;
    property GapX: Integer read FGapX write FGapX default 3;
    property GapY: Integer read FGapY write FGapY default 3;
    property MaxWidth: Integer read FMaxWidth write FMaxWidth default 200;
    property MinWidth: Integer read FMinWidth write FMinWidth default 0;
    property PlainCells: Boolean read FPlainCells write SetPlainCells default False;
    property RepeatHeaders: Boolean read FRepeatHeaders write FRepeatHeaders default True;
    property RowLevels: Integer read FRowLevels write SetRowLevels default 1;
    property ShowColumnHeader: Boolean read FShowColumnHeader write FShowColumnHeader
      default True;
    property ShowColumnTotal: Boolean read GetShowColumnTotal write SetShowColumnTotal
      default True;
    property ShowRowHeader: Boolean read FShowRowHeader write FShowRowHeader
      default True;
    property ShowRowTotal: Boolean read GetShowRowTotal write SetShowRowTotal
      default True;
    property OnCalcHeight: TfrxCalcHeightEvent read FOnCalcHeight write FOnCalcHeight;
    property OnCalcWidth: TfrxCalcWidthEvent read FOnCalcWidth write FOnCalcWidth;
    property OnPrintCell: TfrxPrintCellEvent read FOnPrintCell write FOnPrintCell;
    property OnPrintColumnHeader: TfrxPrintHeaderEvent
      read FOnPrintColumnHeader write FOnPrintColumnHeader;
    property OnPrintRowHeader: TfrxPrintHeaderEvent
      read FOnPrintRowHeader write FOnPrintRowHeader;
  end;

{$IFDEF FR_COM}
  TfrxCrossView = class(TfrxCustomCrossView, IfrxCrossView)
{$ELSE}
  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}
  TfrxDBCrossView = class(TfrxCustomCrossView)
{$ENDIF}
  private
  public
    class function GetDescription: String; override;
    function IsCrossValid: Boolean; override;
    procedure FillMatrix; override;
  published
    property CellFields;
    property ColumnFields;
    property DataSet;
    property DataSetName;
    property RowFields;
  end;


implementation

uses
{$IFNDEF NO_EDITORS}
  frxCrossEditor,
{$ENDIF}  
  frxCrossRTTI, frxDsgnIntf, frxXML, frxUtils, frxXMLSerializer, frxRes,
  frxDMPClass;

type
  THackComponent = class(TfrxComponent);
  THackMemoView = class(TfrxCustomMemoView);


{ TfrxCustomCrossView }

constructor TfrxCustomCrossView.Create(AOwner: TComponent);
begin
  inherited;
  Frame.Typ := [ftLeft, ftRight, ftTop, ftBottom];
  Font.Name := 'Tahoma';
  Font.Size := 8;
  Color := clSilver;

  FMatrix := TfrxCrossMatrix.Create;
  FCellFields := TStringList.Create;
  FColumnFields := TStringList.Create;
  FRowFields := TStringList.Create;
  FColumnBands := TList.Create;
  FRowBands := TList.Create;
  FBorder := True;
  FGapX := 3;
  FGapY := 3;
  FMaxWidth := 200;
  CellLevels := 1;
  ColumnLevels := 1;
  RowLevels := 1;
  FRepeatHeaders := True;
  FShowColumnHeader := True;
  FShowRowHeader := True;
  FMatrix.OnCalcHeight := DoCalcHeight;
  FMatrix.OnCalcWidth := DoCalcWidth;
  if Page is TfrxDMPPage then
    SetDotMatrix(True);
  FClearBeforePrint := True;
end;

destructor TfrxCustomCrossView.Destroy;
begin
  FMatrix.Free;
  FCellFields.Free;
  FColumnFields.Free;
  FRowFields.Free;
  ClearColumnBands;
  ClearRowBands;
  FColumnBands.Free;
  FRowBands.Free;
  inherited;
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);
    m.Frame.Typ := [];
    m.Font.Style := [];
    m.HAlign := haLeft;
    m.VAlign := vaTop;
    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 CROSS_DIM_SIZE - 1 do
    begin
      GetItem(CellMemos[i], 'cellmemos', i);
      GetItem(ColumnMemos[i], 'columnmemos', i);
      GetItem(ColumnTotalMemos[i], 'columntotalmemos', i);
      GetItem(RowMemos[i], 'rowmemos', i);
      GetItem(RowTotalMemos[i], 'rowtotalmemos', i);
      CellFunctions[i] := GetItem1('cellfunctions', i);
      ColumnSort[i] := GetItem2('columnsort', i);
      RowSort[i] := GetItem2('rowsort', i);
    end;

  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, False, 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 := 'columnmemos';
    x.Root.Add.Name := 'columntotalmemos';
    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 CellLevels - 1 do
    begin
      AddItem(CellMemos[i], 'cellmemos');
      AddItem1(CellFunctions[i], 'cellfunctions');
    end;
    for i := 0 to ColumnLevels - 1 do
    begin
      AddItem(ColumnMemos[i], 'columnmemos');
      AddItem(ColumnTotalMemos[i], 'columntotalmemos');
      AddItem2(ColumnSort[i], 'columnsort');
    end;
    for i := 0 to RowLevels - 1 do
    begin
      AddItem(RowMemos[i], 'rowmemos');
      AddItem(RowTotalMemos[i], 'rowtotalmemos');
      AddItem2(RowSort[i], 'rowsort');
    end;

    x.SaveToStream(Stream);
  finally
    x.Free;
  end;
end;

function TfrxCustomCrossView.CreateMemo(Parent: TfrxComponent): TfrxCustomMemoView;
begin
  if FDotMatrix then
    Result := TfrxDMPMemoView.Create(Parent) else
    Result := TfrxMemoView.Create(Parent);
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.GetCellFunctions(Index: Integer): TfrxCrossFunction;
begin
  Result := FMatrix.CellFunctions[Index];
end;

function TfrxCustomCrossView.GetCellMemos(Index: Integer): TfrxCustomMemoView;
begin
  Result := FMatrix.CellMemos[Index];
end;

function TfrxCustomCrossView.GetColumnMemos(Index: Integer): TfrxCustomMemoView;
begin
  Result := FMatrix.ColumnMemos[Index];
end;

function TfrxCustomCrossView.GetColumnTotalMemos(Index: Integer): TfrxCustomMemoView;
begin
  Result := FMatrix.ColumnTotalMemos[Index];
end;

function TfrxCustomCrossView.GetRowMemos(Index: Integer): TfrxCustomMemoView;
begin
  Result := FMatrix.RowMemos[Index];
end;

function TfrxCustomCrossView.GetRowTotalMemos(Index: Integer): TfrxCustomMemoView;
begin
  Result := FMatrix.RowTotalMemos[Index];
end;

procedure TfrxCustomCrossView.SetCellFunctions(Index: Integer; const Value: TfrxCrossFunction);
begin
  FMatrix.CellFunctions[Index] := Value;
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.SetPlainCells(const Value: Boolean);
begin
  FPlainCells := Value;
  FMatrix.PlainCells := Value;
end;

procedure TfrxCustomCrossView.SetRowFields(const Value: TStrings);
begin
  FRowFields.Assign(Value);
end;

function TfrxCustomCrossView.GetColumnSort(Index: Integer): TfrxCrossSortOrder;
begin
  Result := FMatrix.ColumnSort[Index];
end;

function TfrxCustomCrossView.GetRowSort(Index: Integer): TfrxCrossSortOrder;
begin
  Result := FMatrix.RowSort[Index];
end;

procedure TfrxCustomCrossView.SetColumnSort(Index: Integer; Value: TfrxCrossSortOrder);
begin
  FMatrix.ColumnSort[Index] := Value;
end;

procedure TfrxCustomCrossView.SetRowSort(Index: Integer; Value: TfrxCrossSortOrder);
begin
  FMatrix.RowSort[Index] := Value;
end;

function TfrxCustomCrossView.GetShowColumnTotal: Boolean;
begin
  Result := ColumnTotalMemos[0].Visible;
end;

function TfrxCustomCrossView.GetShowRowTotal: Boolean;
begin
  Result := RowTotalMemos[0].Visible;
end;

procedure TfrxCustomCrossView.SetShowColumnTotal(const Value: Boolean);
begin
  ColumnTotalMemos[0].Visible := Value;
end;

procedure TfrxCustomCrossView.SetShowRowTotal(const Value: Boolean);
begin
  RowTotalMemos[0].Visible := Value;
end;

procedure TfrxCustomCrossView.SetCellLevels(const Value: Integer);
begin
  FCellLevels := Value;
end;

procedure TfrxCustomCrossView.SetColumnLevels(const Value: Integer);
begin
  FColumnLevels := Value;
end;

procedure TfrxCustomCrossView.SetRowLevels(const Value: Integer);
begin
  FRowLevels := Value;
end;

procedure TfrxCustomCrossView.SetDotMatrix(const Value: Boolean);
begin
  FDotMatrix := Value;
  FMatrix.InitMemos(Value);
  if FDotMatrix then
  begin
    FGapX := 0;
    FGapY := 0;
  end;
end;

procedure TfrxCustomCrossView.Draw(Canvas: TCanvas; ScaleX, ScaleY,
  OffsetX, OffsetY: Extended);
begin
  inherited;
  with Canvas do
  begin
    Font.Assign(Self.Font);
    TextOut(FX + 2, FY + 2, Name);
  end;
end;

procedure TfrxCustomCrossView.BeginMatrix;
begin
  FMatrix.MaxWidth := FMaxWidth;
  FMatrix.MinWidth := FMinWidth;
  FMatrix.DefHeight := FDefHeight;
  FMatrix.GapX := FGapX;
  FMatrix.GapY := FGapY;
  FMatrix.Init(FRowLevels, FColumnLevels, FCellLevels);
  FMatrix.ColumnHeader.Visible := FShowColumnHeader;
  FMatrix.RowHeader.Visible := FShowRowHeader;
end;

procedure TfrxCustomCrossView.AddValue(const Rows, Columns, Cells: array of Variant);
begin
  if not IsCrossValid then
    raise Exception.Create('Cross-tab is not valid'); 
  FMatrix.AddValue(Rows, Columns, Cells);
end;

procedure TfrxCustomCrossView.EndMatrix;
begin
  FMatrix.CreateHeaders;
  FMatrix.CalcTotals;
  FMatrix.CalcBounds;
end;

procedure TfrxCustomCrossView.FillMatrix;
begin
end;

function TfrxCustomCrossView.ColCount: Integer;
begin
  Result := FMatrix.ColCount;
end;

function TfrxCustomCrossView.RowCount: Integer;
begin
  Result := FMatrix.RowCount;
end;

function TfrxCustomCrossView.IsCrossValid: Boolean;
begin
  Result := True;
end;

function TfrxCustomCrossView.IsGrandTotalColumn(Index: Integer): Boolean;
begin
  Result := FMatrix.IsGrandTotalColumn(Index);
end;

function TfrxCustomCrossView.IsGrandTotalRow(Index: Integer): Boolean;
begin
  Result := FMatrix.IsGrandTotalRow(Index);
end;

function TfrxCustomCrossView.IsTotalColumn(Index: Integer): Boolean;
begin
  Result := FMatrix.IsTotalColumn(Index);
end;

function TfrxCustomCrossView.IsTotalRow(Index: Integer): Boolean;
begin
  Result := FMatrix.IsTotalRow(Index);
end;

function TfrxCustomCrossView.ColumnHeaderHeight: Extended;
begin
  Result := FMatrix.ColumnHeader.Height;
  if FMatrix.NoColumns then
    Result := 0;
end;

function TfrxCustomCrossView.RowHeaderWidth: Extended;
begin
  Result := FMatrix.RowHeader.Width;
  if FMatrix.NoRows then
    Result := 0;
end;

procedure TfrxCustomCrossView.DoCalcHeight(Row: Integer; var Height: Extended);
var
  v: Variant;
begin
  if FOnCalcHeight <> '' then
  begin
    v := VarArrayOf([Row, FMatrix.GetRowIndexes(Row), Height]);
    if Report <> nil then
      Report.DoParamEvent(FOnCalcHeight, v);
    Height := v[2];
  end;
  if Assigned(FOnBeforeCalcHeight) then
    FOnBeforeCalcHeight(Row, FMatrix.GetRowIndexes(Row), Height);
end;

procedure TfrxCustomCrossView.DoCalcWidth(Column: Integer; var Width: Extended);
var
  v: Variant;
begin
  if FOnCalcWidth <> '' then
  begin
    v := VarArrayOf([Column, FMatrix.GetColumnIndexes(Column), Width]);
    if Report <> nil then
      Report.DoParamEvent(FOnCalcWidth, v);
    Width := v[2];
  end;
  if Assigned(FOnBeforeCalcWidth) then
    FOnBeforeCalcWidth(Column, FMatrix.GetColumnIndexes(Column), Width);
end;

procedure TfrxCustomCrossView.DoOnCell(Memo: TfrxCustomMemoView;
  Row, Column, Cell: Integer; const Value: Variant);
var
  v: Variant;
begin
  if FOnPrintCell <> '' then
  begin
    v := VarArrayOf([Integer(Memo), Row, Column, Cell, FMatrix.GetRowIndexes(Row),
      FMatrix.GetColumnIndexes(Column), Value]);
    if Report <> nil then
      Report.DoParamEvent(FOnPrintCell, v);
  end;
  if Assigned(FOnBeforePrintCell) then
    FOnBeforePrintCell(Memo, Row, Column, Cell, FMatrix.GetRowIndexes(Row),
      FMatrix.GetColumnIndexes(Column), Value);
end;

procedure TfrxCustomCrossView.DoOnColumnHeader(Memo: TfrxCustomMemoView;
  Header: TfrxCrossHeader);
var
  v: Variant;
begin
  if FOnPrintColumnHeader <> '' then
  begin
    v := VarArrayOf([Integer(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([Integer(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.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: Integer;
  CurY, AddWidth: Extended;
  Band: TfrxBand;
  ColumnItems: TList;

  function GetCellBand(RowIndex, ColumnIndex: Integer): TfrxBand;
  var
    i, j, iFrom, iTo: Integer;
    Cell: Variant;
    RowItems: TList;
    ColumnItem, RowItem: TfrxCrossHeader;
    m, Memo: TfrxCustomMemoView;
    RowSize, CellSize, CellOffs, LeftMargin, TopMargin: Extended;
  begin
    RowItems := FMatrix.RowHeader.TerminalItems;
    RowItem := RowItems[RowIndex];
    RowSize := RowItem.Bounds.Bottom / CellLevels;

    Result := TfrxNullBand.Create(Report);
    Result.Height := RowItem.Bounds.Bottom;

    iFrom := TfrxBand(FColumnBands[ColumnIndex]).Tag mod 65536;
    iTo := TfrxBand(FColumnBands[ColumnIndex]).Tag div 65536;
    LeftMargin := TfrxCrossHeader(ColumnItems[iFrom]).Bounds.Left;
    TopMargin := RowItem.Bounds.Top;

    for i := iFrom to iTo do
    begin
      ColumnItem := ColumnItems[i];

      CellOffs := 0;
      for j := 0 to CellLevels - 1 do
      begin
        Cell := FMatrix.GetValue(RowIndex, i, j);

        { which memo to use? }
        if RowItem.IsTotal then
          m := RowItem.Memo
        else if ColumnItem.IsTotal then
          m := ColumnItem.Memo
        else
          m := CellMemos[j];

        Memo := CreateMemo(Result);
        Memo.Assign(m);
        SetupOriginalComponent(Memo, m);
        if Cell <> Null then
          THackMemoView(Memo).Value := Cell else
          THackMemoView(Memo).Value := 0;

        Memo.Text := Memo.FormatData(Cell, CellMemos[j].DisplayFormat);
        Memo.Rotation := 0;
        Memo.HAlign := CellMemos[j].HAlign;
        Memo.Frame := CellMemos[j].Frame;
        DoOnCell(Memo, RowIndex, i, j, Cell);

        if FBorder then
        begin
          if FMatrix.PlainCells then
          begin
            if RowIndex = 0 then
              Memo.Frame.Typ := Memo.Frame.Typ + [ftTop];
            if (i = 0) and (j = 0) then
              Memo.Frame.Typ := Memo.Frame.Typ + [ftLeft];
            if (i = ColumnItems.Count - 1) and (j = 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 (j = 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 (j = CellLevels - 1) then
              Memo.Frame.Typ := Memo.Frame.Typ + [ftBottom];
          end;
        end;

        if FMatrix.PlainCells then
        begin
          CellSize := ColumnItem.CellSizes[j];
          Memo.SetBounds(ColumnItem.Bounds.Left - LeftMargin + AddWidth + CellOffs,
            RowItem.Bounds.Top - TopMargin,
            CellSize, RowItem.Bounds.Bottom);
          CellOffs := CellOffs + CellSize;
        end
        else
          Memo.SetBounds(ColumnItem.Bounds.Left - LeftMargin + AddWidth,
            RowItem.Bounds.Top + j * RowSize - TopMargin,
            ColumnItem.Bounds.Right, RowSize);
        CorrectDMPBounds(Memo);
        Memo.Visible := (Memo.Width <> 0) and (Memo.Height <> 0);
      end;
    end;

    RowItems.Free;
  end;

  procedure DoPagination(i, j: Integer);
  var
    k, kFrom, kTo: Integer;
  begin
    if ShowColumnHeader and (FRepeatHeaders or (i = 0)) and not FMatrix.NoColumns then
    begin
      Band := TfrxBand(FColumnBands[j]);
      Band.Top := CurY;
      Report.Engine.ShowBand(Band);
    end;

    if ShowRowHeader and (FRepeatHeaders or (j = 0)) and not FMatrix.NoRows then
    begin
      Band := TfrxBand(FRowBands[i]);
      if j = 0 then
        Band.Left := Left
      else
        Band.Left := 0;
      Band.Top := Band.Top + CurY;
      Report.Engine.ShowBand(Band);
      Band.Top := Band.Top - CurY;
    end;

    if FRepeatHeaders or (i = 0) then
      Report.Engine.CurY := CurY + ColumnHeaderHeight else
      Report.Engine.CurY := CurY;
    if FRepeatHeaders or (j = 0) then
    begin
      AddWidth := RowHeaderWidth;
      if j = 0 then
        AddWidth := AddWidth + Left;
    end
    else
      AddWidth := 0;

    kFrom := TfrxBand(FRowBands[i]).Tag mod 65536;
    kTo := TfrxBand(FRowBands[i]).Tag div 65536;

    for k := kFrom to kTo do
    begin
      Band := GetCellBand(k, j);
      Band.Top := Report.Engine.CurY;
      Report.Engine.ShowBand(Band);
      Band.Free;
    end;
  end;

begin
  AddSourceObjects;
  BuildColumnBands;
  BuildRowBands;
  ColumnItems := FMatrix.ColumnHeader.TerminalItems;
  Page := Report.PreviewPages.CurPage;
  CurY := Report.Engine.CurY;

  if FDownThenAcross then
    for i := 0 to FColumnBands.Count - 1 do
    begin
      for j := 0 to FRowBands.Count - 1 do
      begin
        Report.PreviewPages.CurPage := Page + j;
        DoPagination(j, i);
        if j <> FRowBands.Count - 1 then
          Report.Engine.NewPage;
      end;

      if i <> FColumnBands.Count - 1 then
        Report.Engine.NewPage;
      CurY := Report.Engine.CurY;
      Inc(Page, FRowBands.Count);

      Application.ProcessMessages;
      if Report.Terminated then break;
    end
  else
    for i := 0 to FRowBands.Count - 1 do
    begin
      for j := 0 to FColumnBands.Count - 1 do
      begin
        Report.PreviewPages.CurPage := Page + j;
        DoPagination(i, j);
        if j <> FColumnBands.Count - 1 then
        begin
          Report.PreviewPages.AddPageAction := apWriteOver;
          Report.Engine.NewPage;
        end;
      end;

      if i <> FRowBands.Count - 1 then
      begin
        Report.PreviewPages.AddPageAction := apAdd;
        Report.Engine.NewPage;
        Page := Report.PreviewPages.CurPage;
      end
      else
        Inc(Page, FColumnBands.Count);
      CurY := Report.Engine.CurY;

      Application.ProcessMessages;
      if Report.Terminated then break;
    end;

  if Parent is TfrxBand then
    CurY := CurY - Height;
  { print last page footers }
  if FColumnBands.Count > 1 then
    Report.Engine.EndPage;
  { position to last row, first column page }
  Report.PreviewPages.CurPage := Page - FColumnBands.Count;
  Report.PreviewPages.AddPageAction := apAdd;
  Report.Engine.CurY := CurY;

  ColumnItems.Free;
  ClearColumnBands;
  ClearRowBands;
end;

procedure TfrxCustomCrossView.ClearColumnBands;
var
  i: Integer;
begin
  for i := 0 to FColumnBands.Count - 1 do
    TObject(FColumnBands[i]).Free;
  FColumnBands.Clear;
end;

procedure TfrxCustomCrossView.ClearRowBands;
var
  i: Integer;
begin
  for i := 0 to FRowBands.Count - 1 do
    TObject(FRowBands[i]).Free;
  FRowBands.Clear;
end;

procedure TfrxCustomCrossView.AddSourceObjects;
var
  i: Integer;
begin
  for i := 0 to CellLevels - 1 do
    Report.PreviewPages.AddToSourcePage(CellMemos[i]);
  for i := 0 to ColumnLevels - 1 do
  begin
    Report.PreviewPages.AddToSourcePage(ColumnMemos[i]);
    Report.PreviewPages.AddToSourcePage(ColumnTotalMemos[i]);
  end;
  for i := 0 to RowLevels - 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, LeftIndex, RightIndex: Integer;
  Items: TList;
  Item: TfrxCrossHeader;
  Memo: TfrxCustomMemoView;
  LargeBand: TfrxNullBand;
  CurWidth, AddWidth, LeftMargin, RightMargin: Extended;

  procedure CreateBand;
  var
    i: Integer;
    Band: TfrxNullBand;
    Memo, CutMemo: TfrxCustomMemoView;
    CutSize: Extended;
  begin
    Band := TfrxNullBand.Create(Report);
    Band.Left := AddWidth;
    Band.Tag := LeftIndex + RightIndex * 65536;

    { 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.Assign(Memo);
          CutMemo.Width := CutSize;
          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;
          CutMemo.Frame.Typ := CutMemo.Frame.Typ - [ftRight];
          Memo.Frame.Typ := Memo.Frame.Typ - [ftLeft];
          Memo := CutMemo;
        end;

        Memo.Left := Memo.Left - LeftMargin;
      end;
      Inc(i);
    end;

    FColumnBands.Add(Band);
  end;

begin
  ClearColumnBands;
  LargeBand := TfrxNullBand.Create(nil);
  Items := FMatrix.ColumnHeader.AllItems;

  { create one large band }
  for i := 0 to Items.Count - 1 do
  begin
    Item := Items[i];
    Memo := CreateMemo(LargeBand);
    Memo.Assign(Item.Memo);
    SetupOriginalComponent(Memo, Item.Memo);
    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);
    DoOnColumnHeader(Memo, Item);
  end;

  Items.Free;

  { cut it to small bands for each page }
  Items := FMatrix.ColumnHeader.TerminalItems;
  AddWidth := RowHeaderWidth;
  CurWidth := Report.Engine.PageWidth - AddWidth;
  LeftMargin := -Left;
  RightMargin := LeftMargin + CurWidth;
  LeftIndex := 0;
  RightIndex := Items.Count - 1;

  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;
  { add last band }
  CreateBand;

  LargeBand.Free;
  Items.Free;
end;

procedure TfrxCustomCrossView.BuildRowBands;
var
  i, TopIndex, BottomIndex: Integer;
  Items: TList;
  Item: TfrxCrossHeader;
  Memo: TfrxCustomMemoView;
  LargeBand: TfrxNullBand;
  CurHeight, AddHeight, TopMargin, BottomMargin: Extended;

  procedure CreateBand;
  var
    i: Integer;
    Band: TfrxNullBand;
    Memo, CutMemo: TfrxCustomMemoView;
    CutSize: Extended;
  begin
    Band := TfrxNullBand.Create(Report);
    Band.Top := AddHeight;
    Band.Tag := TopIndex + BottomIndex * 65536;

    { 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.Assign(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;
          CutMemo.Frame.Typ := CutMemo.Frame.Typ - [ftBottom];
          Memo.Frame.Typ := Memo.Frame.Typ - [ftTop];
          Memo := CutMemo;
        end;

        Memo.Top := Memo.Top - TopMargin;
      end;
      Inc(i);
    end;

    FRowBands.Add(Band);
  end;

begin
  ClearRowBands;
  LargeBand := TfrxNullBand.Create(nil);
  Items := FMatrix.RowHeader.AllItems;

  { create one large band }
  for i := 0 to Items.Count - 1 do
  begin
    Item := Items[i];
    Memo := CreateMemo(LargeBand);
    Memo.Assign(Item.Memo);
    SetupOriginalComponent(Memo, Item.Memo);
    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);
  end;

  Items.Free;

  { cut it to small bands for each page }
  Items := FMatrix.RowHeader.TerminalItems;
  AddHeight := ColumnHeaderHeight;
  CurHeight := Report.Engine.FreeSpace - AddHeight;
  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 }
    if 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;
      CurHeight := Report.Engine.PageHeight - Report.Engine.HeaderHeight -
        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( PAnsiChar(String(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( PAnsiChar(String(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( PAnsiChar(String(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);
  SafeArrayUnAccessData( Rows );

  SafeArrayAccessData( Columns, ArrayData );
  C := VariantArray(ArrayData);
  SafeArrayUnAccessData( Columns );

  SafeArrayAccessData( Cells, ArrayData );
  V := VariantArray(ArrayData);
  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;
  for i := 0 to Value - 1 do
    FCellFields.Add('Cell');
end;

procedure TfrxCrossView.SetColumnLevels(const Value: Integer);
var
  i: Integer;
begin
  inherited;
  FColumnFields.Clear;
  for i := 0 to Value - 1 do
    FColumnFields.Add('Column');
end;

procedure TfrxCrossView.SetRowLevels(const Value: Integer);
var
  i: Integer;
begin
  inherited;
  FRowFields.Clear;
  for i := 0 to Value - 1 do
    FRowFields.Add('Row');
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;
  DataSet.GetFieldList(sl);
  sl.Sorted := True;

  DataSet.First;
  while not DataSet.Eof do
  begin
    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;

  sl.Free;
  RowValues := nil;
  ColumnValues := nil;
  CellValues := nil;
end;


initialization
  frxObjects.RegisterObject1(TfrxCrossView, nil, '', 'Other', 0, 42);
  frxObjects.RegisterObject1(TfrxDBCrossView, nil, '', 'Other', 0, 49);
  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)');


end.


//<censored>