{ RxDBGridPrintGrid unit

  Copyright (C) 2005-2017 Lagunov Aleksey alexs@yandex.ru
  original conception from rx library for Delphi (c)

  This library is free software; you can redistribute it and/or modify it
  under the terms of the GNU Library General Public License as published by
  the Free Software Foundation; either version 2 of the License, or (at your
  option) any later version with the following modification:

  As a special exception, the copyright holders of this library give you
  permission to link this library with independent modules to produce an
  executable, regardless of the license terms of these independent modules,and
  to copy and distribute the resulting executable under terms of your choice,
  provided that you also meet, for each linked independent module, the terms
  and conditions of the license of that module. An independent module is a
  module which is not derived from or based on this library. If you modify
  this library, you may extend this exception to your version of the library,
  but you are not obligated to do so. If you do not wish to do so, delete this
  exception statement from your version.

  This program is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
}
unit RxDBGridPrintGrid;

{$I rx.inc}

interface

uses
  Classes, SysUtils, DB, rxdbgrid, LR_Class, LR_DSet, LR_DBSet, contnrs,
  Graphics, Printers, rxlclutils;

type
  TRxDBGridPrintOption =
    (rxpoShowTitle,
     rxpoShowFooter,
     rxpoShowGridColor,
     rxpoShowFooterColor,
     rxpoShowReportTitle,
     rxpoHideZeroValues,
     rxpoColSpanning
     );
  TRxDBGridPrintOptions = set of TRxDBGridPrintOption;


  { TRxColInfo }

  TRxColInfo = class
    Col:TRxColumn;
    ColWidth:integer;
    ColTitles:TStringList;
    constructor Create;
    destructor Destroy; override;
  end;

  { TRxDBGridPrint }

  TRxDBGridPrint = class(TRxDBGridAbstractTools)
  private
    FModifyPrepared: boolean;
    FOptions: TRxDBGridPrintOptions;
    FOrientation: TPrinterOrientation;
    FPageMargin: TRxPageMargin;
    FReport : TfrReport;
    FReportDataSet : TfrDBDataSet;
    FColumnDataSet : TfrUserDataSet;
    FDataSet : TDataset;
    FPage : TfrPage;
    FReportTitle: string;
    FShowColumnHeaderOnAllPage: boolean;

    FShowProgress : Boolean;
    FTitleRowCount : integer;
    FRxColInfoList : TObjectList;

    FYPos: Integer;
    FXPos: Integer;
    procedure DoCreateReport;

    procedure DoShowReportTitle;
    procedure DoSetupColumns;
    procedure DoShowColumnsTitle;
    procedure DoShowFooter;
    procedure OnPrintColumn(ColNo: Integer; var Width: Integer);
    procedure OnEnterRect(Memo: TStringList; View: TfrView);
    procedure SetPageMargin(AValue: TRxPageMargin);
  protected
    function DoExecTools:boolean;override;
    function DoSetupTools:boolean; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure PreviewReport;
  published
    property Orientation: TPrinterOrientation read FOrientation write FOrientation default poPortrait;
    property Options:TRxDBGridPrintOptions read FOptions write FOptions;
    property ShowProgress : Boolean read FShowProgress write FShowProgress default false;
    property PageMargin:TRxPageMargin read FPageMargin write SetPageMargin;
    property ReportTitle:string read FReportTitle write FReportTitle;
    property ShowColumnHeaderOnAllPage:boolean read FShowColumnHeaderOnAllPage write FShowColumnHeaderOnAllPage default false;
    property ModifyPrepared:boolean read FModifyPrepared write FModifyPrepared default false;
  end;

procedure Register;
implementation

uses math, RxDBGridPrintGrid_SetupUnit, Forms, Controls, rxdconst, LCLIntf;

{$R rxdbgridprintgrid.res}

procedure Register;
begin
  RegisterComponents('RX DBAware',[TRxDBGridPrint]);
end;

{ TRxColInfo }

Type
  THackRxDBGrid = class(TRxDBGrid);

constructor TRxColInfo.Create;
begin
  inherited Create;
  ColTitles:=TStringList.Create;
end;

destructor TRxColInfo.Destroy;
begin
  ColTitles.Clear;
  FreeAndNil(ColTitles);
  inherited Destroy;
