📄 rm_cross.pas
字号:
unit RM_Cross;
interface
{$I RM.inc}
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Menus, ExtCtrls, DB, Buttons, RM_Common, RM_Class, RM_DataSet
{$IFDEF COMPILER6_UP}, Variants{$ENDIF};
type
TRMCrossObject = class(TComponent)
end;
TIntArrayCell = array[0..0] of Integer;
PIntArrayCell = ^TIntArrayCell;
{ TRMQuickIntarray }
TRMQuickIntArray = class
private
arr: PIntArrayCell;
len: Integer;
function GetCell(Index: Integer): Integer;
procedure SetCell(Index: Integer; const Value: Integer);
public
constructor Create(Length: Integer);
destructor Destroy; override;
property Cell[Index: Integer]: Integer read GetCell write SetCell; default;
end;
{ TRMArray }
TRMArray = class(TObject)
private
FFlag_Insert: Boolean;
FInsertPos: Integer;
FSortColHeader, FSortRowHeader: Boolean;
FRows: TStringList;
FColumns: TStringList;
FCellItemsCount: Integer;
FReport: TRMReport;
function GetCell(const Row, Col: string; Index3: Integer): Variant;
procedure SetCell(const aRow, aCol: string; aIndex3: Integer; aValue: Variant);
function GetCellByIndex(Row, Col, Index3: Integer): Variant;
function GetCellArray(Row, Col: Integer): Variant;
procedure SetCellArray(Row, Col: Integer; Value: Variant);
procedure SetSortColHeader(Value: Boolean);
procedure SetSortRowHeader(Value: Boolean);
public
constructor Create(CellItemsCount: Integer; aReport: TRMReport);
destructor Destroy; override;
procedure Clear;
property Columns: TStringList read FColumns;
property Rows: TStringList read FRows;
property CellItemsCount: Integer read FCellItemsCount;
property Cell[const Row, Col: string; Index3: Integer]: Variant read GetCell write SetCell;
property CellByIndex[Row, Col, Index3: Integer]: Variant read GetCellByIndex;
property CellArray[Row, Col: Integer]: Variant read GetCellArray write SetCellArray;
property SortColHeader: Boolean read FSortColHeader write SetSortColHeader;
property SortRowHeader: Boolean read FSortRowHeader write SetSortRowHeader;
end;
{ TRMCrossArray }
TRMCrossArray = class(TRMArray)
private
FDataSet: TDataSet;
FRowFields, FColFields, FCellFields: TStringList;
FRowTypes, FColTypes: array[0..31] of Variant;
FTopLeftSize: TSize;
FHeaderString: string;
FRowTotalString: string;
FRowGrandTotalString: string;
FColumnTotalString: string;
FColumnGrandTotalString: string;
FAddColumnsHeader: TStrings;
function GetIsTotalRow(Index: Integer): Boolean;
function GetIsTotalColumn(Index: Integer): Boolean;
public
DoDataCol: Boolean;
ShowRowNo: Boolean;
DataStr: string;
constructor Create(aReport: TRMReport; DS: TDataSet; RowFields, ColFields, CellFields: string);
destructor Destroy; override;
procedure Build;
property HeaderString: string read FHeaderString write FHeaderString;
property RowTotalString: string read FRowTotalString write FRowTotalString;
property RowGrandTotalString: string read FRowGrandTotalString write FRowGrandTotalString;
property ColumnTotalString: string read FColumnTotalString write FColumnTotalString;
property ColumnGrandTotalString: string read FColumnGrandTotalString write FColumnGrandTotalString;
property TopLeftSize: TSize read FTopLeftSize;
property IsTotalRow[Index: Integer]: Boolean read GetIsTotalRow;
property IsTotalColumn[Index: Integer]: Boolean read GetIsTotalColumn;
end;
{ TRMCrossView }
TRMCrossView = class(TRMReportView)
private
FDataHeight, FDataWidth: Integer;
FHeaderHeight, FHeaderWidth: string;
FCrossArray: TRMCrossArray;
FColumnWidths: TRMQuickIntArray;
FColumnHeights: TRMQuickIntArray;
FLastTotalCol: TRMQuickIntArray;
FFlag: Boolean;
FSkip: Boolean;
FRowDataSet: TRMUserDataset;
FColumnDataSet: TRMUserDataset;
FRepeatCaptions: Boolean;
FInternalFrame: Boolean;
FShowHeader: Boolean;
FDefDY: Integer;
FLastX: Integer;
FMaxGTHeight, FMaxCellHeight: Integer;
FMaxString: string;
FDictionary: TStrings;
FAddColumnsHeader: TStrings;
FRowNoHeader: string;
FSavedOnBeforePrint: TRMOnBeforePrintEvent;
FSavedOnPrintColumn: TRMPrintColumnEvent;
function OneObject(aPage: TRMReportPage; Name1, Name2: string): TRMMemoView;
function ParentPage: TRMReportPage;
procedure CreateObjects;
procedure CalcWidths;
procedure MakeBands;
procedure OnReportPrintColumnEvent(aColNo: Integer; var aWidth: Integer);
procedure OnReportBeforePrintEvent(aMemo: TWideStringList; aView: TRMReportView);
function GetShowRowTotal: Boolean;
procedure SetShowRowTotal(Value: Boolean);
function GetShowColTotal: Boolean;
procedure SetShowColTotal(Value: Boolean);
function GetShowIndicator: Boolean;
procedure SetShowIndicator(Value: Boolean);
function GetSortColHeader: Boolean;
procedure SetSortColHeader(Value: Boolean);
function GetSortRowHeader: Boolean;
procedure SetSortRowHeader(Value: Boolean);
function GetMergeRowHeader: Boolean;
procedure SetMergeRowHeader(Value: Boolean);
function GetShowRowNo: Boolean;
procedure SetShowRowNo(Value: Boolean);
function GetInternalFrame: Boolean;
procedure SetInternalFrame(Value: Boolean);
function GetRepeatCaptions: Boolean;
procedure SetRepeatCaptions(Value: Boolean);
function GetDataWidth: Integer;
procedure SetDataWidth(Value: Integer);
function GetDataHeight: Integer;
procedure SetDataHeight(Value: Integer);
function GetHeaderWidth: string;
procedure SetHeaderWidth(Value: string);
function GetHeaderHeight: string;
procedure SetHeaderHeight(Value: string);
function GetRowNoHeader: string;
procedure SetRowNoHeader(Value: string);
function GetDictName(aStr: string): string;
function GetDataCellText: string;
function GetHeaderHeightByIndex(aIndex: Integer): Integer;
function GetHeaderWidthByIndex(aIndex: Integer): Integer;
procedure OnColumnDataSetFirstEvent(Sender: TObject);
protected
procedure Prepare; override;
procedure UnPrepare; override;
function IsCrossView: Boolean; override;
public
class function CanPlaceOnGridView: Boolean; override;
constructor Create; override;
destructor Destroy; override;
procedure Draw(aCanvas: TCanvas); override;
procedure LoadFromStream(aStream: TStream); override;
procedure SaveToStream(aStream: TStream); override;
procedure ShowEditor; override;
property RowDataSet: TRMUserDataset read FRowDataSet;
property ColumnDataSet: TRMUserDataset read FColumnDataSet;
published
property InternalFrame: Boolean read GetInternalFrame write SetInternalFrame;
property RepeatCaptions: Boolean read GetRepeatCaptions write SetRepeatCaptions;
property DataWidth: Integer read GetDataWidth write SetDataWidth;
property DataHeight: Integer read GetDataHeight write SetDataHeight;
property HeaderWidth: string read GetHeaderWidth write SetHeaderWidth;
property HeaderHeight: string read GetHeaderHeight write SetHeaderHeight;
property RowNoHeader: string read GetRowNoHeader write SetRowNoHeader;
property ShowRowTotal: Boolean read GetShowRowTotal write SetShowRowTotal;
property ShowColumnTotal: Boolean read GetShowColTotal write SetShowColTotal;
property ShowIndicator: Boolean read GetShowIndicator write SetShowIndicator;
property SortColHeader: Boolean read GetSortColHeader write SetSortColHeader;
property SortRowHeader: Boolean read GetSortRowHeader write SetSortRowHeader;
property MergeRowHeader: Boolean read GetMergeRowHeader write SetMergeRowHeader;
property ShowRowNo: Boolean read GetShowRowNo write SetShowRowNo;
property Dictionary: TStrings read FDictionary write FDictionary;
property AddColumnHeader: TStrings read FAddColumnsHeader write FAddColumnsHeader;
end;
{ TRMCrossForm }
TRMCrossForm = class(TForm)
GroupBox1: TGroupBox;
ListBox2: TListBox;
ListBox3: TListBox;
ListBox4: TListBox;
Shape1: TShape;
Shape2: TShape;
GroupBox2: TGroupBox;
DatasetsLB: TComboBox;
FieldsLB: TListBox;
btnOK: TButton;
btnCancel: TButton;
Label1: TLabel;
ComboBox2: TComboBox;
CheckBox1: TCheckBox;
btnExchange: TSpeedButton;
procedure FormShow(Sender: TObject);
procedure DatasetsLBClick(Sender: TObject);
procedure FormHide(Sender: TObject);
procedure ListBox3Enter(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure ListBox3Click(Sender: TObject);
procedure ListBox4Click(Sender: TObject);
procedure ComboBox2Click(Sender: TObject);
procedure ListBox4DrawItem(Control: TWinControl; Index: Integer; ARect: TRect; State: TOwnerDrawState);
procedure FieldsLBDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
procedure FieldsLBDragDrop(Sender, Source: TObject; X, Y: Integer);
procedure ListBox3DblClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnExchangeClick(Sender: TObject);
procedure ListBox3KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
FListBox: TListBox;
FBusy: Boolean;
DrawPanel: TPanel;
procedure FillDatasetsLB;
procedure Localize;
procedure ClearSelection(Sender: TObject);
public
{ Public declarations }
Cross: TRMCrossView;
end;
implementation
{$R *.DFM}
uses
RM_Const, RM_Utils, RM_Insp, RM_PropInsp, RM_EditorStrings;
const
flCrossShowRowTotal = $2;
flCrossShowColTotal = $4;
flCrossShowIndicator = $8;
flCrossSortColHeader = $10;
flCrossSortRowHeader = $20;
flCrossMergeRowHeader = $40;
flCrossShowRowNo = $80;
type
PRMArrayCell = ^TRMArrayCell;
TRMArrayCell = record
Items: Variant;
end;
{ TRMCrossGroupItem }
TRMCrossGroupItem = class(TObject)
private
Parent: TRMCrossArray;
FArray: Variant;
FCellItemsCount: Integer;
FGroupName: TStringList;
FIndex: Integer;
FCount: TRMQuickIntArray;
FStartFrom: Integer;
procedure Reset(NewGroupName: string; StartFrom: Integer);
procedure AddValue(Value: Variant);
function IsBreak(GroupName: string): Boolean;
procedure CheckAvg;
property Value: Variant read FArray;
public
constructor Create(AParent: TRMCrossArray; GroupName: string; Index, CellItemsCount: Integer);
destructor Destroy; override;
end;
{ TRMCrossList }
TRMCrossList = class
private
FList: TList;
public
constructor Create;
destructor Destroy; override;
procedure Add(v: TRMCrossView);
procedure Delete(v: TRMCrossView);
end;
{ TDrawPanel }
TDrawPanel = class(TPanel)
private
FColumnFields: TStrings;
FRowFields: TStrings;
FCellFields: TStrings;
LastX, LastY, DefDx, DefDy: Integer;
procedure Draw(x, y, dx, dy: Integer; aStr: string);
procedure DrawColumnCells;
procedure DrawRowCells;
procedure DrawCellField;
procedure DrawBorderLines(pos: byte);
public
procedure Paint; override;
end;
var
FCrossList: TRMCrossList;
function RMCrossList: TRMCrossList;
begin
if FCrossList = nil then
begin
FCrossList := TRMCrossList.Create;
end;
Result := FCrossList;
end;
function HasTotal(s: string): Boolean;
begin
Result := Pos('+', s) <> 0;
end;
function FuncName(s: string): string;
begin
if HasTotal(s) then
begin
Result := LowerCase(Copy(s, Pos('+', s) + 1, 255));
if Result = '' then
Result := 'sum';
end
else
Result := '';
end;
function _PureName(s: string): string;
begin
if HasTotal(s) then
Result := Copy(s, 1, Pos('+', s) - 1)
else
Result := s;
end;
function CharCount(ch: Char; s: string): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(s) do
begin
if s[i] = ch then
Inc(Result);
end;
end;
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{ TRMCrossGroupItem }
constructor TRMCrossGroupItem.Create(AParent: TRMCrossArray; GroupName: string;
Index, CellItemsCount: Integer);
begin
inherited Create;
Parent := AParent;
FCellItemsCount := CellItemsCount;
FArray := VarArrayCreate([0, CellItemsCount - 1], varVariant);
FCount := TRMQuickIntArray.Create(CellItemsCount);
FGroupName := TStringList.Create;
FIndex := Index;
Reset(GroupName, 0);
end;
destructor TRMCrossGroupItem.Destroy;
begin
FGroupName.Free;
VarClear(FArray);
FCount.Free;
inherited Destroy;
end;
procedure TRMCrossGroupItem.Reset(NewGroupName: string; StartFrom: Integer);
var
i: Integer;
s: string;
begin
FStartFrom := StartFrom;
RMSetCommaText(NewGroupName, FGroupName);
for i := 0 to FCellItemsCount - 1 do
begin
FCount[i] := 0;
s := FuncName(Parent.FCellFields[i]);
if (s = 'max') or (s = 'min') then
FArray[i] := Null
else
FArray[i] := 0;
end;
end;
function TRMCrossGroupItem.IsBreak(GroupName: string): Boolean;
var
sl: TStringList;
begin
sl := TStringList.Create;
RMSetCommaText(GroupName, sl);
Result := (FIndex < sl.Count) and (FIndex < FGroupName.Count) and
(sl[FIndex] <> FGroupName[FIndex]);
sl.Free;
end;
procedure TRMCrossGroupItem.AddValue(Value: Variant);
var
i: Integer;
s: string;
begin
if TVarData(Value).VType >= varArray then
begin
for i := 0 to FCellItemsCount - 1 do
begin
if (Value[i] <> Null) and HasTotal(Parent.FCellFields[i]) then
begin
s := FuncName(Parent.FCellFields[i]);
if (s = 'sum') or (s = 'count') then
FArray[i] := FArray[i] + Value[i]
else if s = 'min' then
begin
if (FArray[i] = Null) or (FArray[i] > Value[i]) then
FArray[i] := Value[i];
end
else if s = 'max' then
begin
if FArray[i] < Value[i] then
FArray[i] := Value[i];
end
else if s = 'avg' then
begin
FArray[i] := FArray[i] + Value[i];
FCount[i] := FCount[i] + 1;
end;
end;
end;
end;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -