home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 October / Chip_2002-10_cd1.bin / zkuste / delphi / kolekce / d56 / FLEXCEL.ZIP / FlexCel / UFlexcelReport.pas < prev    next >
Pascal/Delphi Source File  |  2002-07-03  |  30KB  |  921 lines

  1. unit UFlexcelReport;
  2. {******************************************************************
  3.  Component to automate reporting to Excel
  4.  Version 2.0
  5.  
  6. // The contents of this file are subject to the Mozilla Public License
  7. // Version 1.1 (the "License"); you may not use this file except in compliance
  8. // with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
  9. //
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
  12. // specific language governing rights and limitations under the License.
  13. //
  14. // The original code is Excel.pas, released January 23, 2002.
  15. //
  16. // The initial developer of the original code is Adrian Gallero,
  17. // written by Adrian Gallero (agallero@netscape.net).
  18. //
  19. // Portions created by Adrian Gallero are Copyright
  20. // (C) 2002-2002 Adrian Gallero. All Rights Reserved.
  21.  
  22.  Send any comments to agallero@netscape.net
  23. ******************************************************************** }
  24. interface
  25. {$R IFlexcel.RES}
  26. uses
  27.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  28.   contnrs, db,
  29.   {$IFDEF ConditionalExpressions}{$if CompilerVersion >= 14} variants,{$IFEND}{$ENDIF} //Delphi 6 or above
  30.   typinfo, UExcelAdapter, UFlxMessages, UFlxFullDataSets;
  31.  
  32. type
  33.   TCalcRecordCount=(cr_None, cr_Count, cr_SlowCount);
  34.   TRecordcountEvent = procedure (Sender: TObject; const DataSet: TDataSet; var RecordCount: integer) of object;
  35.   TOnGenerateEvent = procedure (Sender: TObject; const ExcelApp: TExcelFile) of object;
  36.   TOnGeneratePageEvent = procedure (Sender: TObject; const ExcelApp: TExcelFile) of object;
  37.   TOnGetCellValue = procedure (Sender: TObject; const FieldName: widestring; var FieldValue: variant) of object;
  38.  
  39.   TExcelBandList= class;
  40.  
  41.   TFlxValueType=(flv_Value, flv_Field, flv_Property, flv_FullDataSet);
  42.  
  43.   TOneBandValue= record
  44.     //It's not a variant record because it has variant and pointer values
  45.     ColOffset: integer;
  46.     CellValue: Widestring;
  47.  
  48.     ValueType: TFlxValueType;
  49.     Value: TXlsCellValue;
  50.     Field: TField;
  51.     PropInfo: PPropInfo;
  52.     PropIndex: string;
  53.     VarName: string;
  54.   end;
  55.  
  56.   TBandValue= array of TOneBandValue;  // it can have many values in one cell.
  57.   TBandValueList=array of array of TBandValue;
  58.  
  59.   TExcelBand= class
  60.     StartRow, EndRow: integer;
  61.     RangePos: integer;
  62.     SubBands: TExcelBandList;
  63.     DataSet: TDataSet;
  64.     constructor Create;
  65.     destructor Destroy;override;
  66.   end;
  67.  
  68.   TExcelBandList= class (TObjectList) //Items are TExcelBand
  69.   {$Include inc\TExcelBandListHdr.inc}
  70.   end;
  71.  
  72.   TExcelDbList=class(TList) //Items are TDataSet
  73.   {$Include inc\TExcelDbListHdr.inc}
  74.     procedure Check(const DataSet: TDataSet);
  75.     destructor Destroy; override;
  76.   end;
  77.  
  78.   TFlexCelReport = class(TComponent)
  79.   private
  80.     Workbook: TExcelFile;
  81.     FullDataSets: TFullDataSetList;
  82.     LazyDbList: TExcelDbList;
  83.     ValueCache: TBandValueList;
  84.  
  85.     FTemplate: TFileName;
  86.     FDataModule: TComponent;
  87.     FAdapter: TExcelAdapter;
  88.  
  89.     FAutoClose: boolean;
  90.     FFileName: TFileName;
  91.     FCalcRecordCount: TCalcRecordCount;
  92.     FPagesDataSet: TDataSet;
  93.     FPagesDataField: string;
  94.  
  95.     FOnRecordCount: TRecordCountEvent;
  96.     FOnBeforeGenerateWorkbook: TOnGenerateEvent;
  97.     FOnAfterGenerateWorkbook: TOnGenerateEvent;
  98.     FOnAfterGeneratePage: TOnGeneratePageEvent;
  99.     FOnBeforeGeneratePage: TOnGeneratePageEvent;
  100.     FOnGetFileName: TOnGetFilenameEvent;
  101.     FOnGetCellValue: TOnGetCellValue;
  102.  
  103.     procedure SetTemplate(const Value: TFileName);
  104.     procedure SetPagesDataSet(const Value: TDataSet);
  105.     procedure SetPagesDataField(const Value: string);
  106.     procedure SetOnRecordCount(const Value: TRecordCountEvent);
  107.     procedure SetDataModule(const Value: TComponent);
  108.     procedure SetAdapter(const Value: TExcelAdapter);
  109.  
  110.     procedure InsertSheets(const FirstPage: integer);
  111.     function RecordCount(const DbSet: TDataSet): integer;
  112.     function ExportFieldData: boolean;
  113.     procedure ReadTemplate(var MainBand: TExcelBand);
  114.     function FindBands(const StartRow, EndRow: integer): TExcelBand;
  115.     function IsDBName(s: widestring): boolean;
  116.     function GetDataSetFromName(s: WideString): WideString;
  117.     function FindSubBands(const StartRow, EndRow: integer): TExcelBandList;
  118.     procedure CopyStructure(const Band: TExcelBand; var RowOffset: integer);
  119.     procedure FillBandData(const Band: TExcelBand; var RowOffset: integer; const MainBandRow: integer);
  120.     function GetValue(const BandValue: TBandValue; const RowOffset, col: integer): variant;
  121.     procedure ReplaceValues(const Band: TExcelBand;const Rof: integer; var RowOffset: integer);
  122.     procedure ReplacePictures(var RowOffset: integer);
  123.     procedure ReplaceComments(var RowOffset: integer);
  124.     function GetPictureType(const s: widestring): TXlsImgTypes;
  125.     procedure FillFullDatasets;
  126.     function SupressCR(const s: Widestring): widestring;
  127.     procedure GetOneDimArray(var v: variant; PropIndex: string; const VarName: string);
  128.     procedure GetMultiDimArray(var v: variant; PropIndex: string; const VarName: string);
  129.  
  130.     procedure FillBandValueList(const RangePos: integer);
  131.     function GetBandValue(const v: Variant;const ColOffset: integer; const XF: integer=-1): TBandValue;
  132.     function GetOneBandValue(const v:Variant; const ColOffset: integer; const XF: integer): TOneBandValue;
  133.     { Private declarations }
  134.  
  135.   protected
  136.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  137.     { Protected declarations }
  138.  
  139.   public
  140.     constructor Create(AOwner:TComponent);override;
  141.     destructor Destroy;override;
  142.     procedure Run;
  143.     { Public declarations }
  144.  
  145.   published
  146.     property Template: TFileName read FTemplate write SetTemplate;
  147.     property DataModule: TComponent read FDataModule write SetDataModule;
  148.     property FileName: TFileName read FFileName write FFileName;
  149.     property AutoClose: boolean read FAutoClose write FAutoClose;
  150.  
  151.     property CalcRecordCount: TCalcRecordCount read FCalcRecordCount write FCalcRecordCount default cr_Count;
  152.  
  153.     property PagesDataSet: TDataSet read FPagesDataSet write SetPagesDataSet;
  154.     property PagesDataField: string read FPagesDataField write SetPagesDataField;
  155.  
  156.     property Adapter: TExcelAdapter read FAdapter write SetAdapter;
  157.  
  158.     //Events
  159.     property OnRecordCount: TRecordCountEvent read FOnRecordCount write SetOnRecordCount;
  160.     property OnBeforeGenerateWorkbook: TOnGenerateEvent read FOnBeforeGenerateWorkbook write FOnBeforeGenerateWorkbook;
  161.     property OnAfterGenerateWorkbook: TOnGenerateEvent read FOnAfterGenerateWorkbook write FOnAfterGenerateWorkbook;
  162.     property OnBeforeGeneratePage: TOnGeneratePageEvent read FOnBeforeGeneratePage write FOnBeforeGeneratePage;
  163.     property OnAfterGeneratePage: TOnGeneratePageEvent read FOnAfterGeneratePage write FOnAfterGeneratePage;
  164.  
  165.     property OnGetFilename: TOnGetFilenameEvent read FOnGetFileName write FOnGetFileName;
  166.  
  167.     property OnGetCellValue: TOnGetCellValue read FOnGetCellValue write FOnGetCellValue;
  168.     { Published declarations }
  169.   end;
  170.  
  171. procedure Register;
  172.  
  173. implementation
  174. {$Include inc\TExcelBandListImp.inc}
  175. {$Include inc\TExcelDbListImp.inc}
  176.  
  177.  
  178. procedure Register;
  179. begin
  180.   RegisterComponents('FlexCel', [TFlexcelReport]);
  181. end;
  182.  
  183.  
  184. { TFlexCelReport }
  185.  
  186. constructor TFlexCelReport.Create(AOwner: TComponent);
  187. begin
  188.   inherited;
  189.   FDataModule:=AOwner;
  190.   FCalcRecordCount:=cr_Count;
  191. end;
  192.  
  193. destructor TFlexCelReport.Destroy;
  194. begin
  195.   inherited;
  196. end;
  197.  
  198. procedure TFlexCelReport.Notification(AComponent: TComponent; Operation: TOperation);
  199. begin
  200.   inherited Notification(AComponent, Operation);
  201.   if Operation = opRemove then
  202.   begin
  203.     if AComponent = FPagesDataSet then
  204.         FPagesDataSet:= nil;
  205.     if AComponent = FDataModule then
  206.         FDataModule:= Owner;
  207.     if AComponent = FAdapter then
  208.         FAdapter:= nil;
  209.   end;
  210. end;
  211.  
  212. ////////////////////////////////////////////////////////////////////////////////
  213. function TFlexCelReport.RecordCount(const DbSet: TDataSet): integer;
  214. begin
  215.   LazyDbList.Check(DbSet);
  216.  
  217.   Result:=0;
  218.   if DbSet<>nil then
  219.   begin
  220.     //Count the records
  221.     DbSet.First;
  222.  
  223.     //If the event OnCountRecords is Assigned, the var FCalcRecordCount has no meaning
  224.     if Assigned (FOnRecordCount) then FOnRecordCount(Self, DbSet, Result)
  225.     else
  226.       case FCalcRecordCount of
  227.         cr_None:
  228.           Result:= DbSet.RecordCount;
  229.         cr_Count:
  230.         begin
  231.           DbSet.Last;
  232.           DbSet.First;
  233.           Result:= DbSet.RecordCount;
  234.         end; //cr_Count
  235.         cr_SlowCount:
  236.         begin
  237.           while not DbSet.Eof do
  238.           begin
  239.             inc(Result);
  240.             DbSet.Next;
  241.           end;
  242.           DbSet.First;
  243.         end; //cr_SlowCount
  244.       end; //case
  245.   end //DbSet <>nil
  246.   else Result:=1;
  247. end;
  248.  
  249.  
  250. procedure TFlexCelReport.InsertSheets(const FirstPage: integer);
  251. var
  252.   Rc: integer;
  253. begin
  254.   Rc:= RecordCount(FPagesDataSet);
  255.   if (Rc>250) then Raise Exception.Create(ErrTooManySheets);
  256.   Workbook.InsertAndCopySheets(FirstPage, FirstPage+1, Rc-1);
  257. end;
  258.  
  259. //Ranges containing '!' (like "'Sheet1'!Name") are special in Excel, they are local and refer to just one page
  260. function TFlexCelReport.IsDBName(s: widestring): boolean;
  261. begin
  262.    if pos('!',s)>0 then delete(s,1,pos('!',s));
  263.    Result:=(copy(s,1,length(DataSetStr))=DataSetStr);
  264. end;
  265.  
  266. function TFlexCelReport.GetDataSetFromName(s: WideString): WideString;
  267. begin
  268.    if pos('!',s)>0 then delete(s,1,pos('!',s));
  269.  
  270.    s:=copy(s,length(DataSetStr)+1,Length( s));
  271.    s:=copy(s,1,pos(DataSetStr,(s+DataSetStr))-1);
  272.    GetDataSetFromName:=s;
  273. end;
  274.  
  275. function TFlexCelReport.FindSubBands(const StartRow, EndRow: integer): TExcelBandList;
  276. var
  277.   b: TExcelBand;
  278. begin
  279.   Result:=TExcelBandList.Create(true);
  280.   b:= FindBands(StartRow, EndRow);
  281.   while b<>nil do
  282.   begin
  283.     Result.Add(b);
  284.     b:=FindBands(b.EndRow+1, EndRow); //Search for all the ranges below the one we found. The ones inside will be subbands of it, not of the parent
  285.   end;
  286. end;
  287.  
  288. procedure TFlexCelReport.FillBandValueList(const RangePos: integer);
  289. var
  290.   i, k, rl, R1, R2, C1, C2: integer;
  291.   v: TXlsCellValue;
  292. begin
  293.   R1:=Workbook.RangeR1[RangePos];
  294.   R2:=Workbook.RangeR2[RangePos];
  295.   C1:=Workbook.RangeC1[RangePos];
  296.   C2:=Workbook.RangeC2[RangePos];
  297.   SetLength(ValueCache, R2-R1+1);
  298.   for i:=0 to Length(ValueCache)-1 do
  299.   begin
  300.     SetLength(ValueCache[i], C2-C1+1); //This is the maximum. Later we adjust it to what we use
  301.     rl:=0;
  302.     for k:=0 to Length(ValueCache[i])-1 do
  303.     begin
  304.       v:=Workbook.GetCellDataX(R1+i, C1+k-1);
  305.       if ((v.XF>=0) or (not VarIsEmpty(v.Value))) and (not v.IsFormula) then
  306.       begin
  307.         ValueCache[i][rl]:= GetBandValue(v.Value, C1+k, v.XF );
  308.         inc(rl);
  309.       end;
  310.     end;
  311.     SetLength(ValueCache[i], rl); //shrink empty values
  312.   end;
  313.  
  314. end;
  315.  
  316.  
  317. function TFlexCelReport.FindBands(const StartRow, EndRow: integer): TExcelBand;
  318. var
  319.   i, St, En, sRow, eRow, First:integer;
  320.   DataName: Widestring;
  321. begin
  322.   St:=EndRow; First:=-1;
  323.   for i:=1 to Workbook.RangeCount do
  324.   if IsDBName(Workbook.RangeName[i]) and
  325.      (Workbook.RangeSheet[i]= Workbook.ActiveSheet) then
  326.   begin
  327.     //Search For the lowest row
  328.     sRow:= Workbook.RangeR1[i];
  329.     eRow:= Workbook.RangeR2[i];
  330.     if (sRow>=StartRow)and
  331.       (sRow<=St) and (eRow<=EndRow) and
  332.       ((sRow<>StartRow) or (eRow<>EndRow))//do not process itself
  333.        then begin;St:=sRow; First:=i;end;
  334.   end;
  335.   if First<0 then Begin; Result:=nil;exit;end;
  336.  
  337.   //Now search for the biggest range starting on St
  338.   en:=St;First:=-1;
  339.   for i:=1 to Workbook.RangeCount do
  340.   if IsDBName(Workbook.RangeName[i]) and
  341.      (Workbook.RangeSheet[i]= Workbook.ActiveSheet) then
  342.   begin
  343.     sRow:= Workbook.RangeR1[i];
  344.     eRow:= Workbook.RangeR2[i];
  345.     if (sRow=St)and
  346.       (eRow>=En) and (eRow<=EndRow) and
  347.       ((sRow<>StartRow) or (eRow<>EndRow))//do not process itself
  348.        then begin; En:=eRow; First:=i;end;
  349.   end;
  350.  
  351.   Result:=TExcelBand.Create;
  352.   Result.StartRow:=St;
  353.   Result.EndRow:=En;
  354.   Result.RangePos:=First;
  355.   DataName:=GetDataSetFromName(Workbook.RangeName[First]);
  356.   Result.DataSet:=FDataModule.FindComponent(DataName) as TDataSet;
  357.   Result.SubBands:=FindSubBands(St,En);
  358. end;
  359.  
  360.  
  361. procedure TFlexCelReport.ReadTemplate(var MainBand: TExcelBand);
  362. begin
  363.   //Find the Bands
  364.   Assert(MainBand=nil,'MainBand must be nil');
  365.   MainBand:=FindBands(1, 65535);
  366.   if MainBand=nil then exit; //nothing to do in this page
  367.   Workbook.SetBounds(MainBand.RangePos);
  368.   if Workbook.CanOptimizeRead then FillBandValuelist(MainBand.RangePos);
  369. end;
  370.  
  371. procedure TFlexCelReport.CopyStructure(const Band: TExcelBand; var RowOffset: integer);
  372. var
  373.   RealOldRangeCount: integer;
  374.   k:integer;
  375.   DataRecordCount: integer;
  376. begin
  377.     //Insert the new Cells
  378.     DataRecordCount:=RecordCount(Band.DataSet);
  379.  
  380.     //insert/delete the corresponding rows
  381.     RealOldRangeCount:=Band.EndRow - Band.StartRow+1;  //in RealOldRangeCount we store the row count without the subbands
  382.     for k:=0 to Band.SubBands.Count-1 do Dec(RealOldRangeCount,(Band.SubBands[k] as TExcelBand).EndRow-(Band.SubBands[k] as TExcelBand).StartRow+1);
  383.  
  384.     if DataRecordCount - 1 > 0 then
  385.     begin
  386.       Workbook.InsertAndCopyRows(Band.StartRow+RowOffset, Band.EndRow+RowOffset, Band.EndRow+1+RowOffset, DataRecordCount - 1, true);
  387.     end else
  388.     if DataRecordCount - 1 < 0 then //We must delete the line
  389.     begin
  390.       Workbook.DeleteRows(Band.StartRow+RowOffset, Band.EndRow-Band.StartRow+1);
  391.     end;
  392.  
  393.     // SubBands
  394.     while (Band.DataSet=nil) or (not Band.DataSet.Eof) do
  395.     begin
  396.       for k:=0 to Band.SubBands.Count-1 do
  397.       begin
  398.         CopyStructure(Band.SubBands[k] as TExcelBand, RowOffset);
  399.         dec(RowOffset, TExcelBand(Band.SubBands[k]).EndRow-TExcelBand(Band.SubBands[k]).StartRow+1); //Compensate the extra line in the ranges
  400.       end;
  401.  
  402.       for k:=0 to Band.SubBands.Count-1 do
  403.         inc(RowOffset, TExcelBand(Band.SubBands[k]).EndRow-TExcelBand(Band.SubBands[k]).StartRow+1); //Restore the extra line for the next record
  404.  
  405.       inc(RowOffset,RealOldRangeCount);  //not just the total range, because the Subbands have already incremented their part.
  406.       if Band.DataSet<>nil then Band.DataSet.Next else break;
  407.     end;
  408. end;
  409.  
  410. function TFlexCelReport.GetPictureType(const s: widestring): TXlsImgTypes;
  411. var
  412.   Id: string;
  413. begin
  414.   Id:=UpperCase(s);
  415.   if pos(FieldStr,Id)<> 0 then
  416.   begin; //is a db image
  417.     while pos(FieldStr,Id)>0 do Delete(Id, 1, pos(FieldStr,Id)+Length(FieldStr)-1 );
  418.     if Id= xls_Wmf then Result:= xli_Wmf else
  419.     if Id= xls_Emf then Result:= xli_Emf else
  420.     if Id= xls_Png then Result:= xli_Png else
  421.     if Id= xls_Jpeg then Result:= xli_Jpeg else
  422.     raise Exception.CreateFmt(ErrInvalidDrawingType, [Id]);
  423.   end else
  424.   if pos(VarStr,Id)<> 0 then
  425.   begin; //is a prop image
  426.     while pos(VarStr,Id)>0 do Delete(Id, 1, pos(VarStr,Id)+Length(VarStr)-1 );
  427.     if Id= xls_Wmf then Result:= xli_Wmf else
  428.     if Id= xls_Emf then Result:= xli_Emf else
  429.     if Id= xls_Png then Result:= xli_Png else
  430.     if Id= xls_Jpeg then Result:= xli_Jpeg else
  431.     raise Exception.CreateFmt(ErrInvalidDrawingType, [Id]);
  432.   end else
  433.   begin  //Is not an image to replace
  434.     Result:=xli_Jpeg;
  435.   end;
  436. end;
  437.  
  438. function TFlexCelReport.SupressCR(const s: Widestring): widestring;
  439. var
  440.   i,k: integer;
  441. begin
  442.   SetLength(Result,Length(s));
  443.   k:=1;
  444.   for i:=1 to Length(s) do if s[i]<>#13 then
  445.   begin
  446.     Result[k]:=s[i];
  447.     inc(k);
  448.   end;
  449.  
  450.   SetLength(Result, k-1);
  451. end;
  452.  
  453.  
  454. procedure TFlexCelReport.GetMultiDimArray(var v: variant; PropIndex: string; const VarName: string);
  455. var
  456.   p:^VariantArray;
  457.   u: variant;
  458.   i, k, abs, LastDim: integer;
  459. begin
  460.   abs:=0;
  461.   k:=1;
  462.   LastDim:=1;
  463.   while PropIndex<>'' do
  464.   begin
  465.     i:=StrToInt(Copy(PropIndex,1,Pos(VarStr,PropIndex+VarStr)-1));
  466.     Delete(PropIndex,1,Pos(VarStr,PropIndex+VarStr)+Length(VarStr)-1);
  467.     if k> VarArrayDimCount(v) then raise exception.CreateFmt(ErrTooManyDimensions,[VarName, VarArrayDimCount(v)]);
  468.     if (i<VarArrayLowBound(v,k)) or (i>VarArrayHighBound(v,k)) then raise Exception.CreateFmt(ErrIndexOutBounds, [i, VarName,VarArrayLowBound(v,k),VarArrayHighBound(v,k)]);
  469.  
  470.     inc (abs, (i-VarArrayLowBound(v,k))* LastDim);
  471.     LastDim:=LastDim* (VarArrayHighBound(v,k)-VarArrayLowBound(v,k)+1);
  472.     inc(k);
  473.   end;
  474.  
  475.   u:=null;
  476.   p:=VarArrayLock(v);
  477.   try
  478.     u:=(p[abs]);
  479.   finally
  480.     VarArrayUnlock(v);
  481.   end;
  482.  
  483.   v:=u;
  484. end;
  485.  
  486.  
  487. procedure TFlexCelReport.GetOneDimArray(var v: variant; PropIndex: string; const VarName: string);
  488. var
  489.   i: integer;
  490. begin
  491.   while PropIndex<>'' do
  492.   begin
  493.     i:=StrToInt(Copy(PropIndex,1,Pos(VarStr,PropIndex+VarStr)-1));
  494.     Delete(PropIndex,1,Pos(VarStr,PropIndex+VarStr)+Length(VarStr)-1);
  495.     if (i<VarArrayLowBound(v,1)) or (i>VarArrayHighBound(v,1)) then raise Exception.CreateFmt(ErrIndexOutBounds, [i, VarName,VarArrayLowBound(v,1),VarArrayHighBound(v,1)]);
  496.     v:=v[i];
  497.   end;
  498. end;
  499.  
  500.  
  501. function TFlexCelReport.GetOneBandValue(const v:Variant; const ColOffset: integer; const XF: integer): TOneBandValue;
  502. var
  503.   Ds: TDataSet;
  504.   DsName, FieldName: string;
  505.   VarName, PropIndex: string;
  506.   s: widestring;
  507. begin
  508.   Result.ValueType:=flv_Value;
  509.   Result.Value.Value:=v;
  510.   Result.Value.XF:=XF;
  511.   Result.CellValue:=v;
  512.   Result.ColOffset:= ColOffset;
  513.  
  514.   s:=v;
  515.   if copy(s,1,Length(FieldStr))=FieldStr then //It's a field from the database
  516.   begin
  517.     DsName:=copy(s,Length(FieldStr)+1, Length(s));
  518.     DsName:=copy(DsName,1, pos(FieldStr,DsName)-1);
  519.     Ds:=(FDataModule.FindComponent(DsName) as TDataSet);
  520.     if Ds=nil then Raise Exception.CreateFmt(ErrNoDataSet,[DsName]);
  521.     FieldName:=copy(s,1+length(DsName)+length(FieldStr)*2,length(s));
  522.     FieldName:=copy(FieldName, 1, pos(FieldStr, FieldName+FieldStr)-1);
  523.     if FieldName= FullDataSetStr then
  524.     begin
  525.       Result.ValueType:=flv_FullDataSet;
  526.       Result.Value.Value:=unassigned;
  527.     end else
  528.     begin
  529.       LazyDbList.Check(Ds);
  530.       Result.ValueType:= flv_Field;
  531.       Result.Field:=Ds.FieldByName(FieldName);
  532.     end;
  533.  
  534.   end else
  535.   if copy(s,1,Length(VarStr))=VarStr then  //It's a Property of the datamodule
  536.   begin
  537.     VarName:=copy(s,Length(VarStr)+1, Length(s));
  538.     PropIndex:='';
  539.     if Pos(VarStr, VarName)<>0 then//It's a Prop with Subindexs
  540.     begin
  541.       PropIndex:=copy(VarName,Pos(VarStr, VarName)+ Length(VarStr), Length(VarName));
  542.       VarName:=copy(VarName,1,Pos(VarStr, VarName)-1);
  543.     end;
  544.     Result.ValueType:= flv_Property;
  545.     Result.PropInfo:=GetPropInfo(FDataModule, VarName);
  546.     if Result.PropInfo=nil then raise exception.CreateFmt(ErrNoPropDefined,[VarName]);
  547.     Result.PropIndex:= PropIndex;
  548.     Result.VarName:= VarName;
  549.   end;
  550. end;
  551.  
  552. function TFlexCelreport.GetBandValue(const v: Variant; const ColOffset: integer; const XF: integer=-1): TBandValue;
  553. begin
  554.   SetLength(Result,1);
  555.   Result[0]:=GetOneBandValue(v, ColOffset, XF);
  556. end;
  557.  
  558. function TFlexCelReport.GetValue(const BandValue: TBandValue; const RowOffset, col: integer): variant;
  559. var
  560.   Field: TField;
  561.   i: integer;
  562. begin
  563.   for i:=0 to Length(BandValue)-1 do
  564.   begin
  565.     case BandValue[i].ValueType of
  566.       flv_Value: Result:= BandValue[i].Value.Value;
  567.       flv_Field:
  568.       begin
  569.         Field:=BandValue[i].Field;
  570.         if Field is TDateTimeField then Result:= Field.AsFloat else
  571.         //There are problems if we assign v:=Field.Value to a TdatetimeField, the result is a string and has problems with the locales
  572.         //Remember to format the cell in the excel template as Date.
  573.         if Field is TMemoField then Result:= SupressCR(Field.Value) else
  574.         Result:=Field.Value;
  575.         if Assigned(FOnGetCellValue) then FOnGetCellValue(Self, BandValue[i].CellValue, Result);
  576.       end;
  577.       flv_Property:
  578.       begin
  579.         try
  580.           Result:=GetVariantProp(FDataModule, BandValue[i].PropInfo);
  581.         except
  582.           raise exception.CreateFmt(ErrBadProp,[BandValue[i].VarName]);
  583.         end; //except
  584.  
  585.         if VarArrayDimCount(Result)>1 then GetMultiDimArray(Result, BandValue[i].PropIndex, BandValue[i].VarName) else
  586.           if VarArrayDimCount(Result)=1 then GetOneDimArray(Result, BandValue[i].PropIndex, BandValue[i].VarName);
  587.  
  588.         if Assigned(FOngetCellValue) then FOnGetCellValue(Self, BandValue[i].CellValue, Result);
  589.         if VarType(Result)=varDate then Result:=VarAsType(Result, varDouble); //Convert dates into numbers
  590.       end;
  591.       flv_FullDataset:
  592.       begin
  593.         FullDataSets.Add(TFullDataSet.Create(BandValue[i].CellValue, RowOffset, Col));
  594.         Result:=Unassigned;
  595.       end;
  596.       else Result:=Unassigned;
  597.     end; //case
  598.   end;
  599. end;
  600.  
  601. procedure TFlexCelReport.FillFullDatasets;
  602. var
  603.   Ds: TDataSet;
  604.   DsName: string;
  605.   i,k, fds, fds2: integer;
  606.   Rc: integer;
  607.   FullDs: TFullDataSet;
  608. begin
  609.   for fds:=0 to FullDataSets.Count-1 do
  610.   begin
  611.     FullDs:= FullDataSets[fds];
  612.     DsName:=copy(FullDs.Text,Length(FieldStr)+1, Length(FullDs.Text));
  613.     DsName:=copy(DsName,1, pos(FieldStr,DsName)-1);
  614.     Ds:=(FDataModule.FindComponent(DsName) as TDataSet);
  615.     if Ds=nil then Raise Exception.CreateFmt(ErrNoDataSet,[DsName]);
  616.  
  617.     Rc:=RecordCount(Ds);
  618.     Ds.First;
  619.     if Rc>0 then
  620.     begin
  621.       Workbook.InsertAndCopyRows(FullDs.RowOffset+2, FullDs.RowOffset+2, FullDs.RowOffset+3, Rc, false);
  622.       for fds2:=fds+1 to FullDataSets.Count-1 do // insert rows into next FullDataSets
  623.         if FullDataSets[fds2].RowOffset>FullDs.RowOffset then inc (FullDataSets[fds2].RowOffset, Rc);
  624.     end;
  625.     Workbook.PrepareBlockData(FullDs.RowOffset+1, FullDs.Col, FullDs.RowOffset+1+Rc+1-1, FullDs.Col+Ds.FieldCount-1);
  626.     for i:= FullDs.Col to FullDs.Col + Ds.FieldCount-1 do
  627.       Workbook.AssignBlockData(FullDs.RowOffset+1, i, Ds.Fields[i-FullDs.Col].DisplayName);
  628.     for k:=1 to Rc do
  629.     begin
  630.       for i:= FullDs.Col to FullDs.Col + Ds.FieldCount-1 do
  631.         if Ds.Fields[i-FullDs.Col] is TMemoField then
  632.           Workbook.AssignBlockData(FullDs.RowOffset+1+k, i, SupressCR(Ds.Fields[i-FullDs.Col].Value)) else
  633.           Workbook.AssignBlockData(FullDs.RowOffset+1+k, i, Ds.Fields[i-FullDs.Col].Value);
  634.           //Here we dont convert dates into numbers, cause this is a generic template, so we cannot format the cells
  635.       Ds.Next;
  636.     end;
  637.     Workbook.PasteBlockData;
  638.   end;
  639. end;
  640.  
  641. procedure TFlexCelReport.ReplaceValues(const Band: TExcelBand;const Rof: integer; var RowOffset: integer);
  642. var
  643.   j:integer;
  644.   CV: TXlsCellValue;
  645.   v:Variant;
  646. begin
  647.   if Workbook.CanOptimizeRead then
  648.   begin
  649.     for j:=0 to Length(ValueCache[Rof])-1 do
  650.     begin
  651.       CV.Value:=GetValue(ValueCache[Rof][j], RowOffset, ValueCache[Rof][j][0].ColOffset-1);
  652.       CV.XF:=ValueCache[Rof][j][0].Value.XF;
  653.       Workbook.AssignCellDataX(RowOffset+1, ValueCache[Rof][j][0].ColOffset-1, CV);
  654.     end
  655.   end else
  656.  
  657.   begin
  658.   for j:=0 to Workbook.CellCount(RowOffset+1)-1 do
  659.   begin
  660.     v:=Workbook.GetCellData(RowOffset+1,j);
  661.       if not VarIsEmpty(v) then Workbook.AssignCellData(RowOffset+1, j, GetValue(GetBandValue(v, j), RowOffset, j));
  662.     end;
  663.   end;
  664. end;
  665.  
  666. procedure TFlexCelReport.ReplacePictures(var RowOffset: integer);
  667. var
  668.   i:integer;
  669.   v, vw:Variant;
  670.   PictureType: TXlsImgTypes;
  671. begin
  672.   for i:=0 to Workbook.PicturesCount[RowOffset+1]-1 do
  673.   begin
  674.     v:=Workbook.PictureName[RowOffset+1,i];
  675.     PictureType:= GetPictureType(v);
  676.     vw:=GetValue(GetBandValue(v,0), RowOffset, 0);
  677.     if v<>vw then Workbook.AssignPicture(RowOffset+1, i, vw, PictureType);
  678.   end;
  679. end;
  680.  
  681. procedure TFlexCelReport.ReplaceComments(var RowOffset: integer);
  682. var
  683.   i:integer;
  684.   v, vw:Variant;
  685. begin
  686.   for i:=Workbook.CommentsCount[RowOffset+1]-1 downto 0 do   //Reverse order so comments might be deleted
  687.   begin
  688.     v:=Workbook.CommentText[RowOffset+1,i];
  689.     vw:= GetValue(GetBandValue(v,0), RowOffset, 0);
  690.     if v<>vw then Workbook.AssignComment(RowOffset+1, i ,vw);
  691.   end;
  692. end;
  693.  
  694.  
  695. procedure TFlexCelReport.FillBandData( const Band: TExcelBand; var RowOffset: integer; const MainBandRow: integer);
  696. var
  697.   i,k, SaveK:integer;
  698. begin
  699.   if Band.DataSet<>nil then
  700.   begin
  701.     Band.DataSet.First;
  702.   end;
  703.  
  704.   //Fill in the data
  705.   while (Band.DataSet=nil) or (not Band.DataSet.Eof) do
  706.   begin
  707.     i:=Band.StartRow;
  708.     while i<=Band.EndRow do
  709.     begin
  710.       SaveK:=-1;
  711.       for k:=0 to Band.SubBands.Count-1 do
  712.         if (Band.SubBands[k] as TExcelBand).StartRow=i then
  713.         begin
  714.           SaveK:=k;
  715.           break;
  716.         end;
  717.       if SaveK>=0 then
  718.       begin
  719.         FillBandData(Band.SubBands[SaveK] as TExcelBand, RowOffset, MainBandRow);
  720.         i:=(Band.SubBands[SaveK] as TExcelBand).EndRow;
  721.       end else
  722.       begin
  723.         ReplaceValues(Band, i-MainBandRow, RowOffset);
  724.         ReplaceComments(RowOffset);
  725.         ReplacePictures(RowOffset);
  726.         inc(RowOffset);
  727.       end;
  728.       inc(i);
  729.     end;
  730.     if Band.DataSet<>nil then Band.DataSet.Next else break;
  731.   end;
  732. end;
  733.  
  734. function TFlexCelReport.ExportFieldData: boolean;
  735. var
  736.   MainBand: TExcelBand;
  737.   RowOffset: integer;
  738. begin
  739.   Result:=false;
  740.   MainBand:= nil;
  741.   try
  742.     ValueCache:= nil;
  743.     try
  744.       ReadTemplate(MainBand);
  745.       if MainBand=nil then exit;
  746.       Result:=true;
  747.  
  748.       RowOffset:=0;
  749.       CopyStructure(MainBand, RowOffset);
  750.  
  751.       RowOffset:=MainBand.StartRow-1;
  752.       Workbook.BeginSheet;
  753.       try
  754.         FillBandData(MainBand, RowOffset, MainBand.StartRow);
  755.       finally
  756.         Workbook.EndSheet(RowOffset);
  757.       end;  //finally
  758.     finally
  759.       ValueCache:=nil;
  760.     end; //finally
  761.   finally
  762.     FreeAndNil(MainBand);
  763.   end; //finally
  764. end;
  765.  
  766.  
  767. ////////////////////////////////////////////////////////////////////////////////
  768. procedure TFlexCelReport.Run;
  769. var
  770.   OldCursor: TCursor;
  771.   i, FirstSheet:integer;
  772.  
  773. begin
  774.   if FAdapter=nil then raise Exception.Create(ErrNoAdapter);
  775.   OldCursor := Screen.Cursor;
  776.   Screen.Cursor := crHourGlass;
  777.   try
  778.     Workbook:=FAdapter.GetWorkbook;
  779.     try
  780.       Workbook.Connect;
  781.       try
  782.         Workbook.OpenFile(FTemplate);
  783.         try
  784.           LazyDbList:=TExcelDbList.Create;
  785.           try
  786.             LazyDbList.Check(FPagesDataSet);
  787.             if Assigned (FOnBeforeGenerateWorkbook) then FOnBeforeGenerateWorkbook(Self, WorkBook);
  788.  
  789.             FirstSheet:=0;
  790.             if Assigned (FPagesDataSet) then
  791.             begin
  792.               FirstSheet:= Workbook.ActiveSheet;
  793.               InsertSheets(FirstSheet);
  794.             end;
  795.  
  796.             for i:=1 to Workbook.SheetCount do
  797.             begin
  798.               if Assigned (FOnBeforeGeneratePage) then FOnBeforeGeneratePage(Self, Workbook);
  799.  
  800.               Workbook.ActiveSheet:=i;
  801.  
  802.               FullDataSets:= TFullDataSetList.Create;
  803.               try
  804.                 if ExportFieldData then
  805.                 begin
  806.                   FillFullDatasets;
  807.                   Workbook.DeleteMarkedRows(MarkedRowStr);
  808.                 end;
  809.                 Workbook.RefreshPivotTables;
  810.                 if Assigned (FOnAfterGeneratePage) then FOnAfterGeneratePage(Self, Workbook);
  811.  
  812.                 if Assigned(FPagesDataSet) and (i>=FirstSheet) then
  813.                 begin
  814.                   if not FPagesDataSet.Eof then
  815.                   begin
  816.                     if (FPagesDataField<>'') then Workbook.ActiveSheetName:=FPagesDataSet.FieldByName(FPagesDataField).Value;
  817.                     FPagesDataSet.Next;
  818.                   end;
  819.                 end;
  820.  
  821.               finally
  822.                 FreeAndNil(FullDataSets);
  823.               end; //finally
  824.  
  825.             end; //For each page
  826.           finally
  827.             FreeAndNil(LazyDbList);
  828.           end; //finally
  829.           
  830.           Workbook.SelectSheet(1);
  831.           if Assigned (FOnAfterGenerateWorkbook) then FOnAfterGenerateWorkbook(Self, Workbook);
  832.  
  833.           Workbook.Save(FAutoClose, FFileName, FOnGetFileName);
  834.         except
  835.           Workbook.CloseFile;
  836.           raise;
  837.         end; //Except
  838.  
  839.         if (FAutoClose) then    //If there was an exception the workbook has already been closed, so we dont have to protect this
  840.         try
  841.           Workbook.CloseFile;  //This wont quit excel, but when the app finishes it will quit too.
  842.         except
  843.           //nothing
  844.         end; //Except
  845.  
  846.       finally
  847.         Workbook.Disconnect;
  848.       end; //finally
  849.     finally
  850.       FreeAndNil(Workbook);
  851.     end; //finally
  852.  
  853.   finally
  854.     Screen.Cursor := OldCursor;
  855.   end;
  856. end;
  857.  
  858. procedure TFlexCelReport.SetTemplate(const Value: TFileName);
  859. begin
  860.   FTemplate := Value;
  861. end;
  862.  
  863. procedure TFlexCelReport.SetPagesDataSet(const Value: TDataSet);
  864. begin
  865.   FPagesDataSet := Value;
  866. end;
  867.  
  868. procedure TFlexCelReport.SetPagesDataField(const Value: string);
  869. begin
  870.   FPagesDataField := Value;
  871. end;
  872.  
  873. procedure TFlexCelReport.SetOnRecordCount(const Value: TRecordCountEvent);
  874. begin
  875.   FOnRecordCount := Value;
  876. end;
  877.  
  878. procedure TFlexCelReport.SetDataModule(const Value: TComponent);
  879. begin
  880.   if Value=nil then FDataModule := Owner else FDataModule:=Value;
  881. end;
  882.  
  883. procedure TFlexCelReport.SetAdapter(const Value: TExcelAdapter);
  884. begin
  885.   FAdapter := Value;
  886. end;
  887.  
  888. { TExcelBand }
  889.  
  890. constructor TExcelBand.Create;
  891. begin
  892.   inherited;
  893. end;
  894.  
  895. destructor TExcelBand.Destroy;
  896. begin
  897.   FreeAndNil(SubBands);
  898.   inherited;
  899. end;
  900.  
  901. { TExcelDbList }
  902.  
  903. procedure TExcelDbList.Check(const DataSet: TDataSet);
  904. begin
  905.   if (DataSet<>nil) and not DataSet.Active then
  906.   begin
  907.     DataSet.Open;
  908.     Add(DataSet);
  909.   end;
  910. end;
  911.  
  912. destructor TExcelDbList.Destroy;
  913. var
  914.   i: integer;
  915. begin
  916.   for i:=0 to Count -1 do Items[i].Active:=False;
  917.   inherited;
  918. end;
  919.  
  920. end.
  921.