end;

{ TRxDBGridPrint }

procedure TRxDBGridPrint.DoShowReportTitle;
var
  FBand: TfrBandView;
  FView: TfrMemoView;
begin
  FBand := TfrBandView(frCreateObject(gtBand, '', FPage));
  FBand.SetBounds(10, FYPos, 1000, 25);
  FBand.BandType := btReportTitle;
//  FPage.Objects.Add(FBand);

  FView := frCreateObject(gtMemo, '', FPage) as TfrMemoView;
  FView.SetBounds(FXPos, FYPos, FPage.PrnInfo.PgW - 40, 25);
  FView.Alignment:=taCenter;
  FView.Font.Size:=12;
//    FView.Font.Assign(FTitleFont);
  FView.Memo.Add(FReportTitle);

//  FPage.Objects.Add(FView);

  Inc(FYPos, 27)
end;

procedure TRxDBGridPrint.DoCreateReport;
var
  FBand: TfrBandView;
  FView: TfrMemoView;
begin
  if FReport.Pages.Count=0 then
      FReport.Pages.add;
  FPage := FReport.Pages[FReport.Pages.Count-1];
  FPage.ChangePaper(FPage.pgSize, FPage.Width, FPage.Height, FOrientation);

  FPage.Margins.Top:=FPageMargin.Top;
  FPage.Margins.Left:=FPageMargin.Left;
  FPage.Margins.Bottom:=FPageMargin.Bottom;
  FPage.Margins.Right:=FPageMargin.Right;

  FYPos:=FPageMargin.Top;
  FXPos:=FPageMargin.Left;

  if rxpoShowReportTitle in FOptions then
    DoShowReportTitle;

  if rxpoShowTitle in FOptions then
    DoShowColumnsTitle;

  FBand := TfrBandView(frCreateObject(gtBand, '', FPage));
  FBand.BandType := btMasterData;
  FBand.Dataset := FReportDataSet.Name;
  FBand.SetBounds(0, FYPos, 1000, 18);
  FBand.Flags:=FBand.Flags or flStretched;
//  FPage.Objects.Add(FBand);

  FBand := TfrBandView(frCreateObject(gtBand, '', FPage));
  FBand.BandType := btCrossData;
  FBand.Dataset := FColumnDataSet.Name;
  FBand.SetBounds(FXPos, 0, 20, 1000);
//  FPage.Objects.Add(FBand);

  FView := frCreateObject(gtMemo, '', FPage) as TfrMemoView;
  FView.SetBounds(FXPos, FYPos, 20, 18);
  FView.Memo.Add('[Cell]');
  FView.Flags:=FView.Flags or flStretched;
  FView.Font.Size:=10;
//  FView.Font.Assign(FFont);
  FView.Frames:=frAllFrames;
  FView.Layout:=tlTop;
//  FPage.Objects.Add(FView);

  FYPos := FYPos + 22;

  if (RxDBGrid.FooterOptions.Active) and (RxDBGrid.FooterOptions.RowCount>0) then
    DoShowFooter;
end;

procedure TRxDBGridPrint.DoSetupColumns;
var
  P:TRxColInfo;
  i: Integer;
  j: Integer;
begin
  FTitleRowCount:=1;
  FRxColInfoList.Clear;
  for i:=0 to RxDBGrid.Columns.Count-1 do
  begin
    if RxDBGrid.Columns[i].Visible then
    begin
      P:=TRxColInfo.Create;
      FRxColInfoList.Add(P);
      P.Col:=RxDBGrid.Columns[i] as TRxColumn;
      P.ColWidth:=RxDBGrid.Columns[i].Width;
      for j:=0 to TRxColumnTitle(RxDBGrid.Columns[i].Title).CaptionLinesCount-1 do
        P.ColTitles.Add(TRxColumnTitle(RxDBGrid.Columns[i].Title).CaptionLine(j).Caption);
      FTitleRowCount:=Max(FTitleRowCount, P.ColTitles.Count)
    end;
  end;
end;

procedure TRxDBGridPrint.DoShowColumnsTitle;
var
  FBand: TfrBandView;
  FView: TfrMemoView;
  i: Integer;
