📄 rm_cross.pas
字号:
unit RM_Cross;
interface
{$I RM.inc}
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Menus, ExtCtrls, DB, RM_Class, RM_DSet, RM_DBRel
{$IFDEF Delphi6}
, Variants
{$ENDIF};
const
flCrossShowRowTotal = 2;
flCrossShowColTotal = 4;
flCrossShowIndicator = 8;
type
TRMCrossObject = class(TComponent) // fake component
end;
{ TRMArray }
TRMArray = class(TObject)
private
FArray: TStringList;
FColumns: TStringList;
FCellItemsCount: Integer;
function GetCell(Index1, Index2: string; Index3: Integer): Variant;
procedure SetCell(Index1, Index2: string; Index3: Integer; Value: Variant);
function GetCellByIndex(Index1, Index2, Index3: Integer): Variant;
function GetCellArray(Index1, Index2: Integer): Variant;
procedure SetCellArray(Index1, Index2: Integer; Value: Variant);
public
constructor Create(CellItemsCount: Integer);
destructor Destroy; override;
procedure Clear;
property Columns: TStringList read FColumns;
property Rows: TStringList read FArray;
property CellItemsCount: Integer read FCellItemsCount;
property Cell[Index1, Index2: string; Index3: Integer]: Variant read GetCell write SetCell;
property CellByIndex[Index1, Index2, Index3: Integer]: Variant read GetCellByIndex;
property CellArray[Index1, Index2: Integer]: Variant read GetCellArray write SetCellArray;
end;
{ TRMCross }
TRMCross = 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;
function GetIsTotalRow(Index: Integer): Boolean;
function GetIsTotalColumn(Index: Integer): Boolean;
public
constructor Create(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(TRMView)
private
FAfterCreateObjects: TNotifyEvent;
FColHeight, FColWidth, FRowHeight, FRowWidth: Integer;
FCross: TRMCross;
FColumnWidths: Variant;
FColumnHeights: Variant;
FFlag: Boolean;
FSkip: Boolean;
FRowDS: TRMUserDataset;
FColumnDS: TRMUserDataset;
FRepeatCaptions: Boolean;
FShowHeader: Boolean;
FInternalFrame: Boolean;
FReport: TRMReport;
FSavedOnBeginDoc: TRMBeginDocEvent;
FSavedOnBeforePrint: TRMEnterRectEvent;
FSavedOnPrintColumn: TRMPrintColumnEvent;
FSavedOnEndDoc: TRMEndDocEvent;
function OneObject(p: TRMPage; Name1, Name2: string): TRMMemoView;
function ParentPage: TRMPage;
procedure CreateObjects;
procedure CalcWidths;
procedure MakeBands;
procedure ReportPrintColumn(ColNo: Integer; var Width: Integer);
procedure ReportBeforePrint(Memo: TStringList; View: TRMView);
procedure ReportEndDoc;
procedure ReportBeginDoc;
procedure P1Click(Sender: TObject);
procedure P2Click(Sender: TObject);
function GetShowRowTotal: Boolean;
procedure SetShowRowTotal(Value: Boolean);
function GetShowColTotal: Boolean;
procedure SetShowColTotal(Value: Boolean);
function GetShowIndicator: Boolean;
procedure SetShowIndicator(Value: Boolean);
protected
procedure SetPropValue(Index: string; Value: Variant); override;
function GetPropValue(Index: string): Variant; override;
public
class function CanPlaceOnGridView: Boolean; override;
constructor Create; override;
destructor Destroy; override;
procedure Draw(Canvas: TCanvas); override;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToStream(Stream: TStream); override;
procedure DefinePopupMenu(Popup: TPopupMenu); override;
procedure DefineProperties; override;
procedure ShowEditor; override;
property RowDS: TRMUserDataset read FRowDS;
property ColumnDS: TRMUserDataset read FColumnDS;
property AfterCreateObjects: TNotifyEvent read FAfterCreateObjects write FAfterCreateObjects;
property PShowRowTotal: Boolean read GetShowRowTotal write SetShowRowTotal;
property PShowColTotal: Boolean read GetShowColTotal write SetShowColTotal;
property PShowIndicator: Boolean read GetShowIndicator write SetShowIndicator;
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;
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);
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_CmpReg, RM_Const, RM_Utils;
type
PRMArrayCell = ^TRMArrayCell;
TRMArrayCell = record
Items: Variant;
end;
{ TRMCrossGroupItem }
TRMCrossGroupItem = class(TObject)
private
Parent: TRMCross;
FArray: Variant;
FCellItemsCount: Integer;
FGroupName: TStringList;
FIndex: Integer;
FCount: Variant;
FStartFrom: Integer;
procedure Reset(NewGroupName: string; StartFrom: Integer);
procedure AddValue(Value: Variant);
function IsBreak(GroupName: string): Boolean;
procedure CheckAvg;
property Value: Variant read FArray;
public
constructor Create(AParent: TRMCross; 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 = class(TPanel)
private
FColumnFields: TStrings;
FRowFields: TStrings;
FCellFields: TStrings;
LastX, LastY, DefDx, DefDy: Integer;
procedure Draw(x, y, dx, dy: Integer; s: 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: TRMCross; GroupName: string;
Index, CellItemsCount: Integer);
begin
inherited Create;
Parent := AParent;
FCellItemsCount := CellItemsCount;
FArray := VarArrayCreate([0, CellItemsCount - 1], varVariant);
FCount := VarArrayCreate([0, CellItemsCount - 1], varInteger);
FGroupName := TStringList.Create;
FIndex := Index;
Reset(GroupName, 0);
end;
destructor TRMCrossGroupItem.Destroy;
begin
FGroupName.Free;
VarClear(FArray);
VarClear(FCount);
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;
procedure TRMCrossGroupItem.CheckAvg;
var
i: Integer;
s: string;
begin
for i := 0 to FCellItemsCount - 1 do
begin
s := FuncName(Parent.FCellFields[i]);
if s = 'avg' then
begin
if FCount[i] <> 0 then
FArray[i] := FArray[i] / FCount[i]
else
FArray[i] := Null;
end;
end;
end;
{------------------------------------------------------------------------------}
{------------------------------------------------------------------------------}
{TRMArray}
constructor TRMArray.Create(CellItemsCount: Integer);
begin
inherited Create;
FCellItemsCount := CellItemsCount;
FArray := TStringList.Create;
FArray.Sorted := True;
FColumns := TStringList.Create;
FColumns.Sorted := True;
end;
destructor TRMArray.Destroy;
begin
Clear;
FArray.Free;
FColumns.Free;
inherited Destroy;
end;
procedure TRMArray.Clear;
var
i, j: Integer;
sl: TList;
p: PRMArrayCell;
begin
for i := 0 to FArray.Count - 1 do
begin
sl := Pointer(FArray.Objects[i]);
if sl <> nil then
begin
for j := 0 to sl.Count - 1 do
begin
p := sl[j];
if p <> nil then
begin
VarClear(p.Items);
Dispose(p);
end;
end;
end;
sl.Free;
end;
FArray.Clear;
end;
function TRMArray.GetCell(Index1, Index2: string; Index3: Integer): Variant;
var
i1, i2: Integer;
sl: TList;
p: PRMArrayCell;
begin
Result := Null;
i1 := FArray.IndexOf(Index1);
i2 := FColumns.IndexOf(Index2);
if (i1 = -1) or (i2 = -1) or (Index3 >= FCellItemsCount) then Exit;
i2 := Integer(FColumns.Objects[i2]);
if i1 < FArray.Count then
sl := Pointer(FArray.Objects[i1])
else
sl := nil;
if sl <> nil then
begin
if i2 < sl.Count then
p := sl[i2]
else
p := nil;
if p <> nil then
Result := p^.Items[Index3];
end;
end;
procedure TRMArray.SetCell(Index1, Index2: string; Index3: Integer; Value: Variant);
var
i, j, i1, i2: Integer;
sl: TList;
p: PRMArrayCell;
begin
i1 := FArray.IndexOf(Index1);
i2 := FColumns.IndexOf(Index2);
if i2 <> -1 then
i2 := Integer(FColumns.Objects[i2]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -