fpspreadsheet: Add files dropped from previous commit

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8970 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-10-22 08:33:55 +00:00
parent f7a7054930
commit 0395d4247c
7 changed files with 3109 additions and 196 deletions

View File

@ -33,7 +33,7 @@
This package is all you need if you don't want graphical components (such as grids and charts)."/>
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
<Version Major="1" Minor="17"/>
<Files Count="56">
<Files Count="58">
<Item1>
<Filename Value="source\fps.inc"/>
<Type Value="Include"/>
@ -297,6 +297,14 @@ This package is all you need if you don&apos;t want graphical components (such a
<Filename Value="source\common\xlsbiff34.pas"/>
<UnitName Value="xlsbiff34"/>
</Item56>
<Item57>
<Filename Value="source\common\fpschart.pas"/>
<UnitName Value="fpschart"/>
</Item57>
<Item58>
<Filename Value="source\common\fpspreadsheet_chart.inc"/>
<Type Value="Binary"/>
</Item58>
</Files>
<CompatibilityMode Value="True"/>
<i18n>

View File

@ -0,0 +1,776 @@
unit fpschart;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Contnrs, fpsTypes, fpsUtils;
const
clsNoLine = -2;
clsSolid = -1;
{@@ Pre-defined chart line styles given as indexes into the chart's LineStyles
list. Get their value in the constructor of TsChart. Default here to -1
while is the code for a solid line, just in case that something goes wrong }
var
clsFineDot: Integer = -1;
clsDot: Integer = -1;
clsDash: Integer = -1;
clsDashDot: Integer = -1;
clsLongDash: Integer = -1;
clsLongDashDot: Integer = -1;
clsLongDashDotDot: Integer = -1;
type
TsChart = class;
TsChartFill = record
Style: TsFillStyle;
FgColor: TsColor;
BgColor: TsColor;
end;
TsChartLineSegment = record
Length: Double; // mm or % of linewidth
Count: Integer;
end;
TsChartLineStyle = class
Name: String;
Segment1: TsChartLineSegment;
Segment2: TsChartLineSegment;
Distance: Double; // mm or % of linewidth
RelativeToLineWidth: Boolean;
function GetID: String;
end;
TsChartLineStyleList = class(TFPObjectList)
private
function GetItem(AIndex: Integer): TsChartLineStyle;
procedure SetItem(AIndex: Integer; AValue: TsChartLineStyle);
public
function Add(AName: String;
ASeg1Length: Double; ASeg1Count: Integer;
ASeg2Length: Double; ASeg2Count: Integer;
ADistance: Double; ARelativeToLineWidth: Boolean): Integer;
property Items[AIndex: Integer]: TsChartLineStyle read GetItem write SetItem; default;
end;
TsChartLine = record
Style: Integer; // index into chart's LineStyle list or predefined clsSolid/clsNoLine
Width: Double; // mm
Color: TsColor; // in hex: $00bbggrr, r=red, g=green, b=blue
Transparency: Double; // in percent
end;
TsChartElement = class
private
FChart: TsChart;
FVisible: Boolean;
public
constructor Create(AChart: TsChart);
property Chart: TsChart read FChart;
property Visible: Boolean read FVisible write FVisible;
end;
TsChartFillElement = class(TsChartElement)
private
FBackground: TsChartFill;
FBorder: TsChartLine;
public
constructor Create(AChart: TsChart);
property Background: TsChartFill read FBackground write FBackground;
property Border: TsChartLine read FBorder write FBorder;
end;
TsChartText = class(TsChartFillElement)
private
FCaption: String;
FShowCaption: Boolean;
FFont: TsFont;
public
constructor Create(AChart: TsChart);
destructor Destroy; override;
property Caption: String read FCaption write FCaption;
property Font: TsFont read FFont write FFont;
property ShowCaption: Boolean read FShowCaption write FShowCaption;
end;
TsChartAxisPosition = (capStart, capEnd, capValue);
TsChartType = (ctEmpty, ctBar, ctLine, ctArea, ctBarLine, ctScatter);
TsChartAxis = class(TsChartText)
private
FAutomaticMax: Boolean;
FAutomaticMin: Boolean;
FAutomaticMajorInterval: Boolean;
FAutomaticMinorSteps: Boolean;
FAxisLine: TsChartLine;
FMajorGridLines: TsChartLine;
FMinorGridLines: TsChartline;
FInverted: Boolean;
FLabelFont: TsFont;
FLabelFormat: String;
FLogarithmic: Boolean;
FMajorInterval: Double;
FMajorTickLines: TsChartLine;
FMax: Double;
FMin: Double;
FMinorSteps: Double;
FMinorTickLines: TsChartLine;
FPosition: TsChartAxisPosition;
FPositionValue: Double;
FShowMajorGridLines: Boolean;
FShowMinorGridLines: Boolean;
FShowLabels: Boolean;
public
constructor Create(AChart: TsChart);
destructor Destroy; override;
property AutomaticMax: Boolean read FAutomaticMax write FAutomaticMax;
property AutomaticMin: Boolean read FAutomaticMin write FAutomaticMin;
property AutomaticMajorInterval: Boolean read FAutomaticMajorInterval write FAutomaticMajorInterval;
property AutomaticMinorSteps: Boolean read FAutomaticMinorSteps write FAutomaticMinorSteps;
property AxisLine: TsChartLine read FAxisLine write FAxisLine;
property Inverted: Boolean read FInverted write FInverted;
property LabelFont: TsFont read FLabelFont write FLabelFont;
property LabelFormat: String read FLabelFormat write FLabelFormat;
property Logarithmic: Boolean read FLogarithmic write FLogarithmic;
property MajorGridLines: TsChartLine read FMajorGridLines write FMajorGridLines;
property MajorInterval: Double read FMajorInterval write FMajorInterval;
property MajorTickLines: TsChartLine read FMajorTickLines write FMajorTickLines;
property Max: Double read FMax write FMax;
property Min: Double read FMin write FMin;
property MinorGrid: TsChartLine read FMinorGridLines write FMinorGridLines;
property MinorSteps: Double read FMinorSteps write FMinorSteps;
property MinorTickLines: TsChartLine read FMinorTickLines write FMinorTickLines;
property Position: TsChartAxisPosition read FPosition write FPosition;
property PositionValue: Double read FPositionValue write FPositionValue;
property ShowMajorGridLines: Boolean read FShowMajorGridLines write FShowMajorGridLines;
property ShowMinorGridLines: Boolean read FShowMinorGridLines write FShowMinorGridLines;
property ShowLabels: Boolean read FShowLabels write FShowLabels;
end;
TsChartLegend = class(TsChartText)
end;
TsChartAxisLink = (alPrimary, alSecondary);
TsChartSeries = class(TsChartElement)
private
FChartType: TsChartType;
FXRange: TsCellRange; // cell range containing the x data
FYRange: TsCellRange;
FLabelRange: TsCellRange;
FYAxis: TsChartAxisLink;
FTitleAddr: TsCellCoord;
FLabelFormat: String;
public
constructor Create(AChart: TsChart);
function GetCount: Integer;
function GetXCount: Integer;
function GetYCount: Integer;
function HasLabels: Boolean;
function HasXValues: Boolean;
function HasYValues: Boolean;
procedure SetTitleAddr(ARow, ACol: Cardinal);
procedure SetLabelRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
procedure SetXRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
procedure SetYRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
function LabelsInCol: Boolean;
function XValuesInCol: Boolean;
function YValuesInCol: Boolean;
property ChartType: TsChartType read FChartType;
property Count: Integer read GetCount;
property LabelFormat: String read FLabelFormat write FLabelFormat; // Number format in Excel notation, e.g. '0.00'
property LabelRange: TsCellRange read FLabelRange;
property TitleAddr: TsCellCoord read FTitleAddr write FTitleAddr;
property XRange: TsCellRange read FXRange;
property YRange: TsCellRange read FYRange;
property YAxis: TsChartAxisLink read FYAxis write FYAxis;
end;
TsChartSeriesSymbol = (
cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssCircle, cssStar
);
TsLineSeries = class(TsChartSeries)
private
FLineStyle: TsChartLine;
FShowLines: Boolean;
FShowSymbols: Boolean;
FSymbol: TsChartSeriesSymbol;
FSymbolFill: TsChartFill;
FSymbolBorder: TsChartLine;
FSymbolHeight: Double; // in mm
FSymbolWidth: Double; // in mm
public
constructor Create(AChart: TsChart);
property LineStyle: TsChartLine read FLineStyle write FLineStyle;
property ShowLines: Boolean read FShowLines write FShowLines;
property ShowSymbols: Boolean read FShowSymbols write FShowSymbols;
property Symbol: TsChartSeriesSymbol read FSymbol write FSymbol;
property SymbolBorder: TsChartLine read FSymbolBorder write FSymbolBorder;
property SymbolFill: TsChartFill read FSymbolFill write FSymbolFill;
property SymbolHeight: double read FSymbolHeight write FSymbolHeight;
property SymbolWidth: double read FSymbolWidth write FSymbolWidth;
end;
TsChartSeriesList = class(TFPList)
private
function GetItem(AIndex: Integer): TsChartSeries;
procedure SetItem(AIndex: Integer; AValue: TsChartSeries);
public
property Items[AIndex: Integer]: TsChartSeries read GetItem write SetItem; default;
end;
TsChart = class(TsChartFillElement)
private
FIndex: Integer; // Index in workbook's chart list
FSheetIndex: Integer;
FRow, FCol: Cardinal;
FOffsetX, FOffsetY: Double;
FWidth, FHeight: Double; // Width, Height of the chart, in mm.
FPlotArea: TsChartFillElement;
FFloor: TsChartFillElement;
FXAxis: TsChartAxis;
FX2Axis: TsChartAxis;
FYAxis: TsChartAxis;
FY2Axis: TsChartAxis;
FTitle: TsChartText;
FSubTitle: TsChartText;
FLegend: TsChartLegend;
FSeriesList: TsChartSeriesList;
FLineStyles: TsChartLineStyleList;
function GetCategoryLabelRange: TsCellRange;
public
constructor Create;
destructor Destroy; override;
function AddSeries(ASeries: TsChartSeries): Integer;
procedure DeleteSeries(AIndex: Integer);
function GetChartType: TsChartType;
function GetLineStyle(AIndex: Integer): TsChartLineStyle;
function IsScatterChart: Boolean;
function NumLineStyles: Integer;
{
function CategoriesInCol: Boolean;
function CategoriesInRow: Boolean;
function GetCategoryCount: Integer;
function HasCategories: Boolean;
}
{ Index of chart in workbook's chart list. }
property Index: Integer read FIndex write FIndex;
{ Index of worksheet sheet which contains the chart. }
property SheetIndex: Integer read FSheetIndex write FSheetIndex;
{ Row index of the cell in which the chart has its top/left corner (anchor) }
property Row: Cardinal read FRow write FRow;
{ Column index of the cell in which the chart has its top/left corner (anchor) }
property Col: Cardinal read FCol write FCol;
{ Offset of the left chart edge relative to the anchor cell, in mm }
property OffsetX: double read FOffsetX write FOffsetX;
{ Offset of the top chart edge relative to the anchor cell, in mm }
property OffsetY: double read FOffsetY write FOffsetY;
{ Width of the chart, in mm }
property Width: double read FWidth write FWidth;
{ Height of the chart, in mm }
property Height: double read FHeight write FHeight;
{ Attributes of the plot area (rectangle enclosed by axes) }
property PlotArea: TsChartFillElement read FPlotArea write FPlotArea;
{ Attributes of the floor of a 3D chart }
property Floor: TsChartFillElement read FFloor write FFloor;
{ Attributes of the chart's title }
property Title: TsChartText read FTitle write FTitle;
{ Attributes of the chart's subtitle }
property Subtitle: TsChartText read FSubtitle write FSubTitle;
{ Attributs of the chart's legend }
property Legend: TsChartLegend read FLegend write FLegend;
{ Attributes of the plot's primary x axis (bottom) }
property XAxis: TsChartAxis read FXAxis write FXAxis;
{ Attributes of the plot's secondary x axis (top) }
property X2Axis: TsChartAxis read FX2Axis write FX2Axis;
{ Attributes of the plot's primary y axis (left) }
property YAxis: TsChartAxis read FYAxis write FYAxis;
{ Attributes of the plot's secondary y axis (right) }
property Y2Axis: TsChartAxis read FY2Axis write FY2Axis;
property CategoryLabelRange: TsCellRange read GetCategoryLabelRange;
{ Attributes of the series }
property Series: TsChartSeriesList read FSeriesList write FSeriesList;
end;
TsChartList = class(TObjectList)
private
function GetItem(AIndex: Integer): TsChart;
procedure SetItem(AIndex: Integer; AValue: TsChart);
public
property Items[AIndex: Integer]: TsChart read GetItem write SetItem; default;
end;
implementation
const
DEFAULT_LINE_WIDTH = 0.75; // pts
{ TsChartLineStyle }
function TsChartLineStyle.GetID: String;
var
i: Integer;
begin
Result := Name;
for i:=1 to Length(Result) do
if Result[i] in [' ', '-'] then Result[i] := '_';
Result := 'FPS' + Result;
end;
{ TsChartLineStyleList }
function TsChartLineStyleList.Add(AName: String;
ASeg1Length: Double; ASeg1Count: Integer;
ASeg2Length: Double; ASeg2Count: Integer;
ADistance: Double; ARelativeToLineWidth: Boolean): Integer;
var
ls: TsChartLineStyle;
begin
ls := TsChartLineStyle.Create;
ls.Name := AName;
ls.Segment1.Count := ASeg1Count;
ls.Segment1.Length := ASeg1Length;
ls.Segment2.Count := ASeg2Count;
ls.Segment2.Length := ASeg2Length;
ls.Distance := ADistance;
ls.RelativeToLineWidth := ARelativeToLineWidth;
result := inherited Add(ls);
end;
function TsChartLineStyleList.GetItem(AIndex: Integer): TsChartLineStyle;
begin
Result := TsChartLineStyle(inherited);
end;
procedure TsChartLineStyleList.SetItem(AIndex: Integer; AValue: TsChartLineStyle);
begin
inherited Items[AIndex] := AValue;
end;
{ TsChartElement }
constructor TsChartElement.Create(AChart: TsChart);
begin
inherited Create;
FChart := AChart;
FVisible := true;
end;
{ TsChartFillElement }
constructor TsChartFillElement.Create(AChart: TsChart);
begin
inherited Create(AChart);
FBackground.Style := fsSolidFill;
FBackground.BgColor := scWhite;
FBackground.FgColor := scWhite;
FBorder.Style := clsSolid;
FBorder.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FBorder.Color := scBlack;
end;
{ TsChartText }
constructor TsChartText.Create(AChart: TsChart);
begin
inherited Create(AChart);
FShowCaption := true;
FFont := TsFont.Create;
FFont.FontName := ''; // replace by workbook's default font
FFont.Size := 0; // replace by workbook's default font size
FFont.Style := [];
FFont.Color := scBlack;
end;
destructor TsChartText.Destroy;
begin
FFont.Free;
inherited;
end;
{ TsChartAxis }
constructor TsChartAxis.Create(AChart: TsChart);
begin
inherited Create(AChart);
FAutomaticMajorInterval := true;
FAutomaticMinorSteps := true;
FLabelFont := TsFont.Create;
FLabelFont.FontName := ''; // replace by workbook's default font
FLabelFont.Size := 0; // Replace by workbook's default font size
FLabelFont.Style := [];
FLabelFont.Color := scBlack;
FShowLabels := true;
FAxisLine.Color := scBlack;
FAxisLine.Style := clsSolid;
FAxisLine.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMajorTickLines.Color := scBlack;
FMajorTickLines.Style := clsSolid;
FMajorTickLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMinorTickLines.Color := scBlack;
FMinorTickLines.Style := clsSolid;
FMinorTickLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMajorGridLines.Color := scSilver;
FMajorGridLines.Style := clsSolid;
FMajorGridLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMinorGridLines.Color := scSilver;
FMinorGridLines.Style := clsDot;
FMinorGridLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
end;
destructor TsChartAxis.Destroy;
begin
FLabelFont.Free;
inherited;
end;
(*
{ Determines how many labels are provided in the CategoryLabelRange. }
function TsChartAxis.GetCategoryCount: Integer;
begin
if CategoriesInCol then
Result := FLabelRange.Col2 - FLabelRange.Col1 + 1
else
if CategoriesInCol then
Result := FLabelRange.Row2 - FLabelRange.Row1 + 1
else
Result := 0;
end;
{ Returns true when the axis owns its own category labels. Otherwise labels
are taken from the series }
function TsChartAxis.HasCategoryLabels: Boolean;
begin
Result := CategoriesInCol or CategoriesInRow;
end;
{ Determines whether the axis labels are taken from columns (true) or rows (false) }
function TsChartAxis.CategoriesInCol: Boolean;
begin
Result := (FCategoryLabelRange.Row1 <> FCategoryLabelRange.Row2) and
(FCategoryLabelRange.Col1 = FCategoryLabelRange.Col2);
end;
function TsChartAxis.CategoriesInRow: Boolean;
begin
Result := (FCategoryLabelRange.Col1 <> FCategoryLabelRange.Col2) and
(FCategoryLabelRange.Row1 = FCategoryLabelRange.Row2);
end;
*)
{ TsChartSeries }
constructor TsChartSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
AChart.AddSeries(self);
end;
function TsChartSeries.GetCount: Integer;
begin
Result := GetYCount;
end;
function TsChartSeries.GetXCount: Integer;
begin
if (FXRange.Row1 = FXRange.Row2) and (FXRange.Col1 = FXRange.Col2) then
Result := 0
else
if (FXRange.Row1 = FXRange.Row2) then
Result := FXRange.Col2 - FXRange.Col1 + 1
else
Result := FXRange.Row2 - FXRange.Row1 + 1;
end;
function TsChartSeries.GetYCount: Integer;
begin
if YValuesInCol then
Result := FYRange.Row2 - FYRange.Row1 + 1
else
Result := FYRange.Col2 - FYRange.Col1 + 1;
end;
function TsChartSeries.HasLabels: Boolean;
begin
Result := not ((FLabelRange.Row1 = FLabelRange.Row2) and (FLabelRange.Col1 = FLabelRange.Col2));
end;
function TsChartSeries.HasXValues: Boolean;
begin
Result := not ((FXRange.Row1 = FXRange.Row2) and (FXRange.Col1 = FXRange.Col2));
end;
function TsChartSeries.HasYValues: Boolean;
begin
Result := not ((FYRange.Row1 = FYRange.Row2) and (FYRange.Col1 = FYRange.Col2));
end;
function TsChartSeries.LabelsInCol: Boolean;
begin
Result := (FLabelRange.Col1 = FLabelRange.Col2) and (FLabelRange.Row1 <> FLabelRange.Row2);
end;
procedure TsChartSeries.SetTitleAddr(ARow, ACol: Cardinal);
begin
FTitleAddr.Row := ARow;
FTitleAddr.Col := ACol;
end;
procedure TsChartSeries.SetLabelRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if (ARow1 <> ARow2) and (ACol1 <> ACol2) then
raise Exception.Create('Series labels can only be located in a single column or row.');
FLabelRange.Row1 := ARow1;
FLabelRange.Col1 := ACol1;
FLabelRange.Row2 := ARow2;
FLabelRange.Col2 := ACol2;
end;
procedure TsChartSeries.SetXRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if (ARow1 <> ARow2) and (ACol1 <> ACol2) then
raise Exception.Create('Series x values can only be located in a single column or row.');
FXRange.Row1 := ARow1;
FXRange.Col1 := ACol1;
FXRange.Row2 := ARow2;
FXRange.Col2 := ACol2;
end;
procedure TsChartSeries.SetYRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
if (ARow1 <> ARow2) and (ACol1 <> ACol2) then
raise Exception.Create('Series y values can only be located in a single column or row.');
FYRange.Row1 := ARow1;
FYRange.Col1 := ACol1;
FYRange.Row2 := ARow2;
FYRange.Col2 := ACol2;
end;
function TsChartSeries.XValuesInCol: Boolean;
begin
Result := (FXRange.Col1 = FXRange.Col2) and (FXRange.Row1 <> FXRange.Row2);
end;
function TsChartSeries.YValuesInCol: Boolean;
begin
Result := (FYRange.Col1 = FYRange.Col2) and (FYRange.Row1 <> FYRange.Row2);
end;
{ TsChartSeriesList }
function TsChartSeriesList.GetItem(AIndex: Integer): TsChartSeries;
begin
Result := TsChartSeries(inherited Items[AIndex]);
end;
procedure TsChartSeriesList.SetItem(AIndex: Integer; AValue: TsChartSeries);
begin
inherited Items[AIndex] := AValue;
end;
{ TsLineSeries }
constructor TsLineSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
FChartType := ctLine;
FLineStyle.Color := scBlack;
FLineStyle.Style := clsSolid;
FLineStyle.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FSymbolBorder.Color := scBlack;
FSymbolBorder.Style := clsSolid;
FSymbolBorder.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FSymbolFill.FgColor := scWhite;
FSymbolFill.BgColor := scWhite;
FSymbolFill.Style := fsSolidFill;
FSymbolWidth := 2.5;
FSymbolHeight := 2.5;
end;
{ TsChart }
constructor TsChart.Create;
begin
inherited Create(nil);
FLineStyles := TsChartLineStyleList.Create;
clsFineDot := FLineStyles.Add('fine-dot', 100, 1, 0, 0, 100, false);
clsDot := FLineStyles.Add('dot', 150, 1, 0, 0, 150, true);
clsDash := FLineStyles.Add('dash', 300, 1, 0, 0, 150, true);
clsDashDot := FLineStyles.Add('dash-dot', 300, 1, 100, 1, 150, true);
clsLongDash := FLineStyles.Add('long dash', 400, 1, 0, 0, 200, true);
clsLongDashDot := FLineStyles.Add('long dash-dot', 500, 1, 100, 1, 200, true);
clsLongDashDotDot := FLineStyles.Add('long dash-dot-dot', 500, 1, 100, 2, 200, true);
FSheetIndex := 0;
FRow := 0;
FCol := 0;
FOffsetX := 0.0;
FOffsetY := 0.0;
FWidth := 12;
FHeight := 9;
FPlotArea := TsChartFillElement.Create(self);
FFloor := TsChartFillElement.Create(self);
FTitle := TsChartText.Create(self);
FTitle.Font.Size := 14;
FSubTitle := TsChartText.Create(self);
FSubTitle.Font.Size := 12;
FLegend := TsChartLegend.Create(self);
FXAxis := TsChartAxis.Create(self);
FXAxis.Caption := 'x axis';
FXAxis.LabelFont.Size := 9;
FXAxis.Font.Size := 10;
FXAxis.Font.Style := [fssBold];
FXAxis.Position := capStart;
FX2Axis := TsChartAxis.Create(self);
FX2Axis.Caption := 'Secondary x axis';
FX2Axis.LabelFont.Size := 9;
FX2Axis.Font.Size := 10;
FX2Axis.Font.Style := [fssBold];
FX2Axis.Visible := false;
FX2Axis.Position := capEnd;
FYAxis := TsChartAxis.Create(self);
FYAxis.Caption := 'y axis';
FYAxis.LabelFont.Size := 9;
FYAxis.Font.Size := 10;
FYAxis.Font.Style := [fssBold];
FYAxis.Position := capStart;
FY2Axis := TsChartAxis.Create(self);
FY2Axis.Caption := 'Secondary y axis';
FY2Axis.LabelFont.Size := 9;
FY2Axis.Font.Size := 10;
FY2Axis.Font.Style := [fssBold];
FY2Axis.Visible := false;
FY2Axis.Position := capEnd;
FSeriesList := TsChartSeriesList.Create;
end;
destructor TsChart.Destroy;
begin
FSeriesList.Free;
FXAxis.Free;
FYAxis.Free;
FY2Axis.Free;
FLegend.Free;
FTitle.Free;
FSubtitle.Free;
FLineStyles.Free;
FFloor.Free;
FPlotArea.Free;
inherited;
end;
function TsChart.AddSeries(ASeries: TsChartSeries): Integer;
begin
Result := FSeriesList.IndexOf(ASeries);
if Result = -1 then
Result := FSeriesList.Add(ASeries);
end;
procedure TsChart.DeleteSeries(AIndex: Integer);
begin
if (AIndex >= 0) and (AIndex < FSeriesList.Count) then
FSeriesList.Delete(AIndex);
end;
function TsChart.GetCategoryLabelRange: TsCellRange;
begin
if FSeriesList.Count > 0 then
Result := Series[0].LabelRange
else
begin
Result.Row1 := 0;
Result.Col1 := 0;
Result.Row2 := 0;
Result.Col2 := 0;
end;
end;
function TsChart.GetChartType: TsChartType;
begin
if FSeriesList.Count > 0 then
Result := Series[0].ChartType
else
Result := ctEmpty;
end;
function TsChart.GetLineStyle(AIndex: Integer): TsChartLineStyle;
begin
if AIndex >= 0 then
Result := FLineStyles[AIndex]
else
Result := nil;
end;
function TsChart.IsScatterChart: Boolean;
begin
Result := GetChartType = ctScatter;
end;
function TsChart.NumLineStyles: Integer;
begin
Result := FLineStyles.Count;
end;
{ TsChartList }
function TsChartList.GetItem(AIndex: Integer): TsChart;
begin
Result := TsChart(inherited Items[AIndex]);
end;
procedure TsChartlist.SetItem(AIndex: Integer; AValue: TsChart);
begin
inherited Items[AIndex] := AValue;
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ uses
{$endif}{$endif}{$endif}
Classes, SysUtils, fpimage, avglvltree, lconvencoding,
fpsTypes, fpsExprParser, fpsClasses, fpsNumFormat, fpsPageLayout,
fpsImages, fpsConditionalFormat;
fpsImages, fpsConditionalFormat, fpsChart;
type
{ Forward declarations }
@ -625,6 +625,15 @@ type
procedure AddHyperlinkToImage(AImageIndex: Integer; ATarget: String;
AToolTip: String = '');
procedure CalcDrawingExtent(
UsePixels: Boolean; AWidth, AHeight: Double;
var ARow1, ACol1: Cardinal; out ARow2, ACol2: Cardinal;
ARowOffs1, AColOffs1: Double; out ARowOffs2, AColOffs2: Double;
out x,y: Double);
{ Chart support }
function GetChartCount: Integer;
{ Protection }
procedure Protect(AEnable: Boolean);
@ -771,6 +780,7 @@ type
FCellFormatList: TsCellFormatList;
FConditionalFormatList: TsConditionalFormatList;
FEmbeddedObjList: TFPList;
FCharts: TsChartList;
{ Internal methods }
class procedure GetFormatFromFileHeader(const AFileName: TFileName;
@ -911,6 +921,12 @@ type
function HasEmbeddedSheetImages: Boolean;
procedure RemoveAllEmbeddedObj;
{ Charts }
function AddChart(ASheet: TsBasicWorksheet; ARow, ACol: Cardinal;
AWidth, AHeight: Double; AOffsetX: Double = 0.0; AOffsetY: Double = 0.0): TsChart;
function GetChartByIndex(AIndex: Integer): TsChart;
function GetChartCount: Integer;
{ Utilities }
function ConvertUnits(AValue: Double; AFromUnits, AToUnits: TsSizeUnits): Double;
procedure UpdateCaches;
@ -1412,6 +1428,147 @@ begin
// to others sheets. If not call the faster "CalcSheet".
end;
{@@ ----------------------------------------------------------------------------
Calculates the extent of an image or chart (in Excel-speak: a "drawing")
@param UsePixels If @TRUE then pixels are used for calculation - this improves the display of images in Excel
@param AWidth Width of the drawing
@param AHeight Height of the drawing
@param ARow1 Index of the row containing the top edge of the drawing
@param ACol1 Index of the column containing the left edege of the drawing
@param ARow2 Index of the row containing the right edge of the drawing
@param ACol2 Index of the column containing the bottom edge of the drawing
@param ARowOffs1 Distance between the top edge of drawing and row 1
@param AColOffs1 Distance between the left edge of drawing and column 1
@param ARowOffs2 Distance between the bottom edge of drawing and top of row 2
@param AColOffs2 Distance between the right edge of drawing and left of col 2
@param x Absolute coordinate of left edge of the drawing
@param y Absolute coordinate of top edge of the drawing
All dimensions are in workbook units
-------------------------------------------------------------------------------}
procedure TsWorksheet.CalcDrawingExtent(
UsePixels: Boolean; AWidth, AHeight: Double;
var ARow1, ACol1: Cardinal; out ARow2, ACol2: Cardinal;
ARowOffs1, AColOffs1: Double; out ARowOffs2, AColOffs2: Double;
out x,y: Double);
var
colW, rowH: Double;
totW, totH: Double;
r, c: Integer;
w_px, h_px: Integer;
totH_px, rowH_px: Integer;
totW_px, colW_px: Integer;
ppi: Integer;
u: TsSizeUnits;
begin
// Abbreviations
ppi := ScreenPixelsPerInch;
u := FWorkbook.Units;
// Find x coordinate of left graphic edge, in workbook units
x := AColOffs1;
for c := 0 to ACol1-1 do
begin
colW := GetColWidth(c, u);
x := x + colW;
end;
// Find y coordinate of top image edge, in workbook units.
y := ARowOffs1;
for r := 0 to ARow1 - 1 do
begin
rowH := CalcRowHeight(r);
y := y + rowH;
end;
if UsePixels then
// Use pixels for calculation. Better for Excel, maybe due to rounding error?
begin
// If we don't know the ppi of the screen the calculation is not exact!
w_px := ptsToPx(FWorkbook.ConvertUnits(AWidth, u, suPoints), ppi);
h_px := ptsToPx(FWorkbook.ConvertUnits(AHeight, u, suPoints), ppi);
// Find column with right image edge. Find horizontal within-cell-offsets
totW_px := -ptsToPx(FWorkbook.ConvertUnits(AColOffs1, u, suPoints), ppi);
ACol2 := ACol1;
while (totW_px < w_px) do
begin
colW := GetColWidth(ACol2, u);
colW_px := ptsToPx(FWorkbook.ConvertUnits(colW, u, suPoints), ppi);
totW_px := totW_px + colW_px;
if totW_px > w_px then
begin
AColOffs2 := FWorkbook.ConvertUnits(pxToPts(colW_px - (totW_px - w_px), ppi), suPoints, u);
break;
end;
inc(ACol2);
end;
// Find row with bottom image edge. Find vertical within-cell-offset.
totH_px := -ptsToPx(FWorkbook.ConvertUnits(ARowOffs1, u, suPoints), ppi);
ARow2 := ARow1;
while (totH_px < h_px) do
begin
rowH := CalcRowHeight(ARow2);
rowH_px := ptsToPx(FWorkbook.ConvertUnits(rowH, u, suPoints), ppi);
totH_px := totH_px + rowH_px;
if totH_px > h_px then
begin
ARowOffs2 := FWorkbook.ConvertUnits(pxToPts(rowH_px - (totH_px - h_px), ppi), suPoints, u);
break;
end;
inc(ARow2);
end;
end
else // Use workbook units for calculation
begin
// Find row with bottom image edge. Find horizontal within-cell offset
totW := -AColOffs1;
ACol2 := ACol1;
while (totW < AWidth) do
begin
colW := GetColWidth(ACol2, u);
totW := totW + colW;
if totW >= AWidth then
begin
AColOffs2 := colW - (totW - AWidth);
break;
end;
inc(ACol2);
end;
// Find row with right image edge. Find vertical within-cell-offsets
totH := -ARowOffs1;
ARow2 := ARow1;
while (totH < AHeight) do
begin
rowH := CalcRowHeight(ARow2);
totH := totH + rowH;
if totH >= AHeight then
begin
ARowOffs2 := rowH - (totH - AHeight);
break;
end;
inc(ARow2);
end;
end;
end;
{@@ ----------------------------------------------------------------------------
Determines the count of charts on this worksheet
-------------------------------------------------------------------------------}
function TsWorksheet.GetChartCount: Integer;
var
i: Integer;
chart: TsChart;
idx: Integer;
begin
Result := 0;
idx := GetIndex;
for i := 0 to Workbook.GetChartCount-1 do
begin
chart := Workbook.GetChartByIndex(i);
if chart.SheetIndex = idx then inc(Result);
end;
end;
{@@ ----------------------------------------------------------------------------
Calculates all formulas of the worksheet
@ -2318,7 +2475,7 @@ begin
WriteFormula(AToRow, AToCol, formulaStr);
// Fix formula references to the source cell (ACell)
for i := 0 to FWorkbook.GetWorksheetcount-1 do begin
for i := 0 to FWorkbook.GetWorksheetCount-1 do begin
sheet := FWorkbook.GetWorksheetByIndex(i);
sheet.Formulas.FixReferenceToMovedCell(ACell, AToRow, AToCol, self);
end;
@ -6383,6 +6540,7 @@ begin
FCellFormatList := TsCellFormatList.Create(false);
FConditionalFormatList := TsConditionalFormatList.Create;
FEmbeddedObjList := TFPList.Create;
FCharts := TsChartList.Create;
// Add default cell format
InitFormatRecord(fmt);
@ -6415,6 +6573,7 @@ begin
RemoveAllEmbeddedObj;
FEmbeddedObjList.Free;
FCharts.Free;
inherited Destroy;
end;
@ -7627,6 +7786,7 @@ end;
{$include fpspreadsheet_hyperlinks.inc} // hyperlinks
{$include fpspreadsheet_embobj.inc} // embedded objects
{$include fpspreadsheet_clipbrd.inc} // clipboard access
{$include fpspreadsheet_chart.inc} // chart support
end. {** End Unit: fpspreadsheet }

View File

@ -72,6 +72,28 @@ end;
All dimensions are in workbook units
-------------------------------------------------------------------------------}
procedure TsWorksheet.CalcImageExtent(AIndex: Integer; UsePixels: Boolean;
out ARow1, ACol1, ARow2, ACol2: Cardinal;
out ARowOffs1, AColOffs1, ARowOffs2, AColOffs2: Double;
out x,y, AWidth, AHeight: Double);
var
img: TsImage;
obj: TsEmbeddedObj;
begin
img := GetImage(AIndex);
ARow1 := img.Row;
ACol1 := img.Col;
ARowOffs1 := img.OffsetX; // in workbook units
AColOffs1 := img.OffsetY; // in workbook units
obj := FWorkbook.GetEmbeddedObj(img.Index);
AWidth := obj.ImageWidth * img.ScaleX; // in workbook units
AHeight := obj.ImageHeight * img.ScaleY; // in workbook units
CalcDrawingExtent(UsePixels, AWidth, AHeight, ARow1, ACol1, ARow2, ACol2,
ARowOffs1, AColOffs1, ARowOffs2, AColOffs2, x, y);
end;
(*
procedure TsWorksheet.CalcImageExtent(AIndex: Integer; UsePixels: Boolean;
out ARow1, ACol1, ARow2, ACol2: Cardinal;
out ARowOffs1, AColOffs1, ARowOffs2, AColOffs2: Double;
@ -172,7 +194,7 @@ begin
end;
end;
end;
*)
{@@ ----------------------------------------------------------------------------
Returns the parameters of the image stored in the internal image list at

View File

@ -127,11 +127,18 @@ function TryStrToCellRange_ODS(const AStr: String; out ASheet1, ASheet2: String;
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags): Boolean;
function GetCellRangeString_ODS(ASheet1, ASheet2: String; ARow1, ACol1, ARow2, ACol2: Cardinal;
AFlags: TsRelFlags = rfAllRel): String; overload;
AFlags: TsRelFlags = rfAllRel; WithBrackets: Boolean = true): String; overload;
function GetCellRangeString_ODS(ARow1, ACol1, ARow2, ACol2: Cardinal;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String; overload;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false;
WithBrackets: Boolean = true): String; overload;
function GetCellRangeString_ODS(ARange: TsCellRange;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String; overload;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false;
WithBrackets: Boolean = true): String; overload;
function GetSheetCellString_ODS(ASheet: String; ARow, ACol: Cardinal;
AFlags: TsRelFlags = rfAllRel; WithBrackets: Boolean = true): String;
function GetSheetCellRangeString_ODS(ASheet1, ASheet2: String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel;
WithBrackets: Boolean = true): String;
// Error strings
@ -1386,7 +1393,8 @@ end;
Calculates a cell range string with sheet specification in OpenDocument syntax
-------------------------------------------------------------------------------}
function GetCellRangeString_ODS(ASheet1, ASheet2: String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel): String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel;
WithBrackets: Boolean = true): String;
var
s1, s2: String;
begin
@ -1402,37 +1410,65 @@ begin
if (ASheet1 = '') and (ASheet2 = '') then
begin
if s1 = s2 then
Result := '[.' + s1 + ']' // --> [.A1]
Result := '.' + s1 // --> [.A1]
else
Result := Format('[.%s:.%s]', [s1, s2]) // --> [.A1:.B3]
Result := Format('.%s:.%s', [s1, s2]) // --> [.A1:.B3]
end else
if (ASheet2 = '') or (ASheet1 = ASheet2) then begin
if s1 = s2 then
Result := Format('[%s.%s]', [ASheet1, s1]) // [Sheet1.A1]
Result := Format('%s.%s', [ASheet1, s1]) // [Sheet1.A1]
else
Result := Format('[%s.%s:.%s]', [ASheet1, s1, s2]); // [Sheet1.A1:.B2]
Result := Format('%s.%s:.%s', [ASheet1, s1, s2]); // [Sheet1.A1:.B2]
end else
Result := Format('[%s.%s:%s.%s]', [ASheet1, s1, ASheet2, s2]); // [Sheet.A1:Sheet2.B2]
Result := Format('%s.%s:%s.%s', [ASheet1, s1, ASheet2, s2]); // [Sheet.A1:Sheet2.B2]
if WithBrackets then
Result := '[' + Result + ']';
end;
function GetCellRangeString_ODS(ARow1, ACol1, ARow2, ACol2: Cardinal;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false; WithBrackets: Boolean = true): String;
begin
if Compact and (ARow1 = ARow2) and (ACol1 = ACol2) then
Result := Format('[.%s]', [GetCellString(ARow1, ACol1, AFlags)])
Result := Format('.%s', [GetCellString(ARow1, ACol1, AFlags)])
else
Result := Format('[.%s%s%s%d:.%s%s%s%d]', [
Result := Format('.%s%s%s%d:.%s%s%s%d', [
RELCHAR[rfRelCol in AFlags], GetColString(ACol1),
RELCHAR[rfRelRow in AFlags], ARow1 + 1,
RELCHAR[rfRelCol2 in AFlags], GetColString(ACol2),
RELCHAR[rfRelRow2 in AFlags], ARow2 + 1
]);
if WithBrackets then
Result := '[' + Result + ']';
end;
function GetCellRangeString_ODS(ARange: TsCellRange;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false): String;
AFlags: TsRelFlags = rfAllRel; Compact: Boolean = false;
WithBrackets: Boolean = true): String;
begin
Result := GetCellRangeString_ODS(ARange, AFlags, Compact);
Result := GetCellRangeString_ODS(ARange, AFlags, Compact, WithBrackets);
end;
function GetSheetCellString_ODS(ASheet: String; ARow, ACol: Cardinal;
AFlags: TsRelFlags = rfAllRel; WithBrackets: Boolean = true): String;
begin
Result := Format('%s.%s%s%s%d', [
ASheet, RELCHAR[rfRelCol in AFlags], GetColString(ACol), RELCHAR[rfRelRow in AFlags], ARow + 1
]);
if WithBrackets then
Result := '[' + Result + ']';
end;
function GetSheetCellRangeString_ODS(ASheet1, ASheet2: String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: TsRelFlags = rfAllRel;
WithBrackets: Boolean = true): String;
begin
Result := Format('%s.%s%s%s%d:%s.%s%s%s%d', [
ASheet1, RELCHAR[rfRelCol in AFlags], GetColString(ACol1), RELCHAR[rfRelRow in AFlags], ARow1 + 1,
ASheet2, RELCHAR[rfRelCol2 in AFlags], GetColString(ACol2), RELCHAR[rfRelRow2 in AFlags], ARow2 + 1
]);
if WithBrackets then
Result := '[' + Result + ']';
end;

File diff suppressed because it is too large Load Diff