begin
  FBand := TfrBandView(frCreateObject(gtBand, '', FPage));
  FBand.BandType := btMasterHeader;
  FBand.SetBounds(0, FYPos, 1000, 20 * FTitleRowCount);
  FBand.Flags:=FBand.Flags or flStretched;
//  FPage.Objects.Add(FBand);
  if FShowColumnHeaderOnAllPage then
    FBand.Flags:=FBand.Flags + flBandRepeatHeader;

  for i:=0 to FTitleRowCount-1 do
  begin
    FView := frCreateObject(gtMemo, '', FPage) as TfrMemoView;
    FView.SetBounds(FXPos, FYPos, 20, 20);
    FView.Alignment:=taCenter;
    FView.FillColor := clSilver;
  //  FView.Font.Assign(FTitleFont);
    FView.Font.Size:=12;
    FView.Frames:=frAllFrames;
    FView.Layout:=tlTop;
    FView.Memo.Add(Format('Header_%d', [i]));
//    FPage.Objects.Add(FView);
    FYPos:=FYPos + 20
  end;
  FYPos := FYPos + 2;
end;

procedure TRxDBGridPrint.DoShowFooter;
var
  FBand: TfrBandView;
  FView: TfrMemoView;
begin
  FBand := TfrBandView(frCreateObject(gtBand, '', FPage));
  FBand.BandType := btMasterFooter;

  FBand.SetBounds(FXPos, FYPos, 1000, 20);
  FBand.Flags:=FBand.Flags or flStretched;

  FView := frCreateObject(gtMemo, '', FPage) as TfrMemoView;
  FView.SetBounds(FXPos, FYPos, 20, 20);

  if rxpoShowFooterColor in FOptions then
    FView.FillColor := RxDBGrid.FooterOptions.Color;

  FView.Font.Size:=12;
  FView.Frames:=frAllFrames;
  FView.Layout:=tlTop;
  FView.Memo.Add('Footer');

  FYPos := FYPos + 22;
end;

procedure TRxDBGridPrint.OnPrintColumn(ColNo: Integer; var Width: Integer);
begin
  if (ColNo > 0) and (ColNo <= FRxColInfoList.Count) then
    Width := TRxColInfo(FRxColInfoList[ColNo-1]).ColWidth;
end;

procedure TRxDBGridPrint.OnEnterRect(Memo: TStringList; View: TfrView);
var
  i, k: Integer;
  F:TRxColInfo;
  FDataField:TField;
  FDataCollumn:TRxColumn;
  S: String;
  C:TColor;
  J, L, R: Integer;
begin
  i := FColumnDataset.RecNo;
  View.Visible:=true;
  FDataField:=nil;
  FDataCollumn:=nil;

  if (i >= 0) and (i < FRxColInfoList.Count) then
  begin
    F:=TRxColInfo(FRxColInfoList[i]);
    if Assigned(F) then
      FDataCollumn:=F.Col;
    if Assigned(FDataCollumn) then
      FDataField:=FDataCollumn.Field;

    View.dx := F.ColWidth;

    if Assigned(F.Col) and (Memo.Count>0) then
    begin
      S:=Memo[0];
      if (S='[Cell]') and Assigned(F.Col.Field) then
      begin

        if (rdgColSpanning in RxDBGrid.OptionsRx) and (rxpoColSpanning in Options) then
        begin
          if RxDBGrid.IsMerged(I + 1, L, R, FDataCollumn) then
          begin
            if I + 1 = L then
            begin
              if Assigned(FDataCollumn) then
                FDataField:=FDataCollumn.Field
              else
              begin
                if Assigned(F) then
                  FDataCollumn:=F.Col;
                if Assigned(FDataCollumn) then
                  FDataField:=FDataCollumn.Field;
              end;

              for j:=L + 1 to R do
                if FRxColInfoList.Count > j - 1 then
                  View.dx := View.dx + TRxColInfo(FRxColInfoList[j - 1]).ColWidth;
            end
            else
            begin
              View.Visible:=false;
              Memo[0] := '';
              exit;
            end;
          end
          else
          begin
            if Assigned(F) then
              FDataCollumn:=F.Col;
            if Assigned(FDataCollumn) then
              FDataField:=FDataCollumn.Field;
          end
        end;

        if not Assigned(FDataCollumn) then
        begin
          C:=0;
        end;

        if rxpoShowGridColor in FOptions then
        begin
          C:=FDataCollumn.Color;
          if Assigned(RxDBGrid.OnGetCellProps) then
            RxDBGrid.OnGetCellProps(RxDBGrid, FDataField, TfrMemoView(View).Font, C);
          if C = clWindow then
            C := clNone;
          TfrMemoView(View).FillColor:=C;
        end;

