FastReport_FMX_2.8.12/LibD28/FMX.frxEngine.pas
2024-07-06 22:41:12 +02:00

2811 lines
74 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport v4.0 }
{ Report engine }
{ }
{ Copyright (c) 1998-2008 }
{ by Alexander Tzyganenko, }
{ Fast Reports Inc. }
{ }
{******************************************}
unit FMX.frxEngine;
interface
{$I fmx.inc}
{$I frx.inc}
uses
System.SysUtils, System.Classes, System.Variants, System.UITypes, System.Types,
FMX.Forms,
FMX.frxClass, FMX.frxAggregate, FMX.frxXML, FMX.frxDMPClass;
type
{ TfrxHeaderList holds a set of bands that should appear on each new page.
This includes page header, column header and header bands with
"Reprint on new page" setting }
TfrxHeaderListItem = class(TObject)
public
Band: TfrxBand;
Left: Extended;
IsInKeepList: Boolean;
end;
TfrxHeaderList = class(TObject)
private
FList: TList;
function GetCount: Integer;
function GetItems(Index: Integer): TfrxHeaderListItem;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure AddItem(ABand: TfrxBand; ALeft: Extended; AInKeepList: Boolean);
procedure RemoveItem(ABand: TfrxBand);
property Count: Integer read GetCount;
property Items[Index: Integer]: TfrxHeaderListItem read GetItems; default;
end;
TfrxEngine = class(TfrxCustomEngine)
private
FAggregates: TfrxAggregateList;
FCallFromAddPage: Boolean;
FCallFromEndPage: Boolean;
FCurBand: TfrxBand;
FLastBandOnPage: TfrxBand;
FDontShowHeaders: Boolean;
FHeaderList: TfrxHeaderList; { list of header bands }
FFirstReportPage: Boolean; { needed for correct setting of PreviewPages.FirstPage }
FFirstColumnY: Extended; { position of the first column }
FIsFirstBand: Boolean; { needed for KeepTogether }
FIsFirstPage: Boolean; { first and last page flags }
FIsLastPage: Boolean; { }
FTitlePrinted: Boolean;
FHBandNamesTree: TStrings; { need for correct work with drill down in master-detail-subtetail-... }
FKeepBand: TfrxBand;
FKeepFooter: Boolean;
FKeeping: Boolean;
FKeepHeader: Boolean;
FKeepCurY: Extended; { need when group doesn't fit on the whole page}
FPrevFooterHeight: Extended; {need for correct freespace calculation when use printOnPreviousPage}
FKeepOutline: TfrxXMLItem;
FKeepPosition: Integer;
FKeepAnchor: Integer;
FCallFromPHeader: Boolean; { endless loop fix }
FOutputTo: TfrxNullBand; { used in the subreports }
FPage: TfrxReportPage; { currently proceeded page }
FPageCurX: Extended;
FStartNewPageBand: TfrxBand; { needed in addpage }
FVHeaderList: TList; { list of vheader bands }
FVMasterBand: TfrxBand; { master hband for vbands }
FVPageList: TList; { list of page breaks for vbands }
FFastCanvas: TObject;
procedure AddBandOutline(Band: TfrxBand);
procedure AddColumn;
procedure AddPage;
procedure AddPageOutline;
procedure AddToHeaderList(Band: TfrxBand);
procedure AddToVHeaderList(Band: TfrxBand);
procedure CheckBandColumns(Band: TfrxDataBand; ColumnKeepPos: Integer;
SaveCurY: Extended);
procedure CheckDrill(Master: TfrxDataBand; Band: TfrxGroupHeader);
procedure CheckGroups(Master: TfrxDataBand; Band: TfrxGroupHeader;
ColumnKeepPos: Integer; SaveCurY: Extended);
procedure CheckSubReports(Band: TfrxBand);
procedure CheckSuppress(Band: TfrxBand);
procedure DoShow(Band: TfrxBand);
procedure DrawSplit(Band: TfrxBand);
procedure EndColumn;
procedure EndKeep(Band: TfrxBand);
procedure InitGroups(Master: TfrxDataBand; Band: TfrxGroupHeader;
Index: Integer; ResetLineN: Boolean = False);
procedure InitPage;
procedure NotifyObjects(Band: TfrxBand);
procedure OutlineRoot;
procedure OutlineUp(Band: TfrxBand);
procedure PreparePage(ErrorList: TStrings; PrepareVBands: Boolean);
procedure PrepareShiftTree(Band: TfrxBand);
procedure RemoveFromHeaderList(Band: TfrxBand);
procedure RemoveFromVHeaderList(Band: TfrxBand);
procedure ResetSuppressValues(Band: TfrxBand);
procedure RunPage(Page: TfrxReportPage);
procedure RunReportPages;
procedure ShowGroupFooters(Band: TfrxGroupHeader; Index: Integer; Master: TfrxDataBand);
procedure ShowVBands(HBand: TfrxBand);
procedure StartKeep(Band: TfrxBand; Position: Integer = 0);
procedure Stretch(Band: TfrxBand);
procedure UnStretch(Band: TfrxBand);
function CanShow(Obj: TObject; PrintIfDetailEmpty: Boolean): Boolean;
function FindBand(Band: TfrxBandClass): TfrxBand;
function RunDialogs: Boolean;
protected
function GetPageHeight: Double; override;
public
constructor Create(AReport: TfrxReport); override;
destructor Destroy; override;
procedure EndPage; override;
procedure NewColumn; override;
procedure NewPage; override;
function Run: Boolean; override;
procedure ShowBand(Band: TfrxBand); overload; override;
procedure ShowBand(Band: TfrxBandClass); overload; override;
function HeaderHeight: Double; override;
function FooterHeight: Double; override;
function FreeSpace: Double; override;
procedure BreakAllKeep; override; { used in crosstab }
function GetAggregateValue(const Name, Expression: String;
Band: TfrxBand; Flags: Integer): Variant; override;
function Initialize: Boolean;
procedure Finalize;
end;
implementation
uses
FMX.frxUtils, FMX.frxPreviewPages, FMX.frxRes, FMX.frxFMX;
type
THackComponent = class(TfrxComponent);
THackMemoView = class(TfrxCustomMemoView);
{ TfrxHeaderList }
constructor TfrxHeaderList.Create;
begin
FList := TList.Create;
end;
destructor TfrxHeaderList.Destroy;
begin
Clear;
FList.Free;
inherited;
end;
procedure TfrxHeaderList.Clear;
begin
while FList.Count > 0 do
begin
TObject(FList[0]).Free;
FList.Delete(0);
end;
end;
function TfrxHeaderList.GetCount: Integer;
begin
Result := FList.Count;
end;
function TfrxHeaderList.GetItems(Index: Integer): TfrxHeaderListItem;
begin
Result := FList[Index];
end;
procedure TfrxHeaderList.AddItem(ABand: TfrxBand; ALeft: Extended; AInKeepList: Boolean);
var
Item: TfrxHeaderListItem;
begin
Item := TfrxHeaderListItem.Create;
Item.Band := ABand;
Item.Left := ALeft;
Item.IsInKeepList := AInKeepList;
FList.Add(Item);
end;
procedure TfrxHeaderList.RemoveItem(ABand: TfrxBand);
var
i: Integer;
begin
for i := 0 to Count - 1 do
if Items[i].Band = ABand then
begin
Items[i].Free;
FList.Delete(i);
break;
end;
end;
{ TfrxEngine }
constructor TfrxEngine.Create(AReport: TfrxReport);
begin
inherited;
FHeaderList := TfrxHeaderList.Create;
FVHeaderList := TList.Create;
FVPageList := TList.Create;
FAggregates := TfrxAggregateList.Create(AReport);
FHBandNamesTree := TStringList.Create;
FLastBandOnPage := nil;
FFastCanvas := nil;
if frxCanvasClass <> nil then
FFastCanvas := frxCanvasClass.Create;
end;
destructor TfrxEngine.Destroy;
begin
FHeaderList.Free;
FVHeaderList.Free;
FVPageList.Free;
FAggregates.Free;
FHBandNamesTree.Free;
if Assigned(FFastCanvas) then
FFastCanvas.Free;
inherited;
end;
function TfrxEngine.Initialize: Boolean;
var
i, j: Integer;
b: TfrxDataBand;
begin
FPrevFooterHeight := 0;
PreviewPages.Initialize;
PreviewPages.AddPageAction := apAdd;
StartDate := Date;
StartTime := Time;
Running := True;
FKeeping := False;
CurVColumn := 0;
FOutputTo := nil;
{ clear all aggregate items }
FAggregates.Clear;
{ add all report pages to the PreviewPages }
for i := 0 to Report.PagesCount - 1 do
if Report.Pages[i] is TfrxReportPage then
begin
{ set the current page }
FPage := TfrxReportPage(Report.Pages[i]);
{ create band tree for the current page }
PreparePage(Report.Errors, False);
PreparePage(Report.Errors, True);
end;
{ check datasets used }
for i := 0 to Report.PagesCount - 1 do
if Report.Pages[i] is TfrxReportPage then
begin
FPage := TfrxReportPage(Report.Pages[i]);
if (Report.DataSet <> nil) and (Report.DataSet = FPage.DataSet) then
begin
Report.Errors.Add('Cannot use the same dataset for Report.DataSet and Page.DataSet');
break;
end;
for j := 0 to FPage.FSubBands.Count - 1 do
begin
b := FPage.FSubBands[j];
if (b <> nil) and (b.DataSet <> nil) then
if Report.DataSet = b.DataSet then
begin
Report.Errors.Add('Cannot use the same dataset for Report.DataSet and Band.DataSet');
break;
end
else if FPage.DataSet = b.DataSet then
begin
Report.Errors.Add('Cannot use the same dataset for Page.DataSet and Band.DataSet');
break;
end
end;
end;
Result := Report.Errors.Count = 0;
end;
procedure TfrxEngine.Finalize;
begin
try
Report.DataSets.Finalize;
finally
PreviewPages.Finish;
Running := False;
end;
end;
function TfrxEngine.Run: Boolean;
var
i: Integer;
begin
Result := False;
try
if Initialize then
try
Report.DataSets.Initialize;
Report.DoNotifyEvent(Report, Report.OnStartReport);
if RunDialogs then
begin
Result := True;
{ add all report pages to the PreviewPages }
for i := 0 to Report.PagesCount - 1 do
if Report.Pages[i] is TfrxReportPage then
begin
FPage := TfrxReportPage(Report.Pages[i]);
PreviewPages.AddSourcePage(FPage);
{ find aggregates }
FAggregates.AddItems(FPage);
end;
{ start the report }
FinalPass := not DoublePass;
TotalPages := 0;
PreviewPages.BeginPass;
RunReportPages;
if DoublePass then
begin
TotalPages := PreviewPages.Count;
PreviewPages.CurPage := PreviewPages.Count - 1;
PreviewPages.ClearFirstPassPages;
FAggregates.ClearValues;
FinalPass := True;
RunReportPages;
end;
end
finally
Report.DoNotifyEvent(Report, Report.OnStopReport);
end;
finally
Finalize;
end;
end;
{$HINTS OFF}
function TfrxEngine.RunDialogs: Boolean;
var
i: Integer;
p: TfrxDialogPage;
v: Variant;
begin
Result := True;
{$IFNDEF FR_VER_BASIC}
if Trim(Report.OnRunDialogs) <> '' then
begin
v := VarArrayOf([True]);
Report.DoParamEvent(Report.OnRunDialogs, v);
Result := v[0];
end
else
for i := 0 to Report.PagesCount - 1 do
if (Report.Pages[i] is TfrxDialogPage) and Report.Pages[i].Visible then
begin
p := TfrxDialogPage(Report.Pages[i]);
{ refresh the border style - it was bsSizeable in the designer }
p.DialogForm.BorderStyle := p.BorderStyle;
{ don't show empty form }
if p.DialogForm.ChildrenCount <> 0 then
begin
if Assigned(OnRunDialog) then
OnRunDialog(p) else
p.ShowModal;
if p.ModalResult = mrCancel then
begin
Result := False;
break;
end;
end;
end;
{$ENDIF}
end;
{$HINTS ON}
procedure TfrxEngine.RunReportPages;
procedure DoPages;
var
i: Integer;
begin
for i := 0 to Report.PagesCount - 1 do
if Report.Pages[i] is TfrxReportPage then
begin
FPage := TfrxReportPage(Report.Pages[i]);
{ ignore subreport pages and invisible pages }
if not FPage.IsSubReport and FPage.Visible then
RunPage(FPage);
if Report.Terminated then break;
FFirstReportPage := False;
end;
end;
begin
FFirstReportPage := True;
if Report.DataSet = nil then
DoPages
else
begin
Report.DataSet.First;
while not Report.DataSet.Eof do
begin
if Report.Terminated then break;
DoPages;
Report.DataSet.Next;
end;
end;
end;
procedure TfrxEngine.PreparePage(ErrorList: TStrings; PrepareVBands: Boolean);
var
i, j, k: Integer;
t, c: TfrxComponent;
b: TfrxBand;
Bands: TList;
SortBands: TStringList;
procedure ClearNils;
var
i: Integer;
begin
i := 0;
while i < Bands.Count do
if Bands[i] = nil then
Bands.Delete(i) else
Inc(i);
end;
procedure MakeTree(Obj: TObject; From: Integer);
var
i: Integer;
b: TfrxBand;
begin
if Obj is TfrxReportPage then
begin
{ fill the first level - TfrxReportPage.FMasterBands }
for i := 0 to Bands.Count - 1 do
begin
b := Bands[i];
if b = nil then continue;
if b is TfrxMasterData then
begin
// if TfrxDataBand(b).DataSet <> nil then { ignore empty datasets }
if PrepareVBands then
TfrxReportPage(Obj).FVSubBands.Add(b)
else
TfrxReportPage(Obj).FSubBands.Add(b);
Bands[i] := nil;
MakeTree(b, i + 1);
end;
end;
end
else
begin
{ fill next levels - TfrxBand.FSubBands }
for i := From to Bands.Count - 1 do
begin
b := Bands[i];
if b = nil then continue;
{ looking for sub-level bands }
if b.BandNumber = TfrxBand(Obj).BandNumber + 1 then
begin
// if TfrxDataBand(b).DataSet <> nil then { ignore empty datasets }
TfrxBand(Obj).FSubBands.Add(b);
Bands[i] := nil;
if not (b is TfrxDataBand6) then
MakeTree(b, i + 1);
end
else if b.BandNumber <= TfrxBand(Obj).BandNumber then
break; { found higher-level data band }
end;
end;
end;
procedure ConnectHeaders;
var
i: Integer;
b1, b2: TfrxBand;
begin
for i := 0 to Bands.Count - 1 do
begin
b1 := Bands[i];
{ looking for data band }
if b1 is TfrxDataBand then
begin
if i > 0 then
begin
b2 := Bands[i - 1];
if b2 is TfrxHeader then { if top band is header, connect it }
begin
b1.FHeader := b2;
Bands[i - 1] := nil;
end;
end;
if i < Bands.Count - 1 then { if bottom band is footer, connect it }
begin
b2 := Bands[i + 1];
if b2 is TfrxFooter then
begin
b1.FFooter := b2;
Bands[i + 1] := nil;
end;
end;
end;
end;
ClearNils;
{ now all headers/footers must be connected. If not, add an error }
for i := 0 to Bands.Count - 1 do
begin
b1 := Bands[i];
if (b1 is TfrxHeader) or (b1 is TfrxFooter) then
begin
ErrorList.Add(frxResources.Get('enUnconnHeader') + ' ' + b1.Name);
Bands[i] := nil;
end;
end;
ClearNils;
end;
procedure ConnectGroups;
var
i, j: Integer;
b1, b2: TfrxBand;
begin
{ connect group headers }
i := 0;
while i < Bands.Count do
begin
b1 := Bands[i];
if b1 is TfrxGroupHeader then
begin
b1.FSubBands.Add(b1);
Inc(i);
{ add all subsequent headers to the first header's FSubBands }
while (i < Bands.Count) and (TfrxBand(Bands[i]) is TfrxGroupHeader) do
begin
b1.FSubBands.Add(Bands[i]);
Inc(i);
end;
{ search for databand }
while (i < Bands.Count) and not (TfrxBand(Bands[i]) is TfrxDataBand) do
Inc(i);
{ now we expect to see the databand }
if (i = Bands.Count) or not (TObject(Bands[i]) is TfrxDataBand) then
ErrorList.Add(frxResources.Get('enUnconnGroup') + ' ' + b1.Name)
else
TfrxBand(Bands[i]).FGroup := b1;
end
else
Inc(i);
end;
{ connect group footers }
for i := 0 to Bands.Count - 1 do
begin
b1 := Bands[i];
if b1 is TfrxGroupFooter then
for j := i - 1 downto 0 do
begin
b2 := Bands[j];
if b2 is TfrxGroupHeader then { connect to top-nearest header }
begin
b2.FFooter := b1;
Bands[i] := nil;
Bands[j] := nil;
break;
end;
end;
end;
{ remove header bands from the list }
for i := 0 to Bands.Count - 1 do
begin
b1 := Bands[i];
if b1 is TfrxGroupHeader then
Bands[i] := nil;
end;
{ looking for footers w/o corresponding header }
for i := 0 to Bands.Count - 1 do
begin
b1 := Bands[i];
if b1 is TfrxGroupFooter then
begin
ErrorList.Add(frxResources.Get('enUnconnGFooter') + ' ' + b1.Name);
Bands[i] := nil;
end;
end;
ClearNils;
end;
begin
SortBands := TfrxStringList.Create;
SortBands.Sorted := True;
{ align all objects with Align property <> baNone }
FPage.AlignChildren;
{ clear all page SubBands }
if PrepareVBands then
FPage.FVSubBands.Clear
else
FPage.FSubBands.Clear;
for i := 0 to FPage.Objects.Count - 1 do
begin
t := FPage.Objects[i];
if t is TfrxBand then
begin
b := TfrxBand(t);
if b.Vertical <> PrepareVBands then
continue;
PrepareShiftTree(b);
b.FSubBands.Clear;
b.FHeader := nil;
b.FFooter := nil;
b.FGroup := nil;
b.FHasVBands := False;
if b is TfrxDataBand then
if (TfrxDataBand(b).DataSet = nil) and (TfrxDataBand(b).RowCount > 0) then
begin
TfrxDataBand(b).DataSet := TfrxDataBand(b).VirtualDataSet;
TfrxDataBand(b).DataSet.Initialize;
end;
{ connect objects to vertical bands }
if (not PrepareVBands) and not (b is TfrxOverlay) then
for j := 0 to FPage.Objects.Count - 1 do
begin
t := FPage.Objects[j];
if (t is TfrxBand) and TfrxBand(t).Vertical then
begin
k := 0;
while k < b.Objects.Count do
begin
c := b.Objects[k];
if (c.Left >= t.Left - 1e-4) and
(c.Left + c.Width <= t.Left + t.Width + 1e-4) then
begin
b.FHasVBands := True;
c.Parent := t;
THackComponent(c).FOriginalBand := b;
c.Left := c.Left - t.Left;
end
else
Inc(k);
end;
end;
end;
end;
end;
{ sort bands by position }
for i := 0 to FPage.Objects.Count - 1 do
begin
t := FPage.Objects[i];
if t is TfrxBand then
begin
b := TfrxBand(t);
if b.Vertical <> PrepareVBands then
continue;
if b.BandNumber in [4..13] then
if b.Vertical then
SortBands.AddObject(Format('%9.2f', [b.Left]), b)
else
SortBands.AddObject(Format('%9.2f', [b.Top]), b);
end;
end;
{ copy sorted items to TList - it's easier to work with it }
Bands := TList.Create;
for i := 0 to SortBands.Count - 1 do
begin
t := TfrxComponent(SortBands.Objects[i]);
Bands.Add(t);
end;
SortBands.Free;
ConnectGroups;
ConnectHeaders;
MakeTree(FPage, 0);
ClearNils;
for i := 0 to Bands.Count - 1 do
begin
t := Bands[i];
ErrorList.Add(frxResources.Get('enBandPos') + ' ' + t.Name);
end;
Bands.Free;
end;
procedure TfrxEngine.PrepareShiftTree(Band: TfrxBand);
var
i, j, k: Integer;
c0, c1, c2, top: TfrxReportComponent;
allObjects: TStringList;
Found: Boolean;
area0, area1, area2, area01: TfrxRectArea;
begin
if Band.FShiftChildren.Count <> 0 then
Exit;
allObjects := TfrxStringList.Create;
allObjects.Duplicates := dupAccept;
{ temporary top object }
top := TfrxMemoView.Create(nil);
top.SetBounds(0, Band.Top-2, Band.Width, 1);
{ sort objects }
for i := 0 to Band.Objects.Count - 1 do
begin
c0 := Band.Objects[i];
allObjects.AddObject(Format('%9.2f', [c0.Top]), c0);
c0.FShiftChildren.Clear;
end;
allObjects.Sort;
allObjects.InsertObject(0, Format('%10.2f', [top.Top]), top);
for i := 0 to allObjects.Count - 1 do
begin
c0 := TfrxReportComponent(allObjects.Objects[i]);
area0 := TfrxRectArea.Create(c0);
{ find an object under c0 }
for j := i + 1 to allObjects.Count - 1 do
begin
c1 := TfrxReportComponent(allObjects.Objects[j]);
area1 := TfrxRectArea.Create(c1);
if not (area0.InterceptsY(area1)) and (area0.Y < area1.Y) and
area0.InterceptsX(area1) then
begin
area01 := area0.InterceptX(area1);
Found := False;
{ check if there is no other objects between c1 and c0 }
for k := j - 1 downto i + 1 do
begin
c2 := TfrxReportComponent(allObjects.Objects[k]);
area2 := TfrxRectArea.Create(c2);
if not (area0.InterceptsY(area2)) and not (area1.InterceptsY(area2)) and
area01.InterceptsX(area2) then
Found := True;
area2.Free;
if Found then
break;
end;
if not Found then
c0.FShiftChildren.Add(c1);
area01.Free;
end;
area1.Free;
end;
area0.Free;
end;
{ copy children from the top object to the band }
Band.FShiftChildren.Clear;
for i := 0 to top.FShiftChildren.Count - 1 do
Band.FShiftChildren.Add(top.FShiftChildren[i]);
allObjects.Free;
top.Free;
end;
function TfrxEngine.CanShow(Obj: TObject; PrintIfDetailEmpty: Boolean): Boolean;
var
i: Integer;
Bands: TList;
b: TfrxDataBand;
res: Boolean;
begin
if Obj is TfrxReportPage then
Bands := TfrxReportPage(Obj).FSubBands else
Bands := TfrxBand(Obj).FSubBands;
Result := True;
{ Check all subdetail bands to ensure they all have records }
if not PrintIfDetailEmpty then
begin
Result := False;
if (Bands.Count = 0) and not (Obj is TfrxPage) then
Result := True;
for i := 0 to Bands.Count - 1 do
begin
b := Bands[i];
if b.DataSet <> nil then
begin
Report.DoNotifyEvent(b, b.OnMasterDetail);
b.DataSet.First;
while not b.DataSet.Eof do
begin
res := CanShow(b, b.PrintIfDetailEmpty);
if res then
begin
Result := True;
break;
end
else
b.DataSet.Next;
end;
end;
end;
end;
end;
procedure TfrxEngine.ResetSuppressValues(Band: TfrxBand);
var
i: Integer;
begin
for i := 0 to Band.Objects.Count - 1 do
if TObject(Band.Objects[i]) is TfrxCustomMemoView then
THackMemoView(Band.Objects[i]).FLastValue := Null;
end;
procedure TfrxEngine.InitGroups(Master: TfrxDataBand; Band: TfrxGroupHeader;
Index: Integer; ResetLineN: Boolean = False);
var
i: Integer;
b: TfrxGroupHeader;
begin
for i := Index to Band.FSubBands.Count - 1 do
begin
b := Band.FSubBands[i];
if ResetLineN then
begin
b.FLineN := 1;
b.FLineThrough := 1;
ResetSuppressValues(b);
end
else
begin
Inc(b.FLineN);
if i < Band.FSubBands.Count - 1 then
TfrxBand(Band.FSubBands[i + 1]).FLineN := 0;
Inc(b.FLineThrough);
end;
end;
CheckDrill(Master, Band);
for i := Index to Band.FSubBands.Count - 1 do
begin
b := Band.FSubBands[i];
CurLine := b.FLineN;
CurLineThrough := b.FLineThrough;
Report.CurObject := b.Name;
b.FLastValue := Report.Calc(b.Condition);
if b.KeepTogether then
StartKeep(b);
ShowBand(b);
AddBandOutline(b);
if b.Vertical then
AddToVHeaderList(b)
else
AddToHeaderList(b);
end;
end;
procedure TfrxEngine.ShowGroupFooters(Band: TfrxGroupHeader; Index: Integer;
Master: TfrxDataBand);
var
i: Integer;
b: TfrxGroupHeader;
begin
for i := Band.FSubBands.Count - 1 downto Index do
begin
b := Band.FSubBands[i];
if b.FFooter <> nil then
if not TfrxGroupFooter(b.FFooter).HideIfSingleDataRecord or (Master.FLineN > 2) then
ShowBand(b.FFooter)
else
FAggregates.Reset(b.FFooter);
OutlineUp(b);
if b.Vertical then
RemoveFromVHeaderList(b)
else
RemoveFromHeaderList(b);
if b.KeepTogether then
EndKeep(b);
end;
end;
procedure TfrxEngine.CheckDrill(Master: TfrxDataBand; Band: TfrxGroupHeader);
var
i, j: Integer;
b, b1: TfrxGroupHeader;
drillVisible: Boolean;
BandNamesList: TStrings;
begin
BandNamesList := FHBandNamesTree;
for i := 0 to Band.FSubBands.Count - 1 do
begin
b := Band.FSubBands[i];
if b.DrillDown then
begin
b.DrillName := '';
for j := 0 to BandNamesList.Count - 1 do
b.DrillName := b.DrillName + BandNamesList[j] + '.' + IntToStr(Integer(BandNamesList.Objects[j])) + '.';
b.DrillName := b.DrillName + b.Name + '.' + IntToStr(b.FLineThrough);
drillVisible := Report.DrillState.IndexOf(b.DrillName) <> -1;
if b.ExpandDrillDown then
drillVisible := not DrillVisible;
if (b.Child <> nil) and not Band.ShowChildIfDrillDown then
b.Child.Visible := drillVisible;
for j := i + 1 to Band.FSubBands.Count - 1 do
begin
b1 := Band.FSubBands[j];
b1.Visible := drillVisible;
if b1.FFooter <> nil then
b1.FFooter.Visible := drillVisible;
end;
Master.Visible := drillVisible;
if not b.ShowFooterIfDrillDown and (b.FFooter <> nil) then
b.FFooter.Visible := drillVisible;
if not drillVisible then
break;
end;
end;
end;
procedure TfrxEngine.CheckGroups(Master: TfrxDataBand; Band: TfrxGroupHeader;
ColumnKeepPos: Integer; SaveCurY: Extended);
var
i: Integer;
b: TfrxGroupHeader;
NextNeeded: Boolean;
begin
CheckDrill(Master, Band);
for i := 0 to Band.FSubBands.Count - 1 do
begin
b := Band.FSubBands[i];
Report.CurObject := b.Name;
if Report.Calc(b.Condition) <> b.FLastValue then
begin
Master.CurColumn := Master.Columns;
CheckBandColumns(Master, ColumnKeepPos, SaveCurY);
{ avoid exception in uni-directional datasets }
NextNeeded := True;
try
Master.DataSet.Prior;
except
NextNeeded := False;
end;
ShowGroupFooters(Band, i, Master);
if NextNeeded then
Master.DataSet.Next;
InitGroups(Master, Band, i);
Master.FLineN := 1;
ResetSuppressValues(Master);
break;
end;
end;
end;
procedure TfrxEngine.CheckBandColumns(Band: TfrxDataBand; ColumnKeepPos: Integer;
SaveCurY: Extended);
begin
if Band.Columns > 1 then
begin
{ collect max position in b.FMaxY }
if CurY > Band.FMaxY then
Band.FMaxY := CurY;
{ all columns have been printed }
if Band.CurColumn >= Band.Columns then
begin
{ need page break, don't break if page has Endless Height}
if ((PageHeight - FooterHeight) - Band.FMaxY <= 1e-4) and (not FPage.EndlessHeight) then
begin
if FKeeping then { standard keep procedure }
NewColumn
else
begin
PreviewPages.CutObjects(ColumnKeepPos);
NewColumn;
PreviewPages.PasteObjects(CurX, CurY);
CurY := CurY + Band.FMaxY - SaveCurY;
end;
end
else
CurY := Band.FMaxY; { start the new band from saved b.FMaxY }
end
else
CurY := SaveCurY; { start the new band from saved SaveCurY }
if Band.Visible then
Band.CurColumn := Band.CurColumn + 1;
end;
end;
procedure TfrxEngine.NotifyObjects(Band: TfrxBand);
var
i: Integer;
c: TfrxComponent;
begin
for i := 0 to NotifyList.Count - 1 do
begin
c := NotifyList[i];
if c <> nil then
c.OnNotify(Band);
end;
end;
procedure TfrxEngine.RunPage(Page: TfrxReportPage);
var
PageCount: Integer;
{$IFNDEF MSWINDOWS}
bIDLE: Boolean;
{$ENDIF}
{ "Null" band contains all free-placed objects that don't have a parent band }
procedure ShowNullBand;
var
i: Integer;
b: TfrxNullBand;
SaveCurY: Extended;
begin
b := TfrxNullBand.Create(nil);
b.Width := PageWidth;
b.Height := PageHeight;
SaveCurY := CurY;
for i := 0 to FPage.Objects.Count - 1 do
if not (TObject(FPage.Objects[i]) is TfrxBand) then
b.Objects.Add(FPage.Objects[i]);
try
b.AlignChildren;
ShowBand(b);
finally
CurY := SaveCurY;
b.Objects.Clear;
b.Free;
end;
end;
{ Band tree is the structure that we created in the PreparePage method }
procedure ShowBandTree(Obj: TObject);
var
i: Integer;
Bands: TList;
b: TfrxDataBand;
FirstTime: Boolean;
FooterKeepPos, ColumnKeepPos: Integer;
SaveCurY: Extended;
begin
if not Report.EngineOptions.EnableThreadSafe then
{$IFDEF MSWINDOWS}
Application.ProcessMessages;
{$ELSE}
Application.DoIdle(bIDLE);
{$ENDIF}
if Report.Terminated then Exit;
FooterKeepPos := 0;
ColumnKeepPos := 0;
SaveCurY := CurY;
if Obj is TfrxReportPage then
Bands := TfrxReportPage(Obj).FSubBands else
Bands := TfrxBand(Obj).FSubBands;
for i := 0 to Bands.Count - 1 do
begin
b := Bands[i];
if b.DataSet = nil then
continue;
b.DataSet.First;
b.FLineN := 1;
b.FLineThrough := 1;
b.CurColumn := 1;
FirstTime := True;
ResetSuppressValues(b);
while not b.DataSet.Eof do
begin
if CanShow(b, b.PrintIfDetailEmpty) then
begin
if FirstTime then
begin
if b.KeepHeader and (b.FHeader <> nil) then
begin
if FIsFirstBand then
FIsFirstBand := not FTitlePrinted;
FKeepHeader := not FIsFirstBand;
StartKeep(b);
end;
AddToHeaderList(b.FHeader);
ShowBand(b.FHeader);
if b.KeepTogether then
StartKeep(b);
end
{ keeping a master-detail differs from keeping a group }
else if (b.FGroup = nil) and b.KeepTogether then
StartKeep(b);
if b.FGroup <> nil then
if FirstTime then
InitGroups(b, TfrxGroupHeader(b.FGroup), 0, True) else
CheckGroups(b, TfrxGroupHeader(b.FGroup), ColumnKeepPos, SaveCurY);
if b.KeepFooter then
FooterKeepPos := PreviewPages.GetCurPosition;
if (b.Columns > 1) and (b.CurColumn = 1) then
ColumnKeepPos := PreviewPages.GetCurPosition;
SaveCurY := CurY;
CurLine := b.FLineN;
CurLineThrough := b.FLineThrough;
ShowBand(b);
FKeepHeader := False;
NotifyObjects(b);
if FirstTime then
if b.KeepHeader and (b.FHeader <> nil) then
EndKeep(b);
FirstTime := False;
FHBandNamesTree.AddObject(b.Name, TObject(b.FLineThrough));
Inc(b.FLineN);
Inc(b.FLineThrough);
CheckBandColumns(b, ColumnKeepPos, SaveCurY);
AddBandOutline(b);
ShowBandTree(b);
FHBandNamesTree.Delete(FHBandNamesTree.Count - 1);
OutlineUp(b);
FIsFirstBand := False;
if b.FooterAfterEach then
ShowBand(b.FFooter);
end;
{ keeping a master-detail differs from keeping a group }
if (b.FGroup = nil) and b.KeepTogether then
EndKeep(b);
b.DataSet.Next;
if b.RowCount <> 0 then
if b.FLineN > b.RowCount then break;
if Report.Terminated then break;
end;
{ update the CurY if band is multicolumn }
b.CurColumn := b.Columns;
CheckBandColumns(b, ColumnKeepPos, SaveCurY);
if not FirstTime then { some bands have been printed }
begin
if b.FGroup <> nil then
ShowGroupFooters(TfrxGroupHeader(b.FGroup), 0, b);
if FKeeping then
RemoveFromHeaderList(b.FHeader);
if not b.FooterAfterEach then
begin
if b.KeepFooter then
StartKeep(b, FooterKeepPos);
FKeepFooter := True;
ShowBand(b.FFooter);
if b.KeepFooter then
EndKeep(b);
FKeepFooter := False;
end;
RemoveFromHeaderList(b.FHeader);
if (b.FGroup <> nil) and b.KeepTogether then
EndKeep(b);
end;
if Report.Terminated then break;
FIsFirstBand := False;
end;
end;
procedure ShowPage;
var
pgWidth, pgHeight: Extended;
begin
if CanShow(FPage, Report.EngineOptions.PrintIfEmpty) then
begin
InitPage;
ShowNullBand;
if Assigned(Report.OnManualBuild) then
Report.OnManualBuild(FPage)
else if Trim(FPage.OnManualBuild) <> '' then
Report.DoNotifyEvent(FPage, FPage.OnManualBuild)
else
ShowBandTree(FPage);
FIsLastPage := True;
if FPage.EndlessHeight or FPage.EndlessWidth then
begin
if FPage.EndlessWidth then
pgWidth := PageWidth + FPage.LeftMargin * fr01cm + FPage.RightMargin * fr01cm
else
pgWidth := FPage.PaperWidth * fr01cm;
if FPage.EndlessHeight then
begin
PageHeight := CurY + FooterHeight;
pgHeight := PageHeight + FPage.TopMargin * fr01cm + FPage.BottomMargin * fr01cm
end
else
pgHeight := FPage.PaperHeight * fr01cm;
TfrxPreviewPages(PreviewPages).UpdatePageDimensions(FPage, pgWidth, pgHeight);
end;
EndPage;
FIsLastPage := False;
end;
end;
begin
{ The Page parameter needed only for subreport pages. General is FPage }
if Page.IsSubReport then
begin
ShowBandTree(Page);
Exit;
end;
FIsFirstBand := True;
Report.DoNotifyEvent(FPage, FPage.OnBeforePrint);
if FPage.DataSet <> nil then
begin
FPage.DataSet.First;
while not FPage.DataSet.Eof do
begin
if Report.Terminated then break;
ShowPage;
FPage.DataSet.Next;
end;
end
else
for PageCount := 1 to FPage.PageCount do
begin
if Report.Terminated then break;
ShowPage;
end;
Report.DoNotifyEvent(FPage, FPage.OnAfterPrint);
end;
procedure TfrxEngine.ShowVBands(HBand: TfrxBand);
var
SavePageNo: Integer;
procedure ShowBandTree(Bands: TList);
var
i: Integer;
b: TfrxDataBand;
FirstTime: Boolean;
begin
if Report.Terminated then Exit;
for i := 0 to Bands.Count - 1 do
begin
b := Bands[i];
if b.DataSet = nil then
continue;
b.DataSet.First;
b.FLineN := 1;
b.FLineThrough := 1;
b.CurColumn := 1;
CurLine := b.FLineN;
CurLineThrough := b.FLineThrough;
FirstTime := True;
ResetSuppressValues(b);
while not b.DataSet.Eof do
begin
if FirstTime then
begin
ShowBand(b.FHeader);
AddToVHeaderList(b.FHeader);
end;
if b.FGroup <> nil then
if FirstTime then
InitGroups(b, TfrxGroupHeader(b.FGroup), 0, True) else
CheckGroups(b, TfrxGroupHeader(b.FGroup), 0, 0);
FirstTime := False;
CurLine := b.FLineN;
CurLineThrough := b.FLineThrough;
ShowBand(b);
NotifyObjects(b);
Inc(b.FLineN);
Inc(b.FLineThrough);
ShowBandTree(b.FSubBands);
if b.FooterAfterEach then
ShowBand(b.FFooter);
b.DataSet.Next;
if b.RowCount <> 0 then
if b.FLineN > b.RowCount then break;
if Report.Terminated then break;
end;
if b.FGroup <> nil then
ShowGroupFooters(TfrxGroupHeader(b.FGroup), 0, b);
if not FirstTime then { some bands have been printed }
begin
RemoveFromVHeaderList(b.FHeader);
if not b.FooterAfterEach then
ShowBand(b.FFooter);
end;
if Report.Terminated then break;
end;
end;
begin
FVMasterBand := HBand;
FVMasterBand.FOriginalObjectsCount := FVMasterBand.Objects.Count;
FVMasterBand.AllowSplit := False;
SavePageNo := PreviewPages.CurPage;
FVHeaderList.Clear;
FVPageList.Clear;
FVPageList.Add(Pointer(0));
CurVColumn := 0;
ShowBandTree(TfrxReportPage(HBand.Page).FVSubBands);
FVPageList.Add(Pointer(FVMasterBand.Objects.Count));
PreviewPages.CurPage := SavePageNo;
end;
procedure TfrxEngine.InitPage;
begin
{ fill in the header/footer lists }
FHeaderList.Clear;
if FPage.TitleBeforeHeader then
begin
FHeaderList.AddItem(FindBand(TfrxReportTitle), 0, False);
FHeaderList.AddItem(FindBand(TfrxPageHeader), 0, False);
end
else
begin
FHeaderList.AddItem(FindBand(TfrxPageHeader), 0, False);
FHeaderList.AddItem(FindBand(TfrxReportTitle), 0, False);
end;
{ calculating the page/footer sizes }
PageHeight := FPage.PaperHeight * fr01cm - FPage.TopMargin * fr01cm -
FPage.BottomMargin * fr01cm;
PageWidth := FPage.PaperWidth * fr01cm - FPage.LeftMargin * fr01cm -
FPage.RightMargin * fr01cm;
{ reset the current position }
CurX := 0;
CurY := 0;
CurColumn := 1;
FPageCurX := 0;
FVMasterBand := nil;
FIsFirstPage := True;
FIsLastPage := False;
OutlineRoot;
if FPage.ResetPageNumbers then
PreviewPages.ResetLogicalPageNumber;
if (PreviewPages.Count = 0) or not FPage.PrintOnPreviousPage then
AddPage
else
begin
PreviewPages.CurPage := PreviewPages.Count - 1;
CurY := PreviewPages.GetLastY;
RemoveFromHeaderList(FindBand(TfrxReportTitle));
ShowBand(TfrxReportTitle);
end;
if FFirstReportPage then
PreviewPages.FirstPage := PreviewPages.CurPage;
FFirstColumnY := CurY;
ShowBand(TfrxColumnHeader);
FHeaderList.AddItem(FindBand(TfrxColumnHeader), 0, False);
RemoveFromHeaderList(FindBand(TfrxReportTitle));
AddPageOutline;
end;
function TfrxEngine.HeaderHeight: Double;
var
Band: TfrxBand;
begin
Result := 0;
Band := FindBand(TfrxColumnHeader);
while Band <> nil do
begin
Result := Result + Band.Height;
Band := Band.Child;
end;
Band := FindBand(TfrxPageHeader);
while Band <> nil do
begin
Result := Result + Band.Height;
Band := Band.Child;
end;
end;
function TfrxEngine.FooterHeight: Double;
var
Band: TfrxBand;
begin
Result := 0;
Band := FindBand(TfrxColumnFooter);
if Band <> nil then
Result := Result + Band.Height;
Band := FindBand(TfrxPageFooter);
if Band <> nil then
Result := Result + Band.Height;
end;
function TfrxEngine.FindBand(Band: TfrxBandClass): TfrxBand;
begin
Result := FPage.FindBand(Band);
end;
procedure TfrxEngine.ShowBand(Band: TfrxBand);
var
chBand: TfrxBand;
begin
if Band <> nil then
begin
if Band.KeepChild then
StartKeep(Band);
DoShow(Band);
if (Band is TfrxDataBand) and (TfrxDataBand(Band).CurColumn > 1) then { dont show childs for columns}
chBand := nil
else
chBand := Band.Child;
if (chBand <> nil) and (Band.Visible or Band.PrintChildIfInvisible) then
ShowBand(chBand);
if Band.KeepChild then
EndKeep(Band);
if Band is TfrxDataBand then
FAggregates.AddValue(Band);
end;
end;
procedure TfrxEngine.ShowBand(Band: TfrxBandClass);
begin
ShowBand(FindBand(Band));
end;
procedure TfrxEngine.AddToHeaderList(Band: TfrxBand);
begin
{ only header bands with "Reprint on new page" flag can be added }
if ((Band is TfrxHeader) and TfrxHeader(Band).ReprintOnNewPage) or
((Band is TfrxGroupHeader) and TfrxGroupHeader(Band).ReprintOnNewPage) then
FHeaderList.AddItem(Band, FPageCurX, FKeeping and not (Band is TfrxHeader));
end;
procedure TfrxEngine.AddToVHeaderList(Band: TfrxBand);
begin
{ only header bands with "Reprint on new page" flag can be added }
if ((Band is TfrxHeader) and TfrxHeader(Band).ReprintOnNewPage) or
((Band is TfrxGroupHeader) and TfrxGroupHeader(Band).ReprintOnNewPage) then
FVHeaderList.Add(Band);
end;
procedure TfrxEngine.BreakAllKeep;
begin
inherited;
FKeepCurY := 0;
FKeeping := False;
FKeepBand := nil;
FAggregates.EndKeep;
FKeepHeader := False;
end;
procedure TfrxEngine.RemoveFromHeaderList(Band: TfrxBand);
begin
if Band <> nil then
FHeaderList.RemoveItem(Band);
end;
procedure TfrxEngine.RemoveFromVHeaderList(Band: TfrxBand);
begin
if Band <> nil then
FVHeaderList.Remove(Band);
end;
function TfrxEngine.FreeSpace: Double;
begin
if FPage.EndlessHeight then
Result := 1e+6
else
if FPrevFooterHeight <> 0 then
Result := PageHeight - FPrevFooterHeight - CurY
else Result := PageHeight - FooterHeight - CurY;
end;
procedure TfrxEngine.Stretch(Band: TfrxBand);
var
i: Integer;
h, maxh: Extended;
c, maxc: TfrxView;
HaveSub, NeedShift: Boolean;
procedure DoSubReports;
var
i: Integer;
SaveCurX, SaveCurY, SavePageCurX: Extended;
Sub: TfrxSubreport;
MainBand: Boolean;
AllObjects: TList;
c: TfrxComponent;
begin
{ create a band which will accepts all subsequent output }
MainBand := False;
if FOutputTo = nil then
begin
Band.FOriginalObjectsCount := Band.Objects.Count;
FOutputTo := TfrxNullBand.Create(nil);
MainBand := True;
end;
{ save the current position }
SaveCurX := CurX;
SaveCurY := CurY;
SavePageCurX := FPageCurX;
{ looking for subreport objects }
for i := 0 to Band.Objects.Count - 1 do
if TObject(Band.Objects[i]) is TfrxSubreport then
begin
Sub := TfrxSubreport(Band.Objects[i]);
if not Sub.Visible or not Sub.PrintOnParent or not MainBand then continue;
{ set up all properties... }
FPageCurX := SavePageCurX + Sub.Left;
CurX := Sub.Left;
CurY := Sub.Top;
{ ...and run the subreport }
RunPage(Sub.Page);
end;
{ restore saved position }
CurX := SaveCurX;
CurY := SaveCurY;
FPageCurX := SavePageCurX;
if MainBand then
begin
{ copy all output to the band }
AllObjects := FOutputTo.AllObjects;
for i := 0 to AllObjects.Count - 1 do
begin
c := AllObjects[i];
if (c is TfrxView) and not (c is TfrxSubreport) then
begin
c.Left := c.AbsLeft;
c.Top := c.AbsTop;
c.ParentFont := False;
c.Parent := Band;
end;
if c is TfrxStretcheable then
TfrxStretcheable(c).StretchMode := smDontStretch;
end;
{ Clear the FOutputTo property. Extra objects will be freed
in the Unstretch method. }
FOutputTo.Free;
FOutputTo := nil;
end;
end;
procedure ShiftObjects(Parent: TfrxReportComponent; Amount: Extended);
var
i: Integer;
v: TfrxView;
diff: Extended;
begin
for i := 0 to Parent.FShiftChildren.Count - 1 do
begin
v := Parent.FShiftChildren[i];
if v.ShiftMode = smAlways then
begin
v.Top := v.Top + Amount;
ShiftObjects(v, Amount + v.FShiftAmount);
end
else if v.ShiftMode = smWhenOverlapped then
begin
if not (Parent is TfrxBand) and (v.Top < Parent.Top + Parent.Height) then
begin
diff := Parent.Top + Parent.Height - v.Top;
v.Top := Parent.Top + Parent.Height;
ShiftObjects(v, diff + v.FShiftAmount);
end
else
ShiftObjects(v, v.FShiftAmount);
end
else {if v.FShiftAmount <> 0 then}
ShiftObjects(v, Amount + v.FShiftAmount);
v.FShiftAmount := 0;
end;
end;
begin
FCurBand := Band;
HaveSub := False;
NeedShift := False;
PrepareShiftTree(Band);
{ it is not necessary for vertical bands }
if Band <> FVMasterBand then
begin
{ firing band OnBeforePrint event }
Report.CurObject := Band.Name;
Band.BeforePrint;
Report.DoBeforePrint(Band);
end;
{ firing OnBeforePrint events, stretching objects }
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
if (c is TfrxSubreport) and TfrxSubreport(c).PrintOnParent then
HaveSub := True;
{ skip getdata for vertical bands' objects }
if (Band <> FVMasterBand) or (i < Band.FOriginalObjectsCount) then
begin
Report.CurObject := c.Name;
c.BeforePrint;
if Band.Visible then
begin
Report.DoBeforePrint(c);
if c.Visible then
begin
c.GetData;
Report.DoNotifyEvent(c, c.OnAfterData);
end;
end;
end;
if not Band.Visible or not c.Visible then continue;
if (c is TfrxStretcheable) and (TfrxStretcheable(c).StretchMode <> smDontStretch) then
begin
TfrxStretcheable(c).SetFastCanvas(TfrxFastCanvaslayer(FFastCanvas));
h := TfrxStretcheable(c).CalcHeight;
TfrxStretcheable(c).SetFastCanvas(nil);
if h > c.Height then
begin
c.FShiftAmount := h - c.Height; { needed to shift underlying objects }
c.Height := h; { stretch the object }
NeedShift := True;
end
else
c.FShiftAmount := 0;
end;
end;
if not Band.Visible then Exit;
{ shift objects }
if NeedShift then
ShiftObjects(Band, 0);
{ check subreports that have PrintOnParent option }
if HaveSub then
DoSubReports;
{ calculate the max height of the band }
maxh := 0;
maxc := nil;
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
if c.Top + c.Height > maxh then
begin
maxh := c.Top + c.Height;
maxc := c;
end;
end;
if (maxc <> nil) and (maxc is TfrxDMPMemoView) and
(ftBottom in TfrxDMPMemoView(maxc).Frame.Typ) then
maxh := maxh + fr1CharY;
if Band.Stretched then
Band.Height := maxh;
{ fire Band.OnAfterCalcHeight event }
Report.CurObject := Band.Name;
Report.DoNotifyEvent(Band, Band.OnAfterCalcHeight);
{ set the height of objects that should stretch to max height }
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
if (c is TfrxStretcheable) and (TfrxStretcheable(c).StretchMode = smMaxHeight) then
begin
c.Height := maxh - c.Top;
if (c is TfrxDMPMemoView) and (ftBottom in TfrxDMPMemoView(c).Frame.Typ) then
c.Height := c.Height - fr1CharY;
end;
end;
end;
procedure TfrxEngine.UnStretch(Band: TfrxBand);
var
i: Integer;
c: TfrxView;
begin
{ fire OnAfterPrint event }
if Band.Visible then
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
Report.CurObject := c.Name;
Report.DoAfterPrint(c);
end;
{ restore state }
for i := 0 to Band.Objects.Count - 1 do
if (Band <> FVMasterBand) or (i < Band.FOriginalObjectsCount) then
begin
c := Band.Objects[i];
c.AfterPrint;
end
else break;
Report.CurObject := Band.Name;
Report.DoAfterPrint(Band);
Band.AfterPrint;
{ remove extra band objects if any }
if Band.FOriginalObjectsCount <> -1 then
begin
while Band.Objects.Count > Band.FOriginalObjectsCount do
TObject(Band.Objects[Band.Objects.Count - 1]).Free;
Band.FOriginalObjectsCount := -1;
end;
end;
procedure TfrxEngine.AddPage;
var
i: Integer;
SaveCurX: Extended;
SaveCurLine, SaveCurLineThrough: Integer;
Band: TfrxBand;
IsHeaderBand: Boolean;
begin
FPrevFooterHeight := 0;
PreviewPages.AddPage(FPage);
CurY := 0;
Band := FindBand(TfrxOverlay);
if (Band <> nil) and not TfrxOverlay(Band).PrintOnTop then
ShowBand(Band);
CurY := 0;
SaveCurX := CurX;
FFirstColumnY := 0;
for i := 0 to FHeaderList.Count - 1 do
begin
{ use own CurX - we may be inside subreports now }
CurX := FHeaderList[i].Left;
Band := FHeaderList[i].Band;
if Band = FStartNewPageBand then
continue;
if FIsFirstPage and (Band is TfrxPageHeader) and
not TfrxPageHeader(Band).PrintOnFirstPage then
begin
if Band.PrintChildIfInvisible then
Band := Band.Child
else
continue;
end;
IsHeaderBand := (Band is TfrxHeader);
if Band <> nil then
if not (FKeepHeader and IsHeaderBand) and not FHeaderList[i].IsInKeepList or FKeepFooter
{or (FKeeping and (FKeepBand.FHeader = Band))} then
begin
if (IsHeaderBand and FDontShowHeaders) or (IsHeaderBand and (FLastBandOnPage = Band)) or
((Band is TfrxGroupHeader) and FDontShowHeaders) then
continue;
Band.Overflow := True;
SaveCurLine := CurLine;
SaveCurLineThrough := CurLineThrough;
CurLine := Band.FLineN;
CurLineThrough := Band.FLineThrough;
FCallFromAddPage := True;
FCallFromPHeader := (Band is TfrxPageHeader);
{ fix the stack overflow error if call NewPage from ReportTitle }
if Band is TfrxReportTitle then
FHeaderList[i].Band := nil;
ShowBand(Band);
{ correct column y position }
if Band is TfrxPageHeader then
FFirstColumnY := Band.FStretchedHeight;
if (FIsFirstBand) and (Band is TfrxReportTitle) then
FTitlePrinted := True;
FCallFromPHeader := False;
FCallFromAddPage := False;
Band.Overflow := False;
CurLine := SaveCurLine;
CurLineThrough := SaveCurLineThrough;
end;
end;
CurX := SaveCurX;
end;
procedure TfrxEngine.EndPage;
var
Band: TfrxBand;
Offset: Extended;
procedure ShowBand(Band: TfrxBand);
begin
if Band = nil then Exit;
Stretch(Band);
try
if Band.Visible then
begin
Band.Left := 0;
Band.Top := CurY;
if Band is TfrxPageFooter then
if (FIsFirstPage and not TfrxPageFooter(Band).PrintOnFirstPage) or
(FIsLastPage and not TfrxPageFooter(Band).PrintOnLastPage and not FCallFromEndPage) then
Exit;
if not PreviewPages.BandExists(Band) then
PreviewPages.AddObject(Band);
CurY := CurY + Band.Height;
end;
finally
UnStretch(Band);
end;
FAggregates.Reset(Band);
end;
begin
if not FCallFromEndPage then
EndColumn;
if not FIsLastPage then
begin
CurX := FPageCurX;
CurColumn := 1;
end;
if FIsLastPage and not FCallFromEndPage then
begin
{ avoid stack overflow if reportsummary does not fit on the page }
FCallFromEndPage := True;
try
Offset := CurY;
Band := FindBand(TfrxReportSummary);
Self.ShowBand(Band);
if (Band <> nil) and (FPage.EndlessHeight) then
begin
Offset := CurY - Offset;
PageHeight := PageHeight + Offset;
TfrxPreviewPages(PreviewPages).UpdatePageDimensions(FPage, PageWidth + FPage.LeftMargin * fr01cm + FPage.RightMargin * fr01cm, PageHeight + FPage.TopMargin * fr01cm + FPage.BottomMargin * fr01cm);
end;
finally
FCallFromEndPage := False;
end;
end;
Band := FindBand(TfrxPageFooter);
if Band <> nil then
begin
CurY := PageHeight - Band.Height;
if FIsLastPage and TfrxPageFooter(Band).PrintOnLastPage and not FCallFromEndPage then
FPrevFooterHeight := Band.Height
else FPrevFooterHeight := 0;
end;
ShowBand(Band);
Band := FindBand(TfrxOverlay);
if (Band <> nil) and TfrxOverlay(Band).PrintOnTop then
begin
CurY := 0;
ShowBand(Band);
end;
FIsFirstPage := False;
end;
procedure TfrxEngine.AddColumn;
var
i: Integer;
AddX: Extended;
procedure DoShow(Band: TfrxBand);
begin
Band.Overflow := True;
Stretch(Band);
try
if Band.Visible then
begin
Band.Left := CurX;
Band.Top := CurY;
PreviewPages.AddObject(Band);
CurY := CurY + Band.Height;
end;
finally
UnStretch(Band);
Band.Overflow := False;
end;
end;
procedure ShowBand(Band: TfrxBand);
begin
while Band <> nil do
begin
DoShow(Band);
if Band.Visible or Band.PrintChildIfInvisible then
Band := Band.Child else
break;
end;
end;
begin
CurColumn := CurColumn + 1;
AddX := frxStrToFloat(FPage.ColumnPositions[CurColumn - 1]) * fr01cm;
CurY := FFirstColumnY;
for i := 0 to FHeaderList.Count - 1 do
begin
CurX := FHeaderList[i].Left + AddX;
if not (FHeaderList[i].Band is TfrxPageHeader) {and ((FHeaderList[i].Band is TfrxHeader)} and
(FLastBandOnPage <> FHeaderList[i].Band) and not FHeaderList[i].IsInKeepList and not FKeepHeader then
ShowBand(FHeaderList[i].Band);
end;
CurX := FPageCurX + AddX;
end;
procedure TfrxEngine.EndColumn;
var
Band: TfrxBand;
begin
Band := FindBand(TfrxColumnFooter);
if Band = nil then Exit;
Stretch(Band);
try
if Band.Visible then
begin
Band.Left := CurX - FPageCurX;
Band.Top := CurY;
PreviewPages.AddObject(Band);
{ move the current position }
CurY := CurY + Band.Height;
end;
finally
UnStretch(Band);
end;
FAggregates.Reset(Band);
end;
procedure TfrxEngine.NewPage;
var
CorrPage: TfrxReportPage;
RepeatedHeader: TfrxBand;
LastY: Extended;
begin
{ keep objects doesn't fit on whole page, so break keeping and leave the objets }
if (FKeepBand <> nil) and (CurY - FKeepCurY + FKeepBand.Height > PageHeight - FooterHeight) then
begin
FKeeping := False;
FAggregates.EndKeep;
end;
RepeatedHeader := nil;
if FKeeping then
begin
if FKeepFooter then
FAggregates.DeleteValue(FKeepBand);
PreviewPages.CutObjects(FKeepPosition);
LastY := PreviewPages.GetLastY;
if (ABS(LastY - CurY) >= 1e-4) and (FCurBand is TfrxDataband) then
RepeatedHeader := TfrxDataband(FCurBand).FHeader;
RemoveFromHeaderList(RepeatedHeader);
end;
FLastBandOnPage := FCurBand;
EndPage;
{ fix for report with several pages and EndlessHeight }
{ data dictionary doesn't using in this case }
if FPage.EndlessHeight then
with TfrxPreviewPages(PreviewPages) do
begin
CorrPage := Page[Count - 1];
CorrPage.PaperWidth := PageWidth / fr01cm + FPage.LeftMargin + FPage.RightMargin;
CorrPage.PaperHeight := (CurY + FooterHeight) / fr01cm + FPage.TopMargin + FPage.BottomMargin;
CorrPage.PaperSize := 256;
ModifyPage(Count - 1, CorrPage);
end;
AddPage;
FLastBandOnPage := nil;
if FKeeping then
begin
FAggregates.EndKeep;
PreviewPages.PasteObjects(0, CurY);
PreviewPages.Outline.ShiftItems(FKeepOutline, Round(CurY));
PreviewPages.ShiftAnchors(FKeepAnchor, Round(CurY));
// if (FKeepBand is TfrxDataBand) and (TfrxDataBand(FKeepBand).CurColumn = 1) then
// FSaveCurY := CurY;
CurY := PreviewPages.GetLastY;
if FKeepFooter then
FAggregates.AddValue(FKeepBand);
AddToHeaderList(RepeatedHeader);
end;
FKeeping := False;
FKeepHeader := False;
FKeepCurY := 0;
AddPageOutline;
end;
procedure TfrxEngine.NewColumn;
begin
if CurColumn >= FPage.Columns then
NewPage
else
begin
{ keeping for columns }
if FKeeping then
begin
if FKeepFooter then
FAggregates.DeleteValue(FKeepBand);
PreviewPages.CutObjects(FKeepPosition);
CurY := PreviewPages.GetLastY;
end;
FLastBandOnPage := FCurBand;
EndColumn;
AddColumn;
FLastBandOnPage := nil;
if FKeeping then
begin
FAggregates.EndKeep;
PreviewPages.PasteObjects(CurX, CurY);
PreviewPages.Outline.ShiftItems(FKeepOutline, Round(CurY));
PreviewPages.ShiftAnchors(FKeepAnchor, Round(CurY));
{ new version of GetLastY has one parameter by default = 0,}
{ it determinate column position for correct Y out result. }
{ if parameter = 0 ,then function return last position on the page, }
{ in other cases it return last Y position on X coordinate .}
CurY := PreviewPages.GetLastY(CurX);
if FKeepFooter then
FAggregates.AddValue(FKeepBand);
end;
FKeeping := False;
FKeepHeader := False;
end;
end;
procedure TfrxEngine.DrawSplit(Band: TfrxBand);
var
i, ObjCount: Integer;
List, SaveObjects, ShiftedList: TList;
View: TfrxView;
StrView: TfrxStretcheable;
CurHeight, Corr, SavedHeight: Extended;
ObjStretch, HasNextPart: Boolean;
{$IFNDEF MSWINDOWS}
bIDLE: Boolean;
{$ENDIF}
procedure ShiftObjects(TopView: TfrxView; Delta: Extended);
var
i: Integer;
View: TfrxView;
begin
for i := 0 to List.Count - 1 do
begin
View := List[i];
if (View <> TopView) and (ShiftedList.IndexOf(View) = -1) and
(View.Top >= TopView.Top + TopView.Height) and
// (View.Left < TopView.Left + TopView.Width) and
(TopView.Left + TopView.Width - View.Left > 1e-4) and // MB: 10212009
// (TopView.Left < View.Left + View.Width)
(View.Left + View.Width - TopView.Left > 1e-4) // MB: 10212009
then
begin
View.Top := View.Top + Delta;
ShiftedList.Add(View);
end;
end;
end;
procedure DrawPart;
var
i: Integer;
View: TfrxView;
begin
{ draw current objects }
Band.Left := CurX;
Band.Top := CurY;
PreviewPages.AddObject(Band);
{ add new column/page }
CurY := CurY + Band.Height;
FCurBand := Band;
if List.Count > 0 then NewColumn;
{ correct the top coordinate of remained objects }
Band.Objects.Clear;
for i := 0 to List.Count - 1 do
begin
View := List[i];
View.Top := View.Top - CurHeight;
{ restore the height of stretched objects }
if View is TfrxStretcheable then
begin
{ there is no splited objects, correct top positions }
if not ObjStretch and (List.Count = ObjCount) then
View.Top := TfrxStretcheable(View).FSavedTop;
if View.Top < 0 then
View.Top := 0;
View.Height := TfrxStretcheable(View).FSaveHeight;
end;
end;
end;
procedure CalcBandHeight;
var
i: Integer;
View: TfrxView;
begin
Band.Height := 0;
{ calculate the band's height }
for i := 0 to Band.Objects.Count - 1 do
begin
View := Band.Objects[i];
if View.Top + View.Height > Band.Height then
Band.Height := View.Top + View.Height;
end;
{ correct objects with StretchToMaxHeight or BandAlign = baBottom }
if List.Count = 0 then
for i := 0 to Band.Objects.Count - 1 do
begin
View := Band.Objects[i];
if View.Align = baBottom then
View.Top := Band.Height - View.Height
else if (View is TfrxStretcheable) and
(TfrxStretcheable(View).StretchMode = smMaxHeight) then
View.Height := Band.Height - View.Top;
end;
end;
begin
List := TList.Create;
SaveObjects := TList.Create;
ShiftedList := TList.Create;
ObjStretch := False;
{ initializing lists }
for i := 0 to Band.Objects.Count - 1 do
begin
View := Band.Objects[i];
if not (View is TfrxSubreport) then
List.Add(View);
SaveObjects.Add(View);
if View is TfrxStretcheable then
begin
TfrxStretcheable(View).InitPart;
TfrxStretcheable(View).FSaveHeight := View.Height;
end;
end;
Band.Objects.Clear;
ObjCount := List.Count;
CurHeight := FreeSpace;
while List.Count > 0 do
begin
ShiftedList.Clear;
i := 0;
if not Report.EngineOptions.EnableThreadSafe then
{$IFDEF MSWINDOWS}
Application.ProcessMessages;
{$ELSE}
Application.DoIdle(bIDLE);
{$ENDIF}
if Report.Terminated then Break;
while i < List.Count do
begin
View := List[i];
Corr := 0;
SavedHeight := View.Height;
HasNextPart := False;
{ call DrawPart above to proceess /page tag in rich object }
if View is TfrxStretcheable then
begin
{ Save object top for streched object }
StrView := List[i];
StrView.FSavedTop := StrView.Top;
StrView.Height := CurHeight - StrView.Top;
StrView.SetFastCanvas(TfrxFastCanvaslayer(FFastCanvas));
if StrView.Top < CurHeight then
{ trying to place it }
{ DrawPart method returns the amount of unused space. If view
can't fit in the height, this method returns the Height }
Corr := StrView.DrawPart;
StrView.SetFastCanvas(nil);
SavedHeight := StrView.FSaveHeight;
{ check: does object has new part of data independently from it size}
HasNextPart := StrView.HasNextDataPart;
end;
{ whole object fits in the page }
if (View.Top + SavedHeight <= CurHeight) and not HasNextPart then
begin
View.Height := SavedHeight;
{ add to band and remove from list }
Band.Objects.Add(View);
List.Remove(View);
continue;
end;
if View is TfrxStretcheable then
begin
StrView := List[i];
{ view is inside draw area }
if StrView.Top < CurHeight then
begin
ShiftObjects(StrView, Corr);
if Abs(Corr - StrView.Height) < 1e-4 then
begin
{ view can't fit, return back the height and correct the top }
StrView.Top := CurHeight;
{ shift the underlying objects down }
StrView.Height := StrView.FSaveHeight;
end
else
begin
{ view can draw something }
Band.Objects.Add(StrView);
{ decrease the remained height }
StrView.FSaveHeight := StrView.FSaveHeight - StrView.Height + Corr;
ObjStretch := True;
end;
end;
end
else
begin
{ non-stretcheable view can't be splitted, draw it in the next page }
if View.Height > PageHeight - FooterHeight then
begin
{ add to band and remove from list }
Band.Objects.Add(View);
List.Remove(View);
{ prepare last part of text }
continue;
end
else if View.Top < CurHeight then
begin
{ shift the underlying objects down }
ShiftObjects(View, CurHeight - View.Top);
View.Top := CurHeight;
end;
end;
Inc(i);
end;
{ draw the visible part }
CalcBandHeight;
DrawPart;
CurHeight := FreeSpace;
end;
{ get objects back to the band }
Band.Objects.Clear;
for i := 0 to SaveObjects.Count - 1 do
Band.Objects.Add(SaveObjects[i]);
List.Free;
SaveObjects.Free;
ShiftedList.Free;
end;
procedure TfrxEngine.CheckSuppress(Band: TfrxBand);
var
i: Integer;
c: TfrxComponent;
hasSuppress: Boolean;
begin
hasSuppress := False;
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
if (c is TfrxCustomMemoView) and TfrxCustomMemoView(c).SuppressRepeated then
begin
hasSuppress := True;
TfrxCustomMemoView(c).ResetSuppress;
end;
end;
if hasSuppress and not Band.FHasVBands then
begin
UnStretch(Band);
CurLine := Band.FLineN;
CurLineThrough := Band.FLineThrough;
SecondScriptCall := True;
try
Stretch(Band);
finally
SecondScriptCall := False;
end;
end;
end;
procedure TfrxEngine.DoShow(Band: TfrxBand);
var
IsMultiColumnBand, IsSplit: Boolean;
TempBand: TfrxBand;
SaveCurX: Extended;
SavePageList: TList;
SaveVMasterBand: TfrxBand;
i: Integer;
procedure RenderVBand;
var
i, j, SavePageN: Integer;
SaveCurY: Extended;
c: TfrxComponent;
SaveObjects: TList;
begin
SaveObjects := TList.Create;
SavePageN := PreviewPages.CurPage;
SaveCurY := CurY;
{ the next NewPage call shouldn't form a new page }
PreviewPages.AddPageAction := apWriteOver;
{ save hband objects }
for i := 0 to FVMasterBand.Objects.Count - 1 do
SaveObjects.Add(FVMasterBand.Objects[i]);
for i := 0 to FVPageList.Count - 2 do
begin
FVMasterBand.Objects.Clear;
for j := Integer(FVPageList[i]) to Integer(FVPageList[i + 1]) - 1 do
begin
c := SaveObjects[j];
FVMasterBand.Objects.Add(c);
end;
PreviewPages.AddObject(FVMasterBand);
if i <> FVPageList.Count - 2 then
begin
FDontShowHeaders := True;
NewPage;
FDontShowHeaders := False;
end
else
EndPage;
end;
{ restore hband objects }
FVMasterBand.Objects.Clear;
for i := 0 to SaveObjects.Count - 1 do
FVMasterBand.Objects.Add(SaveObjects[i]);
SaveObjects.Free;
PreviewPages.CurPage := SavePageN;
CurY := SaveCurY;
CurX := SaveCurX;
{ the next NewPage call should form a new page }
PreviewPages.AddPageAction := apAdd;
end;
procedure AddVBand;
var
i: Integer;
c, c1: TfrxReportComponent;
begin
if Band is TfrxDataBand then
CurVColumn := CurVColumn + 1;
if (Band is TfrxFooter) or (Band is TfrxGroupFooter) then
FCurBand := Band
else
FCurBand := FVMasterBand;
{ fire beforeprint }
Report.CurObject := Band.Name;
Band.BeforePrint;
Report.DoBeforePrint(Band);
if Band.Visible then
begin
if CurX + Band.Width > PageWidth then
if FPage.EndlessWidth then
PageWidth := PageWidth + Band.Width
else
begin
{need for correct Page calculation when move VBand to the next page}
with PreviewPages do
begin
if Count - 1 <= CurPage then
AddPage(FPage)
else
CurPage := CurPage + 1;
end;
CurX := 0;
FVPageList.Add(Pointer(FVMasterBand.Objects.Count));
{ reprint headers }
for i := 0 to FVHeaderList.Count - 1 do
ShowBand(TfrxBand(FVHeaderList[i]));
end;
{ find objects that intersect with vertical Band }
for i := 0 to Band.Objects.Count - 1 do
begin
c := Band.Objects[i];
if THackComponent(c).FOriginalBand = FVMasterBand then
begin
{ fire beforeprint and getdata }
Report.CurObject := c.Name;
c.BeforePrint;
Report.DoBeforePrint(c);
c.GetData;
Report.DoNotifyEvent(c, c.OnAfterData);
{ copy the object }
c1 := TfrxReportComponent(c.NewInstance);
c1.Create(FVMasterBand);
c1.Assign(c);
with THackComponent(c1) do
begin
FAliasName := THackComponent(c).FAliasName;
FOriginalComponent := THackComponent(c).FOriginalComponent;
end;
c1.Left := c1.Left + CurX;
{ restore the object's state }
c.AfterPrint;
end;
end;
CurX := CurX + Band.Width;
end;
{ fire afterprint }
Report.CurObject := Band.Name;
Report.DoAfterPrint(Band);
Band.AfterPrint;
if Band is TfrxDataBand then
FAggregates.AddValue(FVMasterBand, CurVColumn);
{ reset aggregates }
if (Band is TfrxFooter) or (Band is TfrxGroupFooter) then
FAggregates.Reset(Band);
end;
begin
SavePageList := nil;
SaveVMasterBand := nil;
{ make cross-bands }
if Band.FHasVBands and not (Band is TfrxPageHeader) then
begin
SaveCurX := CurX;
{ fire onbeforeprint }
Report.CurObject := Band.Name;
Band.BeforePrint;
Report.DoBeforePrint(Band);
{ show vertical bands }
ShowVBands(Band);
CurX := 0;
{ restore Line variables}
CurLine := Band.FLineN;
CurLineThrough := Band.FLineThrough;
{ the next NewPage call should form a new page }
PreviewPages.AddPageAction := apAdd;
{ save global variables - FVPageList and FVMasterBand }
{ they may be changed in the NewPage call, if cross has a h-header }
{ with ReprintOnNewPage option }
SavePageList := TList.Create;
for i := 0 to FVPageList.Count - 1 do
SavePageList.Add(FVPageList[i]);
SaveVMasterBand := FVMasterBand;
end;
{ show one vertical band }
if Band.Vertical then
begin
AddVBand;
Exit;
end;
IsMultiColumnBand := (Band is TfrxDataBand) and (TfrxDataBand(Band).Columns > 1);
IsSplit := False;
{ check for StartNewPage flag }
if not FCallFromAddPage then
if Band.Visible then { don't process invisible bands }
if Band.StartNewPage then
if FOutputTo = nil then
if not (((Band is TfrxDataBand) or (Band is TfrxGroupHeader)) and (Band.FLineN = 1)) then
begin
FStartNewPageBand := Band;
if (Band is TfrxGroupHeader) and (TfrxGroupHeader(Band).ResetPageNumbers) then
PreviewPages.ResetLogicalPageNumber;
NewPage;
FStartNewPageBand := nil;
end;
Stretch(Band);
Band.FStretchedHeight := Band.Height;
try
if Band.Visible then
begin
{ if band has columns, print all columns in one page. Page feed will be
performed after the last column }
if not IsMultiColumnBand and not (Band is TfrxOverlay) and not (Band is TfrxNullBand) and
(Band.Height > FreeSpace) then
if FOutputTo = nil then
if (Band.AllowSplit and not FKeeping) or
((Band.Height > PageHeight - FooterHeight) and not band.FHasVBands) then
begin
if (not Band.AllowSplit) and (Band.FLineThrough > 1) and (not Band.StartNewPage) then
begin
FCurBand := Band;
NewColumn;
end;
if FKeeping then
EndKeep(Band);
DrawSplit(Band);
IsSplit := True;
end
else
begin
if not FKeeping then
CheckSuppress(Band);
if not((Band is TfrxChild) and FCallFromPHeader) then {endless loop fix}
NewColumn;
end;
if not IsSplit then
begin
if not (Band is TfrxNullBand) then
begin
{ multicolumn band manages its Left property itself }
if IsMultiColumnBand then
begin
Band.Left := Band.Left + CurX;
{if (TfrxDataBand(Band).CurColumn = 1) and (Band.Height > FreeSpace) then
begin
FCurBand := Band;
NewColumn;
// FSaveCurY := CurY;
end;}
end
else
Band.Left := CurX;
Band.Top := CurY;
end;
{ output the band }
if FOutputTo = nil then
begin
if (Band.FHasVBands) and not (Band is TfrxPageHeader) then
begin
{ restore global variables - FVPageList and FVMasterBand }
{ they may be changed in the NewPage call, if cross has a h-header }
{ with ReprintOnNewPage option }
FVPageList.Clear;
for i := 0 to SavePageList.Count - 1 do
FVPageList.Add(SavePageList[i]);
SavePageList.Free;
FVMasterBand := SaveVMasterBand;
Band.Left := 0;
RenderVBand;
end
else if (not FCallFromAddPage) or (not PreviewPages.BandExists(Band)) then
PreviewPages.AddObject(Band)
end
else
begin
TempBand := TfrxBand.Create(FOutputTo);
TempBand.AssignAll(Band, True);
end;
{ move the current position }
CurY := CurY + Band.Height;
end;
end;
finally
UnStretch(Band);
end;
{ reset aggregate values }
// if (Band is TfrxFooter) or (Band is TfrxGroupFooter) or
// (Band is TfrxPageFooter) or (Band is TfrxReportSummary) then
FAggregates.Reset(Band);
{ print subreports contained in this band }
if Band.Visible then
CheckSubReports(Band);
end;
procedure TfrxEngine.CheckSubReports(Band: TfrxBand);
var
i, SavePageN, SaveColumnN: Integer;
SaveCurX, SaveCurY, SavePageCurX: Extended;
HaveSub: Boolean;
Sub: TfrxSubreport;
MaxPageN, MaxColumnN: Integer;
MaxCurY: Extended;
begin
{ save the current position }
HaveSub := False;
SavePageN := PreviewPages.CurPage;
SaveColumnN := CurColumn;
SaveCurX := CurX;
SaveCurY := CurY;
SavePageCurX := FPageCurX;
{ init max position }
MaxPageN := SavePageN; //0
MaxColumnN := SaveColumnN; //0
MaxCurY := SaveCurY; //0
{ looking for subreport objects }
for i := 0 to Band.Objects.Count - 1 do
if TObject(Band.Objects[i]) is TfrxSubreport then
begin
Sub := TfrxSubreport(Band.Objects[i]);
if not Sub.Visible or Sub.PrintOnParent then continue;
HaveSub := True;
{ set up all properties... }
PreviewPages.CurPage := SavePageN;
FPageCurX := SavePageCurX + Sub.Left;
CurColumn := SaveColumnN;
CurX := SaveCurX + Sub.Left;
CurY := SaveCurY - Band.FStretchedHeight + Sub.Top; //SaveCurY - Sub.Height;
{ ...and run the subreport }
RunPage(Sub.Page);
{ calc max position }
if PreviewPages.CurPage > MaxPageN then
begin
MaxPageN := PreviewPages.CurPage;
MaxColumnN := CurColumn;
MaxCurY := CurY;
end
else if PreviewPages.CurPage = MaxPageN then
if CurColumn > MaxColumnN then
begin
MaxColumnN := CurColumn;
MaxCurY := CurY;
end
else if CurColumn = MaxColumnN then
if CurY > MaxCurY then
MaxCurY := CurY;
end;
{ move the current position to the last generated page }
if HaveSub then
begin
PreviewPages.CurPage := MaxPageN;
CurColumn := MaxColumnN;
CurX := SavePageCurX;
if CurColumn > 1 then
CurX := CurX + frxStrToFloat(FPage.ColumnPositions[CurColumn - 1]) * fr01cm;
CurY := MaxCurY;
FPageCurX := SavePageCurX;
end;
end;
procedure TfrxEngine.StartKeep(Band: TfrxBand; Position: Integer = 0);
begin
if FKeeping or FIsFirstBand then Exit;
FKeepCurY := CurY;
FKeeping := True;
FKeepBand := Band;
if Position = 0 then
Position := PreviewPages.GetCurPosition;
FKeepPosition := Position;
FKeepOutline := PreviewPages.Outline.GetCurPosition;
FKeepAnchor := PreviewPages.GetAnchorCurPosition;
FAggregates.StartKeep;
end;
procedure TfrxEngine.EndKeep(Band: TfrxBand);
begin
if FKeepBand = Band then
begin
FKeepCurY := 0;
FKeeping := False;
FKeepBand := nil;
FAggregates.EndKeep;
FKeepHeader := False;
end;
end;
function TfrxEngine.GetAggregateValue(const Name, Expression: String;
Band: TfrxBand; Flags: Integer): Variant;
begin
Result := FAggregates.GetValue(FCurBand, CurVColumn, Name, Expression, Band, Flags);
end;
procedure TfrxEngine.AddBandOutline(Band: TfrxBand);
var
pos: Integer;
begin
if Band.OutlineText <> '' then
begin
Report.CurObject := Band.Name;
if Band.Stretched then
pos := Round(CurY - Band.FStretchedHeight)
else
pos := Round(CurY - Band.Height);
if Band.Visible then
PreviewPages.Outline.AddItem(VarToStr(Report.Calc(Band.OutlineText)), pos);
end;
end;
procedure TfrxEngine.AddPageOutline;
begin
if FPage.OutlineText <> '' then
begin
OutlineRoot;
Report.CurObject := FPage.Name;
PreviewPages.Outline.AddItem(VarToStr(Report.Calc(FPage.OutlineText)), 0);
end;
end;
procedure TfrxEngine.OutlineRoot;
begin
PreviewPages.Outline.LevelRoot;
end;
procedure TfrxEngine.OutlineUp(Band: TfrxBand);
begin
if Band.OutlineText <> '' then
PreviewPages.Outline.LevelUp;
end;
function TfrxEngine.GetPageHeight: Double;
begin
if (FPage <> nil) and FPage.EndlessHeight then
Result := CurY + FooterHeight
else
Result := inherited GetPageHeight;
end;
end.