{        S:= FDataField.DisplayText; //F.Col.Field.DisplayText;
        if Assigned(FDataCollumn) and (FDataCollumn.KeyList.Count > 0) and (FDataCollumn.PickList.Count > 0) then
        begin
          J := FDataCollumn.KeyList.IndexOf(S);
          if (J >= 0) and (J < FDataCollumn.PickList.Count) then
            S := FDataCollumn.PickList[j];
        end
        else}
        if (rxpoHideZeroValues in FOptions) and Assigned(FDataField) and (FDataField.DataType in [ftSmallint, ftInteger, ftWord,
               ftFloat, ftCurrency, ftLargeint]) and (FDataField.AsFloat = 0) then
          S:=''
        else
          S:=THackRxDBGrid(RxDBGrid).GetFieldDisplayText(FDataField, FDataCollumn);

        Memo[0] := S;
        TfrMemoView(View).Alignment:=FDataCollumn.Alignment;
      end
      else
      if Copy(S, 1, 7) = 'Header_' then
      begin
        TfrMemoView(View).Alignment:=F.Col.Title.Alignment;
        K:=StrToIntDef(Copy(S, 8, Length(S)), 0);
        if TRxColumnTitle(F.Col.Title).CaptionLinesCount = 0 then
        begin
          S:=TRxColumnTitle(F.Col.Title).Caption;
          if K = 0 then
            Memo[0] := TRxColumnTitle(F.Col.Title).Caption
          else
            Memo[0] := '';
        end
        else
        if K<TRxColumnTitle(F.Col.Title).CaptionLinesCount then
        begin;
          Memo[0] :=TRxColumnTitle(F.Col.Title).CaptionLine(k).Caption; //F.Col.Title.Caption;
        end
        else
          Memo[0] := '';
      end
      else
      if S = 'Footer' then
      begin
        Memo[0] :=F.Col.Footer.DisplayText;
        TfrMemoView(View).Alignment:=F.Col.Footer.Alignment;
      end;
    end;

  end;
end;

procedure TRxDBGridPrint.SetPageMargin(AValue: TRxPageMargin);
begin
  FPageMargin.Assign(AValue);
end;

function TRxDBGridPrint.DoExecTools: boolean;
var
  SaveDesign: TfrReportDesigner;
begin
  Result:=false;
  if (RxDBGrid = nil) or (RxDBGrid.DataSource = nil) or (RxDBGrid.DataSource.Dataset = nil) then
    Exit;

  SaveDesign:=frDesigner;
  frDesigner:=nil;

  FDataSet := RxDBGrid.Datasource.Dataset;
  FReport:=TfrReport.Create(Self);
  FReport.OnPrintColumn:=@OnPrintColumn;
  FReport.OnEnterRect:=@OnEnterRect;
  FReport.ModifyPrepared:=FModifyPrepared;
  FReportDataSet := TfrDBDataSet.Create(Self);
  FColumnDataSet := TfrUserDataSet.Create(Self);

  try
    DoSetupColumns;

    FReportDataSet.Name := 'frGridDBDataSet1';
    FReportDataSet.DataSet := FDataSet;
//    FReportDataSet.DataSource := RxDBGrid.DataSource;

    FColumnDataSet.Name := 'frGridUserDataSet1';
    FColumnDataSet.RangeEnd := reCount;

    FColumnDataSet.RangeEndCount := FRxColInfoList.Count;

    FReport.ShowProgress:=FShowProgress;
    DoCreateReport;

    frDesigner:=SaveDesign;

    FReport.ShowReport;
    Result:=true;
  finally
    FreeAndNil(FColumnDataSet);
    FreeAndNil(FReportDataSet);
    FreeAndNil(FReport);
//    frDesigner:=SaveDesign;
  end;
end;

function TRxDBGridPrint.DoSetupTools: boolean;
var
  RxDBGridPrintGrid_SetupForm: TRxDBGridPrintGrid_SetupForm;
begin
  RxDBGridPrintGrid_SetupForm:=TRxDBGridPrintGrid_SetupForm.Create(Application);

  RxDBGridPrintGrid_SetupForm.Edit1.Text:=FReportTitle;
  RxDBGridPrintGrid_SetupForm.RadioGroup1.ItemIndex:=ord(FOrientation);

  RxDBGridPrintGrid_SetupForm.SpinEdit2.Value:=FPageMargin.Left;
  RxDBGridPrintGrid_SetupForm.SpinEdit1.Value:=FPageMargin.Top;
  RxDBGridPrintGrid_SetupForm.SpinEdit3.Value:=FPageMargin.Right;
  RxDBGridPrintGrid_SetupForm.SpinEdit4.Value:=FPageMargin.Bottom;

  RxDBGridPrintGrid_SetupForm.CheckBox1.Checked:=FShowColumnHeaderOnAllPage;

  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[0]:=rxpoShowTitle in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[1]:=rxpoShowFooter in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[2]:=rxpoShowFooterColor in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[3]:=rxpoShowGridColor in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[4]:=rxpoShowReportTitle in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[5]:=rxpoHideZeroValues in FOptions;
  RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[6]:=rxpoColSpanning in FOptions;

  Result:=RxDBGridPrintGrid_SetupForm.ShowModal = mrOk;
  if Result then
  begin
    FReportTitle                    := RxDBGridPrintGrid_SetupForm.Edit1.Text;
    FOrientation                    := TPrinterOrientation(RxDBGridPrintGrid_SetupForm.RadioGroup1.ItemIndex);

    FPageMargin.Left                := RxDBGridPrintGrid_SetupForm.SpinEdit2.Value;
    FPageMargin.Top                 := RxDBGridPrintGrid_SetupForm.SpinEdit1.Value;
    FPageMargin.Right               := RxDBGridPrintGrid_SetupForm.SpinEdit3.Value;
    FPageMargin.Bottom              := RxDBGridPrintGrid_SetupForm.SpinEdit4.Value;

    FOptions:=[];
    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[0] then
      FOptions:=FOptions + [rxpoShowTitle];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[1] then
      FOptions:=FOptions + [rxpoShowFooter];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[2] then
      FOptions:=FOptions + [rxpoShowFooterColor];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[3] then
      FOptions:=FOptions + [rxpoShowGridColor];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[4] then
      FOptions:=FOptions + [rxpoShowReportTitle];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[5] then
      FOptions:=FOptions + [rxpoHideZeroValues];

    if RxDBGridPrintGrid_SetupForm.CheckGroup1.Checked[6] then
      FOptions:=FOptions + [rxpoColSpanning];

    FShowColumnHeaderOnAllPage:=RxDBGridPrintGrid_SetupForm.CheckBox1.Checked;
  end;
  RxDBGridPrintGrid_SetupForm.Free;
end;

constructor TRxDBGridPrint.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPageMargin:=TRxPageMargin.Create;

  FCaption:=sPrintGrid;
  FShowProgress:=false;
  FModifyPrepared:=false;
  FRxColInfoList:=TObjectList.Create(true);
  FOrientation:=poPortrait;
  ShowSetupForm:=false;
  FOptions:=[rxpoShowTitle,
     rxpoShowFooter,
     rxpoShowGridColor,
     rxpoShowFooterColor,
     rxpoShowReportTitle];
  FShowColumnHeaderOnAllPage:=false;
end;

destructor TRxDBGridPrint.Destroy;
begin
  FreeAndNil(FRxColInfoList);
  FreeAndNil(FPageMargin);
  inherited Destroy;
end;

procedure TRxDBGridPrint.PreviewReport;
begin
  Execute;
end;

end.

{ DONE -oalexs : Необходимо настраивать отступы в печатной форме}
{ DONE -oalexs : Необходимо настроить отображение раскраски ячеек }
{ DONE -oalexs : Необходимо правильно выгружать лукапные значение KeyList/PickList }
{ TODO -oalexs : Необходимо реализовать настройку шрифтов }

