
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9591 8e941d3f-bd1b-0410-a28a-d453659cc2b4
7363 lines
247 KiB
ObjectPascal
7363 lines
247 KiB
ObjectPascal
{@@ ----------------------------------------------------------------------------
|
|
Unit fpSpreadsheetGrid implements a **grid** component which can load and
|
|
write data from/to FPSpreadsheet documents.
|
|
|
|
Can either be used alone or in combination with a TsWorkbookSource component.
|
|
The latter method requires less written code.
|
|
|
|
AUTHORS: Felipe Monteiro de Carvalho, Werner Pamler
|
|
|
|
LICENSE: See the file COPYING.modifiedLGPL.txt, included in the Lazarus
|
|
distribution, for details about the license.
|
|
-------------------------------------------------------------------------------}
|
|
unit fpspreadsheetgrid;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
|
|
{$I fps.inc}
|
|
|
|
{.$DEFINE GRID_DEBUG}
|
|
|
|
{ To do:
|
|
- When Lazarus 1.4 comes out remove the workaround for the RGB2HLS bug in
|
|
FindNearestPaletteIndex.
|
|
- Arial bold is not shown as such if loaded from ods
|
|
- Background color of first cell is ignored. }
|
|
|
|
interface
|
|
|
|
uses
|
|
LCLType, LCLIntf, LCLVersion, Classes, SysUtils, LMessages, LResources, Variants,
|
|
Forms, Controls, Graphics, Dialogs, Grids, StdCtrls, ExtCtrls,
|
|
fpstypes, fpspreadsheet, fpspreadsheetctrls;
|
|
|
|
type
|
|
|
|
{ TsCustomWorksheetGrid }
|
|
TsCustomWorksheetGrid = class;
|
|
|
|
TsAutoExpandMode = (
|
|
{@@ Expands grid dimensions if a cell is written outside current grid dimensions }
|
|
aeData,
|
|
{@@ Expands grid dimensions if navigation goes outside current grid dimensions }
|
|
aeNavigation,
|
|
{@@ Expands grid dimensions to DEFAULT_ROW_COUNT and DEFAULT_COL_COUNT }
|
|
aeDefault
|
|
);
|
|
TsAutoExpandModes = set of TsAutoExpandMode;
|
|
|
|
TsEditorLineMode = (elmSingleLine, elmMultiLine);
|
|
|
|
TsHyperlinkClickEvent = procedure(Sender: TObject;
|
|
const AHyperlink: TsHyperlink) of object;
|
|
|
|
TsGetCellTextEvent = procedure(Sender: TObject; AIndex: Integer;
|
|
var AText: String) of object;
|
|
|
|
TsSelPen = class(TPen)
|
|
public
|
|
constructor Create; override;
|
|
published
|
|
property Width stored true default 3;
|
|
property JoinStyle default pjsMiter;
|
|
end;
|
|
|
|
TsMultilineStringCellEditor = class(TMemo)
|
|
private
|
|
FGrid: TsCustomWorksheetGrid;
|
|
FCol, FRow: Integer;
|
|
protected
|
|
procedure Change; override;
|
|
procedure KeyDown(var AKey: Word; AShift: TShiftState); override;
|
|
procedure msg_SetValue(var AMsg: TGridMessage); message GM_SETVALUE;
|
|
procedure msg_GetValue(var AMsg: TGridMessage); message GM_GETVALUE;
|
|
procedure msg_SetGrid(var AMsg: TGridMessage); message GM_SETGRID;
|
|
procedure msg_SelectAll(var AMsg: TGridMessage); message GM_SELECTALL;
|
|
procedure msg_SetPos(var AMsg: TGridMessage); message GM_SETPOS;
|
|
procedure msg_GetGrid(var AMsg: TGridMessage); message GM_GETGRID;
|
|
procedure WndProc(var AMsg: TLMessage); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
procedure EditingDone; override;
|
|
property OnEditingDone;
|
|
end;
|
|
|
|
|
|
// TsSelectionRectMode = (srmDThickXOR, srmThick, srmDottedXOR,
|
|
{@@ TsCustomWorksheetGrid is the ancestor of TsWorksheetGrid and is able to
|
|
display spreadsheet data along with their formatting. }
|
|
TsCustomWorksheetGrid = class(TCustomDrawGrid, IsSpreadsheetControl)
|
|
private
|
|
{ Private declarations }
|
|
FWorkbookSource: TsWorkbookSource;
|
|
FInternalWorkbookSource: TsWorkbookSource;
|
|
FHeaderCount: Integer;
|
|
FFrozenCols: Integer;
|
|
FFrozenRows: Integer;
|
|
FEditText: String;
|
|
FLockCount: Integer;
|
|
FLockSetup: Integer;
|
|
FEditing: Boolean;
|
|
FCellFont: TFont;
|
|
FAutoCalc: Boolean;
|
|
FAutoDetectCellType: Boolean;
|
|
FTextOverflow: Boolean;
|
|
FReadFormulas: Boolean;
|
|
FDrawingCell: PCell;
|
|
FTextOverflowing: Boolean;
|
|
FAutoExpand: TsAutoExpandModes;
|
|
FEnhEditMode: Boolean;
|
|
FSelPen: TsSelPen;
|
|
FPageBreakPen: TPen;
|
|
FFrozenBorderPen: TPen;
|
|
FHyperlinkTimer: TTimer;
|
|
FHyperlinkCell: PCell; // Selected cell if it stores a hyperlink
|
|
FDefRowHeight100: Integer; // Default row height for 100% zoom factor, in pixels
|
|
FDefColWidth100: Integer; // Default col width for 100% zoom factor, in pixels
|
|
FZoomLock: Integer;
|
|
FRowHeightLock: Integer;
|
|
FActiveCellLock: Integer;
|
|
FTopLeft: TPoint;
|
|
FReadOnly: Boolean;
|
|
FOnClickHyperlink: TsHyperlinkClickEvent;
|
|
FOldEditorText: String;
|
|
// FSingleLineStringEditor: TsSingleLineStringCellEditor;
|
|
FMultiLineStringEditor: TsMultilineStringCellEditor;
|
|
FLineMode: TsEditorLineMode;
|
|
FAllowDragAndDrop: Boolean;
|
|
FDragStartCol, FDragStartRow: Integer;
|
|
FOldDragStartCol, FOldDragStartRow: Integer;
|
|
FDragSelection: TGridRect;
|
|
FDragTimer: TTimer;
|
|
FGetRowHeaderText: TsGetCellTextEvent;
|
|
FGetColHeaderText: TsGetCellTextEvent;
|
|
FRefocusing: TObject;
|
|
FRefocusingSelStart: Integer;
|
|
FFormulaError: Boolean;
|
|
FFixedColWidth: Integer;
|
|
FShowFormulas: Boolean;
|
|
FShowPageBreaks: Boolean;
|
|
function CalcAutoRowHeight(ARow: Integer): Integer;
|
|
function CalcColWidthFromSheet(AWidth: Single): Integer;
|
|
function CalcRowHeightFromSheet(AHeight: Single): Integer;
|
|
function CalcRowHeightToSheet(AHeight: Integer): Single;
|
|
procedure ChangedCellHandler(ASender: TObject; ARow, ACol: Cardinal);
|
|
procedure ChangedFontHandler(ASender: TObject; ARow, ACol: Cardinal);
|
|
procedure FixNeighborCellBorders(ACell: PCell);
|
|
function GetBorderStyle(ACol, ARow, ADeltaCol, ADeltaRow: Integer;
|
|
ACell: PCell; out ABorderStyle: TsCellBorderStyle): Boolean;
|
|
|
|
// Setter/Getter
|
|
function GetBackgroundColor(ACol, ARow: Integer): TsColor;
|
|
function GetBackgroundColors(ALeft, ATop, ARight, ABottom: Integer): TsColor;
|
|
function GetCellBiDiMode(ACol, ARow: Integer): TsBiDiMode;
|
|
function GetCellBorder(ACol, ARow: Integer): TsCellBorders;
|
|
function GetCellBorders(ALeft, ATop, ARight, ABottom: Integer): TsCellBorders;
|
|
function GetCellBorderStyle(ACol, ARow: Integer; ABorder: TsCellBorder): TsCellBorderStyle;
|
|
function GetCellBorderStyles(ALeft, ATop, ARight, ABottom: Integer;
|
|
ABorder: TsCellBorder): TsCellBorderStyle;
|
|
function GetCellComment(ACol, ARow: Integer): string;
|
|
function GetCellFont(ACol, ARow: Integer): TFont;
|
|
function GetCellFonts(ALeft, ATop, ARight, ABottom: Integer): TFont;
|
|
function GetCellFontColor(ACol, ARow: Integer): TsColor;
|
|
function GetCellFontColors(ALeft, ATop, ARight, ABottom: Integer): TsColor;
|
|
function GetCellFontName(ACol, ARow: Integer): String;
|
|
function GetCellFontNames(ALeft, ATop, ARight, ABottom: Integer): String;
|
|
function GetCellFontSize(ACol, ARow: Integer): Single;
|
|
function GetCellFontSizes(ALeft, ATop, ARight, ABottom: Integer): Single;
|
|
function GetCellFontStyle(ACol, ARow: Integer): TsFontStyles;
|
|
function GetCellFontStyles(ALeft, ATop, ARight, ABottom: Integer): TsFontStyles;
|
|
function GetCellFormula(ACol, ARow: Integer): String;
|
|
function GetCellProtection(ACol, ARow: Integer): TsCellProtections;
|
|
function GetCellProtections(ALeft, ATop, ARight, ABottom: Integer): TsCellProtections;
|
|
function GetCellValue(ACol, ARow: Integer): variant;
|
|
function GetColWidths(ACol: Integer): Integer;
|
|
function GetDefColWidth: Integer;
|
|
function GetDefRowHeight: Integer;
|
|
function GetFixedColWidth: Integer;
|
|
function GetHorAlignment(ACol, ARow: Integer): TsHorAlignment;
|
|
function GetHorAlignments(ALeft, ATop, ARight, ABottom: Integer): TsHorAlignment;
|
|
function GetHyperlink(ACol, ARow: Integer): String;
|
|
function GetNumberFormat(ACol, ARow: Integer): String;
|
|
function GetNumberFormats(ALeft, ATop, ARight, ABottom: Integer): String;
|
|
function GetRowHeights(ARow: Integer): Integer;
|
|
function GetShowGridLines: Boolean;
|
|
function GetShowHeaders: Boolean; inline;
|
|
function GetTextRotation(ACol, ARow: Integer): TsTextRotation;
|
|
function GetTextRotations(ALeft, ATop, ARight, ABottom: Integer): TsTextRotation;
|
|
function GetVertAlignment(ACol, ARow: Integer): TsVertAlignment;
|
|
function GetVertAlignments(ALeft, ATop, ARight, ABottom: Integer): TsVertAlignment;
|
|
function GetWorkbook: TsWorkbook;
|
|
function GetWorkbookSource: TsWorkbookSource;
|
|
function GetWorksheet: TsWorksheet;
|
|
function GetWordwrap(ACol, ARow: Integer): Boolean;
|
|
function GetWordwraps(ALeft, ATop, ARight, ABottom: Integer): Boolean;
|
|
function GetZoomFactor: Double;
|
|
procedure SetAutoCalc(AValue: Boolean);
|
|
procedure SetAutoDetectCellType(AValue: Boolean);
|
|
procedure SetBackgroundColor(ACol, ARow: Integer; AValue: TsColor);
|
|
procedure SetBackgroundColors(ALeft, ATop, ARight, ABottom: Integer; AValue: TsColor);
|
|
procedure SetCellBiDiMode(ACol, ARow: Integer; AValue: TsBiDiMode);
|
|
procedure SetCellBorder(ACol, ARow: Integer; AValue: TsCellBorders);
|
|
procedure SetCellBorders(ALeft, ATop, ARight, ABottom: Integer; AValue: TsCellBorders);
|
|
procedure SetCellBorderStyle(ACol, ARow: Integer; ABorder: TsCellBorder;
|
|
AValue: TsCellBorderStyle);
|
|
procedure SetCellBorderStyles(ALeft, ATop, ARight, ABottom: Integer;
|
|
ABorder: TsCellBorder; AValue: TsCellBorderStyle);
|
|
procedure SetCellComment(ACol, ARow: Integer; AValue: String);
|
|
procedure SetCellFont(ACol, ARow: Integer; AValue: TFont);
|
|
procedure SetCellFonts(ALeft, ATop, ARight, ABottom: Integer; AValue: TFont);
|
|
procedure SetCellFontColor(ACol, ARow: Integer; AValue: TsColor);
|
|
procedure SetCellFontColors(ALeft, ATop, ARight, ABottom: Integer; AValue: TsColor);
|
|
procedure SetCellFontName(ACol, ARow: Integer; AValue: String);
|
|
procedure SetCellFontNames(ALeft, ATop, ARight, ABottom: Integer; AValue: String);
|
|
procedure SetCellFontSize(ACol, ARow: Integer; AValue: Single);
|
|
procedure SetCellFontSizes(ALeft, ATop, ARight, ABottom: Integer; AValue: Single);
|
|
procedure SetCellFontStyle(ACol, ARow: Integer; AValue: TsFontStyles);
|
|
procedure SetCellFontStyles(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TsFontStyles);
|
|
procedure SetCellFormula(ACol, ARow: Integer; AValue: String);
|
|
procedure SetCellProtection(ACol, ARow: Integer; AValue: TsCellProtections);
|
|
procedure SetCellProtections(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TsCellProtections);
|
|
procedure SetCellValue(ACol, ARow: Integer; AValue: variant);
|
|
procedure SetColWidths(ACol: Integer; AValue: Integer);
|
|
procedure SetDefColWidth(AValue: Integer);
|
|
procedure SetDefRowHeight(AValue: Integer);
|
|
procedure SetEditorLineMode(AValue: TsEditorLineMode);
|
|
procedure SetFixedColWidth(AValue: Integer);
|
|
procedure SetFrozenCols(AValue: Integer);
|
|
procedure SetFrozenBorderPen(AValue: TPen);
|
|
procedure SetFrozenRows(AValue: Integer);
|
|
procedure SetHorAlignment(ACol, ARow: Integer; AValue: TsHorAlignment);
|
|
procedure SetHorAlignments(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TsHorAlignment);
|
|
procedure SetHyperlink(ACol, ARow: Integer; AValue: String);
|
|
procedure SetNumberFormat(ACol, ARow: Integer; AValue: String);
|
|
procedure SetNumberFormats(ALeft, ATop, ARight, ABottom: Integer; AValue: String);
|
|
procedure SetPageBreakPen(AValue: TPen);
|
|
procedure SetReadFormulas(AValue: Boolean);
|
|
procedure SetRowHeights(ARow: Integer; AValue: Integer);
|
|
procedure SetSelPen(AValue: TsSelPen);
|
|
procedure SetShowFormulas(AValue: Boolean);
|
|
procedure SetShowGridLines(AValue: Boolean);
|
|
procedure SetShowHeaders(AValue: Boolean);
|
|
procedure SetShowPageBreaks(AValue: Boolean);
|
|
procedure SetTextRotation(ACol, ARow: Integer; AValue: TsTextRotation);
|
|
procedure SetTextRotations(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TsTextRotation);
|
|
procedure SetVertAlignment(ACol, ARow: Integer; AValue: TsVertAlignment);
|
|
procedure SetVertAlignments(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TsVertAlignment);
|
|
procedure SetWorkbookSource(AValue: TsWorkbookSource);
|
|
procedure SetWordwrap(ACol, ARow: Integer; AValue: boolean);
|
|
procedure SetWordwraps(ALeft, ATop, ARight, ABottom: Integer; AValue: boolean);
|
|
procedure SetZoomFactor(AValue: Double);
|
|
|
|
procedure DragTimerElapsed(Sender: TObject);
|
|
procedure HyperlinkTimerElapsed(Sender: TObject);
|
|
|
|
protected
|
|
{ Protected declarations }
|
|
procedure AdaptToZoomFactor;
|
|
procedure AutoAdjustColumn(ACol: Integer); override;
|
|
procedure AutoAdjustRow(ARow: Integer); virtual;
|
|
procedure AutoExpandToCol(ACol: Integer; AMode: TsAutoExpandMode);
|
|
procedure AutoExpandToRow(ARow: Integer; AMode: TsAutoExpandMode);
|
|
function CalcTopLeft(AHeaderOnly: Boolean): TPoint;
|
|
function CalcWorksheetColWidth(AValue: Integer): Single;
|
|
function CalcWorksheetRowHeight(AValue: Integer): Single;
|
|
function CanEditShow: boolean; override;
|
|
function CellOverflow(ACol, ARow: Integer; AState: TGridDrawState;
|
|
out ACol1, ACol2: Integer; var ARect: TRect): Boolean;
|
|
procedure ColRowMoved(IsColumn: Boolean; FromIndex,ToIndex: Integer); override;
|
|
procedure CreateHandle; override;
|
|
procedure CreateNewWorkbook;
|
|
procedure DblClick; override;
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
procedure DoCopyToClipboard; override;
|
|
procedure DoCutToClipboard; override;
|
|
procedure DoEditorShow; override;
|
|
procedure DoEnter; override;
|
|
procedure DoExit; override;
|
|
procedure DoPasteFromClipboard; override;
|
|
procedure DoOnResize; override;
|
|
procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override;
|
|
procedure DragOver(ASource: TObject; X, Y: Integer; AState: TDragState;
|
|
var Accept: Boolean); override;
|
|
procedure DrawAllRows; override;
|
|
procedure DrawCellBorders(AGridPart: Integer = 0); overload;
|
|
procedure DrawCellBorders(ACol, ARow: Integer; ARect: TRect; ACell: PCell); overload;
|
|
procedure DrawCellGrid(ACol,ARow: Integer; ARect: TRect; AState: TGridDrawState); override;
|
|
procedure DrawCommentMarker(ARect: TRect);
|
|
procedure DrawDragSelection;
|
|
procedure DrawFocusRect(aCol,aRow:Integer; ARect:TRect); override;
|
|
procedure DrawFrozenPaneBorder(AStart, AEnd, ACoord: Integer; IsHor: Boolean);
|
|
procedure DrawFrozenPanes;
|
|
procedure DrawImages(AGridPart: Integer = 0);
|
|
procedure DrawPageBreaks(AClipRect: TRect); virtual;
|
|
procedure DrawRow(aRow: Integer); override;
|
|
procedure DrawSelection;
|
|
procedure DrawTextInCell(ACol, ARow: Integer; ARect: TRect; AState: TGridDrawState); override;
|
|
procedure EditorDoGetValue; override;
|
|
procedure ExecuteHyperlink;
|
|
procedure GenericPenChangeHandler(Sender: TObject);
|
|
function GetCellHeight(ACol, ARow: Integer): Integer;
|
|
function GetCellHintText(ACol, ARow: Integer): String; override;
|
|
function GetCells(ACol, ARow: Integer): String; override;
|
|
function GetCellText(ACol, ARow: Integer; ATrim: Boolean = true): String;
|
|
function GetEditText(ACol, ARow: Integer): String; override;
|
|
function GetDefaultColumnTitle(Column: Integer): string; override;
|
|
function GetIsCellTitle({%H-}ACol, ARow: Integer): boolean; override;
|
|
function HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean;
|
|
procedure HeaderSizing(const IsColumn:boolean; const AIndex,ASize:Integer); override;
|
|
procedure HeaderSized(IsColumn: Boolean; AIndex: Integer); override;
|
|
procedure InternalDrawCell(ACol, ARow: Integer; AClipRect, ACellRect: TRect;
|
|
AState: TGridDrawState);
|
|
procedure InternalDrawRow(ARow, AFirstCol, ALastCol: Integer;
|
|
AClipRect: TRect);
|
|
procedure InternalDrawSelection(ASel: TGridRect; IsNormalSelection: boolean);
|
|
procedure InternalDrawTextInCell(AText: String; ARect: TRect;
|
|
ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment;
|
|
ATextRot: TsTextRotation; ATextWrap: Boolean; AFontIndex: Integer;
|
|
AOverrideTextColor: TColor; ARichTextParams: TsRichTextParams;
|
|
AIsRightToLeft: Boolean);
|
|
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
|
|
procedure KeyUp(var Key : Word; Shift : TShiftState); override;
|
|
procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
|
function MouseOnCellBorder(const APoint: TPoint;
|
|
const ACellRect: TGridRect): Boolean;
|
|
function MouseOnHeader(X, Y: Integer): Boolean;
|
|
procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
|
|
{$IFNDEF LCL_FullVersion_LT_190000}
|
|
function MoveNextSelectable(Relative: Boolean; DCol, DRow: Integer): Boolean; override;
|
|
{$ENDIF}
|
|
procedure MoveSelection; override;
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
procedure PrepareCanvasFont;
|
|
function RelaxAutoExpand: TsAutoExpandModes;
|
|
procedure RestoreAutoExpand(AValue: TsAutoExpandModes);
|
|
function SelectCell(ACol, ARow: Integer): Boolean; override;
|
|
procedure SetEditText(ACol, ARow: Longint; const AValue: string); override;
|
|
procedure Setup;
|
|
function ToPixels(AValue: Double): Integer;
|
|
procedure TopLeftChanged; override;
|
|
function TrimToCell(ACell: PCell): String;
|
|
procedure ValidateInput(var Msg: TLMessage); message UM_VALIDATEINPUT;
|
|
function ValidFormula(AExpression: String; out AErrMsg: String): Boolean;
|
|
procedure WMHScroll(var message: TLMHScroll); message LM_HSCROLL;
|
|
procedure WMVScroll(var message: TLMVScroll); message LM_VSCROLL;
|
|
|
|
{@@ Allow built-in drag and drop }
|
|
property AllowDragAndDrop: Boolean read FAllowDragAndDrop
|
|
write FAllowDragAndDrop default true;
|
|
{@@ Automatically recalculate formulas whenever a cell value changes. }
|
|
property AutoCalc: Boolean read FAutoCalc write SetAutoCalc default false;
|
|
{@@ Automatically detect the cell's content type }
|
|
property AutoDetectCellType: Boolean read FAutoDetectCellType
|
|
write SetAutoDetectCellType default true;
|
|
{@@ Automatically expand grid dimensions }
|
|
property AutoExpand: TsAutoExpandModes read FAutoExpand write FAutoExpand
|
|
default [aeData, aeNavigation, aeDefault];
|
|
{@@ Displays column and row headers in the fixed col/row style of the grid.
|
|
Deprecated. Use ShowHeaders instead. }
|
|
property DisplayFixedColRow: Boolean read GetShowHeaders write SetShowHeaders
|
|
default true;
|
|
{@@ Determines whether a single- or multi-line cell editor is used }
|
|
property EditorLineMode: TsEditorLineMode read FLineMode write SetEditorLineMode
|
|
default elmSingleLine;
|
|
{@@ Width of the fixed row header column. 0 = auto width detection }
|
|
property FixedColWidth: Integer read FFixedColWidth
|
|
write SetFixedColWidth default 0;
|
|
{@@ This number of columns at the left is "frozen", i.e. it is not possible to
|
|
scroll these columns }
|
|
property FrozenCols: Integer read FFrozenCols write SetFrozenCols;
|
|
{@@ Pen for the line separating the frozen cols/rows from the regular grid }
|
|
property FrozenBorderPen: TPen read FFrozenBorderPen write SetFrozenBorderPen;
|
|
{@@ This number of rows at the top is "frozen", i.e. it is not possible to
|
|
scroll these rows. }
|
|
property FrozenRows: Integer read FFrozenRows write SetFrozenRows;
|
|
{@@ Defines the pen used for the line drawn at the position of page breaks }
|
|
property PageBreakPen: TPen read FPageBreakPen write SetPageBreakPen;
|
|
{@@ Activates reading of RPN formulas. Should be turned off when
|
|
non-implemented formulas crashe reading of the spreadsheet file. }
|
|
property ReadFormulas: Boolean read FReadFormulas write SetReadFormulas;
|
|
{@@ Pen used for drawing the selection rectangle }
|
|
property SelectionPen: TsSelPen read FSelPen write SetSelPen;
|
|
{@@ Shows/hides formulas in grid when AutoCalc is off}
|
|
property ShowFormulas: Boolean read FShowFormulas write SetShowFormulas default false;
|
|
{@@ Shows/hides vertical and horizontal grid lines }
|
|
property ShowGridLines: Boolean read GetShowGridLines write SetShowGridLines default true;
|
|
{@@ Shows/hides column and row headers in the fixed col/row style of the grid. }
|
|
property ShowHeaders: Boolean read GetShowHeaders write SetShowHeaders default true;
|
|
{@@ Shows/hides lines at the position of page breaks }
|
|
property ShowPageBreaks: Boolean read FShowPageBreaks write SetShowPageBreaks default true;
|
|
{@@ Activates text overflow (cells reaching into neighbors) }
|
|
property TextOverflow: Boolean read FTextOverflow write FTextOverflow default false;
|
|
{@@ Event called when an external hyperlink is clicked }
|
|
property OnClickHyperlink: TsHyperlinkClickEvent read FOnClickHyperlink write FOnClickHyperlink;
|
|
{@@ Event allowing to modifiy the text displayed in a column header}
|
|
property OnGetColHeaderText: TsGetCellTextEvent read FGetColHeaderText write FGetColHeaderText;
|
|
{@@ Event allowing to modifiy the text displayed in a row header }
|
|
property OnGetRowHeaderText: TsGetCellTextEvent read FGetRowHeaderText write FGetRowHeaderText;
|
|
|
|
public
|
|
{ public methods }
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
|
|
procedure AutoColWidth(ACol: Integer);
|
|
procedure AutoRowHeight(ARow: Integer);
|
|
procedure BeginUpdate;
|
|
function CellRect(ACol1, ARow1, ACol2, ARow2: Integer): TRect; overload;
|
|
procedure Clear;
|
|
procedure DefaultDrawCell(ACol, ARow: Integer; var ARect: TRect;
|
|
AState: TGridDrawState); override;
|
|
procedure DeleteCol(AGridCol: Integer); reintroduce;
|
|
procedure DeleteRow(AGridRow: Integer); reintroduce;
|
|
procedure DragDrop(Source: TObject; X, Y: Integer); override;
|
|
function EditorByStyle(Style: TColumnButtonStyle): TWinControl; override;
|
|
procedure EndUpdate(ARefresh: Boolean = true);
|
|
function GetGridCol(ASheetCol: Cardinal): Integer; inline;
|
|
function GetGridRow(ASheetRow: Cardinal): Integer; inline;
|
|
procedure GetSheets(const ASheets: TStrings);
|
|
function GetWorksheetCol(AGridCol: Integer): Cardinal; inline;
|
|
function GetWorksheetRow(AGridRow: Integer): Cardinal; inline;
|
|
procedure InsertCol(AGridCol: Integer);
|
|
procedure InsertRow(AGridRow: Integer);
|
|
|
|
procedure LoadFromSpreadsheetFile(AFileName: string;
|
|
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = -1); overload;
|
|
procedure LoadFromSpreadsheetFile(AFileName: string;
|
|
AFormatID: TsSpreadFormatID = sfidUnknown; AWorksheetIndex: Integer = -1); overload;
|
|
procedure LoadSheetFromSpreadsheetFile(AFileName: String;
|
|
AWorksheetIndex: Integer = -1; AFormatID: TsSpreadFormatID = sfidUnknown);
|
|
procedure LoadFromWorkbook(AWorkbook: TsWorkbook; AWorksheetIndex: Integer = -1);
|
|
|
|
procedure NewWorkbook(AColCount, ARowCount: Integer);
|
|
|
|
procedure SaveToSpreadsheetFile(AFileName: string;
|
|
AOverwriteExisting: Boolean = true); overload;
|
|
procedure SaveToSpreadsheetFile(AFileName: string; AFormat: TsSpreadsheetFormat;
|
|
AOverwriteExisting: Boolean = true); overload; deprecated;
|
|
procedure SaveToSpreadsheetFile(AFileName: string; AFormatID: TsSpreadFormatID;
|
|
AOverwriteExisting: Boolean = true); overload;
|
|
|
|
procedure SelectSheetByIndex(AIndex: Integer);
|
|
|
|
procedure MergeCells; overload;
|
|
procedure MergeCells(ARect: TGridRect); overload;
|
|
procedure MergeCells(ALeft, ATop, ARight, ABottom: Integer); overload;
|
|
procedure UnmergeCells; overload;
|
|
procedure UnmergeCells(ACol, ARow: Integer); overload;
|
|
|
|
procedure ShowCellBorders(ALeft, ATop, ARight, ABottom: Integer;
|
|
const ALeftOuterStyle, ATopOuterStyle, ARightOuterStyle, ABottomOuterStyle,
|
|
AHorInnerStyle, AVertInnerStyle: TsCellBorderStyle);
|
|
|
|
procedure ShowCol(ACol: Integer);
|
|
procedure HideCol(ACol: Integer);
|
|
procedure ShowRow(ARow: Integer);
|
|
procedure HideRow(ARow: Integer);
|
|
|
|
procedure Sort(AColSorting: Boolean; AIndex, AIndxFrom, AIndxTo:Integer); override;
|
|
|
|
{ Row height / col width calculation }
|
|
procedure UpdateColWidth(ACol: Integer);
|
|
procedure UpdateColWidths(AStartIndex: Integer = 0);
|
|
procedure UpdateRowHeight(ARow: Integer; AEnforceCalcRowHeight: Boolean = false);
|
|
procedure UpdateRowHeights(AStartRow: Integer = -1; AEnforceCalcRowHeight: Boolean = false);
|
|
|
|
{ Utilities related to Workbooks }
|
|
procedure Convert_sFont_to_Font(sFont: TsFont; AFont: TFont);
|
|
procedure Convert_Font_to_sFont(AFont: TFont; sFont: TsFont);
|
|
|
|
{ Interfacing with WorkbookSource}
|
|
procedure ListenerNotification(AChangedItems: TsNotificationItems;
|
|
AData: Pointer = nil);
|
|
procedure RemoveWorkbookSource;
|
|
|
|
{ public properties }
|
|
{@@ Link to the workbook }
|
|
property WorkbookSource: TsWorkbookSource read GetWorkbookSource write SetWorkbookSource;
|
|
{@@ Currently selected worksheet of the workbook }
|
|
property Worksheet: TsWorksheet read GetWorksheet;
|
|
{@@ Workbook displayed in the grid }
|
|
property Workbook: TsWorkbook read GetWorkbook;
|
|
{@@ Count of header lines - for conversion between grid- and workbook-based
|
|
row and column indexes. Either 1 if row and column headers are shown or 0 if not}
|
|
property HeaderCount: Integer read FHeaderCount;
|
|
|
|
{@@ Background color of the cell at the given column and row. Expressed as
|
|
index into the workbook's color palette. }
|
|
property BackgroundColor[ACol, ARow: Integer]: TsColor
|
|
read GetBackgroundColor write SetBackgroundColor;
|
|
{@@ Common background color of the cells covered by the given rectangle.
|
|
Expressed as index into the workbook's color palette. }
|
|
property BackgroundColors[ALeft, ATop, ARight, ABottom: Integer]: TsColor
|
|
read GetBackgroundColors write SetBackgroundColors;
|
|
{@@ Override system or sheet right-to-left mode for cell }
|
|
property CellBiDiMode[ACol,ARow: Integer]: TsBiDiMode
|
|
read GetCellBiDiMode write SetCellBiDiMode;
|
|
{@@ Set of flags indicating at which cell border a border line is drawn. }
|
|
property CellBorder[ACol, ARow: Integer]: TsCellBorders
|
|
read GetCellBorder write SetCellBorder;
|
|
{@@ Set of flags indicating at which border of a range of cells a border
|
|
line is drawn }
|
|
property CellBorders[ALeft, ATop, ARight, ABottom: Integer]: TsCellBorders
|
|
read GetCellBorders write SetCellBorders;
|
|
{@@ Style of the border line at the given border of the cell at column ACol
|
|
and row ARow. Requires the cellborder flag of the border to be set
|
|
for the border line to be shown }
|
|
property CellBorderStyle[ACol, ARow: Integer; ABorder: TsCellBorder]: TsCellBorderStyle
|
|
read GetCellBorderStyle write SetCellBorderStyle;
|
|
{@@ Style of the border line at the given border of the cells within the
|
|
range of colum/row indexes defined by the rectangle. Requires the cellborder
|
|
flag of the border to be set for the border line to be shown }
|
|
property CellBorderStyles[ALeft, ATop, ARight, ABottom: Integer;
|
|
ABorder: TsCellBorder]: TsCellBorderStyle
|
|
read GetCellBorderStyles write SetCellBorderStyles;
|
|
{@@ Comment assigned to the cell at column ACol and row ARow }
|
|
property CellComment[ACol, ARow: Integer]: String
|
|
read GetCellComment write SetCellComment;
|
|
{@@ Font to be used for text in the cell at column ACol and row ARow. }
|
|
property CellFont[ACol, ARow: Integer]: TFont
|
|
read GetCellFont write SetCellFont;
|
|
{@@ Font to be used for the cells in the column/row index range
|
|
given by the rectangle }
|
|
property CellFonts[ALeft, ATop, ARight, ABottom: Integer]: TFont
|
|
read GetCellFonts write SetCellFonts;
|
|
{@@ Color of the font used for the cell in column ACol and row ARow }
|
|
property CellFontColor[ACol, ARow: Integer]: TsColor
|
|
read GetCellFontColor write SetCellFontColor;
|
|
{@@ Color of the font used for the cells within the range
|
|
of column/row indexes defined by the rectangle, scUndefined if not constant. }
|
|
property CellFontColors[ALeft, ATop, ARight, ABottom: Integer]: TsColor
|
|
read GetCellFontColors write SetCellFontColors;
|
|
{@@ Name of the font used for the cell in column ACol and row ARow }
|
|
property CellFontName[ACol, ARow: Integer]: String
|
|
read GetCellFontName write SetCellFontName;
|
|
{@@ Name of the font used for the cells within the range
|
|
of column/row indexes defined by the rectangle. }
|
|
property CellFontNames[ALeft, ATop, ARight, ABottom: Integer]: String
|
|
read GetCellFontNames write SetCellFontNames;
|
|
{@@ Style of the font (bold, italic, ...) used for text in the
|
|
cell at column ACol and row ARow. }
|
|
property CellFontStyle[ACol, ARow: Integer]: TsFontStyles
|
|
read GetCellFontStyle write SetCellFontStyle;
|
|
{@@ Style of the font (bold, italic, ...) used for the cells within
|
|
the range of column/row indexes defined by the rectangle. }
|
|
property CellFontStyles[ALeft, ATop, ARight, ABottom: Integer]: TsFontStyles
|
|
read GetCellFontStyles write SetCellFontStyles;
|
|
{@@ Size of the font (in points) used for the cell at column ACol
|
|
and row ARow }
|
|
property CellFontSize[ACol, ARow: Integer]: Single
|
|
read GetCellFontSize write SetCellFontSize;
|
|
{@@ Size of the font (in points) used for the cells within the
|
|
range of column/row indexes defined by the rectangle. }
|
|
property CellFontSizes[ALeft, ATop, ARight, ABottom: Integer]: Single
|
|
read GetCellFontSizes write SetCellFontSizes;
|
|
{@@ Formula in the given cell, empty if there is no formula }
|
|
property CellFormula[ACol, ARow: Integer]: String
|
|
read GetCellFormula write SetCellFormula;
|
|
{@@ Cell protection elements assigned to the given cell }
|
|
property CellProtection[ACol, ARow: Integer]: TsCellProtections
|
|
read GetCellProtection write SetCellProtection;
|
|
{@@ Cell protection elements assigned to the given cell range. Empty if not
|
|
consistent with all cells. }
|
|
property CellProtections[ALeft, ATop, ARight, ABottom: Integer]: TsCellProtections
|
|
read GetCellProtections write SetCellProtections;
|
|
{@@ Cell values }
|
|
property Cells[ACol, ARow: Integer]: Variant
|
|
read GetCellValue write SetCellValue;
|
|
{@@ Parameter for horizontal text alignment within the cell at column ACol
|
|
and row ARow }
|
|
property HorAlignment[ACol, ARow: Integer]: TsHorAlignment
|
|
read GetHorAlignment write SetHorAlignment;
|
|
{@@ Parameter for the horizontal text alignments in all cells within the
|
|
range cf column/row indexes defined by the rectangle. }
|
|
property HorAlignments[ALeft, ATop, ARight, ABottom: Integer]: TsHorAlignment
|
|
read GetHorAlignments write SetHorAlignments;
|
|
{@@ Hyperlink assigned to the cell in row ARow and column ACol }
|
|
property Hyperlink[ACol, ARow: Integer]: String
|
|
read GetHyperlink write SetHyperlink;
|
|
{@@ Number format (as Excel string) to be applied to cell at column ACol and row ARow. }
|
|
property NumberFormat[ACol, ARow: Integer]: String
|
|
read GetNumberFormat write SetNumberFormat;
|
|
{@@ Number format (as Excel string) to be applied to all cells within the
|
|
range of column/row indexes defined by the rectangle. }
|
|
property NumberFormats[ALeft, ATop, ARight, ABottom: Integer]: String
|
|
read GetNumberFormats write SetNumberFormats;
|
|
{@@ Rotation of the text in the cell at column ACol and row ARow. }
|
|
property TextRotation[ACol, ARow: Integer]: TsTextRotation
|
|
read GetTextRotation write SetTextRotation;
|
|
{@@ Rotation of the text in the cells within the range of column/row indexes
|
|
defined by the rectangle. }
|
|
property TextRotations[ALeft, ATop, ARight, ABottom: Integer]: TsTextRotation
|
|
read GetTextRotations write SetTextRotations;
|
|
{$IFNDEF LCL_FullVersion_LT_180}
|
|
{@@ Pixel coordinates of the top-left corner of the grid's cell area}
|
|
property TopLeftPx: TPoint read GetPxTopLeft;
|
|
{$ENDIF}
|
|
{@@ Parameter for vertical text alignment in the cell at column ACol and row ARow. }
|
|
property VertAlignment[ACol, ARow: Integer]: TsVertAlignment
|
|
read GetVertAlignment write SetVertAlignment;
|
|
{@@ Parameter for vertical text alignment in the cells having column/row
|
|
indexes defined by the rectangle. }
|
|
property VertAlignments[ALeft, ATop, ARight, ABottom: Integer]: TsVertAlignment
|
|
read GetVertAlignments write SetVertAlignments;
|
|
{@@ If true, word-wrapping of text within the cell at column ACol and row ARow
|
|
is activated. }
|
|
property Wordwrap[ACol, ARow: Integer]: Boolean
|
|
read GetWordwrap write SetWordwrap;
|
|
{@@ If true, word-wrapping of text within all cells within the range defined
|
|
by the rectangle is activated. }
|
|
property Wordwraps[ALeft, ATop, ARight, ABottom: Integer]: Boolean
|
|
read GetWordwraps write SetWordwraps;
|
|
{@@ Zoomfactor of the grid }
|
|
property ZoomFactor: Double
|
|
read GetZoomFactor write SetZoomFactor;
|
|
|
|
// inherited, but modified
|
|
|
|
{@@ Column width, in pixels }
|
|
property ColWidths[ACol: Integer]: Integer
|
|
read GetColWidths write SetColWidths;
|
|
{@@ Default column width, in pixels }
|
|
property DefaultColWidth: Integer
|
|
read GetDefColWidth write SetDefColWidth;
|
|
{@@ Default row height, in pixels }
|
|
property DefaultRowHeight: Integer
|
|
read GetDefRowHeight write SetDefRowHeight;
|
|
{@@ Row height in pixels }
|
|
property RowHeights[ARow: Integer]: Integer
|
|
read GetRowHeights write SetRowHeights;
|
|
|
|
// inherited
|
|
|
|
{$IFNDEF FPS_NO_GRID_MULTISELECT}
|
|
{@@ Allow multiple selections}
|
|
property RangeSelectMode default rsmMulti;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
|
|
{ TsWorksheetGrid }
|
|
|
|
{@@
|
|
TsWorksheetGrid is a grid which displays spreadsheet data along with
|
|
formatting. As it is linked to an instance of TsWorkbook, it provides
|
|
methods for reading data from or writing to spreadsheet files. It has the
|
|
same funtionality as TsCustomWorksheetGrid, but has published all properties.
|
|
}
|
|
TsWorksheetGrid = class(TsCustomWorksheetGrid)
|
|
published
|
|
// inherited from TsCustomWorksheetGrid
|
|
{@@ Allow built-in drag and drop }
|
|
property AllowDragAndDrop;
|
|
{@@ Automatically recalculates the worksheet formulas if a cell value changes. }
|
|
property AutoCalc;
|
|
{@@ Automatically detect the cell's content type }
|
|
property AutoDetectCellType;
|
|
{@@ Automatically expand grid dimensions }
|
|
property AutoExpand;
|
|
{@@ Displays column and row headers in the fixed col/row style of the grid.
|
|
Deprecated. Use ShowHeaders instead. }
|
|
property DisplayFixedColRow; deprecated 'Use ShowHeaders';
|
|
{@@ Determines whether a single- or multiline cell editor is used }
|
|
property EditorLineMode;
|
|
{@@ Width of the first fixed column (= row headers), in pixels }
|
|
property FixedColWidth;
|
|
{@@ Pen for the line separating the frozen cols/rows from the regular grid }
|
|
property FrozenBorderPen;
|
|
{@@ This number of columns at the left is "frozen", i.e. it is not possible to
|
|
scroll these columns. }
|
|
property FrozenCols;
|
|
{@@ This number of rows at the top is "frozen", i.e. it is not possible to
|
|
scroll these rows. }
|
|
property FrozenRows;
|
|
{@@ Defines the pen used for the lines drawn at the position of page breaks }
|
|
property PageBreakPen;
|
|
{@@ Activates reading of RPN formulas. Should be turned off when
|
|
non-implemented formulas crashes reading of the spreadsheet file. }
|
|
property ReadFormulas;
|
|
{@@ Pen used for drawing the selection rectangle }
|
|
property SelectionPen;
|
|
{@@ Shows/hides formulas in grid cells when AutoCalc is off. }
|
|
property ShowFormulas;
|
|
{@@ Shows/hides vertical and horizontal grid lines. }
|
|
property ShowGridLines;
|
|
{@@ Shows/hides column and row headers in the fixed col/row style of the grid. }
|
|
property ShowHeaders;
|
|
{@@ Shows/hides lines at the position of page breaks }
|
|
property ShowPageBreaks;
|
|
{@@ Activates text overflow (cells reaching into neighbors) }
|
|
property TextOverflow;
|
|
{@@ Link to the workbook }
|
|
property WorkbookSource;
|
|
|
|
{@@ inherited from ancestors}
|
|
property Align;
|
|
(*
|
|
{@@ inherited from ancestors}
|
|
property AlternateColor;
|
|
*)
|
|
{@@ inherited from ancestors}
|
|
property Anchors;
|
|
{@@ inherited from ancestors}
|
|
property AutoAdvance;
|
|
{@@ inherited from ancestors}
|
|
property AutoEdit;
|
|
{@@ inherited from ancestors}
|
|
property AutoFillColumns;
|
|
//property BiDiMode;
|
|
{@@ inherited from ancestors}
|
|
property BorderSpacing;
|
|
{@@ inherited from ancestors}
|
|
property BorderStyle;
|
|
{@@ inherited from ancestors}
|
|
property CellHintPriority;
|
|
{@@ inherited from ancestors}
|
|
property Color;
|
|
{@@ inherited from ancestors}
|
|
property ColCount default 27; //stored;
|
|
//property Columns;
|
|
{@@ inherited from ancestors}
|
|
property Constraints;
|
|
{@@ inherited from ancestors}
|
|
property DefaultColWidth;
|
|
{@@ inherited from ancestors}
|
|
property DefaultDrawing;
|
|
{@@ inherited from ancestors}
|
|
property DefaultRowHeight;
|
|
{@@ inherited from ancestors}
|
|
property DragCursor;
|
|
{@@ inherited from ancestors}
|
|
property DragKind;
|
|
{@@ inherited from ancestors}
|
|
property DragMode;
|
|
{@@ inherited from ancestors}
|
|
property Enabled;
|
|
{@@ inherited from ancestors}
|
|
property ExtendedSelect default true;
|
|
{@@ inherited from ancestors}
|
|
property FixedColor;
|
|
{@@ inherited from ancestors}
|
|
property Flat;
|
|
{@@ inherited from ancestors}
|
|
property Font;
|
|
{@@ inherited from ancestors}
|
|
property GridLineWidth;
|
|
{@@ inherited from ancestors}
|
|
property HeaderHotZones;
|
|
{@@ inherited from ancestors}
|
|
property HeaderPushZones;
|
|
{@@ inherited from ancestors}
|
|
property MouseWheelOption;
|
|
{@@ inherited from TCustomGrid. Select the option goEditing to make the grid editable! }
|
|
property Options;
|
|
{@@ inherited from ancestors }
|
|
property ParentBiDiMode;
|
|
{@@ inherited from ancestors}
|
|
property ParentColor default false;
|
|
{@@ inherited from ancestors}
|
|
property ParentFont;
|
|
{@@ inherited from ancestors}
|
|
property ParentShowHint;
|
|
{@@ inherited from ancestors}
|
|
property PopupMenu;
|
|
{@@ inherited from ancestors}
|
|
property RowCount default 101;
|
|
{@@ inherited from ancestors}
|
|
property ScrollBars;
|
|
{@@ inherited from ancestors}
|
|
property ShowHint;
|
|
{@@ inherited from ancestors}
|
|
property TabOrder;
|
|
{@@ inherited from ancestors}
|
|
property TabStop;
|
|
{@@ inherited from ancestors}
|
|
property TitleFont;
|
|
{@@ inherited from ancestors}
|
|
property TitleImageList;
|
|
{@@ inherited from ancestors}
|
|
property TitleStyle;
|
|
{@@ inherited from ancestors}
|
|
property UseXORFeatures;
|
|
{@@ inherited from ancestors}
|
|
property Visible;
|
|
{@@ inherited from ancestors}
|
|
property VisibleColCount;
|
|
{@@ inherited from ancestors}
|
|
property VisibleRowCount;
|
|
|
|
{@@ inherited from ancestors}
|
|
property OnBeforeSelection;
|
|
{@@ inherited from ancestors}
|
|
property OnChangeBounds;
|
|
{@@ inherited from ancestors}
|
|
property OnClick;
|
|
{@@ inherited from TCustomWorksheetGrid}
|
|
property OnClickHyperlink;
|
|
{@@ inherited from ancestors}
|
|
property OnColRowDeleted;
|
|
{@@ inherited from ancestors}
|
|
property OnColRowExchanged;
|
|
{@@ inherited from ancestors}
|
|
property OnColRowInserted;
|
|
{@@ inherited from ancestors}
|
|
property OnColRowMoved;
|
|
{@@ inherited from TCustomWorksheetGrid}
|
|
property OnGetColHeaderText;
|
|
{@@ inherited from TCustomWorksheetGrid}
|
|
property OnGetRowHeaderText;
|
|
(*
|
|
{@@ inherited from ancestors}
|
|
property OnCompareCells; // apply userdefined sorting to worksheet directly!
|
|
*)
|
|
{@@ inherited from ancestors}
|
|
property OnDragDrop;
|
|
{@@ inherited from ancestors}
|
|
property OnDragOver;
|
|
{@@ inherited from ancestors}
|
|
property OnDblClick;
|
|
{@@ inherited from ancestors}
|
|
property OnDrawCell;
|
|
{@@ inherited from ancestors}
|
|
property OnEditButtonClick;
|
|
{@@ inherited from ancestors}
|
|
property OnEditingDone;
|
|
{@@ inherited from ancestors}
|
|
property OnEndDock;
|
|
{@@ inherited from ancestors}
|
|
property OnEndDrag;
|
|
{@@ inherited from ancestors}
|
|
property OnEnter;
|
|
{@@ inherited from ancestors}
|
|
property OnExit;
|
|
{@@ inherited from ancestors}
|
|
property OnGetEditMask;
|
|
{@@ inherited from ancestors}
|
|
property OnGetEditText;
|
|
{@@ inherited from ancestors}
|
|
property OnHeaderClick;
|
|
{@@ inherited from ancestors}
|
|
property OnHeaderSized;
|
|
{@@ inherited from ancestors}
|
|
property OnHeaderSizing;
|
|
{@@ inherited from ancestors}
|
|
property OnKeyDown;
|
|
{@@ inherited from ancestors}
|
|
property OnKeyPress;
|
|
{@@ inherited from ancestors}
|
|
property OnKeyUp;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseDown;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseMove;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseUp;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseWheel;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseWheelDown;
|
|
{@@ inherited from ancestors}
|
|
property OnMouseWheelUp;
|
|
{@@ inherited from ancestors}
|
|
property OnPickListSelect;
|
|
{@@ inherited from ancestors}
|
|
property OnPrepareCanvas;
|
|
{@@ inherited from ancestors}
|
|
property OnResize;
|
|
{@@ inherited from ancestors}
|
|
property OnSelectEditor;
|
|
{@@ inherited from ancestors}
|
|
property OnSelection;
|
|
{@@ inherited from ancestors}
|
|
property OnSelectCell;
|
|
{@@ inherited from ancestors}
|
|
property OnSetEditText;
|
|
{@@ inherited from ancestors}
|
|
property OnShowHint;
|
|
{@@ inherited from ancestors}
|
|
property OnStartDock;
|
|
{@@ inherited from ancestors}
|
|
property OnStartDrag;
|
|
{@@ inherited from ancestors}
|
|
property OnTopLeftChanged;
|
|
{@@ inherited from ancestors}
|
|
property OnUTF8KeyPress;
|
|
{@@ inherited from ancestors}
|
|
property OnValidateEntry;
|
|
{@@ inherited from ancestors}
|
|
property OnContextPopup;
|
|
end;
|
|
|
|
var
|
|
{@@ Default number of columns prepared for a new empty worksheet }
|
|
DEFAULT_COL_COUNT: Integer = 26;
|
|
|
|
{@@ Default number of rows prepared for a new empty worksheet }
|
|
DEFAULT_ROW_COUNT: Integer = 100;
|
|
|
|
{@@ Tolerance for mouse for hitting cell border }
|
|
CELL_BORDER_DELTA: Integer = 4;
|
|
|
|
{@@ Cursor for copy operation during drag and drop }
|
|
crDragCopy: Integer;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
Types, LazUTF8, Math, StrUtils,
|
|
fpCanvas, {%H-}fpsPatches, fpsStrings, fpsUtils, fpsVisualUtils, fpsHTMLUtils,
|
|
fpsImages, fpsNumFormat, fpsExprParser;
|
|
|
|
const
|
|
{@@ Interval how long the mouse buttons has to be held down on a
|
|
hyperlink cell until the associated hyperlink is executed. }
|
|
HYPERLINK_TIMER_INTERVAL = 500;
|
|
|
|
{@@ Interval how long the mouse must stay on the border of the selected
|
|
cell for the drag-cursor to appear. }
|
|
DRAG_TIMER_INTERVAL = 200;
|
|
|
|
const
|
|
// Constants for AGridPart parameter
|
|
DRAW_NON_FROZEN = 0;
|
|
DRAW_FROZEN_ROWS = 1;
|
|
DRAW_FROZEN_COLS = 2;
|
|
DRAW_FROZEN_CORNER = 3;
|
|
|
|
var
|
|
{@@ Auxiliary bitmap containing the previously used non-trivial fill pattern }
|
|
FillPatternBitmap: TBitmap = nil;
|
|
FillPatternStyle: TsFillStyle;
|
|
FillPatternFgColor: TColor;
|
|
FillPatternBgColor: TColor;
|
|
|
|
DragBorderBitmap: TBitmap = nil;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Helper procedure which creates bitmaps used for fill patterns in cell
|
|
backgrounds.
|
|
|
|
The parameters are buffered in FillPatternXXXX variables to avoid unnecessary
|
|
creation of the same bitmaps again and again.
|
|
-------------------------------------------------------------------------------}
|
|
procedure CreateFillPattern(var ABitmap: TBitmap; AStyle: TsFillStyle;
|
|
AFgColor, ABgColor: TColor);
|
|
|
|
procedure SolidFill(AColor: TColor);
|
|
begin
|
|
ABitmap.Canvas.Brush.Color := AColor;
|
|
ABitmap.Canvas.FillRect(0, 0, ABitmap.Width, ABitmap.Height);
|
|
end;
|
|
|
|
var
|
|
x,y: Integer;
|
|
begin
|
|
if (FillPatternStyle = AStyle) and (FillPatternBgColor = ABgColor) and
|
|
(FillPatternFgColor = AFgColor) and (ABitmap <> nil)
|
|
then
|
|
exit;
|
|
|
|
FreeAndNil(ABitmap);
|
|
ABitmap := TBitmap.Create;
|
|
with ABitmap do begin
|
|
if AStyle = fsGray6 then SetSize(8, 4) else SetSize(4, 4);
|
|
case AStyle of
|
|
fsNoFill:
|
|
SolidFill(ABgColor);
|
|
fsSolidFill:
|
|
SolidFill(AFgColor);
|
|
fsGray75:
|
|
begin
|
|
SolidFill(AFgColor);
|
|
Canvas.Pixels[0, 0] := ABgColor;
|
|
Canvas.Pixels[2, 1] := ABgColor;
|
|
Canvas.Pixels[0, 2] := ABgColor;
|
|
Canvas.Pixels[2, 3] := ABgColor;
|
|
end;
|
|
fsGray50:
|
|
begin
|
|
SolidFill(AFgColor);
|
|
for y := 0 to 3 do for
|
|
x := 0 to 3 do
|
|
if odd(x+y) then Canvas.Pixels[x,y] := ABgColor;
|
|
end;
|
|
fsGray25:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
Canvas.Pixels[0, 0] := AFgColor;
|
|
Canvas.Pixels[2, 1] := AFgColor;
|
|
Canvas.Pixels[0, 2] := AFgColor;
|
|
Canvas.Pixels[2, 3] := AFgColor;
|
|
end;
|
|
fsGray12:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
Canvas.Pixels[0, 0] := AFgColor;
|
|
Canvas.Pixels[2, 2] := AFgColor;
|
|
end;
|
|
fsGray6:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
Canvas.Pixels[0, 0] := AFgColor;
|
|
Canvas.Pixels[4, 2] := AFgColor;
|
|
end;
|
|
fsStripeHor:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for y := 0 to 1 do
|
|
for x := 0 to 3 do
|
|
Canvas.Pixels[x,y] := AFgColor;
|
|
end;
|
|
fsStripeVert:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for y := 0 to 3 do
|
|
for x := 0 to 1 do
|
|
Canvas.Pixels[x,y] := AFgColor;
|
|
end;
|
|
fsStripeDiagUp:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for y := 0 to 3 do
|
|
for x := 0 to 1 do
|
|
Canvas.Pixels[(x+y) mod 4, 3-y] := AFgColor;
|
|
end;
|
|
fsStripeDiagDown:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for y := 0 to 3 do
|
|
for x := 0 to 1 do
|
|
Canvas.Pixels[(x+y) mod 4, y] := AFgColor;
|
|
end;
|
|
fsThinStripeHor:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for x := 0 to 3 do Canvas.Pixels[x, 0] := AFgColor;
|
|
end;
|
|
fsThinStripeVert:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for y := 0 to 3 do Canvas.Pixels[0, y] := AFgColor;
|
|
end;
|
|
fsThinStripeDiagUp:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for x := 0 to 3 do Canvas.Pixels[3-x, x] := AFgColor;
|
|
end;
|
|
fsThinStripeDiagDown, fsThinHatchDiag:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for x := 0 to 3 do Canvas.Pixels[x, x] := AFgColor;
|
|
if AStyle = fsThinHatchDiag then begin
|
|
Canvas.Pixels[0, 2] := AFgColor;
|
|
Canvas.Pixels[2, 0] := AFgColor;
|
|
end;
|
|
end;
|
|
fsHatchDiag:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for x := 0 to 1 do
|
|
for y := 0 to 1 do begin
|
|
Canvas.Pixels[x,y] := AFgColor;
|
|
Canvas.Pixels[x+2, y+2] := AFgColor;
|
|
end;
|
|
end;
|
|
fsThickHatchDiag:
|
|
begin
|
|
SolidFill(AFgColor);
|
|
for x := 2 to 3 do Canvas.Pixels[x, 0] := ABgColor;
|
|
for x := 0 to 1 do Canvas.Pixels[x, 2] := ABgColor;
|
|
end;
|
|
fsThinHatchHor:
|
|
begin
|
|
SolidFill(ABgColor);
|
|
for x := 0 to 3 do begin
|
|
Canvas.Pixels[x, 0] := AFgColor;
|
|
Canvas.Pixels[0, x] := AFgColor;
|
|
end;
|
|
end;
|
|
end; // case
|
|
end;
|
|
|
|
FillPatternStyle := AStyle;
|
|
FillPatternBgColor := ABgColor;
|
|
FillPatternFgColor := AFgColor;
|
|
end;
|
|
|
|
(*
|
|
{@@ ----------------------------------------------------------------------------
|
|
Helper procedure which draws a densely dotted horizontal line. In Excel
|
|
this is called a "hair line".
|
|
|
|
@param x1, x2 x coordinates of the end points of the line
|
|
@param y y coordinate of the horizontal line
|
|
-------------------------------------------------------------------------------}
|
|
procedure DrawHairLineHor(ACanvas: TCanvas; x1, x2, y: Integer);
|
|
var
|
|
clr: TColor;
|
|
x: Integer;
|
|
begin
|
|
if odd(x1) then inc(x1);
|
|
x := x1;
|
|
clr := ACanvas.Pen.Color;
|
|
while (x <= x2) do begin
|
|
ACanvas.Pixels[x, y] := clr;
|
|
inc(x, 2);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Helper procedure which draws a densely dotted vertical line. In Excel
|
|
this is called a "hair line".
|
|
|
|
@param x x coordinate of the vertical line
|
|
@param y1, y2 y coordinates of the end points of the line
|
|
-------------------------------------------------------------------------------}
|
|
procedure DrawHairLineVert(ACanvas: TCanvas; x, y1, y2: Integer);
|
|
var
|
|
clr: TColor;
|
|
y: Integer;
|
|
begin
|
|
if odd(y1) then inc(y1);
|
|
y := y1;
|
|
clr := ACanvas.Pen.Color;
|
|
while (y <= y2) do begin
|
|
ACanvas.Pixels[x, y] := clr;
|
|
inc(y, 2);
|
|
end;
|
|
end; *)
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Calculates a background color for selected cells. The procedures takes the
|
|
original background color and dims or brightens it by adding the value ADelta
|
|
to the RGB components.
|
|
|
|
@param c Color to be modified
|
|
@param ADelta Value to be added to the RGB components of the inpur color
|
|
@returns Modified color.
|
|
-------------------------------------------------------------------------------}
|
|
function CalcSelectionColor(c: TColor; ADelta: Byte) : TColor;
|
|
type
|
|
TRGBA = record R,G,B,A: Byte end;
|
|
begin
|
|
c := ColorToRGB(c);
|
|
TRGBA(Result).A := 0;
|
|
if TRGBA(c).R < 128
|
|
then TRGBA(Result).R := TRGBA(c).R + ADelta
|
|
else TRGBA(Result).R := TRGBA(c).R - ADelta;
|
|
if TRGBA(c).G < 128
|
|
then TRGBA(Result).G := TRGBA(c).G + ADelta
|
|
else TRGBA(Result).G := TRGBA(c).G - ADelta;
|
|
if TRGBA(c).B < 128
|
|
then TRGBA(Result).B := TRGBA(c).B + ADelta
|
|
else TRGBA(Result).B := TRGBA(c).B - ADelta;
|
|
end;
|
|
|
|
function VerticalIntersect(const ARect, BRect: TRect): Boolean;
|
|
begin
|
|
Result := (ARect.Top < BRect.Bottom) and (ARect.Bottom > BRect.Top);
|
|
end;
|
|
|
|
function HorizontalIntersect(const ARect, BRect: TRect): Boolean;
|
|
begin
|
|
Result := (ARect.Left < BRect.Right) and (ARect.Right > BRect.Left);
|
|
end;
|
|
|
|
|
|
{*******************************************************************************
|
|
* TsSelPen *
|
|
*******************************************************************************}
|
|
constructor TsSelPen.Create;
|
|
begin
|
|
inherited;
|
|
Width := 3;
|
|
JoinStyle := pjsMiter;
|
|
end;
|
|
|
|
|
|
{*******************************************************************************
|
|
* TsMultiLineStringCellEditor *
|
|
*******************************************************************************}
|
|
|
|
constructor TsMultilineStringCellEditor.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
AutoSize := false;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.Change;
|
|
begin
|
|
inherited Change;
|
|
if (FGrid <> nil) and Visible then
|
|
FGrid.EditorTextChanged(FCol, FRow, Text);
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.EditingDone;
|
|
begin
|
|
inherited EditingDone;
|
|
if FGrid <> nil then
|
|
FGrid.EditingDone;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.KeyDown(var AKey: Word; AShift: TShiftState);
|
|
|
|
function AllSelected: boolean;
|
|
begin
|
|
Result := (SelLength > 0) and (SelLength = UTF8Length(Text));
|
|
end;
|
|
|
|
function AtStart: Boolean;
|
|
begin
|
|
Result:= (SelStart = 0);
|
|
end;
|
|
|
|
function AtEnd: Boolean;
|
|
begin
|
|
Result := ((SelStart + 1) > UTF8Length(Text)) or AllSelected;
|
|
end;
|
|
|
|
procedure DoEditorKeyDown;
|
|
begin
|
|
if FGrid <> nil then
|
|
FGrid.EditorkeyDown(Self, AKey, AShift);
|
|
end;
|
|
|
|
procedure DoGridKeyDown;
|
|
begin
|
|
if FGrid <> nil then
|
|
FGrid.KeyDown(AKey, AShift);
|
|
end;
|
|
|
|
function GetFastEntry: boolean;
|
|
begin
|
|
if FGrid <> nil then
|
|
Result := FGrid.FastEditing
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
procedure CheckEditingKey;
|
|
begin
|
|
if (FGrid = nil) or FGrid.EditorIsReadOnly then
|
|
AKey := 0;
|
|
end;
|
|
|
|
var
|
|
IntSel: boolean;
|
|
begin
|
|
inherited KeyDown(AKey, AShift);
|
|
case AKey of
|
|
VK_F2:
|
|
if AllSelected then begin
|
|
SelLength := 0;
|
|
SelStart := Length(Text);
|
|
end;
|
|
VK_DELETE, VK_BACK:
|
|
CheckEditingKey;
|
|
VK_UP, VK_DOWN:
|
|
doGridKeyDown;
|
|
VK_LEFT, VK_RIGHT:
|
|
if GetFastEntry then begin
|
|
IntSel:=
|
|
((AKey = VK_LEFT) and not AtStart) or
|
|
((AKey = VK_RIGHT) and not AtEnd);
|
|
if not IntSel then begin
|
|
doGridKeyDown;
|
|
end;
|
|
end;
|
|
VK_END, VK_HOME:
|
|
;
|
|
VK_ESCAPE:
|
|
begin
|
|
doGridKeyDown;
|
|
if AKey <> 0 then begin
|
|
Text := FGrid.FOldEditorText;
|
|
FGrid.EditorHide;
|
|
end;
|
|
end;
|
|
|
|
else
|
|
DoEditorKeyDown;
|
|
end;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_GetGrid(var AMsg: TGridMessage);
|
|
begin
|
|
AMsg.Grid := FGrid;
|
|
AMsg.Options:= EO_IMPLEMENTED;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_GetValue(var AMsg: TGridMessage);
|
|
begin
|
|
AMsg.Col := FCol;
|
|
AMsg.Row := FRow;
|
|
AMsg.Value := Text;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_SelectAll(var AMsg: TGridMessage);
|
|
begin
|
|
Unused(AMsg);
|
|
SelectAll;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_SetGrid(var AMsg: TGridMessage);
|
|
begin
|
|
FGrid := AMsg.Grid as TsCustomWorksheetGrid;
|
|
AMsg.Options := EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_SetPos(var AMsg: TGridMessage);
|
|
begin
|
|
FCol := AMsg.Col;
|
|
FRow := AMsg.Row;
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.msg_SetValue(var AMsg: TGridMessage);
|
|
begin
|
|
Text := AMsg.Value;
|
|
SelStart := UTF8Length(Text);
|
|
end;
|
|
|
|
procedure TsMultilineStringCellEditor.WndProc(var AMsg: TLMessage);
|
|
begin
|
|
if FGrid <> nil then
|
|
case AMsg.Msg of
|
|
LM_CLEAR, LM_CUT, LM_PASTE:
|
|
if FGrid.EditorIsReadOnly then exit;
|
|
end;
|
|
inherited WndProc(AMsg);
|
|
end;
|
|
|
|
|
|
{*******************************************************************************
|
|
* TsCustomWorksheetGrid *
|
|
*******************************************************************************}
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Constructor of the grid. Activates the display of column and row headers
|
|
and creates an internal "CellFont". Creates a pre-defined number of empty rows
|
|
and columns.
|
|
|
|
@param AOwner Owner of the grid
|
|
-------------------------------------------------------------------------------}
|
|
constructor TsCustomWorksheetGrid.Create(AOwner: TComponent);
|
|
begin
|
|
inc(FRowHeightLock);
|
|
|
|
FInternalWorkbookSource := TsWorkbookSource.Create(self);
|
|
FInternalWorkbookSource.Name := 'internal';
|
|
|
|
inc(FActiveCellLock);
|
|
inherited Create(AOwner);
|
|
dec(FActiveCellLock);
|
|
|
|
AutoAdvance := aaDown;
|
|
ExtendedSelect := true;
|
|
FHeaderCount := 1;
|
|
FAutoDetectCellType := true;
|
|
ColCount := DEFAULT_COL_COUNT + FHeaderCount;
|
|
RowCount := DEFAULT_ROW_COUNT + FHeaderCount;
|
|
|
|
FDefRowHeight100 := inherited GetDefaultRowHeight;
|
|
FDefColWidth100 := inherited DefaultColWidth;
|
|
FFixedColWidth := 0;
|
|
|
|
FCellFont := TFont.Create;
|
|
FSelPen := TsSelPen.Create;
|
|
FSelPen.Style := psSolid;
|
|
FSelPen.Color := clBlack;
|
|
FSelPen.JoinStyle := pjsMiter;
|
|
FSelPen.OnChange := @GenericPenChangeHandler;
|
|
|
|
FFrozenBorderPen := TPen.Create;
|
|
FFrozenBorderPen.Style := psSolid;
|
|
FFrozenBorderPen.Color := clBlack;
|
|
FFrozenBorderPen.OnChange := @GenericPenChangeHandler;
|
|
|
|
FPageBreakPen := TPen.Create;
|
|
FPageBreakPen.Style := psDash;
|
|
FPageBreakPen.Color := clBlue;
|
|
FPageBreakPen.OnChange := @GenericPenChangeHandler;
|
|
FShowPageBreaks := true;
|
|
|
|
FAutoExpand := [aeData, aeNavigation, aeDefault];
|
|
FHyperlinkTimer := TTimer.Create(self);
|
|
FHyperlinkTimer.Interval := HYPERLINK_TIMER_INTERVAL;
|
|
FHyperlinkTimer.OnTimer := @HyperlinkTimerElapsed;
|
|
|
|
FDragTimer := TTimer.Create(self);
|
|
FDragTimer.Interval := DRAG_TIMER_INTERVAL;
|
|
FDragTimer.OnTimer := @DragTimerElapsed;
|
|
FAllowDragAndDrop := true;
|
|
|
|
SetWorkbookSource(FInternalWorkbookSource);
|
|
{$IFNDEF FPS_NO_GRID_MULTISELECT}
|
|
RangeSelectMode := rsmMulti;
|
|
{$ENDIF}
|
|
|
|
dec(FRowHeightLock);
|
|
UpdateRowHeights;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Destructor of the grid: Destroys the workbook and the internal CellFont.
|
|
-------------------------------------------------------------------------------}
|
|
destructor TsCustomWorksheetGrid.Destroy;
|
|
begin
|
|
SetWorkbookSource(nil);
|
|
if FInternalWorkbookSource <> nil then
|
|
FInternalWorkbookSource.RemoveListener(self); // will be destroyed automatically
|
|
FreeAndNil(FCellFont);
|
|
FreeAndNil(FSelPen);
|
|
FreeAndNil(FPageBreakPen);
|
|
FreeAndNil(FFrozenBorderPen);
|
|
FreeAndNil(FMultiLineStringEditor);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.AdaptToZoomFactor;
|
|
var
|
|
c, r: Integer;
|
|
begin
|
|
inc(FZoomLock);
|
|
DefaultRowHeight := round(GetZoomfactor * FDefRowHeight100);
|
|
DefaultColWidth := round(GetZoomFactor * FDefColWidth100);
|
|
UpdateColWidths;
|
|
UpdateRowHeights;
|
|
dec(FZoomLock);
|
|
|
|
// Bring active cell back into the viewport: There is a ScrollToCell but
|
|
// this method is private. It is called by SetCol/SetRow, though.
|
|
if ((Col < GCache.Visiblegrid.Left) or (Col >= GCache.VisibleGrid.Right)) and
|
|
(GCache.VisibleGrid.Left <> GCache.VisibleGrid.Right) then
|
|
begin
|
|
c := Col;
|
|
Col := c-1; // "Col" must change in order to call ScrtollToCell
|
|
Col := c;
|
|
end;
|
|
if ((Row < GCache.VisibleGrid.Top) or (Row >= GCache.VisibleGrid.Bottom)) and
|
|
(GCache.VisibleGrid.Top <> GCache.VisibleGrid.Bottom) then
|
|
begin
|
|
r := Row;
|
|
Row := r-1;
|
|
Row := r;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.AutoColWidth(ACol: Integer);
|
|
begin
|
|
AutoAdjustColumn(ACol);
|
|
end;
|
|
|
|
procedure TscustomWorksheetGrid.AutoRowHeight(ARow: Integer);
|
|
begin
|
|
AutoAdjustRow(ARow);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Is called when goDblClickAutoSize is in the grid's options and a double click
|
|
has occured at the border of a column header. Sets optimum column with.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.AutoAdjustColumn(ACol: Integer);
|
|
var
|
|
gRow: Integer; // row in grid coordinates
|
|
w, maxw: Integer;
|
|
txt: String;
|
|
cell: PCell;
|
|
RTL: Boolean;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
RTL := IsRightToLeft;
|
|
maxw := -1;
|
|
for cell in Worksheet.Cells.GetColEnumerator(GetWorkSheetCol(ACol)) do
|
|
begin
|
|
// Merged cells are not considered for calculating AutoColWidth -- see Excel.
|
|
if Worksheet.IsMerged(cell) then
|
|
continue;
|
|
gRow := GetGridRow(cell^.Row);
|
|
txt := GetCellText(ACol, gRow, false);
|
|
if txt = '' then
|
|
Continue;
|
|
case Worksheet.ReadBiDiMode(cell) of
|
|
bdRTL: RTL := true;
|
|
bdLTR: RTL := false;
|
|
end;
|
|
w := RichTextWidth(Canvas, Workbook, Rect(0, 0, MaxInt, MaxInt),
|
|
txt, cell^.RichTextParams, Worksheet.ReadCellFontIndex(cell),
|
|
Worksheet.ReadTextRotation(cell), false, RTL, ZoomFactor);
|
|
if w > maxw then maxw := w;
|
|
end;
|
|
if maxw > -1 then
|
|
maxw := maxw + 2*constCellPadding
|
|
else
|
|
maxw := DefaultColWidth;
|
|
ColWidths[ACol] := maxW;
|
|
HeaderSized(true, ACol);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Is called when goDblClickAutoSize is in the grid's options and a double click
|
|
has occured at the border of a row header. Sets optimum row height.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.AutoAdjustRow(ARow: Integer);
|
|
begin
|
|
inc(FZoomLock);
|
|
if Worksheet <> nil then
|
|
RowHeights[ARow] := CalcAutoRowHeight(ARow)
|
|
else
|
|
RowHeights[ARow] := DefaultRowHeight;
|
|
HeaderSized(false, ARow);
|
|
dec(FZoomLock);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Automatically expands the ColCount such that the specified column fits in
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.AutoExpandToCol(ACol: Integer;
|
|
AMode: TsAutoExpandMode);
|
|
begin
|
|
if ACol >= ColCount then
|
|
begin
|
|
if (AMode in FAutoExpand) then
|
|
ColCount := ACol + 1
|
|
else
|
|
raise Exception.CreateFmt(rsOperationExceedsColCount, [ACol, ColCount]);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Automatically expands the RowCount such that the specified column fits in
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.AutoExpandToRow(ARow: Integer;
|
|
AMode: TsAutoExpandMode);
|
|
begin
|
|
if ARow >= RowCount then
|
|
begin
|
|
if (AMode in FAutoExpand) then
|
|
RowCount := ARow + 1
|
|
else
|
|
raise Exception.CreateFmt(rsOperationExceedsRowCount, [ARow, RowCount]);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
The BeginUpdate/EndUpdate pair suppresses unnecessary painting of the grid.
|
|
|
|
Call BeginUpdate to stop refreshing the grid, and call EndUpdate to release
|
|
the lock and to repaint the grid again.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.BeginUpdate;
|
|
begin
|
|
inc(FLockCount);
|
|
Workbook.DisableNotifications;
|
|
Workbook.LockFormulas;
|
|
inherited BeginUpdate;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts the column width, given in units used by the worksheet, to pixels.
|
|
|
|
@param AWidth Width of a column in units used by the worksheet
|
|
@return Column width in pixels.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcColWidthFromSheet(AWidth: Single): Integer;
|
|
var
|
|
w_pts: Double;
|
|
begin
|
|
w_pts := Workbook.ConvertUnits(AWidth, Workbook.Units, suPoints);
|
|
Result := PtsToPx(w_pts, Screen.PixelsPerInch);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Finds the maximum cell height per row and uses this to define the RowHeights[].
|
|
Returns DefaultRowHeight if the row does not contain any cells, or if the
|
|
worksheet does not have a TRow record for this particular row.
|
|
ARow is a grid row index.
|
|
|
|
@param ARow Index of the row, in grid units
|
|
@return Row height
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcAutoRowHeight(ARow: Integer): Integer;
|
|
var
|
|
c: Integer;
|
|
h: Integer;
|
|
begin
|
|
h := 0;
|
|
for c := FHeaderCount to ColCount-1 do
|
|
h := Max(h, GetCellHeight(c, ARow)); // Zoom factor is applied to font size
|
|
if h = 0 then
|
|
Result := DefaultRowHeight // Zoom factor applied by getter function
|
|
else
|
|
Result := h;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts the row height (from a worksheet row record), given in units used by
|
|
the sheet, to pixels as needed by the grid
|
|
|
|
@param AHeight Row height expressed in units used by the worksheet.
|
|
@returns Row height in pixels.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcRowHeightFromSheet(AHeight: Single): Integer;
|
|
var
|
|
h_pts: Single;
|
|
begin
|
|
h_pts := Workbook.ConvertUnits(abs(AHeight), Workbook.Units, suPoints);;
|
|
Result := PtsToPx(h_pts, Screen.PixelsPerInch); // + 4;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.CalcRowHeightToSheet(AHeight: Integer): Single;
|
|
var
|
|
h_pts: Single;
|
|
begin
|
|
h_pts := PxToPts(AHeight, Screen.PixelsPerInch);
|
|
Result := Workbook.ConvertUnits(h_pts, suPoints, Workbook.Units);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Calculates the top-left corner (in pixels) of the area which can be
|
|
scrolled. Is bordered by the fixed header cells and the frozen columns and
|
|
rows.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcTopLeft(AHeaderOnly: Boolean): TPoint;
|
|
var
|
|
fc, fr: Integer;
|
|
tmp: Integer = 0;
|
|
begin
|
|
Result := Point(0, 0); // to silence the compiler...
|
|
|
|
fc := IfThen(AHeaderOnly, FHeaderCount, FHeaderCount + FFrozenCols);
|
|
if IsRightToLeft then
|
|
begin
|
|
if fc > 0 then
|
|
ColRowToOffset(true, true, fc-1, Result.X, tmp)
|
|
else
|
|
Result.X := ClientRect.Right;
|
|
end else
|
|
begin
|
|
if fc > 0 then
|
|
ColRowToOffset(true, true, fc-1, tmp, Result.X)
|
|
else
|
|
Result.X := ClientRect.Left;
|
|
end;
|
|
|
|
fr := IfThen(AHeaderOnly, FHeaderCount, FHeaderCount + FFrozenRows);
|
|
if fr > 0 then
|
|
ColRowToOffset(false, true, fr-1, tmp, Result.Y)
|
|
else
|
|
Result.Y := ClientRect.Top;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts the column height given in screen pixels to the units used by the
|
|
worksheet.
|
|
|
|
@param AValue Column width in pixels
|
|
@returns Column width expressed in units defined by the workbook.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcWorksheetColWidth(AValue: Integer): Single;
|
|
var
|
|
w_pts: Double;
|
|
begin
|
|
Result := 0;
|
|
if Worksheet <> nil then
|
|
begin
|
|
// The grid's column width is in "pixels", the worksheet's column width
|
|
// has the units defined by the workbook.
|
|
w_pts := PxToPts(AValue/ZoomFactor, Screen.PixelsPerInch);
|
|
Result := Workbook.ConvertUnits(w_pts, suPoints, Workbook.Units);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts the row height given in screen pixels to the units used by the
|
|
worksheet.
|
|
|
|
@param AValue Row height in pixels
|
|
@returns Row height expressed in units defined by the workbook.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CalcWorksheetRowHeight(AValue: Integer): Single;
|
|
var
|
|
h_pts: Double;
|
|
begin
|
|
Result := 0;
|
|
if Worksheet <> nil then
|
|
begin
|
|
// The grid's row heights are in "pixels", the worksheet's row height
|
|
// has the units defined by the workbook.
|
|
h_pts := PxToPts(AValue/ZoomFactor, Screen.PixelsPerInch);
|
|
Result := Workbook.ConvertUnits(h_pts, suPoints, Workbook.Units);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.CanEditShow: Boolean;
|
|
begin
|
|
Result := inherited and (not FReadOnly);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Looks for overflowing cells: if the text of the given cell is longer than
|
|
the cell width the function calculates the column indexes and the rectangle
|
|
to show the complete text.
|
|
|
|
Ony for non-wordwrapped label cells and for horizontal orientation.
|
|
Function returns false if text overflow needs not to be considered.
|
|
|
|
@param ACol,ARow Column and row indexes (in grid coordinates) of the cell to be drawn
|
|
@param AState GridDrawState of the cell (normal, fixed, selected etc)
|
|
@param ACol1,ACol2 (output) Index of the first and last column covered by the overflowing text
|
|
@param ARect (output) Pixel rectangle enclosing the cell and its neighbors affected
|
|
@returns @TRUE if text overflow into neighbor cells is to be considered, @FALSE if not.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.CellOverflow(ACol, ARow: Integer;
|
|
AState: TGridDrawState; out ACol1, ACol2: Integer; var ARect: TRect): Boolean;
|
|
var
|
|
txt: String;
|
|
len: Integer;
|
|
cell: PCell;
|
|
txtalign: TsHorAlignment;
|
|
r: Cardinal;
|
|
w, w0: Integer;
|
|
fmt: PsCellFormat;
|
|
fc: Integer;
|
|
idx: Integer;
|
|
begin
|
|
Result := false;
|
|
cell := FDrawingCell;
|
|
|
|
// Nothing to do in these cases (like in Excel):
|
|
if (cell = nil) or not (cell^.ContentType in [cctUTF8String]) then // ... non-label cells
|
|
exit;
|
|
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(cell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
if (uffWordWrap in fmt^.UsedFormattingFields) then // ... word-wrap
|
|
exit;
|
|
if (uffTextRotation in fmt^.UsedFormattingFields) and // ... vertical text
|
|
(fmt^.TextRotation <> trHorizontal)
|
|
then
|
|
exit;
|
|
|
|
fc := FHeaderCount + FFrozenCols;
|
|
|
|
txt := cell^.UTF8Stringvalue;
|
|
if (uffHorAlign in fmt^.UsedFormattingFields) then
|
|
txtalign := fmt^.HorAlignment
|
|
else
|
|
txtalign := haDefault;
|
|
PrepareCanvas(ACol, ARow, AState);
|
|
len := Canvas.TextWidth(txt) + 2*constCellPadding;
|
|
ACol1 := ACol;
|
|
ACol2 := ACol;
|
|
r := GetWorksheetRow(ARow);
|
|
case txtalign of
|
|
haLeft,
|
|
haJustified, haDistributed, haFilled, // to do: get a separate option for these
|
|
haDefault:
|
|
// overflow to the right
|
|
while (len > ARect.Right - ARect.Left) and (ACol2 < ColCount-1) do
|
|
begin
|
|
result := true;
|
|
inc(ACol2);
|
|
cell := Worksheet.FindCell(r, GetWorksheetCol(ACol2));
|
|
if (cell <> nil) and (cell^.ContentType <> cctEmpty) then
|
|
begin
|
|
dec(ACol2);
|
|
break;
|
|
end;
|
|
ARect.Right := ARect.Right + ColWidths[ACol2];
|
|
end;
|
|
haRight:
|
|
// overflow to the left
|
|
while (len > ARect.Right - ARect.Left) and (ACol1 > fc) do
|
|
begin
|
|
result := true;
|
|
dec(ACol1);
|
|
cell := Worksheet.FindCell(r, GetWorksheetCol(ACol1));
|
|
if (cell <> nil) and (cell^.ContentType <> cctEmpty) then
|
|
begin
|
|
inc(ACol1);
|
|
break;
|
|
end;
|
|
ARect.Left := ARect.Left - ColWidths[ACol1];
|
|
end;
|
|
haCenter:
|
|
begin
|
|
len := len div 2;
|
|
w0 := (ARect.Right - ARect.Left) div 2;
|
|
w := w0;
|
|
// right part
|
|
while (len > w) and (ACol2 < ColCount-1) do
|
|
begin
|
|
Result := true;
|
|
inc(ACol2);
|
|
cell := Worksheet.FindCell(r, GetWorksheetCol(ACol2));
|
|
if (cell <> nil) and (cell^.ContentType <> cctEmpty) then
|
|
begin
|
|
dec(ACol2);
|
|
break;
|
|
end;
|
|
ARect.Right := ARect.Right + ColWidths[ACol2];
|
|
inc(w, ColWidths[ACol2]);
|
|
end;
|
|
// left part
|
|
w := w0;
|
|
while (len > w) and (ACol1 > fc) do
|
|
begin
|
|
Result := true;
|
|
dec(ACol1);
|
|
cell := Worksheet.FindCell(r, GetWorksheetCol(ACol1));
|
|
if (cell <> nil) and (cell^.Contenttype <> cctEmpty) then
|
|
begin
|
|
inc(ACol1);
|
|
break;
|
|
end;
|
|
ARect.Left := ARect.left - ColWidths[ACol1];
|
|
inc(w, ColWidths[ACol1]);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.CellRect(ACol1, ARow1, ACol2, ARow2: Integer): TRect;
|
|
var
|
|
cmin, cmax: Integer;
|
|
rmin, rmax: Integer;
|
|
tmp: Integer = 0;
|
|
begin
|
|
Result := Rect(0, 0, 0, 0); // to silence the compiler...
|
|
cmin := Min(ACol1, ACol2);
|
|
cmax := Max(ACol1, ACol2);
|
|
rmin := Min(ARow1, ARow2);
|
|
rmax := Max(ARow1, ARow2);
|
|
if IsRightToLeft then begin
|
|
ColRowToOffset(True, True, cmin, tmp, Result.Right);
|
|
ColRowToOffset(True, True, cmax, Result.Left, tmp);
|
|
end else
|
|
begin
|
|
ColRowToOffset(True, True, cmin, Result.Left, tmp);
|
|
ColRowToOffset(True, True, cmax, tmp, Result.Right);
|
|
end;
|
|
ColRowToOffSet(False, True, rmin, Result.Top, tmp);
|
|
ColRowToOffset(False, True, rmax, tmp, Result.Bottom);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Handler for the event OnChangeCell fired by the worksheet when the contents
|
|
or formatting of a cell have changed.
|
|
As a consequence, the grid may have to update the cell.
|
|
Row/Col coordinates are in worksheet units here!
|
|
|
|
@param ASender Sender of the event OnChangeFont (the worksheet)
|
|
@param ARow Row index of the changed cell, in worksheet units!
|
|
@param ACol Column index of the changed cell, in worksheet units!
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ChangedCellHandler(ASender: TObject; ARow, ACol:Cardinal);
|
|
begin
|
|
Unused(ASender, ARow, ACol);
|
|
if FLockCount = 0 then Invalidate;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Handler for the event OnChangeFont fired by the worksheet when the font has
|
|
changed in a cell.
|
|
As a consequence, the grid may have to update the row height.
|
|
Row/Col coordinates are in worksheet units here!
|
|
|
|
@param ASender Sender of the event OnChangeFont (the worksheet)
|
|
@param ARow Row index of the cell with the changed font, in worksheet units!
|
|
@param ACol Column index of the cell with the changed font, in worksheet units!
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ChangedFontHandler(ASender: TObject;
|
|
ARow, ACol: Cardinal);
|
|
var
|
|
lRow: PRow;
|
|
gr: Integer; // row index in grid units
|
|
begin
|
|
Unused(ASender, ACol);
|
|
if (Worksheet <> nil) then begin
|
|
lRow := Worksheet.FindRow(ARow);
|
|
if lRow = nil then begin
|
|
// There is no row record --> row height changes according to font height
|
|
// Otherwise the row height would be fixed according to the value in the row record.
|
|
gr := GetGridRow(ARow); // convert row index to grid units
|
|
RowHeights[gr] := CalcAutoRowHeight(gr);
|
|
end;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Clears the grid contents
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Clear;
|
|
begin
|
|
if (Worksheet <> nil) then Worksheet.Clear;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts a spreadsheet font to a font used for painting (TCanvas.Font).
|
|
|
|
@param sFont Font as used by fpspreadsheet (input)
|
|
@param AFont Font as used by TCanvas for painting (output)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Convert_sFont_to_Font(sFont: TsFont; AFont: TFont);
|
|
begin
|
|
fpsVisualUtils.Convert_sFont_to_Font(sFont, AFont);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts a font used for painting (TCanvas.Font) to a spreadsheet font.
|
|
|
|
@param AFont Font as used by TCanvas for painting (input)
|
|
@param sFont Font as used by fpspreadsheet (output)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Convert_Font_to_sFont(AFont: TFont;
|
|
sFont: TsFont);
|
|
begin
|
|
fpsVisualUtils.Convert_Font_to_sFont(AFont, sFont);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
This is one of the main painting methods inherited from TsCustomGrid. It is
|
|
overridden here to achieve the feature of "frozen" cells which should be
|
|
painted in the same style as normal cells.
|
|
|
|
Internally, "frozen" cells are "fixed" cells of the grid. Therefore, it is
|
|
not possible to select any cell within the frozen panes - in contrast to the
|
|
standard spreadsheet applications.
|
|
|
|
@param ACol Column index of the cell being drawn
|
|
@param ARow Row index of the cell beging drawn
|
|
@param ARect Rectangle, in grid pixels, covered by the cell
|
|
@param AState Grid drawing state, as defined by TsCustomGrid
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DefaultDrawCell(aCol, aRow: Integer;
|
|
var aRect: TRect; AState: TGridDrawState);
|
|
var
|
|
wasFixed: Boolean;
|
|
begin
|
|
wasFixed := false;
|
|
if (gdFixed in AState) then
|
|
if ShowHeaders then begin
|
|
if ((ARow < FixedRows) and (ARow > 0) and (ACol > 0)) or
|
|
((ACol < FixedCols) and (ACol > 0) and (ARow > 0))
|
|
then
|
|
wasFixed := true;
|
|
end else begin
|
|
if (ARow < FixedRows) or (ACol < FixedCols) then
|
|
wasFixed := true;
|
|
end;
|
|
|
|
if wasFixed then begin
|
|
AState := AState - [gdFixed];
|
|
Canvas.Brush.Color := clWindow;
|
|
DoPrepareCanvas(ACol, ARow, AState);
|
|
end;
|
|
|
|
inherited DefaultDrawCell(ACol, ARow, ARect, AState);
|
|
|
|
if wasFixed then begin
|
|
DrawCellGrid(ACol, ARow, ARect, AState);
|
|
AState := AState + [gdFixed];
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Deletes the column specified.
|
|
|
|
@param AGridCol Grid index of the column to be deleted
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DeleteCol(AGridCol: Integer);
|
|
begin
|
|
if AGridCol < FHeaderCount then
|
|
exit;
|
|
|
|
Worksheet.DeleteCol(GetWorksheetCol(AGridCol));
|
|
UpdateColWidths(AGridCol);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Deletes the row specified.
|
|
|
|
@param AGridRow Grid index of the row to be deleted
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DeleteRow(AGridRow: Integer);
|
|
begin
|
|
if AGridRow < FHeaderCount then
|
|
exit;
|
|
|
|
Worksheet.DeleteRow(GetWorksheetRow(AGridRow));
|
|
|
|
// Update following row heights because their index has changed
|
|
UpdateRowHeights(AGridRow);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.ColRowMoved(IsColumn: Boolean;
|
|
FromIndex,ToIndex: Integer);
|
|
begin
|
|
inherited;
|
|
if IsColumn then
|
|
Worksheet.MoveCol(GetWorksheetCol(FromIndex), GetWorksheetCol(ToIndex));
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.CreateHandle;
|
|
begin
|
|
inherited;
|
|
Setup;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Creates a new empty workbook into which a file will be loaded. Destroys the
|
|
previously used workbook.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.CreateNewWorkbook;
|
|
begin
|
|
GetWorkbookSource.CreateNewWorkbook;
|
|
if FReadFormulas then
|
|
WorkbookSource.Options := WorkbookSource.Options + [boReadFormulas] else
|
|
WorkbookSource.Options := Workbooksource.Options - [boReadFormulas];
|
|
SetAutoCalc(FAutoCalc);
|
|
SetAutoDetectCellType(FAutoDetectCellType);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Is called when a Double-click occurs. Overrides the inherited method to
|
|
react on double click on cell border in row headers to auto-adjust the
|
|
row heights
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DblClick;
|
|
var
|
|
oldHeight: Integer;
|
|
gRow: Integer;
|
|
begin
|
|
SelectActive := False;
|
|
FGridState := gsNormal;
|
|
if (goRowSizing in Options) and (Cursor = crVSplit) and (FHeaderCount > 0) then
|
|
begin
|
|
if (goDblClickAutoSize in Options) then
|
|
begin
|
|
gRow := GCache.MouseCell.y;
|
|
if CellRect(0, gRow).Bottom - GCache.ClickMouse.y > 0 then dec(gRow);
|
|
oldHeight := RowHeights[gRow];
|
|
AutoAdjustRow(gRow);
|
|
if oldHeight <> RowHeights[gRow] then
|
|
Cursor := crDefault; //ChangeCursor;
|
|
end
|
|
end
|
|
else
|
|
inherited DblClick;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DefineProperties(Filer: TFiler);
|
|
begin
|
|
//inherited;
|
|
|
|
// Don't call inherited, this is where the ColWidths/RowHeights are written
|
|
// to the lfm file - we don't need them, we get them from the workbook!
|
|
Unused(Filer);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoCopyToClipboard;
|
|
begin
|
|
WorkbookSource.CopyCellsToClipboard;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoCutToClipboard;
|
|
begin
|
|
// This next comment does not seem to be valid any more: Issue handled by eating key in KeyDown
|
|
// Remove for the moment: If TsCopyActions is available this code would be executed twice (and destroy the clipboard)
|
|
WorkbookSource.CutCellsToClipboard;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoPasteFromClipboard;
|
|
begin
|
|
// This next comment does not seem to be valid any more: Issue handled by eating key in KeyDown
|
|
// Remove for the moment: If TsPasteActions is available this code would be executed twice
|
|
WorkbookSource.PasteCellsFromClipboard(coCopyCell);
|
|
end;
|
|
|
|
{ Make the cell editor the same size as the edited cell, in particular for
|
|
even for merged cells; otherwise the merge base content would be seen during
|
|
editing at several places. }
|
|
procedure TsCustomWorksheetGrid.DoEditorShow;
|
|
var
|
|
r1, c1, r2, c2: Cardinal;
|
|
cell: PCell;
|
|
Rct: TRect;
|
|
delta: Integer;
|
|
begin
|
|
FOldEditorText := GetCellText(Col, Row);
|
|
inherited;
|
|
if Worksheet = nil then
|
|
exit;
|
|
if (Editor is TStringCellEditor) or (Editor is TsMultiLineStringCellEditor) then
|
|
begin
|
|
delta := FSelPen.Width div 2;
|
|
cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
|
|
if Worksheet.IsMerged(cell) then begin
|
|
Worksheet.FindMergedRange(cell, r1,c1,r2,c2);
|
|
Rct := CellRect(GetGridCol(c1), GetGridRow(r1), GetGridCol(c2), GetGridRow(r2));
|
|
end else
|
|
Rct := CellRect(Col, Row);
|
|
InflateRect(Rct, -delta, -delta);
|
|
inc(Rct.Top);
|
|
if not odd(FSelPen.Width) then dec(Rct.Left);
|
|
Editor.Font := CellFont[Col, Row];
|
|
Editor.Font.Height := Round(Font.Height * ZoomFactor);
|
|
Editor.Color := BackgroundColor[Col, Row];
|
|
Editor.SetBounds(Rct.Left, Rct.Top, Rct.Right-Rct.Left-1, Rct.Bottom-Rct.Top-1);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoEnter;
|
|
begin
|
|
if FRefocusing = self then
|
|
FRefocusing := nil;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoExit;
|
|
var
|
|
ed: TWinControl;
|
|
begin
|
|
{ Post a message to myself which indicates it's time to validate the input.
|
|
Pass the grid instance (Self) as the message lParam. }
|
|
if FRefocusing = nil then begin
|
|
ed := EditorByStyle(cbsAuto);
|
|
if ed is TCustomEdit then
|
|
FRefocusingSelStart := TCustomEdit(ed).SelStart;
|
|
PostMessage(Handle, um_ValidateInput, 0, LParam(Self));
|
|
end;
|
|
FGridState := gsNormal;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DoOnResize;
|
|
begin
|
|
if (csDesigning in ComponentState) and (Worksheet = nil) then
|
|
NewWorkbook(ColCount, RowCount);
|
|
inherited;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Adjusts the grid's canvas before painting a given cell. Considers
|
|
background color, horizontal alignment, vertical alignment, etc.
|
|
|
|
@param ACol Column index of the cell being painted
|
|
@param ARow Row index of the cell being painted
|
|
@param AState Grid drawing state -- see TsCustomGrid.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DoPrepareCanvas(ACol, ARow: Integer;
|
|
AState: TGridDrawState);
|
|
var
|
|
ts: TTextStyle;
|
|
lCell: PCell;
|
|
fmt: PsCellFormat;
|
|
r, c: Integer;
|
|
fnt: TsFont;
|
|
isSelected: Boolean;
|
|
fgcolor, bgcolor: TColor;
|
|
// numFmt: TsNumFormatParams;
|
|
begin
|
|
GetSelectedState(AState, isSelected);
|
|
Canvas.Font.Assign(Font);
|
|
//Canvas.Font.Height := Round(ZoomFactor * Canvas.Font.Height);
|
|
Canvas.Brush.Bitmap := nil;
|
|
Canvas.Brush.Color := Color;
|
|
ts := Canvas.TextStyle;
|
|
|
|
if ShowHeaders then
|
|
begin
|
|
// Formatting of row and column headers
|
|
if ARow = 0 then
|
|
begin
|
|
ts.Alignment := taCenter;
|
|
ts.Layout := tlCenter;
|
|
end else
|
|
if ACol = 0 then
|
|
begin
|
|
ts.Alignment := taRightJustify;
|
|
ts.Layout := tlCenter;
|
|
end;
|
|
if ShowHeaders and ((ACol = 0) or (ARow = 0)) then
|
|
Canvas.Brush.Color := FixedColor
|
|
end;
|
|
|
|
if (Worksheet <> nil) and (ARow >= FHeaderCount) and (ACol >= FHeaderCount) then
|
|
begin
|
|
r := ARow - FHeaderCount;
|
|
c := ACol - FHeaderCount;
|
|
|
|
fmt := Worksheet.GetPointerToEffectiveCellFormat(r, c);
|
|
lCell := Worksheet.FindCell(r, c);
|
|
|
|
//if lCell <> nil then
|
|
//begin
|
|
|
|
// fmt := Workbook.GetPointerToCellFormat(lCell^.FormatIndex);
|
|
// numFmt := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
|
|
|
|
// Background color
|
|
if (uffBackground in fmt^.UsedFormattingFields) then
|
|
begin
|
|
if Workbook.FileFormatID = ord(sfExcel2) then
|
|
begin
|
|
CreateFillPattern(FillPatternBitmap, fsGray12, clBlack, Color);
|
|
Canvas.Brush.Style := bsImage;
|
|
Canvas.Brush.Bitmap := FillPatternBitmap;
|
|
end else
|
|
begin
|
|
case fmt^.Background.Style of
|
|
fsNoFill:
|
|
Canvas.Brush.Style := bsClear;
|
|
fsSolidFill:
|
|
begin
|
|
Canvas.Brush.Style := bsSolid;
|
|
Canvas.Brush.Color := fmt^.Background.FgColor and $00FFFFFF;
|
|
end;
|
|
else
|
|
if fmt^.Background.BgColor = scTransparent
|
|
then bgcolor := Color
|
|
else bgcolor := fmt^.Background.BgColor and $00FFFFFF;
|
|
if fmt^.Background.FgColor = scTransparent
|
|
then fgcolor := Color
|
|
else fgcolor := fmt^.Background.FgColor and $00FFFFFF;
|
|
CreateFillPattern(FillPatternBitmap, fmt^.Background.Style, fgColor, bgColor);
|
|
Canvas.Brush.Style := bsImage;
|
|
Canvas.Brush.Bitmap := FillPatternBitmap;
|
|
end;
|
|
end;
|
|
end else
|
|
begin
|
|
Canvas.Brush.Style := bsSolid;
|
|
Canvas.Brush.Color := Color;
|
|
end;
|
|
|
|
// Font
|
|
if (lcell <> nil) and Worksheet.HasHyperlink(lCell) then
|
|
fnt := Workbook.GetHyperlinkFont
|
|
else
|
|
fnt := Workbook.GetDefaultFont;
|
|
if (uffFont in fmt^.UsedFormattingFields) then
|
|
fnt := Workbook.GetFont(fmt^.FontIndex);
|
|
|
|
Convert_sFont_to_Font(fnt, Canvas.Font);
|
|
Canvas.Font.Height := Round(ZoomFactor * Canvas.Font.Height);
|
|
|
|
// Wordwrap, text alignment and text rotation are handled by "DrawTextInCell".
|
|
//end;
|
|
end;
|
|
|
|
if IsSelected then
|
|
Canvas.Brush.Color := CalcSelectionColor(Canvas.Brush.Color, 16);
|
|
|
|
Canvas.TextStyle := ts;
|
|
|
|
inherited DoPrepareCanvas(ACol, ARow, AState);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DragDrop(Source: TObject; X, Y: Integer);
|
|
var
|
|
sel: TsCellRange;
|
|
srccell, destcell: PCell;
|
|
r: LongInt = 0;
|
|
c: LongInt = 0;
|
|
dr, dc: LongInt;
|
|
dragMove: Boolean;
|
|
begin
|
|
Unused(X, Y);
|
|
|
|
inherited;
|
|
|
|
if not ((goEditing in Options)) or (not FAllowDragAndDrop) then
|
|
Exit;
|
|
|
|
// Offset of col/row coordinates from source to destination cell
|
|
MouseToCell(X,Y, c, r);
|
|
dr := r - FDragStartRow;
|
|
dc := c - FDragStartCol;
|
|
|
|
// Copy cells or only move them?
|
|
dragMove := not (ssCtrl in GetKeyShiftState);
|
|
|
|
// Copy cells to destination and delete the source cells if required.
|
|
for sel in Worksheet.GetSelection do
|
|
begin
|
|
for r := sel.Row1 to sel.Row2 do
|
|
for c := sel.Col1 to sel.Col2 do
|
|
begin
|
|
srccell := Worksheet.FindCell(r, c);
|
|
if Worksheet.IsMerged(srccell) then
|
|
srccell := Worksheet.FindMergeBase(srccell);
|
|
if srccell <> nil then begin
|
|
destcell := Worksheet.GetCell(r + dr, c + dc);
|
|
if dragMove then
|
|
Worksheet.MoveCell(srccell, destcell^.Row, destcell^.Col)
|
|
else
|
|
Worksheet.CopyCell(srccell, destcell);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DragOver(ASource: TObject; X, Y: Integer;
|
|
AState: TDragState; var Accept: Boolean);
|
|
var
|
|
destcell: PCell;
|
|
sc, sr: Integer;
|
|
gc: Integer = 0;
|
|
gr: Integer = 0;
|
|
dc, dr: Integer;
|
|
sel: TsCellRange;
|
|
dragMove: Boolean;
|
|
begin
|
|
inherited;
|
|
Unused(AState);
|
|
|
|
if FAllowDragAndDrop and (ASource = self) and (goEditing in Options) then
|
|
begin
|
|
MouseToCell(X,Y, gc, gr);
|
|
|
|
// Don't drop over over the header cells
|
|
if (gc < FHeaderCount) or (gr < FHeaderCount) then
|
|
exit;
|
|
|
|
// Find dragged selection rectangle
|
|
dc := gc - FDragStartCol;
|
|
dr := gr - FDragStartRow;
|
|
FDragSelection := Selection;
|
|
OffsetRect(FDragSelection, dc, dr);
|
|
|
|
// Draw dragged selection rectangle
|
|
if (FOldDragStartRow <> gr) or (FOldDragStartCol <> gc) then
|
|
Invalidate;
|
|
FOldDragStartRow := gr;
|
|
FOldDragStartCol := gc;
|
|
|
|
// Change mouse cursor: Copy or Move
|
|
dragMove := not (ssCtrl in GetKeyShiftState);
|
|
if dragMove then
|
|
DragCursor := crDrag
|
|
else
|
|
DragCursor := crDragCopy;
|
|
|
|
if Worksheet.IsProtected then
|
|
// Allow drop only if no destination cell is locked
|
|
for sel in Worksheet.GetSelection do
|
|
for sr := sel.Row1 to sel.Row2 do
|
|
for sc := sel.Col1 to sel.Col2 do
|
|
begin
|
|
destcell := Worksheet.FindCell(sr + dr, sc + dc);
|
|
if (cpLockCell in Worksheet.ReadCellProtection(destcell)) then
|
|
exit;
|
|
end;
|
|
Accept := true;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DragTimerElapsed(Sender: TObject);
|
|
var
|
|
P: TPoint;
|
|
begin
|
|
FDragTimer.Enabled := false;
|
|
P := ScreenToClient(Mouse.CursorPos);
|
|
if MouseOnCellBorder(P, Selection) then
|
|
Cursor := crSize
|
|
else
|
|
Cursor := crDefault;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
This method is inherited from TsCustomGrid, but is overridden here in order
|
|
to paint the cell borders and the selection rectangle.
|
|
Both features can extend into the neighboring cells and thus would be clipped
|
|
at the cell borders by the standard painting mechanism. At the time when
|
|
DrawAllRows is called, however, clipping at cell borders is no longer active.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawAllRows;
|
|
var
|
|
cliprect: TRect;
|
|
rgn: HRGN;
|
|
//tmp: Integer = 0;
|
|
//fc, fr: Integer;
|
|
TL: TPoint;
|
|
begin
|
|
inherited;
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
FTopLeft := CalcTopLeft(false);
|
|
|
|
if (FrozenRows > 0) or (FrozenCols > 0) then
|
|
DrawFrozenPanes;
|
|
|
|
// Set cliprect for scrollable grid area
|
|
cliprect := ClientRect;
|
|
TL := CalcTopLeft(false);
|
|
if IsRightToLeft then
|
|
cliprect.Right := TL.X + 1
|
|
else
|
|
cliprect.Left := TL.X - 1;
|
|
cliprect.Top := TL.Y;
|
|
|
|
DrawPageBreaks(cliprect);
|
|
|
|
// Paint cell borders, selection rectangle, images and frozen-pane-borders
|
|
// into this clipped area
|
|
rgn := CreateRectRgn(cliprect.Left, cliprect.top, cliprect.Right, cliprect.Bottom);
|
|
try
|
|
SelectClipRgn(Canvas.Handle, Rgn);
|
|
DrawCellBorders;
|
|
DrawSelection;
|
|
DrawDragSelection;
|
|
DrawImages(DRAW_NON_FROZEN);
|
|
// DrawFrozenPaneBorders(clipRect);
|
|
finally
|
|
DeleteObject(rgn);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DrawFrozenPanes;
|
|
var
|
|
cliprect, R: TRect;
|
|
rgn: HRGN;
|
|
tmp: Integer = 0;
|
|
fc, fr: Integer;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
Canvas.SaveHandleState;
|
|
try
|
|
// Avoid painting into header cells.
|
|
R := ClientRect;
|
|
if HeaderCount > 0 then begin
|
|
ColRowToOffset(false, True, 0, tmp, R.Top);
|
|
if IsRightToLeft then
|
|
ColRowToOffset(true, True, 0, R.Right, tmp)
|
|
else
|
|
ColRowToOffset(true, True, 0, tmp, R.Left);
|
|
end;
|
|
|
|
fr := FHeaderCount + FFrozenRows;
|
|
fc := FHeaderCount + FFrozenCols;
|
|
|
|
// Paint cell border in frozen rows
|
|
if fr > 0 then begin
|
|
if IsRightToLeft then
|
|
clipRect := Rect(ClientRect.Left, ClientRect.Top, FTopLeft.X, FTopLeft.Y)
|
|
else
|
|
cliprect := Rect(FTopLeft.X, ClientRect.Top, ClientRect.Right, FTopLeft.Y);
|
|
rgn := CreateRectRgn(cliprect.Left, cliprect.Top, cliprect.Right, cliprect.Bottom);
|
|
try
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
DrawCellBorders(DRAW_FROZEN_ROWS);
|
|
DrawImages(DRAW_FROZEN_ROWS);
|
|
DrawFrozenPaneBorder(cliprect.Left, clipRect.Right, cliprect.Bottom-1, true);
|
|
finally
|
|
DeleteObject(rgn);
|
|
end;
|
|
end;
|
|
|
|
// Paint cell border in frozen columns
|
|
if fc > 0 then begin
|
|
if IsRightToLeft then
|
|
cliprect := Rect(FTopLeft.X, FTopLeft.Y, ClientRect.Right, ClientRect.Bottom)
|
|
else
|
|
cliprect := Rect(ClientRect.Left, FTopLeft.Y, FTopLeft.X, ClientRect.Bottom);
|
|
rgn := CreateRectRgn(cliprect.Left, cliprect.Top, cliprect.Right, cliprect.Bottom);
|
|
try
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
DrawCellBorders(DRAW_FROZEN_COLS);
|
|
DrawImages(DRAW_FROZEN_COLS);
|
|
if IsRightToLeft then
|
|
DrawFrozenPaneBorder(cliprect.Top, cliprect.Bottom, cliprect.Left+1, false)
|
|
else
|
|
DrawFrozenPaneBorder(cliprect.Top, cliprect.Bottom, cliprect.Right-1, false);
|
|
finally
|
|
DeleteObject(rgn);
|
|
end;
|
|
end;
|
|
|
|
// Paint intersection of frozen cols and frozen rows
|
|
if (fr > 0) and (fc > 0) then begin
|
|
if IsRightToLeft then
|
|
cliprect := Rect(FTopLeft.X, ClientRect.Top, ClientRect.Right, FTopLeft.Y)
|
|
else
|
|
cliprect := Rect(ClientRect.Left, ClientRect.Top, FTopLeft.X, FTopLeft.Y);
|
|
rgn := CreateRectRgn(clipRect.Left, cliprect.Top, cliprect.Right, cliprect.Bottom);
|
|
try
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
DrawCellBorders(DRAW_FROZEN_CORNER);
|
|
DrawImages(DRAW_FROZEN_CORNER);
|
|
finally
|
|
DeleteObject(rgn);
|
|
end;
|
|
end;
|
|
finally
|
|
Canvas.RestoreHandleState;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the borders of all cells. Calls DrawCellBorders for each individual cell.
|
|
AGridPart denotes where the cells are painted:
|
|
@unorderedlist(
|
|
@item(0 = normal grid area)
|
|
@item(1 = FrozenRows)
|
|
@item(2 = FrozenCols)
|
|
@item(3 = Top-left corner where FrozenCols and FrozenRows intersect)
|
|
)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawCellBorders(AGridPart: Integer = 0);
|
|
var
|
|
cell, base: PCell;
|
|
gc, gr: Integer;
|
|
sr1, sc1, sr2, sc2: Cardinal;
|
|
rect: TRect;
|
|
cellHasBorder: Boolean;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
case AGridPart of
|
|
0: begin
|
|
sr1 := GetWorksheetRow(GCache.VisibleGrid.Top);
|
|
sc1 := GetWorksheetCol(GCache.VisibleGrid.Left);
|
|
sr2 := GetWorksheetRow(GCache.VisibleGrid.Bottom);
|
|
sc2 := GetWorksheetCol(GCache.VisibleGrid.Right);
|
|
end;
|
|
1: begin
|
|
sr1 := 0;
|
|
if FFrozenRows = 0 then sr2 := 0 else sr2 := FFrozenRows - 1;
|
|
if FFrozenCols = 0 then sc1 := 0 else sc1 := FFrozenCols - 1;
|
|
sc2 := GetWorksheetCol(GCache.VisibleGrid.Right);
|
|
end;
|
|
2: begin
|
|
sc1 := 0;
|
|
if FFrozenCols = 0 then sc2 := 0 else sc2 := FFrozenCols - 1;
|
|
if FFrozenRows = 0 then sr1 := 0 else sr1 := FFrozenRows - 1;
|
|
sr2 := GetWorksheetRow(GCache.VisibleGrid.Bottom);
|
|
end;
|
|
3: begin
|
|
sc1 := 0;
|
|
if FFrozenCols = 0 then sc2 := 0 else sc2 := FFrozenCols - 1;
|
|
sr1 := 0;
|
|
if FFrozenRows = 0 then sr2 := 0 else sr2 := FFrozenRows - 1;
|
|
end;
|
|
end;
|
|
if sr1 = UNASSIGNED_ROW_COL_INDEX then sr1 := 0;
|
|
if sc1 = UNASSIGNED_ROW_COL_INDEX then sc1 := 0;
|
|
|
|
for cell in Worksheet.Cells.GetRangeEnumerator(sr1, sc1, sr2, sc2) do
|
|
begin
|
|
if Worksheet.IsMerged(cell) then
|
|
begin
|
|
base := Worksheet.FindMergeBase(cell);
|
|
cellHasBorder := uffBorder in Worksheet.ReadUsedFormatting(base);
|
|
end else
|
|
cellHasBorder := uffBorder in Worksheet.ReadUsedFormatting(cell);
|
|
if cellHasBorder then
|
|
begin
|
|
gc := GetGridCol(cell^.Col);
|
|
gr := GetGridRow(cell^.Row);
|
|
rect := CellRect(gc, gr);
|
|
DrawCellBorders(gc, gr, rect, cell);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the border lines around a given cell. Note that when this procedure is
|
|
called the output is clipped by the cell rectangle, but thick and double
|
|
border styles extend into the neighboring cell. Therefore, these border lines
|
|
are drawn in parts.
|
|
|
|
@param ACol Column Index
|
|
@param ARow Row index
|
|
@param ARect Rectangle in pixels occupied by the cell.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawCellBorders(ACol, ARow: Integer;
|
|
ARect: TRect; ACell: PCell);
|
|
const
|
|
drawHor = 0;
|
|
drawVert = 1;
|
|
drawDiagUp = 2;
|
|
drawDiagDown = 3;
|
|
|
|
procedure DrawBorderLine(ACoord: Integer; ARect: TRect; ADrawDirection: Byte;
|
|
ABorderStyle: TsCellBorderStyle);
|
|
const
|
|
//TsLineStyle = (
|
|
// lsThin, lsMedium, lsDashed, lsDotted, lsThick, lsDouble, lsHair,
|
|
// lsMediumDash, lsDashDot, lsMediumDashDot, lsDashDotDot, lsMediumDashDotDot,
|
|
// lsSlantDashDot);
|
|
PEN_STYLES: array[TsLineStyle] of TPenStyle =
|
|
(psSolid, psSolid, psDash, psDot, psSolid, psSolid, psDot,
|
|
psDash, psDashDot, psDashDot, psDashDotDot, psDashDotDot,
|
|
psDashDot);
|
|
PEN_WIDTHS: array[TsLineStyle] of Integer =
|
|
(1, 2, 1, 1, 3, 1, 1,
|
|
2, 1, 2, 1, 2,
|
|
2);
|
|
var
|
|
deltax, deltay: Integer;
|
|
angle: Double;
|
|
savedCosmetic: Boolean;
|
|
begin
|
|
savedCosmetic := Canvas.Pen.Cosmetic;
|
|
Canvas.Pen.Style := PEN_STYLES[ABorderStyle.LineStyle];
|
|
Canvas.Pen.Width := PEN_WIDTHS[ABorderStyle.LineStyle];
|
|
Canvas.Pen.Color := ABorderStyle.Color and $00FFFFFF;
|
|
Canvas.Pen.EndCap := pecSquare;
|
|
if ABorderStyle.LineStyle = lsHair then
|
|
Canvas.Pen.Cosmetic := false;
|
|
// Painting
|
|
case ABorderStyle.LineStyle of
|
|
lsThin, lsMedium, lsThick, lsDotted, lsDashed, lsDashDot, lsDashDotDot,
|
|
lsMediumDash, lsMediumDashDot, lsMediumDashDotDot, lsSlantDashDot, lsHair:
|
|
case ADrawDirection of
|
|
drawHor : Canvas.Line(ARect.Left, ACoord, ARect.Right, ACoord);
|
|
drawVert : Canvas.Line(ACoord, ARect.Top, ACoord, ARect.Bottom);
|
|
drawDiagUp : Canvas.Line(ARect.Left, ARect.Bottom, ARect.Right, ARect.Top);
|
|
drawDiagDown: Canvas.Line(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
|
|
end;
|
|
{
|
|
lsHair:
|
|
case ADrawDirection of
|
|
drawHor : DrawHairLineHor(Canvas, ARect.Left, ARect.Right, ACoord);
|
|
drawVert : DrawHairLineVert(Canvas, ACoord, ARect.Top, ARect.Bottom);
|
|
drawDiagUp : ;
|
|
drawDiagDown: ;
|
|
end;
|
|
}
|
|
lsDouble:
|
|
case ADrawDirection of
|
|
drawHor:
|
|
begin
|
|
Canvas.Line(ARect.Left, ACoord-1, ARect.Right, ACoord-1);
|
|
Canvas.Line(ARect.Left, ACoord+1, ARect.Right, ACoord+1);
|
|
Canvas.Pen.Color := Color;
|
|
Canvas.Line(ARect.Left, ACoord, ARect.Right, ACoord);
|
|
end;
|
|
drawVert:
|
|
begin
|
|
Canvas.Line(ACoord-1, ARect.Top, ACoord-1, ARect.Bottom);
|
|
Canvas.Line(ACoord+1, ARect.Top, ACoord+1, ARect.Bottom);
|
|
Canvas.Pen.Color := Color;
|
|
Canvas.Line(ACoord, ARect.Top, ACoord, ARect.Bottom);
|
|
end;
|
|
drawDiagUp:
|
|
begin
|
|
if ARect.Right = ARect.Left then
|
|
angle := pi/2
|
|
else
|
|
angle := arctan((ARect.Bottom-ARect.Top) / (ARect.Right-ARect.Left));
|
|
deltax := Max(1, round(1.0 / sin(angle)));
|
|
deltay := Max(1, round(1.0 / cos(angle)));
|
|
Canvas.Line(ARect.Left, ARect.Bottom-deltay-1, ARect.Right-deltax, ARect.Top-1);
|
|
Canvas.Line(ARect.Left+deltax, ARect.Bottom-1, ARect.Right, ARect.Top+deltay-1);
|
|
end;
|
|
drawDiagDown:
|
|
begin
|
|
if ARect.Right = ARect.Left then
|
|
angle := pi/2
|
|
else
|
|
angle := arctan((ARect.Bottom-ARect.Top) / (ARect.Right-ARect.Left));
|
|
deltax := Max(1, round(1.0 / sin(angle)));
|
|
deltay := Max(1, round(1.0 / cos(angle)));
|
|
Canvas.Line(ARect.Left, ARect.Top+deltay-1, ARect.Right-deltax, ARect.Bottom-1);
|
|
Canvas.Line(ARect.Left+deltax, ARect.Top-1, ARect.Right, ARect.Bottom-deltay-1);
|
|
end;
|
|
end;
|
|
end;
|
|
Canvas.Pen.Cosmetic := savedCosmetic;
|
|
end;
|
|
|
|
var
|
|
bs: TsCellBorderStyle;
|
|
fmt: PsCellFormat;
|
|
idx: Integer;
|
|
r1, c1, r2, c2: Cardinal;
|
|
begin
|
|
if Assigned(Worksheet) then begin
|
|
if Worksheet.IsMergeBase(ACell) then begin
|
|
Worksheet.FindMergedRange(ACell, r1, c1, r2, c2);
|
|
ARect := CellRect(GetGridCol(c1), GetGridRow(r1), GetGridCol(c2), GetGridRow(r2));
|
|
end;
|
|
|
|
// Left border
|
|
if GetBorderStyle(ACol, ARow, -1, 0, ACell, bs) and (ACol <> LeftCol) then
|
|
if IsRightToLeft then
|
|
DrawBorderLine(ARect.Right, ARect, drawVert, bs)
|
|
else
|
|
DrawBorderLine(ARect.Left-ord(not IsRightToLeft), ARect, drawVert, bs);
|
|
// Right border
|
|
if GetBorderStyle(ACol, ARow, +1, 0, ACell, bs) and (ACol + 1 <> LeftCol) then
|
|
if IsRightToLeft then
|
|
DrawBorderLine(ARect.Left, ARect, drawVert, bs)
|
|
else
|
|
DrawBorderLine(ARect.Right-ord(not IsRightToLeft), ARect, drawVert, bs);
|
|
// Top border
|
|
if GetBorderstyle(ACol, ARow, 0, -1, ACell, bs) then
|
|
DrawBorderLine(ARect.Top-1, ARect, drawHor, bs);
|
|
// Bottom border
|
|
if GetBorderStyle(ACol, ARow, 0, +1, ACell, bs) then
|
|
DrawBorderLine(ARect.Bottom-1, ARect, drawHor, bs);
|
|
|
|
if ACell <> nil then begin
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(ACell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
// fmt := Worksheet.GetPointerToEffectiveCellFormat(ACell);
|
|
// Diagonal up
|
|
if cbDiagUp in fmt^.Border then begin
|
|
bs := fmt^.Borderstyles[cbDiagUp];
|
|
if IsRightToLeft then
|
|
DrawBorderLine(0, ARect, drawDiagDown, bs)
|
|
else
|
|
DrawBorderLine(0, ARect, drawDiagUp, bs);
|
|
end;
|
|
// Diagonal down
|
|
if cbDiagDown in fmt^.Border then begin
|
|
bs := fmt^.BorderStyles[cbDiagDown];
|
|
if IsRightToLeft then
|
|
DrawBorderLine(0, ARect, drawDiagUp, bs)
|
|
else
|
|
DrawborderLine(0, ARect, drawDiagDown, bs);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Method inherited method from TCustomGrid. Is overridden here to avoid painting
|
|
of the border of frozen cells in black under some circumstances.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawCellGrid(ACol, ARow: Integer; ARect: TRect;
|
|
AState: TGridDrawState);
|
|
begin
|
|
if (TitleStyle <> tsNative) and (gdFixed in AState) and
|
|
{DisplayFixedColRow and} ((FFrozenCols > 0) or (FFrozenRows > 0)) then
|
|
begin
|
|
// Draw default cell borders only in the header cols/rows.
|
|
// If there are frozen cells they would get a black border, so we don't
|
|
// draw their borders here - they are drawn by "DrawRow" anyway.
|
|
if ((ACol=0) or (ARow = 0)) and DisplayFixedColRow then
|
|
inherited;
|
|
end else
|
|
inherited;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the red rectangle in the upper right corner of a cell to indicate that
|
|
this cell contains a popup comment
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawCommentMarker(ARect: TRect);
|
|
const
|
|
COMMENT_SIZE = 7;
|
|
var
|
|
P: Array[0..3] of TPoint;
|
|
commentSize: Integer;
|
|
begin
|
|
Canvas.Brush.Color := clRed;
|
|
Canvas.Brush.Style := bsSolid;
|
|
Canvas.Pen.Style := psClear;
|
|
{$IFDEF LCL_FULLVERSION_LT_180}
|
|
commentSize := ScalePPI(COMMENT_SIZE);
|
|
{$ELSE}
|
|
commentSize := Scale96ToFont(COMMENT_SIZE);
|
|
{$ENDIF}
|
|
if IsRightToLeft then
|
|
begin
|
|
P[0] := Point(ARect.Left, ARect.Top);
|
|
P[1] := Point(ARect.Left + commentSize, ARect.Top);
|
|
P[2] := Point(ARect.Left, ARect.Top + commentSize);
|
|
end else
|
|
begin
|
|
P[0] := Point(ARect.Right, ARect.Top);
|
|
P[1] := Point(ARect.Right - commentSize, ARect.Top);
|
|
P[2] := Point(ARect.Right, ARect.Top + commentSize);
|
|
end;
|
|
P[3] := P[0];
|
|
Canvas.Polygon(P);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
This procedure is responsible for painting the focus rectangle. We don't want
|
|
the red dashed rectangle here, but prefer the thick Excel-like black border
|
|
line. This new focus rectangle is drawn by the method DrawSelection because
|
|
the thick Excel border reaches into adjacent cells.
|
|
|
|
@param ACol Grid column index of the focused cell
|
|
@param ARow Grid row index of the focused cell
|
|
@param ARect Rectangle in pixels covered by the focused cell
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawFocusRect(aCol, aRow: Integer; ARect: TRect);
|
|
begin
|
|
Unused(ACol, ARow, ARect);
|
|
// Nothing do to
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws a solid line along the borders of frozen panes.
|
|
|
|
@param AStart Start coordinate of the pane border line
|
|
@param AEnd End coordinate of the pane border line
|
|
@param ACoord Other coordinate of the border line (y if horizontal, x if vertical)
|
|
@param(IsHor Determines whether a horizontal or vertical line is drawn and,
|
|
thus, how AStart, AEnd and ACoord are interpreted.)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawFrozenPaneBorder(AStart, AEnd, ACoord: Integer;
|
|
IsHor: Boolean);
|
|
var
|
|
delta: Integer;
|
|
begin
|
|
if (IsHor and (FFrozenRows = 0)) or (not IsHor and (FFrozenCols = 0)) then
|
|
exit;
|
|
|
|
if FFrozenBorderPen.Style = psClear then
|
|
exit;
|
|
|
|
delta := (FFrozenBorderPen.Width - 1) div 2;
|
|
|
|
Canvas.Pen.Assign(FFrozenBorderPen);
|
|
if IsHor then
|
|
Canvas.Line(AStart, ACoord-delta, AEnd, ACoord-delta)
|
|
else
|
|
if IsRightToLeft then
|
|
Canvas.Line(ACoord+delta, AStart, ACoord+delta, AEnd)
|
|
else
|
|
Canvas.Line(ACoord-delta, AStart, ACoord-delta, AEnd);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the embedded images of the worksheet. Is called at the end of the
|
|
painting process.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawImages(AGridPart: Integer);
|
|
|
|
procedure CalcClipRect(var ARect: TRect);
|
|
var
|
|
headerTL: TPoint;
|
|
begin
|
|
ARect := ClientRect;
|
|
headerTL := CalcTopLeft(true);
|
|
case AGridPart of
|
|
DRAW_NON_FROZEN:
|
|
begin
|
|
if IsRightToLeft then
|
|
ARect.Right := FTopLeft.X
|
|
else
|
|
ARect.Left := FTopLeft.X;
|
|
ARect.Top := FTopLeft.Y;
|
|
end;
|
|
DRAW_FROZEN_ROWS:
|
|
begin
|
|
if IsRightToLeft then
|
|
ARect.Right := FTopLeft.X
|
|
else
|
|
ARect.Left := FTopLeft.X;
|
|
ARect.Top := headerTL.Y;
|
|
ARect.Bottom := FTopLeft.Y;
|
|
end;
|
|
DRAW_FROZEN_COLS:
|
|
begin
|
|
if IsRightToLeft then
|
|
begin
|
|
ARect.Left := FTopLeft.X;
|
|
ARect.Right := headerTL.X;
|
|
end else
|
|
begin
|
|
ARect.Left := headerTL.X;
|
|
ARect.Right := FTopLeft.X;
|
|
end;
|
|
ARect.Top := FTopLeft.Y;
|
|
end;
|
|
DRAW_FROZEN_CORNER:
|
|
begin
|
|
if IsRightToLeft then
|
|
begin
|
|
ARect.Left := FTopLeft.X;
|
|
ARect.Right := headerTL.X;
|
|
end else
|
|
begin
|
|
ARect.Left := headerTL.X;
|
|
ARect.Right := FTopLeft.X;
|
|
end;
|
|
ARect.Top := headerTL.Y;
|
|
ARect.Bottom := FTopLeft.Y;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Offset to convert relative to absolute row/col coordinates for ColRowToOffset
|
|
procedure GetScrollOffset(out ARowDelta, AColDelta: Integer);
|
|
var
|
|
tmp: Integer;
|
|
x: Integer = 0;
|
|
y: Integer = 0;
|
|
begin
|
|
AColDelta := 0;
|
|
if FrozenCols > 0 then begin
|
|
if IsRightToLeft then begin
|
|
tmp := LeftCol;
|
|
ColRowToOffset(true, false, LeftCol, AColDelta, tmp); //tmp, AColDelta);
|
|
ColRowToOffset(true, true, LeftCol, tmp, x);
|
|
dec(AColDelta, ClientWidth - x);
|
|
end else
|
|
begin
|
|
ColRowToOffset(true, false, LeftCol, AColDelta, tmp);
|
|
ColRowToOffset(true, true, LeftCol, x, tmp);
|
|
dec(AColDelta, x);
|
|
end;
|
|
end;
|
|
|
|
ARowDelta := 0;
|
|
if FrozenRows > 0 then begin
|
|
ColRowToOffset(false, false, TopRow, ARowDelta, tmp);
|
|
ColRowToOffset(false, true, TopRow, y, tmp);
|
|
dec(ARowDelta, y);
|
|
end;
|
|
end;
|
|
|
|
function GetImageRect(img: PsImage; AWidth, AHeight: Integer;
|
|
ARowDelta, AColDelta: Integer): TRect;
|
|
var
|
|
tmp: Integer = 0;
|
|
gcol, grow: Integer;
|
|
relativeX, relativeY: Boolean;
|
|
begin
|
|
Result := Rect(0, 0, 0, 0); // To silence the compiler
|
|
|
|
grow := GetGridRow(img^.row);
|
|
gcol := GetGridCol(img^.Col);
|
|
|
|
case AGridPart of
|
|
DRAW_NON_FROZEN:
|
|
begin
|
|
relativeX := (FrozenCols = 0);
|
|
relativeY := (FrozenRows = 0);
|
|
end;
|
|
DRAW_FROZEN_COLS:
|
|
begin
|
|
relativeX := true;
|
|
relativeY := not ((integer(img^.Row) < FrozenRows) and
|
|
(integer(img^.Col) < FrozenCols));
|
|
end;
|
|
DRAW_FROZEN_ROWS:
|
|
begin
|
|
relativeX := not ((integer(img^.Row) < FrozenRows) and
|
|
(integer(img^.Col) < FrozenCols));
|
|
relativeY := true;
|
|
end;
|
|
DRAW_FROZEN_CORNER:
|
|
begin
|
|
relativeX := true;
|
|
relativeY := true;
|
|
end;
|
|
end;
|
|
|
|
if IsRightToLeft then begin
|
|
if not relativeX then
|
|
ColRowToOffset(true, false, gcol, Result.Right, tmp)
|
|
else
|
|
ColRowToOffset(true, true, gcol, tmp, Result.Right);
|
|
if not relativeX then
|
|
Result.Right := ClientWidth - Result.Right + AColDelta;
|
|
Result.Left := Result.Right - AWidth;
|
|
end else
|
|
begin
|
|
ColRowToOffset(true, relativeX, gcol, Result.Left, tmp);
|
|
if not relativeX then
|
|
dec(Result.Left, AColDelta);
|
|
Result.Right := Result.Left + AWidth;
|
|
end;
|
|
|
|
ColRowToOffset(false, relativeY, grow, Result.Top, tmp);
|
|
if not relativeY then
|
|
dec(Result.Top, ARowDelta);
|
|
Result.Bottom := Result.Top + AHeight;
|
|
|
|
if IsRightToLeft then
|
|
OffsetRect(Result, -ToPixels(img^.OffsetX), ToPixels(img^.OffsetY))
|
|
else
|
|
OffsetRect(Result, ToPixels(img^.OffsetX), ToPixels(img^.OffsetY));
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
img: PsImage;
|
|
obj: TsEmbeddedObj;
|
|
clipArea: TRect = (Left:0; Top:0; Right:0; Bottom:0);
|
|
imgRect: TRect;
|
|
w, h: Integer;
|
|
coloffs, rowoffs: Integer;
|
|
rgn: HRGN;
|
|
fc, fr: Integer;
|
|
R: TRect = (Left:0; Top:0; Right: 0; Bottom: 0);
|
|
begin
|
|
if Worksheet.GetImageCount = 0 then
|
|
exit;
|
|
|
|
CalcClipRect(clipArea);
|
|
GetScrollOffset(rowOffs, colOffs);
|
|
fc := FHeaderCount + FFrozenCols;
|
|
fr := FHeaderCount + FFrozenRows;
|
|
|
|
for i := 0 to Worksheet.GetImageCount-1 do begin
|
|
img := Worksheet.GetPointerToImage(i);
|
|
obj := Workbook.GetEmbeddedObj(img^.Index);
|
|
|
|
// Frozen part of the grid draw only images which are anchored there.
|
|
case AGridPart of
|
|
DRAW_NON_FROZEN:
|
|
;
|
|
DRAW_FROZEN_ROWS:
|
|
if (integer(img^.Row) >= fr) then Continue;
|
|
DRAW_FROZEN_COLS:
|
|
if (integer(img^.Col) >= fc) then Continue;
|
|
DRAW_FROZEN_CORNER:
|
|
if (integer(img^.Row) >= fr) or (integer(img^.Col) >= fc) then Continue;
|
|
end;
|
|
|
|
// Size of image and its position
|
|
w := ToPixels(obj.ImageWidth * img^.ScaleX);
|
|
h := ToPixels(obj.ImageHeight * img^.ScaleY);
|
|
imgRect := GetImageRect(img, w, h, rowoffs, coloffs);
|
|
|
|
// Nothing to do if image is outside the visible grid area
|
|
if not IntersectRect(R, clipArea, imgRect) then
|
|
continue;
|
|
|
|
// If not yet done load image stream into picture and scale to required size
|
|
if img^.Picture = nil then
|
|
begin
|
|
img^.Picture := TPicture.Create;
|
|
obj.Stream.Position := 0;
|
|
TPicture(img^.Picture).LoadFromStream(obj.Stream);
|
|
end;
|
|
|
|
// Scale and draw image
|
|
rgn := CreateRectRgn(clipArea.Left, clipArea.Top, clipArea.Right, clipArea.Bottom);
|
|
try
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
R := Rect(0, 0, w, h);
|
|
OffsetRect(R, imgRect.Left, imgRect.Top);
|
|
if (img^.ScaleX = 1.0) and (img^.ScaleY = 1.0) then
|
|
Canvas.Draw(R.Left, R.Top, TPicture(img^.Picture).Graphic)
|
|
else
|
|
Canvas.StretchDraw(R, TPicture(img^.Picture).Graphic);
|
|
finally
|
|
DeleteObject(rgn);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws lines with the PageBreakPen along the cell border indicating the
|
|
position of page breaks
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawPageBreaks(AClipRect: TRect);
|
|
var
|
|
i: Integer;
|
|
lCol: PCol;
|
|
lRow: PRow;
|
|
idx: Integer;
|
|
R: TRect;
|
|
begin
|
|
if not FShowPageBreaks then
|
|
exit;
|
|
|
|
Canvas.Pen.Assign(FPageBreakPen);
|
|
|
|
for i:= 0 to Worksheet.Rows.Count-1 do begin
|
|
lRow := Worksheet.Rows[i];
|
|
if (croPageBreak in lRow^.Options) then begin
|
|
idx := GetGridRow(lRow^.Row);
|
|
// R := CellRect(0, idx);
|
|
ColRowToOffSet(false, true, idx, R.Top, R.Bottom);
|
|
Canvas.Line(AClipRect.Left, R.Top-1, AClipRect.Right, R.Top-1);
|
|
end;
|
|
end;
|
|
|
|
for i:= 0 to Worksheet.Cols.Count-1 do begin
|
|
lCol := Worksheet.Cols[i];
|
|
if (croPageBreak in lCol^.Options) then begin
|
|
idx := GetGridRow(lCol^.Col);
|
|
// R := CelLRect(idx, 0);
|
|
ColRowToOffSet(true, true, idx, R.Left, R.Right);
|
|
Canvas.Line(R.Left-1, AClipRect.Top, R.Left-1, AClipRect.Bottom);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws a complete row of cells. Is mostly duplicated from Grids.pas, but adds
|
|
code for merged cells and overflow text, the section for drawing the default
|
|
focus rectangle is removed.
|
|
|
|
@param ARow Index of the row to be drawn (in grid coordinates)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawRow(ARow: Integer);
|
|
var
|
|
gr, gc, gcLast: Integer; // grid row/column
|
|
fc: Integer; // Fixed columns (= header column + frozen columns)
|
|
rct, row_rct, header_rct: TRect;
|
|
clipArea: TRect;
|
|
begin
|
|
clipArea := Canvas.ClipRect;
|
|
|
|
// Upper and lower bounds for this row
|
|
row_rct := Rect(clipArea.Left, 0, clipArea.Right, 0);
|
|
ColRowToOffSet(False, True, ARow, row_rct.Top, row_rct.Bottom);
|
|
|
|
// Rectangle covering the fixed row headers (but not the frozen cells)
|
|
header_rct := Rect(0, 0, 0, 0);
|
|
header_rct.Top := row_rct.Top;
|
|
header_rct.Bottom := row_rct.Bottom;
|
|
if HeaderCount > 0 then
|
|
ColRowToOffset(true, true, 0, header_rct.Left, header_rct.Right);
|
|
|
|
// Don't draw rows outside the clipping area
|
|
if (row_rct.Top >= row_rct.Bottom) or not VerticalIntersect(row_rct, clipArea) then
|
|
begin
|
|
{$IFDEF DbgVisualChange}
|
|
DebugLn('Drawrow: Skipped row: ', IntToStr(aRow));
|
|
{$ENDIF}
|
|
exit;
|
|
end;
|
|
|
|
// Count of non-scrolling columns
|
|
fc := FHeaderCount + FFrozenCols;
|
|
|
|
// (1) Draw data columns in this row (non-fixed part)
|
|
with GCache.VisibleGrid do
|
|
begin
|
|
gcLast := Right;
|
|
gc := Left;
|
|
rct := row_rct;
|
|
if IsRightToLeft then
|
|
rct.Right := FTopLeft.X
|
|
else
|
|
rct.Left := FTopLeft.X;
|
|
InternalDrawRow(ARow, gc, gcLast, rct);
|
|
end;
|
|
|
|
// (2) Draw fixed columns consisting of header columns and frozen cells
|
|
gr := ARow;
|
|
// (2a) Draw header column
|
|
if FHeaderCount > 0 then begin
|
|
FDrawingCell := nil;
|
|
gc := 0;
|
|
InternalDrawCell(gc, gr, header_rct, header_rct, [gdFixed]);
|
|
end;
|
|
|
|
// (2b) Draw frozen cells
|
|
if FFrozenCols > 0 then begin
|
|
rct := row_rct;
|
|
if IsRightToLeft then
|
|
rct.Left := FTopLeft.X
|
|
else
|
|
rct.Right := FTopLeft.X;
|
|
InternalDrawRow(ARow, FHeaderCount, fc-1, rct);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.DrawDragSelection;
|
|
begin
|
|
if Assigned(DragManager) and DragManager.IsDragging then
|
|
InternalDrawSelection(FDragSelection, false);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the selection rectangle around selected cells.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawSelection;
|
|
begin
|
|
InternalDrawSelection(Selection, true);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.InternalDrawSelection(ASel: TGridRect;
|
|
IsNormalSelection: boolean);
|
|
var
|
|
R: TRect;
|
|
cell: PCell;
|
|
r1,c1,r2,c2: Cardinal;
|
|
delta: Integer;
|
|
savedPenMode: TPenMode;
|
|
P: array[0..9] of TPoint;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
// Selected cell
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ASel.Top), GetWorksheetCol(ASel.Left));
|
|
if Worksheet.IsMerged(cell) then
|
|
begin
|
|
Worksheet.FindMergedRange(cell, r1,c1,r2,c2);
|
|
R := CellRect(GetGridCol(c1), GetGridRow(r1), GetGridCol(c2), GetGridRow(r2));
|
|
end else
|
|
R := CellRect(ASel.Left, ASel.Top, ASel.Right, ASel.Bottom);
|
|
|
|
if IsNormalSelection then begin
|
|
// Fine-tune position of selection rect
|
|
if odd(FSelPen.Width) then delta := -1 else delta := 0;
|
|
inc(R.Top, delta);
|
|
if IsRightToLeft then begin
|
|
if not odd(FSelPen.Width) then
|
|
OffsetRect(R, 1, 0) else
|
|
inc(R.Right, 1);
|
|
end else
|
|
inc(R.Left, delta);
|
|
if (ASel.Top = TopRow) then
|
|
inc(R.Top);
|
|
if ASel.Left = LeftCol then begin
|
|
if IsRightToLeft then
|
|
dec(R.Right)
|
|
else
|
|
inc(R.Left);
|
|
end;
|
|
|
|
// Set up the canvas
|
|
savedPenMode := Canvas.Pen.Mode;
|
|
Canvas.Pen.Assign(FSelPen);
|
|
if UseXORFeatures then begin
|
|
Canvas.Pen.Color := clWhite;
|
|
Canvas.Pen.Mode := pmXOR;
|
|
end;
|
|
Canvas.Brush.Style := bsClear;
|
|
// Paint
|
|
Canvas.Rectangle(R);
|
|
// Restore canvas.
|
|
Canvas.Pen.Mode := savedPenMode;
|
|
end
|
|
else
|
|
begin
|
|
// Selection during dragging: draw a dotted filled outline
|
|
delta := 2;
|
|
// outer rectangle
|
|
P[0] := Point(R.Left - delta, R.Top - delta);
|
|
P[1] := Point(R.Right + delta, R.Top - delta);
|
|
P[2] := Point(R.Right + delta, R.Bottom + delta);
|
|
P[3] := Point(R.Left - delta, R.Bottom + delta);
|
|
P[4] := P[0];
|
|
// inner rectangle
|
|
P[5] := Point(R.Left + delta, R.Top + delta);
|
|
P[6] := Point(R.Left + delta, R.Bottom - delta);
|
|
P[7] := Point(R.Right - delta, R.Bottom - delta);
|
|
P[8] := Point(R.Right - delta, R.Top + delta);
|
|
P[9] := P[5];
|
|
Canvas.Pen.style := psClear;
|
|
Canvas.Brush.Style := bsImage;
|
|
Canvas.Brush.Bitmap := DragBorderBitmap;
|
|
// Canvas.Brush.Color := clblack;
|
|
Canvas.Polygon(P);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Draws the cell text. Calls "GetCellText" to determine the text for the cell.
|
|
Takes care of horizontal and vertical text alignment, text rotation and
|
|
text wrapping.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@param ARect Rectangle in pixels occupied by the cell
|
|
@param AState Drawing state of the grid -- see TCustomGrid
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.DrawTextInCell(ACol, ARow: Integer; ARect: TRect;
|
|
AState: TGridDrawState);
|
|
var
|
|
ts: TTextStyle;
|
|
txt: String;
|
|
wrapped: Boolean;
|
|
horAlign: TsHorAlignment;
|
|
vertAlign: TsVertAlignment;
|
|
txtRot: TsTextRotation;
|
|
fntIndex: Integer;
|
|
lCell: PCell;
|
|
fmt: PsCellFormat;
|
|
numfmt: TsNumFormatParams;
|
|
idx: Integer;
|
|
numFmtColor: TColor;
|
|
sidx: Integer; // number format section index
|
|
RTL: Boolean;
|
|
begin
|
|
if (Worksheet = nil) then
|
|
exit;
|
|
|
|
if (ACol < FHeaderCount) or (ARow < FHeaderCount) then
|
|
lCell := nil
|
|
else begin
|
|
if FDrawingCell = nil then
|
|
lCell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol))
|
|
else
|
|
lCell := FDrawingCell
|
|
end;
|
|
|
|
// Header
|
|
if (lCell = nil) and ShowHeaders and ((ACol = 0) or (ARow = 0)) then
|
|
begin
|
|
ts := Canvas.TextStyle;
|
|
ts.Alignment := taCenter;
|
|
ts.Layout := tlCenter;
|
|
ts.Opaque := false;
|
|
Canvas.TextStyle := ts;
|
|
inherited DrawCellText(aCol, aRow, aRect, aState, GetCellText(ACol,ARow));
|
|
exit;
|
|
end;
|
|
|
|
// Cells
|
|
if lCell = nil then
|
|
exit;
|
|
|
|
txt := GetCellText(GetGridRow(lCell^.Col), GetGridCol(lCell^.Row));
|
|
if txt = '' then
|
|
exit;
|
|
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(lCell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
wrapped := (uffWordWrap in fmt^.UsedFormattingFields) or (fmt^.TextRotation = rtStacked);
|
|
RTL := IsRightToLeft;
|
|
if (uffBiDi in fmt^.UsedFormattingFields) then
|
|
case Worksheet.ReadBiDiMode(lCell) of
|
|
bdRTL : RTL := true;
|
|
bdLTR : RTL := false;
|
|
end;
|
|
|
|
// Text rotation
|
|
if (uffTextRotation in fmt^.UsedFormattingFields)
|
|
then txtRot := fmt^.TextRotation
|
|
else txtRot := trHorizontal;
|
|
|
|
// vertical alignment
|
|
if (uffVertAlign in fmt^.UsedFormattingFields)
|
|
then vertAlign := fmt^.VertAlignment
|
|
else vertAlign := vaDefault;
|
|
if vertAlign = vaDefault then
|
|
vertAlign := vaBottom;
|
|
|
|
// Horizontal alignment
|
|
if (uffHorAlign in fmt^.UsedFormattingFields)
|
|
then horAlign := fmt^.HorAlignment
|
|
else horAlign := haDefault;
|
|
if (horAlign = haDefault) then
|
|
begin
|
|
if (lCell^.ContentType in [cctNumber, cctDateTime]) then
|
|
begin
|
|
if RTL then
|
|
horAlign := haLeft
|
|
else
|
|
horAlign := haRight;
|
|
end else
|
|
if (lCell^.ContentType in [cctBool]) then
|
|
horAlign := haCenter
|
|
else begin
|
|
if RTL then
|
|
horAlign := haRight
|
|
else
|
|
horAlign := haLeft;
|
|
end;
|
|
end;
|
|
|
|
// Font index
|
|
if (uffFont in fmt^.UsedFormattingFields)
|
|
then fntIndex := fmt^.FontIndex
|
|
else fntIndex := DEFAULT_FONTINDEX;
|
|
|
|
// Font color as derived from number format
|
|
numFmtColor := clNone;
|
|
if not IsNaN(lCell^.NumberValue) and (uffNumberFormat in fmt^.UsedFormattingFields) then
|
|
begin
|
|
numFmt := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
|
|
if numFmt <> nil then
|
|
begin
|
|
sidx := 0;
|
|
if (Length(numFmt.Sections) > 1) and (lCell^.NumberValue < 0) then
|
|
sidx := 1
|
|
else
|
|
if (Length(numFmt.Sections) > 2) and (lCell^.NumberValue = 0) then
|
|
sidx := 2;
|
|
if (nfkHasColor in numFmt.Sections[sidx].Kind) then
|
|
numFmtColor := numFmt.Sections[sidx].Color and $00FFFFFF;
|
|
end;
|
|
end;
|
|
|
|
InflateRect(ARect, -constCellPadding, -constCellPadding);
|
|
|
|
InternalDrawTextInCell(txt, ARect, horAlign, vertAlign, txtRot, wrapped,
|
|
fntIndex, numfmtColor, lCell^.RichTextParams, RTL);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Is called when editing is complete; checks whether input is valid already
|
|
have been done.
|
|
The method must transfer the edited cell text to the worksheet cell
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.EditorDoGetValue;
|
|
var
|
|
cell: Pcell;
|
|
begin
|
|
inherited;
|
|
if (FOldEditorText <> FEditText) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
|
|
if Worksheet.IsMerged(cell) then
|
|
cell := Worksheet.FindMergeBase(cell);
|
|
if (FEditText <> '') and (FEditText[1] = '=') then
|
|
Worksheet.WriteFormula(cell, Copy(FEditText, 2, Length(FEditText)), true)
|
|
else
|
|
Worksheet.WriteCellValueAsString(cell, FEditText);
|
|
FEditText := '';
|
|
FOldEditorText := '';
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl;
|
|
begin
|
|
if (Style = cbsAuto) and (FLineMode = elmMultiLine) then
|
|
Result := FMultiLineStringEditor
|
|
else
|
|
Result := inherited;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
The BeginUpdate/EndUpdate pair suppresses unnecessary painting of the grid.
|
|
Call BeginUpdate to stop refreshing the grid, and call EndUpdate to release
|
|
the lock and to repaint the grid again.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.EndUpdate(ARefresh: Boolean = true);
|
|
begin
|
|
inherited EndUpdate(false);
|
|
Workbook.UnlockFormulas;
|
|
Workbook.EnableNotifications;
|
|
dec(FLockCount);
|
|
if (FLockCount = 0) and ARefresh then
|
|
VisualChange;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Executes a hyperlink stored in the FHyperlinkCell
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ExecuteHyperlink;
|
|
var
|
|
hlink: TsHyperlink;
|
|
target, bookmark: String;
|
|
sheetname: String;
|
|
sheet: TsWorksheet;
|
|
r, c: Cardinal;
|
|
begin
|
|
if FHyperlinkCell = nil then
|
|
exit;
|
|
|
|
hlink := Worksheet.ReadHyperlink(FHyperlinkCell);
|
|
SplitHyperlink(hlink.Target, target, bookmark);
|
|
if target = '' then begin
|
|
// Goes to a cell within the current workbook
|
|
if ParseSheetCellString(bookmark, sheetname, r, c) then
|
|
begin
|
|
if sheetname <> '' then
|
|
begin
|
|
sheet := Workbook.GetWorksheetByName(sheetname);
|
|
if sheet = nil then
|
|
raise Exception.CreateFmt(rsWorksheetNotFound, [sheetname]);
|
|
Workbook.SelectWorksheet(sheet);
|
|
end;
|
|
Worksheet.SelectCell(r, c);
|
|
end else
|
|
raise Exception.CreateFmt(rsNoValidHyperlinkInternal, [hlink.Target]);
|
|
end else
|
|
// Fires the OnClickHyperlink event which should open a file or a URL
|
|
if Assigned(FOnClickHyperlink) then FOnClickHyperlink(self, hlink);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Copies the borders of a cell to the correspondig edges of its neighbors.
|
|
This avoids the nightmare of changing borders due to border conflicts
|
|
of adjacent cells.
|
|
|
|
@param ACell Pointer to the cell
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACell: PCell);
|
|
|
|
procedure SetNeighborBorder(NewRow, NewCol: Cardinal;
|
|
ANewBorder: TsCellBorder; const ANewBorderStyle: TsCellBorderStyle;
|
|
AInclude: Boolean);
|
|
var
|
|
neighbor: PCell;
|
|
border: TsCellBorders;
|
|
begin
|
|
neighbor := Worksheet.FindCell(NewRow, NewCol);
|
|
if neighbor <> nil then
|
|
begin
|
|
border := Worksheet.ReadCellBorders(neighbor);
|
|
if AInclude then
|
|
begin
|
|
Include(border, ANewBorder);
|
|
Worksheet.WriteBorderStyle(NewRow, NewCol, ANewBorder, ANewBorderStyle);
|
|
end else
|
|
Exclude(border, ANewBorder);
|
|
Worksheet.WriteBorders(NewRow, NewCol, border);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
fmt: PsCellFormat;
|
|
idx: Integer;
|
|
begin
|
|
if (Worksheet = nil) or (ACell = nil) then
|
|
exit;
|
|
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(ACell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
// fmt := Worksheet.GetPointerToEffectiveCellFormat(ACell);
|
|
with ACell^ do
|
|
begin
|
|
// fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
|
|
if Col > 0 then
|
|
SetNeighborBorder(Row, Col-1, cbEast, fmt^.BorderStyles[cbWest], cbWest in fmt^.Border);
|
|
SetNeighborBorder(Row, Col+1, cbWest, fmt^.BorderStyles[cbEast], cbEast in fmt^.Border);
|
|
if Row > 0 then
|
|
SetNeighborBorder(Row-1, Col, cbSouth, fmt^.BorderStyles[cbNorth], cbNorth in fmt^.Border);
|
|
SetNeighborBorder(Row+1, Col, cbNorth, fmt^.BorderStyles[cbSouth], cbSouth in fmt^.Border);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the background color of a cell. The color is given as an index into
|
|
the workbook's color palette.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@returns Color index of the cell's background color.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetBackgroundColor(ACol, ARow: Integer): TsColor;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := scNotDefined;
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadBackgroundColor(cell);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the background color of a cell range defined by a rectangle. The color
|
|
is given as an index into the workbook's color palette. If the colors are
|
|
different from cell to cell the value scUndefined is returned.
|
|
|
|
@param ALeft Index of the left column of the cell range
|
|
@param ATop Index of the top row of the cell range
|
|
@param ARight Index of the right column of the cell range
|
|
@param ABottom Index of the bottom row of the cell range
|
|
@return(Color index common to all cells within the selection. If the cells'
|
|
background colors are different the value scUndefined is returned.)
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetBackgroundColors(ALeft, ATop, ARight, ABottom: Integer): TsColor;
|
|
var
|
|
c, r: Integer;
|
|
clr: TsColor;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetBackgroundColor(ALeft, ATop);
|
|
clr := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
begin
|
|
Result := GetBackgroundColor(c, r);
|
|
if Result <> clr then
|
|
begin
|
|
Result := scNotDefined;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellBiDiMode(ACol,ARow: Integer): TsBiDiMode;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if cell <> nil then
|
|
Result := Worksheet.ReadBiDiMode(cell) else
|
|
Result := bdDefault;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the cell borders which are drawn around a given cell.
|
|
If the cell is part of a merged block then the borders of the merge base
|
|
are applied to the location of the cell (no inner borders for merged cells).
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@return Set with flags indicating where borders are drawn (top/left/right/bottom)
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellBorder(ACol, ARow: Integer): TsCellBorders;
|
|
var
|
|
cell: PCell;
|
|
base: PCell;
|
|
r, c, r1, c1, r2, c2: Cardinal;
|
|
begin
|
|
Result := [];
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
r := GetWorksheetRow(ARow);
|
|
c := GetWorksheetCol(ACol);
|
|
cell := Worksheet.FindCell(r, c);
|
|
if Worksheet.IsMerged(cell) then
|
|
begin
|
|
Worksheet.FindMergedRange(cell, r1, c1, r2, c2);
|
|
base := Worksheet.FindCell(r1, c1);
|
|
Result := Worksheet.ReadCellBorders(base);
|
|
if (cbNorth in Result) and (r > r1) then Exclude(Result, cbNorth);
|
|
if (cbSouth in Result) and (r < r2) then Exclude(Result, cbSouth);
|
|
if IsRightToLeft then
|
|
begin
|
|
if (cbEast in Result) and (c > c1) then Exclude(Result, cbEast);
|
|
if (cbWest in Result) and (c < c2) then Exclude(Result, cbWest);
|
|
end else
|
|
begin
|
|
if (cbWest in Result) and (c > c1) then Exclude(Result, cbWest);
|
|
if (cbEast in Result) and (c < c2) then Exclude(Result, cbEast);
|
|
end;
|
|
end else
|
|
Result := Worksheet.ReadCellBorders(cell);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the cell borders which are drawn around a given rectangular cell range.
|
|
|
|
@param ALeft Index of the left column of the cell range
|
|
@param ATop Index of the top row of the cell range
|
|
@param ARight Index of the right column of the cell range
|
|
@param ABottom Index of the bottom row of the cell range
|
|
@return(Set with flags indicating where borders are drawn (top/left/right/bottom)
|
|
If the individual cells within the range have different borders an
|
|
empty set is returned.)
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellBorders(ALeft, ATop, ARight, ABottom: Integer): TsCellBorders;
|
|
var
|
|
c, r: Integer;
|
|
b: TsCellBorders;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellBorder(ALeft, ATop);
|
|
b := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
begin
|
|
Result := GetCellBorder(c, r);
|
|
if Result <> b then
|
|
begin
|
|
Result := [];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the style of the cell border line drawn along the edge specified
|
|
by the parameter ABorder of a cell. The style is defined by line style and
|
|
line color.
|
|
|
|
If the cell belongs to a merged block then the border styles of the merge
|
|
base are returned.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@param ABorder Identifier of the border at which the line will be drawn (see TsCellBorder)
|
|
@return CellBorderStyle record containing information on line style and line color.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellBorderStyle(ACol, ARow: Integer;
|
|
ABorder: TsCellBorder): TsCellBorderStyle;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := DEFAULT_BORDERSTYLES[ABorder];
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if Worksheet.IsMerged(cell) then
|
|
cell := Worksheet.FindMergeBase(cell);
|
|
Result := Worksheet.ReadCellBorderStyle(cell, ABorder);
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the style of the cell border line drawn along the edge specified
|
|
by the parameter ABorder of a range of cells defined by the rectangle of
|
|
column and row indexes. The style is defined by linestyle and line color.
|
|
|
|
@param ALeft Index of the left column of the cell range
|
|
@param ATop Index of the top row of the cell range
|
|
@param ARight Index of the right column of the cell range
|
|
@param ABottom Index of the bottom row of the cell range
|
|
@param ABorder Identifier of the border where the line will be drawn (see TsCellBorder)
|
|
@return CellBorderStyle record containing information on line style and line color.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellBorderStyles(ALeft, ATop, ARight, ABottom: Integer;
|
|
ABorder: TsCellBorder): TsCellBorderStyle;
|
|
var
|
|
c, r: Integer;
|
|
bs: TsCellBorderStyle;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellBorderStyle(ALeft, ATop, ABorder);
|
|
bs := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
begin
|
|
Result := GetCellBorderStyle(c, r, ABorder);
|
|
if (Result.LineStyle <> bs.LineStyle) or (Result.Color <> bs.Color) then
|
|
begin
|
|
Result := DEFAULT_BORDERSTYLES[ABorder];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the comment assigned to a cell.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@return String used as a cell comment.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellComment(ACol, ARow: Integer): String;
|
|
begin
|
|
if Worksheet <> nil then
|
|
Result := Worksheet.ReadComment(GetWorksheetRow(ARow), GetWorksheetCol(ACol))
|
|
else
|
|
Result :='';
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the (LCL) font to be used when painting text in a cell.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@return Font usable when painting on a canvas.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellFont(ACol, ARow: Integer): TFont;
|
|
var
|
|
cell: PCell;
|
|
fnt: TsFont;
|
|
begin
|
|
Result := nil;
|
|
if (Workbook <> nil) then
|
|
begin
|
|
fnt := Workbook.GetDefaultFont;
|
|
if (Worksheet <> nil) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
fnt := Worksheet.ReadCellFont(cell);
|
|
end;
|
|
Convert_sFont_to_Font(fnt, FCellFont);
|
|
Result := FCellFont;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the (LCL) font to be used when painting text in the cells defined
|
|
by the rectangle of row/column indexes.
|
|
|
|
@param ALeft Index of the left column of the cell range
|
|
@param ATop Index of the top row of the cell range
|
|
@param ARight Index of the right column of the cell range
|
|
@param ABottom Index of the bottom row of the cell range
|
|
@return Font usable when painting on a canvas.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellFonts(ALeft, ATop, ARight, ABottom: Integer): TFont;
|
|
var
|
|
r1,c1,r2,c2: Cardinal;
|
|
sFont, sDefFont: TsFont;
|
|
cell: PCell;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellFont(ALeft, ATop);
|
|
sDefFont := Workbook.GetDefaultFont; // Default font
|
|
r1 := GetWorksheetRow(ATop);
|
|
c1 := GetWorksheetCol(ALeft);
|
|
r2 := GetWorksheetRow(ABottom);
|
|
c2 := GetWorksheetRow(ARight);
|
|
for cell in Worksheet.Cells.GetRangeEnumerator(r1, c1, r2, c2) do
|
|
begin
|
|
sFont := Worksheet.ReadCellFont(cell);
|
|
if (sFont.FontName <> sDefFont.FontName) and (sFont.Size <> sDefFont.Size)
|
|
and (sFont.Style <> sDefFont.Style) and (sFont.Color <> sDefFont.Color)
|
|
then
|
|
begin
|
|
Convert_sFont_to_Font(sDefFont, FCellFont);
|
|
Result := FCellFont;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the height (in pixels) of the cell at ACol/ARow (of the grid).
|
|
|
|
@param(ACol Grid column index of the cell.)
|
|
@param(ARow Grid row index of the cell.)
|
|
@return(Height of the cell in pixels. Wrapped text is handled correctly.
|
|
Value contains the zoom factor.)
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellHeight(ACol, ARow: Integer): Integer;
|
|
var
|
|
lCell: PCell;
|
|
s: String;
|
|
wrapped: Boolean;
|
|
cellR: TRect;
|
|
r1,c1,r2,c2: Cardinal;
|
|
fmt: PsCellFormat;
|
|
idx: Integer;
|
|
fntIndex: Integer;
|
|
txtRot: TsTextRotation;
|
|
RTL: Boolean;
|
|
begin
|
|
Result := 0;
|
|
if (ACol < FHeaderCount) or (ARow < FHeaderCount) then
|
|
exit;
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
lCell := Worksheet.FindCell(ARow-FHeaderCount, ACol-FHeaderCount);
|
|
if lCell <> nil then
|
|
begin
|
|
cellR := CellRect(ACol, ARow);
|
|
if Worksheet.IsMerged(lCell) then
|
|
begin
|
|
Worksheet.FindMergedRange(lCell, r1, c1, r2, c2);
|
|
if r1 <> r2 then
|
|
// If the merged range encloses several rows we skip automatic row height
|
|
// determination since only the height of the first row of the block
|
|
// (containing the merge base cell) would change which is very confusing.
|
|
exit;
|
|
cellR := CellRect(LongInt(c1)+FHeaderCount, ARow);
|
|
cellR.Right := CellRect(LongInt(c2)+FHeaderCount, ARow).Right;
|
|
end;
|
|
InflateRect(cellR, -constCellPadding, -constCellPadding);
|
|
|
|
s := GetCellText(ACol, ARow, false);
|
|
if s = '' then
|
|
exit;
|
|
|
|
DoPrepareCanvas(ACol, ARow, []);
|
|
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(lCell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
// fmt := Worksheet.GetPointerToEffectiveCellFormat(lCell);
|
|
if (uffFont in fmt^.UsedFormattingFields) then
|
|
fntIndex := fmt^.FontIndex else fntIndex := DEFAULT_FONTINDEX;
|
|
if (uffTextRotation in fmt^.UsedFormattingFields) then
|
|
txtRot := fmt^.TextRotation else txtRot := trHorizontal;
|
|
wrapped := (uffWordWrap in fmt^.UsedFormattingFields);
|
|
|
|
RTL := IsRightToLeft;
|
|
if (uffBiDi in fmt^.UsedFormattingFields) then
|
|
case fmt^.BiDiMode of
|
|
bdRTL: RTL := true;
|
|
bdLTR: RTL := false;
|
|
end;
|
|
|
|
case txtRot of
|
|
trHorizontal: ;
|
|
rt90DegreeClockwiseRotation,
|
|
rt90DegreeCounterClockwiseRotation:
|
|
cellR := Rect(0, 0, MaxInt, MaxInt);
|
|
rtStacked:
|
|
cellR := Rect(0, 0, MaxInt, MaxInt);
|
|
end;
|
|
|
|
Result := RichTextHeight(Canvas, Workbook, cellR, s, lCell^.RichTextParams,
|
|
fntIndex, txtRot, wrapped, RTL, ZoomFactor)
|
|
+ 2 * constCellPadding;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
This function defines the text to be displayed as a cell hint. By default, it
|
|
is the comment and/or the hyperlink attached to a cell; it can further be
|
|
modified by using the OnGetCellHint event.
|
|
|
|
Option goCellHints must be active for the cell hint feature to work.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellHintText(ACol, ARow: Integer): String;
|
|
var
|
|
cell: PCell;
|
|
hlink: PsHyperlink;
|
|
comment: String;
|
|
begin
|
|
Result := '';
|
|
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if cell = nil then
|
|
exit;
|
|
|
|
// Read comment
|
|
comment := Worksheet.ReadComment(cell);
|
|
|
|
// Avoid splitting the comment at the '|': draw a unicode character which looks similar.
|
|
if pos('|', comment) > 0 then
|
|
comment := StringReplace(comment, '|', #$E2#$94#$82, [rfReplaceAll]);
|
|
|
|
// Read hyperlink info
|
|
if Worksheet.HasHyperlink(cell) then begin
|
|
hlink := Worksheet.FindHyperlink(cell);
|
|
if hlink <> nil then
|
|
begin
|
|
if hlink^.ToolTip <> '' then
|
|
Result := hlink^.ToolTip
|
|
else
|
|
Result := Format('Hyperlink: %s' + LineEnding + rsStdHyperlinkTooltip,
|
|
[hlink^.Target]
|
|
);
|
|
end;
|
|
end;
|
|
|
|
// Combine comment and hyperlink
|
|
if (Result <> '') and (comment <> '') then
|
|
Result := comment + LineEnding + LineEnding + Result
|
|
else
|
|
if (Result = '') and (comment <> '') then
|
|
Result := comment;
|
|
|
|
// Call hint event handler
|
|
if Assigned(OnGetCellHint) then
|
|
OnGetCellHint(self, ACol, ARow, Result);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCells(ACol, ARow: Integer): String;
|
|
var
|
|
msg: TGridMessage;
|
|
begin
|
|
if (Editor <> nil) and Editor.Visible then
|
|
begin
|
|
msg.LclMsg.msg := GM_GETVALUE;
|
|
msg.Grid := Self;
|
|
msg.Col := ACol;
|
|
msg.Row := ARow;
|
|
msg.Value := ''; //GetCells(FCol, FRow);
|
|
Editor.Dispatch(msg);
|
|
Result := msg.value;
|
|
end else
|
|
Result := GetCellText(ACol, ARow);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
This function returns the text to be shown in a grid cell. The text is looked
|
|
up in the corresponding cell of the worksheet by calling its ReadAsUTF8Text
|
|
method. In case of "stacked" text rotation, line endings are inserted after
|
|
each character.
|
|
|
|
@param ACol Grid column index of the cell
|
|
@param ARow Grid row index of the cell
|
|
@param ATrim If true show replacement characters if numerical data are wider than cell.
|
|
@return Text to be displayed in the cell.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetCellText(ACol, ARow: Integer;
|
|
ATrim: Boolean = true): String;
|
|
var
|
|
cell: PCell;
|
|
r, c: Integer;
|
|
begin
|
|
Result := '';
|
|
|
|
if ShowHeaders then
|
|
begin
|
|
// Headers
|
|
if (ARow = 0) and (ACol = 0) then
|
|
exit;
|
|
if (ARow = 0) then
|
|
begin
|
|
Result := GetColString(ACol - FHeaderCount);
|
|
if Assigned(FGetColHeaderText) then
|
|
FGetColHeaderText(Self, ACol, Result);
|
|
exit;
|
|
end
|
|
else
|
|
if (ACol = 0) then
|
|
begin
|
|
Result := IntToStr(ARow);
|
|
if Assigned(FGetRowHeaderText) then
|
|
FGetRowHeaderText(Self, ARow, Result);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if Worksheet <> nil then
|
|
begin
|
|
r := ARow - FHeaderCount;
|
|
c := ACol - FHeaderCount;
|
|
cell := Worksheet.FindCell(r, c);
|
|
if cell <> nil then
|
|
begin
|
|
if HasFormula(cell) and not (boAutoCalc in Workbook.Options) and FShowFormulas then
|
|
Result := '=' + Worksheet.ReadFormula(cell)
|
|
else
|
|
if ATrim then
|
|
Result := TrimToCell(cell)
|
|
else
|
|
Result := Worksheet.ReadAsText(cell);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetDefaultColumnTitle(Column: Integer): string;
|
|
var
|
|
s: String = '';
|
|
begin
|
|
if Assigned(FGetColHeaderText) then
|
|
FGetColHeaderText(Self, Column, s)
|
|
else
|
|
s := GetColString(Column - FHeaderCount);
|
|
Result := s;
|
|
end;
|
|
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Determines the text to be passed to the cell editor. The text is determined
|
|
from the underlying worksheet cell, but it is possible to intercept this by
|
|
adding a handler for the OnGetEditText event.
|
|
|
|
@param ACol Grid column index of the cell being edited
|
|
@param ARow Grid row index of the grid cell being edited
|
|
@return Text to be passed to the cell editor.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetEditText(ACol, ARow: Integer): string;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if FEnhEditMode then // Initiated by "F2"
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if Worksheet.IsMerged(cell) then
|
|
cell := Worksheet.FindMergeBase(cell);
|
|
Result := Worksheet.ReadFormulaAsString(cell, true);
|
|
if Result <> '' then
|
|
begin
|
|
if Result[1] <> '=' then Result := '=' + Result;
|
|
end else
|
|
if cell <> nil then
|
|
case cell^.ContentType of
|
|
cctNumber:
|
|
Result := FloatToStr(cell^.NumberValue);
|
|
cctDateTime:
|
|
if cell^.DateTimeValue < 1.0 then
|
|
Result := FormatDateTime('tt', cell^.DateTimeValue)
|
|
else
|
|
Result := FormatDateTime('c', cell^.DateTimeValue);
|
|
else
|
|
Result := Worksheet.ReadAsText(cell);
|
|
end
|
|
else
|
|
Result := '';
|
|
end else
|
|
Result := GetCellText(aCol, aRow);
|
|
if Assigned(OnGetEditText) then
|
|
OnGetEditText(Self, aCol, aRow, Result);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Determines the style of the border between a cell and its neighbor given by
|
|
ADeltaCol and ADeltaRow (one of them must be 0, the other one can only be +/-1).
|
|
ACol and ARow are in grid units.
|
|
|
|
@return Result is @FALSE if there is no border line.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetBorderStyle(ACol, ARow, ADeltaCol, ADeltaRow: Integer;
|
|
ACell: PCell; out ABorderStyle: TsCellBorderStyle): Boolean;
|
|
var
|
|
neighborcell: PCell;
|
|
border, neighborborder: TsCellBorder;
|
|
r, c: Cardinal;
|
|
begin
|
|
Result := true;
|
|
|
|
if (ADeltaCol = -1) and (ADeltaRow = 0) then
|
|
begin
|
|
border := cbWest;
|
|
neighborborder := cbEast;
|
|
end else
|
|
if (ADeltaCol = +1) and (ADeltaRow = 0) then
|
|
begin
|
|
border := cbEast;
|
|
neighborborder := cbWest;
|
|
end else
|
|
if (ADeltaCol = 0) and (ADeltaRow = -1) then
|
|
begin
|
|
border := cbNorth;
|
|
neighborborder := cbSouth;
|
|
end else
|
|
if (ADeltaCol = 0) and (ADeltaRow = +1) then
|
|
begin
|
|
border := cbSouth;
|
|
neighborBorder := cbNorth;
|
|
end else
|
|
raise Exception.Create('[TsCustomWorksheetGrid] Incorrect col/row for GetBorderStyle.');
|
|
|
|
r := GetWorksheetRow(ARow);
|
|
c := GetWorksheetCol(ACol);
|
|
if (longint(r) + ADeltaRow < 0) or (longint(c) + ADeltaCol < 0) then
|
|
neighborcell := nil
|
|
else
|
|
neighborcell := Worksheet.FindCell(longint(r) + ADeltaRow, longint(c) + ADeltaCol);
|
|
|
|
// Only cell has border, but neighbor has not
|
|
if HasBorder(ACell, border) and not HasBorder(neighborCell, neighborBorder) then
|
|
begin
|
|
if Worksheet.InSameMergedRange(ACell, neighborcell) then
|
|
result := false
|
|
else
|
|
ABorderStyle := GetCellBorderStyle(ACol, ARow, border);
|
|
end
|
|
else
|
|
// Only neighbor has border, cell has not
|
|
if not HasBorder(ACell, border) and HasBorder(neighborCell, neighborBorder) then
|
|
begin
|
|
if Worksheet.InSameMergedRange(ACell, neighborcell) then
|
|
result := false
|
|
else
|
|
ABorderStyle := GetCellBorderStyle(ACol+ADeltaCol, ARow+ADeltaRow, neighborborder);
|
|
end
|
|
else
|
|
// Both cells have shared border -> use top or left border
|
|
if HasBorder(ACell, border) and HasBorder(neighborCell, neighborBorder) then
|
|
begin
|
|
if Worksheet.InSameMergedRange(ACell, neighborcell) then
|
|
result := false
|
|
else
|
|
if (border in [cbNorth, cbWest]) then
|
|
ABorderStyle := GetCellBorderStyle(ACol+ADeltaCol, ARow+ADeltaRow, neighborborder)
|
|
else
|
|
ABorderStyle := GetCellBorderStyle(ACol, ARow, border);
|
|
end else
|
|
Result := false;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns the width of the fixed header column 0, in pixels
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetFixedColWidth: Integer;
|
|
begin
|
|
if FFixedColWidth = 0 then begin
|
|
PrepareCanvasFont;
|
|
Result := Canvas.TextWidth(' 9999999 ');
|
|
end else
|
|
Result := FFixedColWidth;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts a column index of the worksheet to a column index usable in the grid.
|
|
This is required because worksheet indexes always start at zero while
|
|
grid indexes also have to account for the column/row headers.
|
|
|
|
@param ASheetCol Worksheet column index
|
|
@return Grid column index
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetGridCol(ASheetCol: Cardinal): Integer;
|
|
begin
|
|
Result := Integer(ASheetCol) + FHeaderCount
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts a row index of the worksheet to a row index usable in the grid.
|
|
This is required because worksheet indexes always start at zero while
|
|
grid indexes also have to account for the column/row headers.
|
|
|
|
@param ASheetRow Worksheet row index
|
|
@return Grid row index
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetGridRow(ASheetRow: Cardinal): Integer;
|
|
begin
|
|
Result := Integer(ASheetRow) + FHeaderCount;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inherited method. Is overridden to make sure that no column headers are
|
|
drawn when ShowHeaders is false.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetIsCellTitle(ACol, ARow: Integer): Boolean;
|
|
begin
|
|
Result := (FHeaderCount > 0) and (ARow = 0);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns a list of worksheets contained in the file. Useful for assigning to
|
|
user controls like TabControl, Combobox etc. in order to select a sheet.
|
|
|
|
@param ASheets List of strings containing the names of the worksheets of the workbook
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.GetSheets(const ASheets: TStrings);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
ASheets.Clear;
|
|
if Assigned(Workbook) then
|
|
for i:=0 to Workbook.GetWorksheetCount-1 do
|
|
ASheets.Add(Workbook.GetWorksheetByIndex(i).Name);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Calculates the index of the worksheet column that is displayed in the
|
|
given column of the grid. If the sheet headers are turned on, both numbers
|
|
differ by 1, otherwise they are equal. Saves an "if" in some cases.
|
|
|
|
@param AGridCol Index of a grid column
|
|
@return Index of a the corresponding worksheet column
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetWorksheetCol(AGridCol: Integer): cardinal;
|
|
begin
|
|
if (FHeaderCount > 0) and (AGridCol = 0) then
|
|
Result := UNASSIGNED_ROW_COL_INDEX
|
|
else
|
|
Result := AGridCol - FHeaderCount;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Calculates the index of the worksheet row that is displayed in the
|
|
given row of the grid. If the sheet headers are turned on, both numbers
|
|
differ by 1, otherwise they are equal. Saves an "if" in some cases.
|
|
|
|
@param AGridRow Index of a grid row
|
|
@return Index of the corresponding worksheet row.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.GetWorksheetRow(AGridRow: Integer): Cardinal;
|
|
begin
|
|
if (FHeaderCount > 0) and (AGridRow = 0) then
|
|
Result := UNASSIGNED_ROW_COL_INDEX
|
|
else
|
|
Result := AGridRow - FHeaderCount;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Returns true if the cell has the given border. In case of merged cell the
|
|
borders of the merge base are checked. Inner merged cells don't have a border.
|
|
|
|
@param ACell Pointer to cell considered
|
|
@param ABorder Indicator for border to be checked for visibility
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.HasBorder(ACell: PCell; ABorder: TsCellBorder): Boolean;
|
|
var
|
|
base: PCell;
|
|
r1, c1, r2, c2: Cardinal;
|
|
begin
|
|
if Worksheet = nil then
|
|
result := false
|
|
else
|
|
if Worksheet.IsMerged(ACell) then
|
|
begin
|
|
Worksheet.FindMergedRange(ACell, r1, c1, r2, c2);
|
|
base := Worksheet.FindCell(r1, c1);
|
|
Result := ABorder in Worksheet.ReadCellBorders(base);
|
|
case ABorder of
|
|
cbNorth : if ACell^.Row > r1 then Result := false;
|
|
cbSouth : if ACell^.Row < r2 then Result := false;
|
|
cbEast : if ACell^.Col < c2 then Result := false;
|
|
cbWest : if ACell^.Col > c1 then Result := false;
|
|
end;
|
|
end else
|
|
Result := ABorder in Worksheet.ReadCellBorders(ACell);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
HeaderSizing is called while a column width or row height is resized by the
|
|
mouse. Is overridden here to enforce a grid repaint if merged cells are
|
|
affected by the resizing column/row. Otherwise parts of the merged cells would
|
|
not be updated if the cell text moves during the resizing action.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.HeaderSizing(const IsColumn:boolean;
|
|
const AIndex,ASize:Integer);
|
|
var
|
|
gc, gr: Integer;
|
|
sr1, sr2, sc1, sc2, si: Cardinal;
|
|
cell: PCell;
|
|
begin
|
|
inherited;
|
|
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
// replaint the grid if merged cells are affected by the resizing col/row.
|
|
si := IfThen(IsColumn, GetWorksheetCol(AIndex), GetWorksheetRow(AIndex));
|
|
for gc := GetFirstVisibleColumn to GetLastVisibleColumn do
|
|
begin
|
|
for gr := GetFirstVisibleRow to GetLastVisibleRow do
|
|
begin
|
|
cell := Worksheet.FindCell(gr, gc);
|
|
if Worksheet.IsMerged(cell) then begin
|
|
Worksheet.FindMergedRange(cell, sr1, sc1, sr2, sc2);
|
|
if IsColumn and InRange(si, sc1, sc2) or
|
|
(not IsColumn) and InRange(si, sr1, sr2) then
|
|
begin
|
|
InvalidateGrid;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inherited from TCustomGrid. Is called when column widths or row heights
|
|
have changed. Stores the new column width or row height in the worksheet.
|
|
|
|
@param(IsColumn Specifies whether the changed parameter is a column width
|
|
(@true) or a row height (@false))
|
|
@param(Index Index of the changed column or row)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.HeaderSized(IsColumn: Boolean; AIndex: Integer);
|
|
const
|
|
EPS = 0.1;
|
|
var
|
|
idx: Integer;
|
|
w, h, wdef, hdef: Single;
|
|
begin
|
|
if (Worksheet = nil) or (FZoomLock <> 0) or (AIndex < FHeaderCount) then
|
|
exit;
|
|
|
|
if IsColumn then
|
|
begin
|
|
w := CalcWorksheetColWidth(ColWidths[AIndex]); // w and wdef are at 100% zoom
|
|
wdef := Worksheet.ReadDefaultColWidth(Workbook.Units);
|
|
if not SameValue(w, wdef, EPS) then begin
|
|
idx := GetWorksheetCol(AIndex);
|
|
Worksheet.WriteColWidth(idx, w, Workbook.Units);
|
|
end;
|
|
end else
|
|
begin
|
|
h := CalcWorksheetRowHeight(RowHeights[AIndex]);
|
|
hdef := Worksheet.ReadDefaultRowHeight(Workbook.Units);
|
|
if not SameValue(h, hdef, EPS) then begin
|
|
idx := GetWorksheetRow(AIndex);
|
|
Worksheet.WriteRowHeight(idx, h, Workbook.Units);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Clicking into cells with hyperlinks poses a user-interface problem:
|
|
normally the cell should go into edit mode. But with hyperlinks a click should
|
|
also execute the hyperlink. How to distinguish both cases?
|
|
In order to keep both features for hyperlinks we follow a strategy similar to
|
|
Excel: a short click selects the cell for editing as usual; a longer click
|
|
opens the hyperlink by means of a timer ("FHyperlinkTimer") (in Excel, in
|
|
fact, the behavior is opposite, but this one here is easier to implement.)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.HyperlinkTimerElapsed(Sender: TObject);
|
|
begin
|
|
if FHyperlinkTimer.Enabled then begin
|
|
FHyperlinkTimer.Enabled := false;
|
|
FGridState := gsNormal; // this prevents selecting a cell block
|
|
EditorMode := false; // this prevents editing the clicked cell
|
|
ExecuteHyperlink; // Execute the hyperlink
|
|
FHyperlinkCell := nil;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inserts an empty column before the column specified
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.InsertCol(AGridCol: Integer);
|
|
var
|
|
c: Cardinal;
|
|
begin
|
|
if AGridCol < FHeaderCount then
|
|
exit;
|
|
|
|
// if LongInt(Worksheet.GetLastColIndex) + 1 + FHeaderCount >= ColCount then //FInitColCount then
|
|
if GetGridCol(Worksheet.GetLastColIndex + 1) >= ColCount then
|
|
ColCount := ColCount + 1;
|
|
c := GetWorksheetCol(AGridCol);
|
|
Worksheet.InsertCol(c);
|
|
|
|
UpdateColWidths(AGridCol);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inserts an empty row before the row specified
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.InsertRow(AGridRow: Integer);
|
|
var
|
|
r: Cardinal;
|
|
begin
|
|
if AGridRow < FHeaderCount then
|
|
exit;
|
|
|
|
// if LongInt(Worksheet.GetlastRowIndex) + 1 + FHeaderCount >= FInitRowCount then
|
|
if GetGridRow(Worksheet.GetLastRowIndex + 1) >= RowCount then
|
|
RowCount := RowCount + 1;
|
|
r := GetWorksheetRow(AGridRow);
|
|
Worksheet.InsertRow(r);
|
|
|
|
// Calculate row height if new row
|
|
UpdateRowHeight(AGridRow, true);
|
|
|
|
// Update following row heights because their index has changed.
|
|
UpdateRowHeights(AGridRow);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.InternalDrawCell(ACol, ARow: Integer;
|
|
AClipRect, ACellRect: TRect; AState: TGridDrawState);
|
|
|
|
function IsPushCellActive: boolean;
|
|
begin
|
|
with GCache do
|
|
Result := (PushedCell.X <> -1) and (PushedCell.Y <> -1);
|
|
end;
|
|
|
|
var
|
|
rgn: HRGN;
|
|
begin
|
|
with GCache do begin
|
|
if (ACol = HotCell.x) and (ARow = HotCell.y) and not IsPushCellActive() then begin
|
|
Include(AState, gdHot);
|
|
HotCellPainted := True;
|
|
end;
|
|
if ClickCellPushed and (ACol = PushedCell.x) and (ARow = PushedCell.y) then begin
|
|
Include(AState, gdPushed);
|
|
end;
|
|
end;
|
|
Canvas.SaveHandleState;
|
|
try
|
|
rgn := CreateRectRgn(AClipRect.Left, AClipRect.Top, AClipRect.Right, AClipRect.Bottom);
|
|
SelectClipRgn(Canvas.Handle, rgn);
|
|
DrawCell(ACol, ARow, ACellRect, AState);
|
|
DeleteObject(rgn);
|
|
finally
|
|
Canvas.RestoreHandleState;
|
|
end;
|
|
end;
|
|
|
|
{ Draws the cells in the specified row. Drawing takes care of text overflow
|
|
and merged cells.
|
|
AClipRect covers the paintable row, painting outside will be clipped. }
|
|
procedure TsCustomWorksheetGrid.InternalDrawRow(ARow, AFirstCol, ALastCol: Integer;
|
|
AClipRect: TRect);
|
|
var
|
|
sr: Cardinal;
|
|
scLastUsed: Cardinal;
|
|
sr1, sc1, sr2, sc2: Cardinal;
|
|
gr, gc, gc1, gc2, gcNext, gcLast, gcLastUsed: Integer;
|
|
i: Integer;
|
|
tmp: Integer = 0;
|
|
cell: PCell;
|
|
idx: Integer;
|
|
fmt: PsCellFormat;
|
|
rct, clip_rct, commentcell_rct, temp_rct: TRect;
|
|
gds: TGridDrawState;
|
|
clipArea: TRect;
|
|
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
sr := GetWorksheetRow(ARow);
|
|
scLastused := Worksheet.GetLastColIndex;
|
|
gc := AFirstCol;
|
|
gcLast := ALastCol;
|
|
|
|
clipArea := Canvas.ClipRect;
|
|
|
|
with GCache.VisibleGrid do
|
|
begin
|
|
// Because of possible cell overflow from cells left of the visible range
|
|
// we have to seek to the left for the first occupied text cell
|
|
// and start painting from here.
|
|
if FTextOverflow and (sr <> UNASSIGNED_ROW_COL_INDEX) and Assigned(Worksheet) then
|
|
while (gc > FHeaderCount) do
|
|
begin
|
|
dec(gc);
|
|
cell := Worksheet.FindCell(sr, GetWorksheetCol(gc));
|
|
// Empty cell --> proceed with next cell to the left
|
|
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
|
((cell^.ContentType = cctUTF8String) and (cell^.UTF8StringValue = ''))
|
|
then
|
|
Continue;
|
|
// Overflow possible from non-merged, non-right-aligned, horizontal label cells
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(cell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
// fmt := Worksheet.GetPointerToEffectiveCellFormat(cell);
|
|
if (not Worksheet.IsMerged(cell)) and
|
|
(cell^.ContentType = cctUTF8String) and
|
|
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
|
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haRight)
|
|
then
|
|
Break;
|
|
// All other cases --> no overflow --> return to initial left cell
|
|
gc := AFirstCol;
|
|
break;
|
|
end;
|
|
|
|
// Now find the last column. Again text can overflow into the visible area
|
|
// from invisible cells at the right.
|
|
gcLast := ALastCol;
|
|
if FTextOverflow and (sr <> UNASSIGNED_ROW_COL_INDEX) and Assigned(Worksheet) then
|
|
begin
|
|
gcLastUsed := GetGridCol(scLastUsed);
|
|
while (gcLast < ColCount-1) and (gcLast < gcLastUsed) do begin
|
|
inc(gcLast);
|
|
cell := Worksheet.FindCell(sr, GetWorksheetCol(gcLast));
|
|
// Empty cell --> proceed with next cell to the right
|
|
if (cell = nil) or (cell^.ContentType = cctEmpty) or
|
|
((cell^.ContentType = cctUTF8String) and (cell^.UTF8StringValue = ''))
|
|
then
|
|
continue;
|
|
// Overflow possible from non-merged, horizontal, non-left-aligned label cells
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(cell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
if (not Worksheet.IsMerged(cell)) and
|
|
(cell^.ContentType = cctUTF8String) and
|
|
not (uffTextRotation in fmt^.UsedFormattingFields) and
|
|
(uffHorAlign in fmt^.UsedFormattingFields) and (fmt^.HorAlignment <> haLeft)
|
|
then
|
|
Break;
|
|
// All other cases --> no overflow --> return to initial right column
|
|
gcLast := ALastCol;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
// Here begins the drawing loop of all cells in the row between gc and gcLast
|
|
while (gc <= gcLast) do begin
|
|
gr := ARow;
|
|
rct := AClipRect;
|
|
commentcell_rct := Rect(0, 0, 0, 0);
|
|
// FDrawingCell is the cell which is currently being painted. We store
|
|
// it to avoid excessive calls to "FindCell".
|
|
FDrawingCell := nil;
|
|
gcNext := gc + 1;
|
|
if Assigned(Worksheet) and (gr >= FHeaderCount) and (gc >= FHeaderCount) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(gr), GetWorksheetCol(gc));
|
|
if (cell = nil) or (not Worksheet.IsMerged(cell)) then
|
|
begin
|
|
// single cell
|
|
FDrawingCell := cell;
|
|
if Worksheet.HasComment(cell) then
|
|
commentcell_rct := CellRect(gc, gr);
|
|
// Special treatment of overflowing cells
|
|
if FTextOverflow then
|
|
begin
|
|
gds := GetGridDrawState(gc, gr);
|
|
ColRowToOffset(true, true, gc, rct.Left, rct.Right);
|
|
if CellOverflow(gc, gr, gds, gc1, gc2, rct) then
|
|
begin
|
|
// Draw individual cells of the overflown range
|
|
if IsRightToLeft then begin
|
|
ColRowToOffset(true, true, gc1, tmp, rct.Right);
|
|
ColRowToOffset(true, true, gc2, rct.Left, tmp);
|
|
end else begin
|
|
ColRowToOffset(true, true, gc1, rct.Left, tmp); // rct is the clip rect
|
|
ColRowToOffset(true, true, gc2, tmp, rct.Right);
|
|
end;
|
|
FDrawingCell := nil;
|
|
temp_rct := rct;
|
|
for i:= gc2 downto gc1 do begin
|
|
// Starting from last col will ensure drawing grid lines below text
|
|
// when text is overflowing in RTL, no problem in LTR
|
|
// (Modification by "shobits1" - ok)
|
|
ColRowToOffset(true, true, i, temp_rct.Left, temp_rct.Right);
|
|
if HorizontalIntersect(temp_rct, clipArea) and (i <> gc) then
|
|
begin
|
|
gds := GetGridDrawState(i, gr);
|
|
InternalDrawCell(i, gr, rct, temp_rct, gds);
|
|
end;
|
|
end;
|
|
// Repaint the base cell text (it was partly overwritten before)
|
|
FDrawingCell := cell;
|
|
FTextOverflowing := true;
|
|
ColRowToOffset(true, true, gc, temp_rct.Left, temp_rct.Right);
|
|
if HorizontalIntersect(temp_rct, clipArea) then
|
|
begin
|
|
gds := GetGridDrawState(gc, gr);
|
|
IntersectRect(rct, rct, clipArea);
|
|
InternalDrawCell(gc, gr, rct, temp_rct, gds);
|
|
if Worksheet.HasComment(FDrawingCell) then
|
|
DrawCommentMarker(temp_rct);
|
|
end;
|
|
FTextOverflowing := false;
|
|
|
|
gcNext := gc2 + 1;
|
|
gc := gcNext;
|
|
continue;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
// merged cells
|
|
FDrawingCell := Worksheet.FindMergeBase(cell);
|
|
if Worksheet.FindMergedRange(FDrawingCell, sr1, sc1, sr2, sc2) then
|
|
begin
|
|
gr := GetGridRow(sr1);
|
|
if Worksheet.HasComment(FDrawingCell) then
|
|
commentcell_rct := CellRect(GetGridCol(sc2), gr);
|
|
ColRowToOffSet(False, True, gr, rct.Top, tmp);
|
|
ColRowToOffSet(False, True, gr + integer(sr2) - integer(sr1), tmp, rct.Bottom);
|
|
gc := GetGridCol(sc1);
|
|
gcNext := gc + (sc2 - sc1) + 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Take care of upper and lower bounds of merged cells!
|
|
temp_rct := rct;
|
|
rct := CellRect(gc, gr, gcNext-1, gr);
|
|
rct.Top := temp_rct.Top;
|
|
rct.Bottom := temp_rct.Bottom;
|
|
|
|
if (rct.Left < rct.Right) and HorizontalIntersect(rct, clipArea) then
|
|
begin
|
|
// Define clipping rectangle in order to avoid painting into the header cells
|
|
clip_rct := rct;
|
|
clip_rct.Top := AClipRect.Top;
|
|
clip_rct.Bottom := AClipRect.Bottom;
|
|
if (gc >= FHeaderCount + FFrozenCols) then begin
|
|
if UseRightToLeftReading then begin
|
|
if (clip_rct.Right > FTopLeft.X) then
|
|
clip_rct.Right := FTopLeft.X;
|
|
end else
|
|
if (clip_rct.Left < FTopLeft.X) then
|
|
clip_rct.Left := FTopLeft.X;
|
|
end;
|
|
gds := GetGridDrawState(gc, gr);
|
|
|
|
// Draw cell
|
|
InternalDrawCell(gc, gr, clip_rct, rct, gds);
|
|
// Draw comment marker
|
|
if (commentcell_rct.Left <> 0) and (commentcell_rct.Right <> 0) and
|
|
(commentcell_rct.Top <> 0) and (commentcell_rct.Bottom <> 0)
|
|
then
|
|
DrawCommentMarker(commentcell_rct);
|
|
end;
|
|
|
|
gc := gcNext;
|
|
end;
|
|
end; // with GCache.VisibleGrid ...
|
|
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Internal general text drawing method.
|
|
|
|
@param(AText Text to be drawn)
|
|
@param(AMeasureText Text used for checking if the text fits into the text
|
|
rectangle. If too large and ReplaceTooLong = @true,
|
|
a series of # characters is drawn.)
|
|
@param(ARect Rectangle in which the text is drawn.)
|
|
@param(AJustification Determines whether the text is drawn at the "start" (0),
|
|
"center" (1) or "end" (2) of the drawing rectangle.
|
|
Start/center/end are seen along the text drawing direction.)
|
|
@param(ACellHorAlign Is the HorAlignment property stored in the cell.)
|
|
@param(ACellVertAlign Is the VertAlignment property stored in the cell.)
|
|
@param(ATextRot Determines the rotation angle of the text.)
|
|
@param(ATextWrap Determines if the text can wrap into multiple lines.)
|
|
@param(AFontIndex Font index to be used for drawing non-rich-text.)
|
|
@param(ARichTextParams An array of character and font index combinations
|
|
for rich-text formatting of text in cell)
|
|
@param(AIsRightToLeft If @true cell must be drawn in right-to-left mode.)
|
|
|
|
@Note(The reason to separate AJustification from ACellHorAlign and ACelVertAlign
|
|
is the output of nfAccounting formatted numbers where the numbers are
|
|
always right-aligned, and the currency symbol is left-aligned.
|
|
THIS FEATURE IS CURRENTLY NO LONGER SUPPORTED.)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.InternalDrawTextInCell(AText: String;
|
|
ARect: TRect; ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment;
|
|
ATextRot: TsTextRotation; ATextWrap: Boolean; AFontIndex: Integer;
|
|
AOverrideTextColor: TColor; ARichTextParams: TsRichTextParams;
|
|
AIsRightToLeft: Boolean);
|
|
begin
|
|
// Since - due to the rich-text mode - characters are drawn individually their
|
|
// background occasionally overpaints the prev characters (italic). To avoid
|
|
// this we do not paint the character background - it is not needed anyway.
|
|
Canvas.Brush.Style := bsClear;
|
|
|
|
// Work horse for text drawing, both standard text and rich-text
|
|
DrawRichText(Canvas, Workbook, ARect, AText, ARichTextParams, AFontIndex,
|
|
ATextWrap, ACellHorAlign, ACellVertAlign, ATextRot, AOverrideTextColor,
|
|
AIsRightToLeft, ZoomFactor
|
|
);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard key handling method inherited from TCustomGrid. Is overridden to
|
|
catch some keys for special processing.
|
|
|
|
@param Key Key which has been pressed
|
|
@param Shift Additional shift keys which are pressed
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.KeyDown(var Key : Word; Shift : TShiftState);
|
|
var
|
|
R: TRect;
|
|
msg: String;
|
|
begin
|
|
FFormulaError := false;
|
|
// Check validity for formula before navigating to another cell.
|
|
case Key of
|
|
VK_RETURN,
|
|
VK_TAB,
|
|
VK_LEFT, VK_RIGHT,
|
|
VK_UP, VK_DOWN,
|
|
VK_PRIOR, VK_NEXT,
|
|
VK_END, VK_HOME:
|
|
if not ValidFormula(FEditText, msg) then begin
|
|
FFormulaError := true;
|
|
MessageDlg(msg, mtError, [mbOK], 0);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
case Key of
|
|
VK_RIGHT:
|
|
if (aeNavigation in FAutoExpand) and (Col = ColCount-1) then
|
|
ColCount := ColCount + 1;
|
|
VK_DOWN:
|
|
if (aeNavigation in FAutoExpand) and (Row = RowCount-1) then
|
|
RowCount := RowCount + 1;
|
|
VK_END:
|
|
if (aeNavigation in FAutoExpand) and (Col = ColCount-1) then
|
|
begin
|
|
R := GCache.FullVisibleGrid;
|
|
ColCount := ColCount + R.Right - R.Left;
|
|
end;
|
|
VK_NEXT: // Page down
|
|
if (aeNavigation in FAutoExpand) and (Row = RowCount-1) then
|
|
begin
|
|
R := GCache.FullVisibleGrid;
|
|
RowCount := Row + R.Bottom - R.Top;
|
|
end;
|
|
VK_F2:
|
|
FEnhEditMode := true;
|
|
VK_DELETE:
|
|
if (not FReadOnly) and (goEditing in Options) then begin
|
|
if (ssCtrl in Shift) then
|
|
Worksheet.DeleteSelection
|
|
else
|
|
Worksheet.EraseSelection(true);
|
|
end;
|
|
VK_X, VK_V:
|
|
if (Shift = [ssCtrl]) and (Worksheet.IsProtected or not (goEditing in Options)) then
|
|
Key := 0;
|
|
end;
|
|
|
|
inherited;
|
|
|
|
case Key of
|
|
VK_C, VK_X, VK_V:
|
|
if Shift = [ssCtrl] then Key := 0;
|
|
// Clipboard has already been handled, avoid passing key to CellAction
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.KeyUp(var Key : Word; Shift : TShiftState);
|
|
begin
|
|
if FFormulaError and (Key = VK_RETURN) then
|
|
Key := 0;
|
|
inherited;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Creates a new workbook and loads the given file into it. The file is assumed
|
|
to have the given file format. Shows the sheet with the given sheet index.
|
|
|
|
Call this method only for built-in file formats.
|
|
|
|
@param(AFileName Name of the file to be loaded)
|
|
@param(AFormat Spreadsheet file format assumed for the file)
|
|
@param(AWorksheetIndex Index of the worksheet to be displayed in the grid
|
|
(If empty then the active worksheet is loaded))
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
|
|
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer);
|
|
var
|
|
ae: TsAutoExpandModes;
|
|
begin
|
|
ae := RelaxAutoExpand;
|
|
GetWorkbookSource.LoadFromSpreadsheetFile(AFileName, AFormat, AWorksheetIndex);
|
|
RestoreAutoExpand(ae);
|
|
SetAutoDetectCellType(FAutoDetectCellType);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Creates a new workbook and loads the given file into it. The file is assumed
|
|
to have the given file format. Shows the sheet with the given sheet index.
|
|
|
|
Call this method for both built-in and user-provided file formats.
|
|
|
|
@param(AFileName Name of the file to be loaded)
|
|
@param(AFormatID Spreadsheet file format identifier assumed for the
|
|
file (automatic detection if empty))
|
|
@param(AWorksheetIndex Index of the worksheet to be displayed in the grid
|
|
(If empty then the active worksheet is loaded))
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
|
|
AFormatID: TsSpreadFormatID = sfidUnknown; AWorksheetIndex: Integer = -1);
|
|
var
|
|
ae: TsAutoExpandModes;
|
|
begin
|
|
ae := RelaxAutoExpand;
|
|
GetWorkbookSource.LoadFromSpreadsheetFile(AFileName, AFormatID, AWorksheetIndex);
|
|
RestoreAutoExpand(ae);
|
|
SetAutoDetectCellType(FAutoDetectCellType);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Creates a new workbook and loads the given file into it. Shows the sheet
|
|
with the specified sheet index. The file format is determined automatically.
|
|
|
|
@param(AFileName Name of the file to be loaded)
|
|
@param(AWorksheetIndex Index of the worksheet to be shown in the grid
|
|
(If empty then the active worksheet is loaded))
|
|
@param(AFormatID Spreadsheet file format identifier assumed for the
|
|
file (automatic detection if empty))
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.LoadSheetFromSpreadsheetFile(AFileName: String;
|
|
AWorksheetIndex: Integer = -1; AFormatID: TsSpreadFormatID = sfidUnknown);
|
|
var
|
|
ae: TsAutoExpandModes;
|
|
begin
|
|
ae := RelaxAutoExpand;
|
|
GetWorkbookSource.LoadFromSpreadsheetFile(AFilename, AFormatID, AWorksheetIndex);
|
|
RestoreAutoExpand(ae);
|
|
SetAutoDetectCellType(FAutoDetectCellType);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Loads an existing workbook into the grid.
|
|
|
|
@param(AWorkbook Workbook that had been created/loaded before. )
|
|
@param(AWorksheetIndex Index of the worksheet to be shown in the grid
|
|
(If empty then the active worksheet is loaded))
|
|
|
|
@Note(THE CALLING PROCEDURE MUST NOT DESTROY THE WORKBOOK!
|
|
The workbook will be destroyed by the workbook source.)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.LoadFromWorkbook(AWorkbook: TsWorkbook;
|
|
AWorksheetIndex: Integer = -1);
|
|
var
|
|
ae: TsAutoExpandModes;
|
|
begin
|
|
ae := RelaxAutoExpand;
|
|
GetWorkbookSource.LoadFromWorkbook(AWorkbook, AWorksheetIndex);
|
|
RestoreAutoExpand(ae);
|
|
SetAutoDetectCellType(FAutoDetectCellType);
|
|
Invalidate;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Notification message received from the WorkbookLink telling which item of the
|
|
spreadsheet has changed.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ListenerNotification(AChangedItems: TsNotificationItems;
|
|
AData: Pointer = nil);
|
|
var
|
|
actgrow, actgcol: Integer;
|
|
grow, gcol: Integer;
|
|
srow, scol: Cardinal;
|
|
cell: PCell;
|
|
lRow: PRow;
|
|
|
|
{$IFDEF GRID_DEBUG}
|
|
procedure DebugNotification(ACaption: String);
|
|
var
|
|
s: String;
|
|
begin
|
|
WriteLn(ACaption);
|
|
s := '';
|
|
if (lniWorksheet in AChangedItems) then s := s + 'lniWorksheet, ';
|
|
if (lniCell in AChangedItems) then s := s + 'lniCell, ';
|
|
if (lniSelection in AChangedItems) then s := s + 'lniSelection, ';
|
|
if (lniAbortSelection in AChangedItems) then s := s + 'lniAbortSelection, ';
|
|
if (lniRow in AChangedItems) then s := s + 'lniRow, ';
|
|
if (lniCol in AChangedItems) then s := s + 'lniCol, ';
|
|
if (lniWorksheetZoom in AChangedItems) then s := s + 'lniWorksheetZoom, ';
|
|
if s <> '' then SetLength(s, Length(s) - 2);
|
|
WriteLn(' AChangedItems = [', s, ']');
|
|
WriteLn(' ActiveCellRow: ', Worksheet.ActiveCellRow, ' ActiveCellCol: ', Worksheet.ActiveCellCol);
|
|
WriteLn(' TopRow: ', Worksheet.TopRow, ' LeftCol: ', Worksheet.LeftCol);
|
|
WriteLn;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
begin
|
|
Unused(AData);
|
|
if WorkbookSource.UsesBuiltinWorkbook then
|
|
exit;
|
|
|
|
{$IFDEF GRID_DEBUG}
|
|
if Worksheet <> nil then
|
|
DebugNotification('BEFORE ListenerNotification WorksheetGrid "' + Worksheet.Name + '":');
|
|
{$ENDIF}
|
|
|
|
// Nothing to do for "workbook changed" because this is always combined with
|
|
// "worksheet changed".
|
|
|
|
// Worksheet changed
|
|
if (lniWorksheet in AChangedItems) then
|
|
begin
|
|
BeginUpdate; // avoid flicker...
|
|
try
|
|
if (Worksheet <> nil) then
|
|
begin
|
|
// remember indexes of top/left and active cell
|
|
grow := GetGridRow(Worksheet.TopRow);
|
|
gcol := GetGridCol(Worksheet.LeftCol);
|
|
actgrow := GetGridRow(Worksheet.ActiveCellRow);
|
|
actgcol := GetGridCol(Worksheet.ActiveCellCol);
|
|
AutoExpandToRow(grow, aeNavigation);
|
|
AutoExpandToCol(gcol, aeNavigation);
|
|
if (grow <> Row) or (gcol <> Col) then
|
|
MoveExtend(false, gcol, grow);
|
|
inc(FLockSetup);
|
|
// Setup grid headers and col/row count
|
|
ShowHeaders := (soShowHeaders in Worksheet.Options);
|
|
ShowGridLines := (soShowGridLines in Worksheet.Options);
|
|
if (soHasFrozenPanes in Worksheet.Options) then begin
|
|
FrozenCols := Worksheet.LeftPaneWidth;
|
|
FrozenRows := Worksheet.TopPaneHeight;
|
|
end else begin
|
|
FrozenCols := 0;
|
|
FrozenRows := 0;
|
|
end;
|
|
case Worksheet.BiDiMode of
|
|
bdDefault: ParentBiDiMode := true;
|
|
bdLTR : begin
|
|
ParentBiDiMode := false;
|
|
BiDiMode := bdLeftToRight;
|
|
end;
|
|
bdRTL : begin
|
|
ParentBiDiMode := false;
|
|
BiDiMode := bdRightToLeft;
|
|
end;
|
|
end;
|
|
dec(FLockSetup);
|
|
end;
|
|
Setup;
|
|
// scroll the grid for top/left to be as stored in the sheet
|
|
if (grow <> TopRow) or (gcol <> LeftCol) then
|
|
begin
|
|
TopRow := gRow;
|
|
LeftCol := gCol;
|
|
end;
|
|
// Select active cell
|
|
AutoExpandToRow(actgrow, aeNavigation);
|
|
AutoExpandToCol(actgcol, aeNavigation);
|
|
if (actgrow <> Row) or (actgcol <> Col) then
|
|
MoveExtend(false, actgcol, actgrow);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
// Cell value or format changed
|
|
if (lniCell in AChangedItems) then
|
|
begin
|
|
cell := PCell(AData);
|
|
if (cell <> nil) then begin
|
|
grow := GetGridRow(cell^.Row);
|
|
gcol := GetGridCol(cell^.Col);
|
|
AutoExpandToRow(grow, aeData);
|
|
AutoExpandToCol(gcol, aeData);
|
|
lRow := Worksheet.FindRow(cell^.Row);
|
|
if (lRow = nil) or (lRow^.RowHeightType <> rhtCustom) then
|
|
UpdateRowHeight(grow, true);
|
|
end;
|
|
Invalidate;
|
|
end;
|
|
|
|
// Selection changed
|
|
if (lniSelection in AChangedItems) and (Worksheet <> nil) then
|
|
begin
|
|
grow := GetGridRow(Worksheet.ActiveCellRow);
|
|
gcol := GetGridCol(Worksheet.ActiveCellCol);
|
|
AutoExpandToRow(grow, aeNavigation);
|
|
AutoExpandToCol(gcol, aeNavigation);
|
|
if (grow <> Row) or (gcol <> Col) then
|
|
MoveExtend(false, gcol, grow);
|
|
if Worksheet.IsProtected then
|
|
begin
|
|
cell := Worksheet.FindCell(Worksheet.ActiveCellRow, Worksheet.ActiveCellCol);
|
|
FReadOnly := (cell = nil) or (cpLockCell in Worksheet.ReadCellProtection(cell));
|
|
end else
|
|
FReadOnly := false;
|
|
end;
|
|
|
|
// Abort selection because of an error
|
|
if (lniAbortSelection in AChangedItems) and (Worksheet <> nil) then
|
|
begin
|
|
MouseUp(mbLeft, [], GCache.ClickMouse.X, GCache.ClickMouse.Y);
|
|
// HOW TO DO THIS???? SelectActive not working...
|
|
end;
|
|
|
|
// Row height (after font or row record change).
|
|
if (lniRow in AChangedItems) and (Worksheet <> nil) then
|
|
begin
|
|
srow := {%H-}PtrInt(AData); // sheet row
|
|
grow := GetGridRow(srow); // grid row
|
|
AutoExpandToRow(grow, aeData);
|
|
lRow := Worksheet.FindRow(srow);
|
|
if (lRow = nil) or (lRow^.RowHeightType <> rhtCustom) then
|
|
UpdateRowHeight(grow, true);
|
|
end;
|
|
|
|
// Column width
|
|
if (lniCol in AChangedItems) and (Worksheet <> nil) then
|
|
begin
|
|
scol := {%H-}PtrInt(AData); // sheet column index
|
|
gcol := GetGridCol(scol);
|
|
//lCol := Worksheet.FindCol(scol);
|
|
UpdateColWidth(gcol);
|
|
end;
|
|
|
|
// Worksheet zoom
|
|
if (lniWorksheetZoom in AChangedItems) and (Worksheet <> nil) then
|
|
AdaptToZoomFactor; // Reads value directly from Worksheet
|
|
|
|
{$IFDEF GRID_DEBUG}
|
|
if Worksheet <> nil then
|
|
DebugNotification('AFTER ListenerNotification WorksheetGrid "' + Worksheet.Name + '":');
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Merges the selected cells to a single large cell
|
|
Only the upper left cell can have content and formatting (which is extended
|
|
into the other cells).
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MergeCells;
|
|
begin
|
|
MergeCells(Selection);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Merges the cells of the specified cell block to a single large cell
|
|
Only the upper left cell can have content and formatting (which is extended
|
|
into the other cells).
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MergeCells(ARect: TGridRect);
|
|
begin
|
|
MergeCells(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Merges the cells of the specified cell block to a single large cell
|
|
Only the upper left cell can have content and formatting (which is extended
|
|
into the other cells).
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MergeCells(ALeft, ATop, ARight, ABottom: Integer);
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Worksheet.MergeCells(
|
|
GetWorksheetRow(ATop),
|
|
GetWorksheetCol(ALeft),
|
|
GetWorksheetRow(ABottom),
|
|
GetWorksheetCol(ARight)
|
|
);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard mouse down handler. Is overridden here to handle hyperlinks and to
|
|
enter "enhanced edit mode" which removes formatting from the values and
|
|
presents formulas for editing.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MouseDown(Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
{todo: extend such that the hyperlink is handled only when the text is clicked
|
|
(tough because of overflow cells!) }
|
|
var
|
|
mouseCell: TPoint;
|
|
cell: PCell;
|
|
r, c: Cardinal;
|
|
err: String;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
if not ValidFormula(FEditText, err) then begin
|
|
MessageDlg(err, mtError, [mbOK], 0);
|
|
exit;
|
|
end;
|
|
|
|
mouseCell := MouseToCell(Point(X, Y));
|
|
if FAllowDragAndDrop and
|
|
(not Assigned(DragManager) or not DragManager.IsDragging) and
|
|
(ssLeft in Shift) and
|
|
MouseOnCellBorder(Point(X, Y), Selection) then
|
|
begin
|
|
if DragBorderBitmap = nil then
|
|
CreateFillPattern(DragBorderBitmap, fsGray50, clBlack, clWhite);
|
|
FDragStartCol := Col;
|
|
FDragStartRow := Row;
|
|
FOldDragStartCol := Col;
|
|
FOldDragStartRow := Row;
|
|
BeginDrag(false, DragManager.DragThreshold);
|
|
exit;
|
|
end;
|
|
|
|
inherited;
|
|
|
|
if (ssLeft in Shift) then
|
|
begin
|
|
{ Prepare processing of the hyperlink: triggers a timer, the hyperlink is
|
|
executed when the timer has expired (see HyperlinkTimerElapsed). }
|
|
r := GetWorksheetRow(mouseCell.Y);
|
|
c := GetWorksheetCol(mouseCell.X);
|
|
cell := Worksheet.FindCell(r, c);
|
|
if Worksheet.IsMerged(cell) then
|
|
cell := Worksheet.FindMergeBase(cell);
|
|
if Worksheet.HasHyperlink(cell) then
|
|
begin
|
|
FHyperlinkCell := cell;
|
|
FHyperlinkTimer.Enabled := true;
|
|
end else
|
|
begin
|
|
FHyperlinkCell := nil;
|
|
FHyperlinkTimer.Enabled := false;
|
|
end;
|
|
end;
|
|
|
|
FEnhEditMode := true;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard mouse move handler. Is overridden because, if TextOverflow is active,
|
|
overflown cell may be erased when the mouse leaves them; repaints entire
|
|
grid instead.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
inherited;
|
|
|
|
if MouseOnHeader(X,Y) then
|
|
exit;
|
|
|
|
if Assigned(Dragmanager) and DragManager.IsDragging then
|
|
begin
|
|
Cursor := crDefault;
|
|
end else
|
|
begin
|
|
if FHyperlinkTimer.Enabled and (ssLeft in Shift) then
|
|
FHyperlinkTimer.Enabled := false;
|
|
|
|
if MouseOnCellBorder(Point(X, Y), Selection) then
|
|
FDragTimer.Enabled := true
|
|
else
|
|
Cursor := crDefault;
|
|
end;
|
|
end;
|
|
|
|
{@@ Checks whether the specified point is on the border of a given cell.
|
|
The tolerance is defined by the global variable CELL_BORDER_DELTA }
|
|
function TsCustomWorksheetGrid.MouseOnCellBorder(const APoint: TPoint;
|
|
const ACellRect: TGridRect): Boolean;
|
|
var
|
|
R: TRect;
|
|
R1, R2: TRect;
|
|
begin
|
|
R := CellRect(ACellRect.Left, ACellRect.Top, ACellRect.Right, ACellRect.Bottom);
|
|
R1 := R;
|
|
InflateRect(R1, CELL_BORDER_DELTA, CELL_BORDER_DELTA);
|
|
R2 := R;
|
|
InflateRect(R2, -CELL_BORDER_DELTA, -CELL_BORDER_DELTA);
|
|
Result := PtInRect(R1, APoint) and not PtInRect(R2, APoint);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.MouseOnHeader(X, Y: Integer): Boolean;
|
|
var
|
|
R: TRect;
|
|
begin
|
|
if FHeaderCount = 0 then
|
|
exit(false);
|
|
|
|
R := CellRect(0, 0);
|
|
Result := (Y < R.Bottom) or (X < R.Right);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.MouseUp(Button: TMouseButton;
|
|
Shift:TShiftState; X,Y:Integer);
|
|
begin
|
|
if FHyperlinkTimer.Enabled then begin
|
|
FHyperlinkTimer.Enabled := false;
|
|
FHyperlinkCell := nil;
|
|
end;
|
|
|
|
inherited;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard method inherited from TCustomGrid.
|
|
Notifies the WorkbookSource of the changed selected cell.
|
|
Repaints the grid after moving selection to avoid spurious rests of the
|
|
old thick selection border.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.MoveSelection;
|
|
var
|
|
sel: TsCellRangeArray = nil;
|
|
{$IFNDEF FPS_NO_GRID_MULTISELECT}
|
|
i: Integer;
|
|
{$ENDIF}
|
|
begin
|
|
if (FActiveCellLock > 0) or (Assigned(DragManager) and DragManager.IsDragging) then
|
|
exit;
|
|
|
|
if Worksheet <> nil then
|
|
begin
|
|
{$IFNDEF FPS_NO_GRID_MULTISELECT}
|
|
if HasMultiSelection then
|
|
begin
|
|
SetLength(sel, SelectedRangeCount);
|
|
for i:=0 to High(sel) do
|
|
with SelectedRange[i] do
|
|
begin
|
|
sel[i].Row1 := GetWorksheetRow(Top);
|
|
sel[i].Col1 := GetWorksheetCol(Left);
|
|
sel[i].Row2 := GetWorksheetRow(Bottom);
|
|
sel[i].Col2 := GetWorksheetCol(Right);
|
|
end;
|
|
end else
|
|
begin
|
|
SetLength(sel, 1);
|
|
sel[0].Row1 := GetWorksheetRow(Selection.Top);
|
|
sel[0].Col1 := GetWorksheetCol(Selection.Left);
|
|
sel[0].Row2 := GetWorksheetRow(Selection.Bottom);
|
|
sel[0].Col2 := GetWorksheetRow(Selection.Right);
|
|
end;
|
|
{$ELSE}
|
|
SetLength(sel, 1);
|
|
sel[0].Row1 := GetWorksheetRow(Selection.Top);
|
|
sel[0].Col1 := GetWorksheetCol(Selection.Left);
|
|
sel[0].Row2 := GetWorksheetRow(Selection.Bottom);
|
|
sel[0].Col2 := GetWorksheetRow(Selection.Right);
|
|
{$ENDIF}
|
|
Worksheet.SetSelection(sel);
|
|
|
|
Worksheet.SelectCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
|
|
end;
|
|
inherited;
|
|
Refresh;
|
|
end;
|
|
|
|
{$IFNDEF LCL_FULLVERSION_LT_v190} // Supported by Laz v1.9+
|
|
function TsCustomWorksheetGrid.MoveNextSelectable(Relative: Boolean; DCol, DRow: Integer
|
|
): Boolean;
|
|
var
|
|
cell: PCell;
|
|
r1, r2, c1, c2: Cardinal;
|
|
begin
|
|
if Relative then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col));
|
|
if Worksheet.IsMerged(cell) then begin
|
|
Worksheet.FindMergedRange(cell, r1,c1,r2,c2);
|
|
// we are only interested on relative movement (basically by keyboard)
|
|
if DCol>0 then DCol := GetGridCol(c2) - Col + 1 else
|
|
if DCol<0 then DCol := GetGridCol(c1) - Col - 1 else
|
|
if DRow>0 then DRow := GetGridRow(r2) - Row + 1 else
|
|
if DRow<0 then DRow := GetGridRow(r1) - Row - 1;
|
|
end;
|
|
end;
|
|
Result := inherited MoveNextSelectable(Relative, DCol, DRow);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Creates a new empty workbook with the specified number of columns and rows.
|
|
|
|
@param AColCount Number of columns
|
|
@param ARowCount Number of rows
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.NewWorkbook(AColCount, ARowCount: Integer);
|
|
begin
|
|
GetWorkbookSource.CreateNewWorkbook;
|
|
ColCount := AColCount + FHeaderCount;
|
|
RowCount := ARowCount + FHeaderCount;
|
|
Setup;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard component notification: The grid is notified that the WorkbookLink
|
|
is being removed.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) and (AComponent = FWorkbookSource) then
|
|
SetWorkbookSource(nil);
|
|
end;
|
|
|
|
{@@ Prepares the Canvas default font for methods determining text size }
|
|
procedure TsCustomWorksheetGrid.PrepareCanvasFont;
|
|
var
|
|
fnt: TsFont;
|
|
begin
|
|
if Worksheet = nil then
|
|
Canvas.Font.Assign(Font)
|
|
else
|
|
begin
|
|
fnt := Workbook.GetDefaultFont;
|
|
Convert_sFont_to_Font(fnt, Canvas.Font);
|
|
end;
|
|
Canvas.Font.Height := Round(ZoomFactor * Canvas.Font.Height);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.RelaxAutoExpand: TsAutoExpandModes;
|
|
begin
|
|
Result := FAutoExpand;
|
|
FAutoExpand := [aeData, aeNavigation];
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.RestoreAutoExpand(AValue: TsAutoExpandModes);
|
|
begin
|
|
FAutoExpand := AValue;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Removes the link of the WorksheetGrid to the WorkbookSource.
|
|
Required before destruction.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.RemoveWorkbookSource;
|
|
begin
|
|
SetWorkbookSource(nil);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Writes the workbook represented by the grid to a spreadsheet file.
|
|
|
|
Call this method only for built-in file formats.
|
|
|
|
@param(AFileName Name of the file to which the workbook is to be saved.)
|
|
@param(AFormat Spreadsheet file format in which the file is to be saved.)
|
|
@param(AOverwriteExisting If the file already exists, it is overwritten in the
|
|
case of AOverwriteExisting = @true, or an exception
|
|
is raised if AOverwriteExisting = @false)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SaveToSpreadsheetFile(AFileName: String;
|
|
AFormat: TsSpreadsheetFormat; AOverwriteExisting: Boolean = true);
|
|
begin
|
|
if Workbook <> nil then
|
|
Workbook.WriteToFile(AFileName, AFormat, AOverwriteExisting);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Writes the workbook represented by the grid to a spreadsheet file.
|
|
|
|
Call this method for both built-in and user-provided file formats.
|
|
|
|
@param(AFileName Name of the file to which the workbook is to be saved.)
|
|
@param(AFormatID Identifier for the spreadsheet file format in which
|
|
the file is to be saved.)
|
|
@param(AOverwriteExisting If the file already exists, it is overwritten in
|
|
the case of AOverwriteExisting = @true, or an
|
|
exception is raised if AOverwriteExisting = @false)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SaveToSpreadsheetFile(AFileName: String;
|
|
AFormatID: TsSpreadFormatID; AOverwriteExisting: Boolean = true);
|
|
begin
|
|
if Workbook <> nil then
|
|
Workbook.WriteToFile(AFileName, AFormatID, AOverwriteExisting);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Saves the workbook into a file with the specified file name. If this file
|
|
name already exists the file is overwritten if AOverwriteExisting is true.
|
|
|
|
@param(AFileName Name of the file to which the workbook is to be saved.
|
|
If the file format is not known it is written as BIFF8/XLS.)
|
|
@param(AOverwriteExisting If this file already exists it is overwritten if
|
|
AOverwriteExisting = @true, or an exception is raised
|
|
if AOverwriteExisting = @false.)
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SaveToSpreadsheetFile(AFileName: String;
|
|
AOverwriteExisting: Boolean = true);
|
|
begin
|
|
if Workbook <> nil then
|
|
Workbook.WriteToFile(AFileName, AOverwriteExisting);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Loads the workbook into the grid and selects the sheet with the given index.
|
|
"Selected" means here that the sheet is loaded into the grid.
|
|
|
|
@param AIndex Index of the worksheet to be shown in the grid
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SelectSheetByIndex(AIndex: Integer);
|
|
begin
|
|
GetWorkbookSource.SelectWorksheet(Workbook.GetWorksheetByIndex(AIndex));
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard method inherited from TCustomGrid. Is overridden to prevent
|
|
selection of cells in a protected worksheet. Details depend on whether
|
|
the elements spSelectLockedCells and/or spSelectUnlockedCells are included in
|
|
the worksheet's set of protections, and whether the cell to be selected is
|
|
locked or not.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.SelectCell(ACol, ARow: Integer): Boolean;
|
|
var
|
|
cell: PCell;
|
|
cp: TsCellProtections;
|
|
err: String;
|
|
begin
|
|
// Checking validity of formula in current cell
|
|
if Assigned(Worksheet) and EditorMode then begin
|
|
if not ValidFormula(FEditText, err) then begin
|
|
FGridState := gsNormal;
|
|
MessageDlg(err, mtError, [mbOK], 0);
|
|
Result := false;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Result := inherited;
|
|
|
|
if Result and Assigned(Worksheet) and Worksheet.IsProtected then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
cp := Worksheet.ReadCellProtection(cell);
|
|
if (cpLockCell in cp) and (spSelectLockedCells in Worksheet.Protection) then
|
|
Result := false;
|
|
if not (cpLockCell in cp) and (spSelectUnlockedCells in Worksheet.Protection) then
|
|
Result := false;
|
|
end;
|
|
end;
|
|
|
|
{@@ Event handler which fires when an element of the SelectionPen changes. }
|
|
procedure TsCustomWorksheetGrid.GenericPenChangeHandler(Sender: TObject);
|
|
begin
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard method inherited from TCustomGrid. Fetches the text that is
|
|
currently in the editor. It is not yet transferred to the worksheet because
|
|
input will be checked only at the end of editing.
|
|
|
|
@param ACol Grid column index of the cell being edited
|
|
@param ARow Grid row index of the cell being edited
|
|
@param AValue String which is currently in the cell editor
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SetEditText(ACol, ARow: Longint; const AValue: string);
|
|
begin
|
|
FEditText := AValue;
|
|
FEditing := true;
|
|
inherited SetEditText(aCol, aRow, aValue);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Helper method for setting up the rows and columns after a new workbook is
|
|
loaded or created. Sets up the grid's column and row count, as well as the
|
|
initial column widths and row heights.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Setup;
|
|
var
|
|
maxColCount, maxRowCount: Integer;
|
|
begin
|
|
if csLoading in ComponentState then
|
|
exit;
|
|
|
|
if FLockSetup > 0 then
|
|
exit;
|
|
|
|
if not HandleAllocated then //or (not Parent.HandleAllocated) then
|
|
//Avoid crash when accessing the canvas, e.g. in GetDefaultHeaderColWidth
|
|
exit;
|
|
|
|
if (Worksheet = nil) or (Worksheet.GetCellCount = 0) then begin
|
|
FixedCols := FFrozenCols + FHeaderCount;
|
|
FixedRows := FFrozenRows + FHeaderCount;
|
|
if ShowHeaders then begin
|
|
PrepareCanvasFont; // Applies the zoom factor
|
|
ColWidths[0] := GetFixedColWidth;
|
|
RowHeights[0] := GetDefaultRowHeight;
|
|
end;
|
|
FTopLeft := CalcTopLeft(false);
|
|
end else
|
|
if Worksheet <> nil then begin
|
|
maxColCount := IfThen(aeDefault in FAutoExpand, DEFAULT_COL_COUNT, 1);
|
|
maxRowCount := IfThen(aeDefault in FAutoExpand, DEFAULT_ROW_COUNT, 1);
|
|
ColCount := Max(GetGridCol(Worksheet.GetLastColIndex) + 1, maxColCount);
|
|
RowCount := Max(GetGridRow(Worksheet.GetLastRowIndex) + 1, maxRowCount);
|
|
FixedCols := FFrozenCols + FHeaderCount;
|
|
FixedRows := FFrozenRows + FHeaderCount;
|
|
if ShowHeaders then begin
|
|
PrepareCanvasFont;
|
|
ColWidths[0] := GetFixedColWidth;
|
|
RowHeights[0] := GetDefaultRowHeight;
|
|
end;
|
|
FTopLeft := CalcTopLeft(false);
|
|
end;
|
|
UpdateColWidths;
|
|
UpdateRowHeights;
|
|
//Invalidate; // wp: really needed? Might cause flicker
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Setter to define the link to the workbook.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.SetWorkbookSource(AValue: TsWorkbookSource);
|
|
begin
|
|
if AValue = FWorkbookSource then
|
|
exit;
|
|
|
|
if FWorkbookSource <> nil then
|
|
FWorkbookSource.RemoveListener(self);
|
|
FInternalWorkbookSource.RemoveListener(self);
|
|
|
|
if (AValue = FInternalWorkbookSource) or (AValue = nil) then
|
|
begin
|
|
FWorkbookSource := nil;
|
|
FInternalWorkbookSource.AddListener(self);
|
|
end else
|
|
begin
|
|
FWorkbookSource := AValue;
|
|
FWorkbookSource.AddListener(self);
|
|
end;
|
|
|
|
if not (csDestroying in ComponentState) and Assigned(Parent) then
|
|
ListenerNotification([lniWorksheet, lniSelection]);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Shows cell borders for the cells in the range between columns ALeft and ARight
|
|
and rows ATop and ABottom.
|
|
The border of the block's left outer edge is defined by ALeftOuterStyle,
|
|
that of the block's top outer edge by ATopOuterStyle, etc.
|
|
Set the color of a border style to scNotDefined or scTransparent in order to
|
|
hide the corresponding border line, or use the constant NO_CELL_BORDER.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ShowCellBorders(ALeft, ATop, ARight, ABottom: Integer;
|
|
const ALeftOuterStyle, ATopOuterStyle, ARightOuterStyle, ABottomOuterStyle,
|
|
AHorInnerStyle, AVertInnerStyle: TsCellBorderStyle);
|
|
|
|
function BorderVisible(const AStyle: TsCellBorderStyle): Boolean;
|
|
begin
|
|
Result := (AStyle.Color <> scNotDefined) and (AStyle.Color <> scTransparent);
|
|
end;
|
|
|
|
procedure ProcessBorder(ARow, ACol: Cardinal; ABorder: TsCellBorder;
|
|
const AStyle: TsCellBorderStyle);
|
|
var
|
|
cb: TsCellBorders = [];
|
|
cell: PCell;
|
|
begin
|
|
cell := Worksheet.FindCell(ARow, ACol);
|
|
if cell <> nil then
|
|
cb := Worksheet.ReadCellBorders(cell);
|
|
if BorderVisible(AStyle) then
|
|
begin
|
|
Include(cb, ABorder);
|
|
cell := Worksheet.WriteBorders(ARow, ACol, cb);
|
|
Worksheet.WriteBorderStyle(cell, ABorder, AStyle);
|
|
end else
|
|
if cb <> [] then
|
|
begin
|
|
Exclude(cb, ABorder);
|
|
cell := Worksheet.WriteBorders(ARow, ACol, cb);
|
|
end;
|
|
FixNeighborCellBorders(cell);
|
|
end;
|
|
|
|
var
|
|
r, c, r1, c1, r2, c2: Cardinal;
|
|
begin
|
|
if Worksheet = nil then
|
|
exit;
|
|
|
|
// Preparations
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
r1 := GetWorksheetRow(ATop);
|
|
r2 := GetWorksheetRow(ABottom);
|
|
c1 := GetWorksheetCol(ALeft);
|
|
c2 := GetWorksheetCol(ARight);
|
|
|
|
// Top outer border
|
|
for c := c1 to c2 do
|
|
ProcessBorder(r1, c, cbNorth, ATopOuterStyle);
|
|
// Bottom outer border
|
|
for c := c1 to c2 do
|
|
ProcessBorder(r2, c, cbSouth, ABottomOuterStyle);
|
|
// Left outer border
|
|
for r := r1 to r2 do
|
|
ProcessBorder(r, c1, cbWest, ALeftOuterStyle);
|
|
// Right outer border
|
|
for r := r1 to r2 do
|
|
ProcessBorder(r, c2, cbEast, ARightOuterStyle);
|
|
// Horizontal inner border
|
|
if r1 <> r2 then
|
|
for r := r1 to r2-1 do
|
|
for c := c1 to c2 do
|
|
ProcessBorder(r, c, cbSouth, AHorInnerStyle);
|
|
// Vertical inner border
|
|
if c1 <> c2 then
|
|
for r := r1 to r2 do
|
|
for c := c1 to c2-1 do
|
|
ProcessBorder(r, c, cbEast, AVertInnerStyle);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Shows the column with the specified index previously hidden.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ShowCol(ACol: Integer);
|
|
var
|
|
c: Cardinal;
|
|
begin
|
|
c := GetWorksheetCol(ACol);
|
|
if Worksheet.ColHidden(c) then begin
|
|
Worksheet.ShowCol(c);
|
|
UpdateColWidth(ACol);
|
|
InvalidateGrid;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Hides the column with the specifed index
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.HideCol(ACol: Integer);
|
|
var
|
|
c: Cardinal;
|
|
begin
|
|
c := GetWorksheetCol(ACol);
|
|
if not Worksheet.ColHidden(c) then begin
|
|
Worksheet.HideCol(c);
|
|
UpdateColWidth(ACol);
|
|
InvalidateGrid;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Shows the row with the specified index previously hidden
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.ShowRow(ARow: Integer);
|
|
var
|
|
r: Cardinal;
|
|
begin
|
|
r := GetWorksheetRow(ARow);
|
|
if Worksheet.RowHidden(r) then begin
|
|
Worksheet.ShowRow(r);
|
|
UpdateRowHeight(ARow);
|
|
InvalidateGrid;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Hides the row with the specifed index
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.HideRow(ARow: Integer);
|
|
var
|
|
r: Cardinal;
|
|
begin
|
|
r := GetWorksheetRow(ARow);
|
|
if not Worksheet.RowHidden(r) then begin
|
|
Worksheet.HideRow(r);
|
|
UpdateRowHeight(ARow);
|
|
InvalidateGrid;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Sorts the grid by calling the corresponding method of the worksheet.
|
|
Sorting extends across the entire worksheet.
|
|
Sort direction is determined by the property "SortOrder". Other sorting
|
|
criteria are "case-sensitive" and "numbers first".
|
|
|
|
@param(AColSorting If @true the grid is sorted from top to bottom and the
|
|
next parameter, "Index", refers to a column. Otherwise
|
|
sorting goes from left to right and "Index" refers to a row.)
|
|
@param(AIndex Index of the column (if ColSorting=@true) or row (ColSorting=@false)
|
|
which is sorted.)
|
|
@param(AIndxFrom Sorting starts at this row (ColSorting=@true) / column (ColSorting=@false))
|
|
@param(AIndxTo Sorting ends at this row (ColSorting=@true) / column (ColSorting=@false))
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.Sort(AColSorting: Boolean;
|
|
AIndex, AIndxFrom, AIndxTo:Integer);
|
|
var
|
|
sortParams: TsSortParams;
|
|
begin
|
|
sortParams := InitSortParams(AColSorting, 1);
|
|
sortParams.Keys[0].ColRowIndex := AIndex - HeaderCount;
|
|
if SortOrder = soDescending then
|
|
sortParams.Keys[0].Options := [ssoDescending];
|
|
if AColSorting then
|
|
Worksheet.Sort(
|
|
sortParams,
|
|
AIndxFrom-HeaderCount, 0, AIndxTo-HeaderCount, Worksheet.GetLastColIndex
|
|
)
|
|
else
|
|
Worksheet.Sort(
|
|
sortParams,
|
|
0, AIndxFrom-HeaderCount, Worksheet.GetLastRowIndex, AIndxTo-HeaderCount
|
|
);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Converts a coordinate given in workbook units to pixels using the current
|
|
screen resolution
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.ToPixels(AValue: Double): Integer;
|
|
var
|
|
inches: Double;
|
|
begin
|
|
inches := Workbook.ConvertUnits(AValue, Workbook.Units, suInches);
|
|
Result := round(inches * Screen.PixelsPerInch);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Is called whenever the worksheet is scrolled.
|
|
Stores the coordinates of the TopLeft cell in the worksheet
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.TopLeftChanged;
|
|
begin
|
|
inherited;
|
|
Worksheet.ScrollTo(GetWorkSheetRow(TopRow), GetWorksheetCol(LeftCol));
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Modifies the text that is shown for cells which are too narrow to hold the
|
|
entire text. The method follows the behavior of Excel and Open/LibreOffice:
|
|
If the specified cell contains a non-formatted number, then it is formatted
|
|
such that the text fits into the cell. If the text is still too long or
|
|
the cell does not contain a label then the cell is filled by '#' characters.
|
|
Label cell texts are not modified, they can overflow into the adjacent cells.
|
|
-------------------------------------------------------------------------------}
|
|
function TsCustomWorksheetGrid.TrimToCell(ACell: PCell): String;
|
|
var
|
|
cellSize, txtSize: Integer;
|
|
decs: Integer;
|
|
p: Integer;
|
|
isRotated: Boolean;
|
|
isStacked: Boolean;
|
|
fmt: PsCellFormat;
|
|
numFmt: TsNumFormatParams;
|
|
nfs: String;
|
|
isGeneralFmt: Boolean;
|
|
r1,c1,r2,c2: Cardinal;
|
|
begin
|
|
Result := Worksheet.ReadAsText(ACell);
|
|
if (Result = '') or ((ACell <> nil) and (ACell^.ContentType = cctUTF8String)) then
|
|
exit;
|
|
|
|
// fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
|
|
fmt := Worksheet.GetPointerToEffectiveCellFormat(ACell^.Row, ACell^.Col);
|
|
isRotated := (fmt^.TextRotation <> trHorizontal);
|
|
isStacked := (fmt^.TextRotation = rtStacked);
|
|
numFmt := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
|
|
isGeneralFmt := (numFmt = nil) or (numFmt.NumFormat = nfGeneral);
|
|
|
|
// Determine space available in cell
|
|
if Worksheet.IsMerged(ACell) then
|
|
begin
|
|
Worksheet.FindMergedRange(ACell, r1, c1, r2, c2);
|
|
cellSize := 0;
|
|
if isRotated then
|
|
for p:=GetGridRow(r1) to GetGridRow(r2) do cellSize := cellSize + RowHeights[p]
|
|
else
|
|
for p:=GetGridCol(c1) to GetGridCol(c2) do cellSize := cellSize + ColWidths[p];
|
|
end else
|
|
begin
|
|
if isRotated then
|
|
cellSize := RowHeights[GetGridRow(ACell^.Row)]
|
|
else
|
|
cellSize := ColWidths[GetGridCol(ACell^.Col)];
|
|
end;
|
|
cellSize := cellSize - 2*ConstCellPadding;
|
|
|
|
// Determine space needed for text
|
|
if isStacked then
|
|
txtSize := Length(Result) * Canvas.TextHeight('A')
|
|
else
|
|
txtSize := Canvas.TextWidth(Result);
|
|
|
|
// Nothing to do if text fits into cell
|
|
if txtSize <= cellSize then
|
|
exit;
|
|
|
|
if (ACell^.ContentType = cctNumber) and isGeneralFmt then
|
|
begin
|
|
// Determine number of decimal places
|
|
p := pos(Workbook.FormatSettings.DecimalSeparator, Result);
|
|
if p = 0 then
|
|
decs := 0
|
|
else
|
|
decs := Length(Result) - p;
|
|
|
|
// Use floating point format, but reduce number of decimal places until
|
|
// text fits in
|
|
while decs > 0 do
|
|
begin
|
|
dec(decs);
|
|
Result := Format('%.*f', [decs, ACell^.NumberValue], Workbook.FormatSettings);
|
|
if isStacked then
|
|
txtSize := Length(Result) * Canvas.TextHeight('A')
|
|
else
|
|
txtSize := Canvas.TextWidth(Result);
|
|
if txtSize <= cellSize then
|
|
exit;
|
|
end;
|
|
|
|
// There seem to be too many integer digits. Switch to exponential format.
|
|
decs := 13;
|
|
while decs > 0 do
|
|
begin
|
|
dec(decs);
|
|
nfs := IfThen(decs = 0, '0E-00', '0.' + DupeString('0', decs) + 'E-00');
|
|
Result := FormatFloat(nfs, ACell^.NumberValue, Workbook.FormatSettings);
|
|
// Result := Format('%.*e', [decs, ACell^.NumberValue], Workbook.FormatSettings);
|
|
if isStacked then
|
|
txtSize := Length(Result) * Canvas.TextHeight('A')
|
|
else
|
|
txtSize := Canvas.TextWidth(Result);
|
|
if txtSize <= cellSize then
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
// Still text too long or non-number --> Fill with # characters.
|
|
Result := '';
|
|
txtSize := 0;
|
|
while txtSize < cellSize do
|
|
begin
|
|
Result := Result + '#';
|
|
if isStacked then
|
|
txtSize := Length(Result) * Canvas.TextHeight('#')
|
|
else
|
|
txtSize := Canvas.TextWidth(Result);
|
|
end;
|
|
|
|
// We added one character too many
|
|
Delete(Result, Length(Result), 1);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Splits a merged cell block into single cells
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.UnmergeCells;
|
|
begin
|
|
Worksheet.UnmergeCells(
|
|
GetWorksheetRow(Selection.Top),
|
|
GetWorksheetCol(Selection.Left)
|
|
);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
If the specified cell belongs to a merged block, the merged block is
|
|
split into single cells
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.UnmergeCells(ACol, ARow: Integer);
|
|
begin
|
|
Worksheet.UnmergeCells(
|
|
GetWorksheetRow(ARow),
|
|
GetworksheetCol(ACol)
|
|
);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.UpdateColWidth(ACol: Integer);
|
|
var
|
|
lCol: PCol;
|
|
w: Integer; // Col width at current zoom level
|
|
w100: Integer; // Col width at 100% zoom level
|
|
begin
|
|
if Worksheet <> nil then
|
|
begin
|
|
lCol := Worksheet.FindCol(ACol - FHeaderCount);
|
|
if (lCol <> nil) and (croHidden in lCol^.Options) then
|
|
w := 0
|
|
else begin
|
|
if (lCol <> nil) and (lCol^.ColWidthType = cwtCustom) then
|
|
w100 := CalcColWidthFromSheet(lCol^.Width)
|
|
else
|
|
w100 := CalcColWidthFromSheet(Worksheet.ReadDefaultColWidth(Workbook.Units));
|
|
w := round(w100 * ZoomFactor);
|
|
end;
|
|
end else
|
|
w := DefaultColWidth; // Zoom factor has already been applied by getter
|
|
ColWidths[ACol] := w;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Updates column widths according to the data in the TCol records
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.UpdateColWidths(AStartIndex: Integer = 0);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if AStartIndex = 0 then
|
|
AStartIndex := FHeaderCount;
|
|
BeginUpdate;
|
|
try
|
|
for i := AStartIndex to ColCount-1 do
|
|
UpdateColWidth(i);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Updates the height if the specified row in the grid with the value stored in
|
|
the worksheet multiplied by the current zoom factor. If the stored row height
|
|
type is rhtAuto (meaning: "row height is auto-calculated") and the current
|
|
row height in the row record is 0 then the row height is calculated by
|
|
iterating over all cells in this row. This happens also if the parameter
|
|
AEnforceCalcRowHeight is @true.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.UpdateRowHeight(ARow: Integer;
|
|
AEnforceCalcRowHeight: Boolean = false);
|
|
var
|
|
lRow: PRow;
|
|
sr: Cardinal;
|
|
h: Integer; // Row height, in pixels. Contains zoom factor.
|
|
doCalcRowHeight: Boolean;
|
|
begin
|
|
if not HandleAllocated then
|
|
exit;
|
|
|
|
if ARow < FHeaderCount then
|
|
exit;
|
|
|
|
h := DefaultRowHeight;
|
|
if Worksheet <> nil then
|
|
begin
|
|
sr := ARow - FHeaderCount; // worksheet row index
|
|
lRow := Worksheet.FindRow(sr);
|
|
if (lRow <> nil) then
|
|
begin
|
|
if (croHidden in lRow^.Options) then
|
|
h := 0
|
|
else begin
|
|
case lRow^.RowHeightType of
|
|
rhtCustom:
|
|
begin
|
|
h := round(CalcRowHeightFromSheet(lRow^.Height) * ZoomFactor);
|
|
if AEnforceCalcRowHeight then begin
|
|
h := CalcAutoRowHeight(ARow);
|
|
if h = 0 then begin
|
|
h := DefaultRowHeight;
|
|
lRow^.RowHeightType := rhtDefault;
|
|
end else
|
|
lRow^.RowHeightType := rhtAuto;
|
|
lRow^.Height := CalcRowHeightToSheet(round(h / ZoomFactor));
|
|
end;
|
|
end;
|
|
rhtAuto, rhtDefault:
|
|
begin
|
|
doCalcRowHeight := AEnforceCalcRowHeight or (lRow^.Height = 0);
|
|
if doCalcRowHeight then begin
|
|
// Calculate current grid row height in pixels by iterating over all cells in row
|
|
h := CalcAutoRowHeight(ARow); // ZoomFactor already applied to font heights
|
|
if h = 0 then begin
|
|
h := DefaultRowHeight; // Zoom factor applied by getter function
|
|
lRow^.RowHeightType := rhtDefault;
|
|
end else
|
|
lRow^.RowHeightType := rhtAuto;
|
|
// Calculate the unzoomed row height in workbook units and store
|
|
// in row record
|
|
lRow^.Height := CalcRowHeightToSheet(round(h / ZoomFactor));
|
|
end else
|
|
// If autocalc mode is off we just take the row height from the row record
|
|
case lRow^.RowHeightType of
|
|
rhtDefault : h := DefaultRowHeight;
|
|
rhtAuto : h := round(CalcRowHeightFromSheet(lRow^.Height) * ZoomFactor);
|
|
end;
|
|
end;
|
|
end; // case
|
|
if h = 0 then
|
|
h := DefaultRowHeight; // Zoom factor is applied by getter function
|
|
end;
|
|
end;
|
|
|
|
// No row record so far.
|
|
if lRow = nil then
|
|
begin
|
|
if Worksheet.GetCellCountInRow(sr) > 0 then
|
|
begin
|
|
// Case 1: This row does contain cells
|
|
lRow := Worksheet.AddRow(sr);
|
|
if AEnforceCalcRowHeight then
|
|
h := CalcAutoRowHeight(ARow)
|
|
else
|
|
h := DefaultRowHeight;
|
|
lRow^.Height := CalcRowHeightToSheet(round(h / ZoomFactor));
|
|
if h <> DefaultRowHeight then
|
|
lRow^.RowHeightType := rhtAuto
|
|
else
|
|
lRow^.RowHeightType := rhtDefault;
|
|
if h = 0 then
|
|
h := DefaultRowHeight; // Zoom factor is applied by getter function
|
|
end else
|
|
// Case 2: No cells in row
|
|
h := DefaultRowHeight; // Zoom factor is applied by getter function
|
|
end;
|
|
end;
|
|
|
|
inc(FZoomLock); // We don't want to modify the sheet row heights here.
|
|
RowHeights[ARow] := h;
|
|
dec(FZoomLock);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Updates grid row heights by using the data from the TRow records.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsCustomWorksheetGrid.UpdateRowHeights(AStartRow: Integer = -1;
|
|
AEnforceCalcRowHeight: Boolean = false);
|
|
var
|
|
r, r1: Integer;
|
|
begin
|
|
if FRowHeightLock > 0 then
|
|
exit;
|
|
|
|
if AStartRow = -1 then
|
|
r1 := FHeaderCount else
|
|
r1 := AStartRow;
|
|
|
|
BeginUpdate;
|
|
try
|
|
for r:=r1 to RowCount-1 do
|
|
UpdateRowHeight(r, AEnforceCalcRowHeight);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.WMHScroll(var message: TLMHScroll);
|
|
begin
|
|
inherited;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.WMVScroll(var message: TLMVScroll);
|
|
begin
|
|
inherited;
|
|
Invalidate;
|
|
end;
|
|
|
|
|
|
{*******************************************************************************
|
|
* Setter / getter methods *
|
|
*******************************************************************************}
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontColor(ACol, ARow: Integer): TsColor;
|
|
var
|
|
cell: PCell;
|
|
fnt: TsFont;
|
|
begin
|
|
Result := scNotDefined;
|
|
if (Workbook <> nil) and (Worksheet <> nil) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
fnt := Worksheet.ReadCellFont(cell);
|
|
Result := fnt.Color;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontColors(ALeft, ATop, ARight, ABottom: Integer): TsColor;
|
|
var
|
|
c, r: Integer;
|
|
clr: TsColor;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellFontColor(ALeft, ATop);
|
|
clr := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetCellFontColor(c, r);
|
|
if (Result <> clr) then begin
|
|
Result := scNotDefined;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontName(ACol, ARow: Integer): String;
|
|
var
|
|
cell: PCell;
|
|
fnt: TsFont;
|
|
begin
|
|
Result := '';
|
|
if (Workbook <> nil) and (Worksheet <> nil) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
fnt := Worksheet.ReadCellFont(cell);
|
|
if fnt <> nil then
|
|
Result := fnt.FontName;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontNames(ALeft, ATop, ARight, ABottom: Integer): String;
|
|
var
|
|
c, r: Integer;
|
|
s: String;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellFontName(ALeft, ATop);
|
|
s := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetCellFontName(c, r);
|
|
if (Result <> '') and (Result <> s) then begin
|
|
Result := '';
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontSize(ACol, ARow: Integer): Single;
|
|
var
|
|
cell: PCell;
|
|
fnt: TsFont;
|
|
begin
|
|
Result := -1.0;
|
|
if (Workbook <> nil) and (Worksheet <> nil) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
fnt := Worksheet.ReadCellFont(cell);
|
|
Result := fnt.Size;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontSizes(ALeft, ATop, ARight, ABottom: Integer): Single;
|
|
var
|
|
c, r: Integer;
|
|
sz: Single;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellFontSize(ALeft, ATop);
|
|
sz := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetCellFontSize(c, r);
|
|
if (Result <> -1) and not SameValue(Result, sz, 1E-3) then begin
|
|
Result := -1.0;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontStyle(ACol, ARow: Integer): TsFontStyles;
|
|
var
|
|
cell: PCell;
|
|
fnt: TsFont;
|
|
begin
|
|
Result := [];
|
|
if (Workbook <> nil) and (Worksheet <> nil) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
fnt := Worksheet.ReadCellFont(cell);
|
|
Result := fnt.Style;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFontStyles(ALeft, ATop,
|
|
ARight, ABottom: Integer): TsFontStyles;
|
|
var
|
|
c, r: Integer;
|
|
style: TsFontStyles;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellFontStyle(ALeft, ATop);
|
|
style := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetCellFontStyle(c, r);
|
|
if Result <> style then begin
|
|
Result := [];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellFormula(ACol, ARow: Integer): String;
|
|
begin
|
|
if Worksheet <> nil then
|
|
Result := Worksheet.ReadFormula(GetWorksheetRow(ARow), GetWorksheetCol(ACol))
|
|
else
|
|
Result :='';
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellProtection(ACol, ARow: Integer
|
|
): TsCellProtections;
|
|
var
|
|
cell:PCell;
|
|
begin
|
|
Result :=[];
|
|
if Worksheet <> nil then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadCellProtection(cell) ;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetCellProtections(ALeft, ATop, ARight,
|
|
ABottom: Integer): TsCellProtections;
|
|
var
|
|
c, r: Integer;
|
|
b: TsCellProtections;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetCellProtection(ALeft, ATop);
|
|
b := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
begin
|
|
Result := GetCellProtection(c, r);
|
|
if Result <> b then
|
|
begin
|
|
Result := [];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
function TsCustomWorksheetGrid.GetCellValue(ACol, ARow: Integer): variant;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := Null;
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if cell <> nil then
|
|
case cell^.ContentType of
|
|
cctEmpty : ;
|
|
cctNumber,
|
|
cctDateTime : Result := cell^.NumberValue;
|
|
cctUTF8String: Result := cell^.UTF8Stringvalue;
|
|
cctBool : Result := cell^.BoolValue;
|
|
cctError : Result := cell^.ErrorValue;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetColWidths(ACol: Integer): Integer;
|
|
begin
|
|
Result := inherited ColWidths[ACol];
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetDefColWidth: Integer;
|
|
begin
|
|
Result := round(FDefColWidth100 * ZoomFactor);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetDefRowHeight: Integer;
|
|
begin
|
|
Result := round(FDefRowHeight100 * Zoomfactor);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetHorAlignment(ACol, ARow: Integer): TsHorAlignment;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := haDefault;
|
|
if Assigned(Worksheet) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadHorAlignment(cell);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetHorAlignments(ALeft, ATop, ARight, ABottom: Integer): TsHorAlignment;
|
|
var
|
|
c, r: Integer;
|
|
horalign: TsHorAlignment;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetHorAlignment(ALeft, ATop);
|
|
horalign := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetHorAlignment(c, r);
|
|
if Result <> horalign then begin
|
|
Result := haDefault;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetHyperlink(ACol, ARow: Integer): String;
|
|
var
|
|
hlink: TsHyperlink;
|
|
begin
|
|
Result := '';
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
hlink := Worksheet.ReadHyperLink(Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol)));
|
|
if hlink.Target <> '' then begin
|
|
Result := hlink.Target;
|
|
if hlink.Tooltip <> '' then Result := Result + '|' + hlink.ToolTip;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetNumberFormat(ACol, ARow: Integer): String;
|
|
var
|
|
nf: TsNumberFormat;
|
|
cell: PCell;
|
|
begin
|
|
Result := '';
|
|
if Assigned(Worksheet) and Assigned(Workbook) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if cell <> nil then
|
|
Worksheet.ReadNumFormat(cell, nf, Result);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetNumberFormats(ALeft, ATop,
|
|
ARight, ABottom: Integer): String;
|
|
var
|
|
c, r: Integer;
|
|
nfs: String;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
nfs := GetNumberformat(ALeft, ATop);
|
|
for r := ALeft to ARight do
|
|
for c := ATop to ABottom do
|
|
if nfs <> GetNumberFormat(c, r) then
|
|
begin
|
|
Result := '';
|
|
exit;
|
|
end;
|
|
Result := nfs;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetRowHeights(ARow: Integer): Integer;
|
|
begin
|
|
Result := inherited RowHeights[ARow];
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetShowGridLines: Boolean;
|
|
begin
|
|
Result := (Options * [goHorzLine, goVertLine] <> []);
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetShowHeaders: Boolean;
|
|
begin
|
|
Result := FHeaderCount <> 0;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetTextRotation(ACol, ARow: Integer): TsTextRotation;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := trHorizontal;
|
|
if Assigned(Worksheet) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadTextRotation(cell);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetTextRotations(ALeft, ATop,
|
|
ARight, ABottom: Integer): TsTextRotation;
|
|
var
|
|
c, r: Integer;
|
|
textrot: TsTextRotation;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetTextRotation(ALeft, ATop);
|
|
textrot := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
begin
|
|
Result := GetTextRotation(c, r);
|
|
if Result <> textrot then
|
|
begin
|
|
Result := trHorizontal;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetWorkbookSource: TsWorkbookSource;
|
|
begin
|
|
if FWorkbookSource <> nil then
|
|
Result := FWorkbookSource
|
|
else
|
|
Result := FInternalWorkbookSource;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetVertAlignment(ACol, ARow: Integer): TsVertAlignment;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := vaDefault;
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadVertAlignment(cell);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetVertAlignments(
|
|
ALeft, ATop, ARight, ABottom: Integer): TsVertAlignment;
|
|
var
|
|
c, r: Integer;
|
|
vertalign: TsVertAlignment;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetVertalignment(ALeft, ATop);
|
|
vertalign := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetVertAlignment(c, r);
|
|
if Result <> vertalign then begin
|
|
Result := vaDefault;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetWorkbook: TsWorkbook;
|
|
begin
|
|
Result := GetWorkbookSource.Workbook;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetWorksheet: TsWorksheet;
|
|
begin
|
|
Result := GetWorkbookSource.Worksheet;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetWordwrap(ACol, ARow: Integer): Boolean;
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
Result := false;
|
|
if Assigned(Worksheet) then begin
|
|
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Result := Worksheet.ReadWordwrap(cell);
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetWordwraps(ALeft, ATop,
|
|
ARight, ABottom: Integer): Boolean;
|
|
var
|
|
c, r: Integer;
|
|
wrapped: Boolean;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
Result := GetWordwrap(ALeft, ATop);
|
|
wrapped := Result;
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do begin
|
|
Result := GetWordwrap(c, r);
|
|
if Result <> wrapped then begin
|
|
Result := false;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TsCustomWorksheetGrid.GetZoomFactor: Double;
|
|
begin
|
|
if Worksheet <> nil then
|
|
Result := Worksheet.Zoomfactor
|
|
else
|
|
Result := 1.0;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetAutoCalc(AValue: Boolean);
|
|
var
|
|
optns: TsWorkbookOptions;
|
|
begin
|
|
FAutoCalc := AValue;
|
|
|
|
if Assigned(WorkbookSource) then
|
|
begin
|
|
optns := WorkbookSource.Options;
|
|
if FAutoCalc then
|
|
Include(optns, boAutoCalc)
|
|
else
|
|
Exclude(optns, boAutoCalc);
|
|
WorkbookSource.Options := optns;
|
|
if FInternalWorkbookSource <> nil then
|
|
FInternalWorkbookSource.Options := optns;
|
|
Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetAutoDetectCellType(AValue: Boolean);
|
|
begin
|
|
FAutoDetectCellType := AValue;
|
|
|
|
if Assigned(Worksheet) then begin
|
|
if FAutoDetectCellType then
|
|
Worksheet.Options := Worksheet.Options + [soAutoDetectCellType]
|
|
else
|
|
Worksheet.Options := Worksheet.Options - [soAutoDetectCellType];
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetBackgroundColor(ACol, ARow: Integer;
|
|
AValue: TsColor);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then begin
|
|
BeginUpdate;
|
|
try
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteBackgroundColor(cell, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetBackgroundColors(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsColor);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetBackgroundColor(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellBiDiMode(ACol, ARow: Integer;
|
|
AValue: TsBiDiMode);
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
Worksheet.WriteBiDiMode(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellBorder(ACol, ARow: Integer;
|
|
AValue: TsCellBorders);
|
|
var
|
|
cell: PCell;
|
|
sr1, sc1, sr2, sc2: Cardinal;
|
|
gr1, gc1, gr2, gc2: Integer;
|
|
styles, saved_styles: TsCellBorderStyles;
|
|
begin
|
|
if Assigned(Worksheet) then begin
|
|
BeginUpdate;
|
|
try
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
|
|
if Worksheet.IsMergeBase(cell) then
|
|
begin
|
|
styles := Worksheet.ReadCellBorderStyles(cell);
|
|
saved_styles := styles;
|
|
if not (cbEast in AValue) then
|
|
styles[cbEast] := NO_CELL_BORDER;
|
|
if not (cbWest in AValue) then styles[cbWest] := NO_CELL_BORDER;
|
|
if not (cbNorth in AValue) then styles[cbNorth] := NO_CELL_BORDER;
|
|
if not (cbSouth in AValue) then styles[cbSouth] := NO_CELL_BORDER;
|
|
Worksheet.FindMergedRange(cell, sr1, sc1, sr2, sc2);
|
|
gr1 := GetGridRow(sr1);
|
|
gr2 := GetGridRow(sr2);
|
|
gc1 := GetGridCol(sc1);
|
|
gc2 := GetGridCol(sc2);
|
|
// Set border flags and styles for all outer cells of the merged block
|
|
// Note: This overwrites the styles of the base ...
|
|
ShowCellBorders(gc1,gr1, gc2,gr2, styles[cbWest], styles[cbNorth],
|
|
styles[cbEast], styles[cbSouth], NO_CELL_BORDER, NO_CELL_BORDER);
|
|
// ... Restores base border style overwritten in prev instruction
|
|
Worksheet.WriteBorderStyles(cell, saved_styles);
|
|
Worksheet.WriteBorders(cell, AValue);
|
|
end else
|
|
begin
|
|
Worksheet.WriteBorders(cell, AValue);
|
|
// FixNeighborCellBorders(cell);
|
|
end;
|
|
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellBorders(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsCellBorders);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellBorder(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellBorderStyle(ACol, ARow: Integer;
|
|
ABorder: TsCellBorder; AValue: TsCellBorderStyle);
|
|
var
|
|
cell: PCell;
|
|
borders: TsCellBorders;
|
|
begin
|
|
if Assigned(Worksheet) then begin
|
|
BeginUpdate;
|
|
try
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if Worksheet.IsMergeBase(cell) then
|
|
begin
|
|
borders := Worksheet.ReadCellBorders(cell);
|
|
Worksheet.WriteBorderStyle(cell, ABorder, AValue);
|
|
// This will apply the new border style to the outer cells of the range.
|
|
SetCellBorder(ACol, ARow, borders);
|
|
end else
|
|
begin
|
|
Worksheet.WriteBorderStyle(cell, ABorder, AValue);
|
|
// FixNeighborCellBorders(cell);
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellBorderStyles(ALeft, ATop,
|
|
ARight, ABottom: Integer; ABorder: TsCellBorder; AValue: TsCellBorderStyle);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellBorderStyle(c, r, ABorder, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellComment(ACol, ARow: Integer;
|
|
AValue: String);
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
Worksheet.WriteComment(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFont(ACol, ARow: Integer; AValue: TFont);
|
|
var
|
|
fnt: TsFont;
|
|
cell: PCell;
|
|
begin
|
|
FCellFont.Assign(AValue);
|
|
if Assigned(Worksheet) then begin
|
|
fnt := TsFont.Create;
|
|
try
|
|
Convert_Font_To_sFont(FCellFont, fnt);
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteFont(cell, fnt.FontName, fnt.Size, fnt.Style, fnt.Color);
|
|
finally
|
|
fnt.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFonts(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: TFont);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellFont(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontColor(ACol, ARow: Integer; AValue: TsColor);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteFontColor(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontColors(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsColor);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellFontColor(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontName(ACol, ARow: Integer; AValue: String);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteFontName(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontNames(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: String);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellFontName(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontSize(ACol, ARow: Integer;
|
|
AValue: Single);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteFontSize(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontSizes(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: Single);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellFontSize(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontStyle(ACol, ARow: Integer;
|
|
AValue: TsFontStyles);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteFontStyle(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFontStyles(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsFontStyles);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellFontStyle(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellFormula(ACol, ARow: Integer;
|
|
AValue: String);
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
Worksheet.WriteFormula(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellProtection(ACol, ARow: Integer;
|
|
AValue: TsCellProtections);
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
Worksheet.WriteCellProtection(GetWorksheetRow(ARow), GetWorksheetCol(ACol), AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellProtections(ALeft, ATop, ARight,
|
|
ABottom: Integer; AValue: TsCellProtections);
|
|
var
|
|
c, r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetCellProtection(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetCellValue(ACol, ARow: Integer; AValue: Variant);
|
|
var
|
|
cell: PCell = nil;
|
|
fmt: PsCellFormat = nil;
|
|
idx: Integer;
|
|
nfp: TsNumFormatParams;
|
|
r, c: Cardinal;
|
|
s, plain: String;
|
|
rtParams: TsRichTextParams;
|
|
begin
|
|
if not Assigned(Worksheet) then
|
|
exit;
|
|
|
|
r := GetWorksheetRow(ARow);
|
|
c := GetWorksheetCol(ACol);
|
|
|
|
// If the cell already exists and contains a formula then the formula must be
|
|
// removed. The formula would dominate over the data value.
|
|
cell := Worksheet.FindCell(r, c);
|
|
if HasFormula(cell) then
|
|
Worksheet.UseformulaInCell(cell, nil); //cell^.FormulaValue := '';
|
|
|
|
if VarIsNull(AValue) then
|
|
Worksheet.WriteBlank(r, c)
|
|
else
|
|
if VarIsStr(AValue) then
|
|
begin
|
|
s := VarToStr(AValue);
|
|
if (s <> '') and (s[1] = '=') then
|
|
Worksheet.WriteFormula(r, c, Copy(s, 2, Length(s)), true)
|
|
else
|
|
begin
|
|
if cell = nil then cell := Worksheet.GetCell(r, c);
|
|
if s = '' then
|
|
Worksheet.WriteBlank(cell)
|
|
else begin
|
|
HTMLToRichText(Workbook, Worksheet.ReadCellFont(cell), s, plain, rtParams);
|
|
Worksheet.WriteText(cell, plain, rtParams); // This will erase a non-formatted cell if s = ''
|
|
end;
|
|
end;
|
|
end else
|
|
if VarIsType(AValue, varDate) then
|
|
Worksheet.WriteDateTime(r, c, VarToDateTime(AValue))
|
|
else
|
|
if VarIsNumeric(AValue) then
|
|
begin
|
|
// Check if the cell already exists and contains a format.
|
|
// If it is a date/time format write a date/time cell...
|
|
if cell <> nil then
|
|
begin
|
|
idx := Worksheet.GetEffectiveCellFormatIndex(cell);
|
|
fmt := Workbook.GetPointerToCellFormat(idx);
|
|
if fmt <> nil then
|
|
nfp := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
|
|
if (fmt <> nil) and IsDateTimeFormat(nfp) then
|
|
Worksheet.WriteDateTime(r, c, VarToDateTime(AValue)) else
|
|
Worksheet.WriteNumber(r, c, AValue);
|
|
end
|
|
else
|
|
// ... otherwise write a number cell
|
|
Worksheet.WriteNumber(r, c, AValue);
|
|
end else
|
|
if VarIsBool(AValue) then
|
|
Worksheet.WriteBoolValue(r, c, AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetColWidths(ACol: Integer; AValue: Integer);
|
|
begin
|
|
if GetColWidths(ACol) = AValue then
|
|
exit;
|
|
inherited ColWidths[ACol] := AValue;
|
|
HeaderSized(true, ACol);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetDefColWidth(AValue: Integer);
|
|
begin
|
|
if (AValue = GetDefColWidth) or (AValue < 0) then
|
|
exit;
|
|
|
|
{ AValue contains the zoom factor.
|
|
FDefColWidth1000 is the col width at zoom factor 1.0 }
|
|
FDefColWidth100 := round(AValue / ZoomFactor);
|
|
inherited DefaultColWidth := AValue;
|
|
if (FHeaderCount > 0) and HandleAllocated then begin
|
|
PrepareCanvasFont;
|
|
ColWidths[0] := GetFixedColWidth;
|
|
end;
|
|
if (FZoomLock = 0) and (Worksheet <> nil) then
|
|
Worksheet.WriteDefaultColWidth(CalcWorksheetColWidth(GetDefColWidth), Workbook.Units);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetDefRowHeight(AValue: Integer);
|
|
begin
|
|
if (AValue = GetDefRowHeight) or (AValue < 0) then
|
|
exit;
|
|
{ AValue contains the zoom factor
|
|
FDefRowHeight100 is the row height at zoom factor 1.0 }
|
|
FDefRowHeight100 := round(AValue / ZoomFactor);
|
|
inherited DefaultRowHeight := AValue;
|
|
if FHeaderCount > 0 then
|
|
RowHeights[0] := GetDefaultRowHeight;
|
|
if (FZoomLock = 0) and (Worksheet <> nil) then
|
|
Worksheet.WriteDefaultRowHeight(CalcWorksheetRowHeight(GetDefaultRowHeight), Workbook.Units);
|
|
end;
|
|
|
|
procedure TscustomWorksheetGrid.SetEditorLineMode(AValue: TsEditorLineMode);
|
|
begin
|
|
if FLineMode = AValue then
|
|
exit;
|
|
|
|
FLineMode := AValue;
|
|
if (FLineMode = elmMultiline) and (FMultilineStringEditor = nil) then
|
|
begin
|
|
FMultilineStringEditor := TsMultilineStringCellEditor.Create(nil);
|
|
FMultilineStringEditor.name := 'MultilineStringEditor';
|
|
FMultilineStringEditor.Text := '';
|
|
FMultilineStringEditor.Visible := False;
|
|
FMultilineStringEditor.Align := alNone;
|
|
FMultilineStringEditor.BorderStyle := bsNone;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetFixedColWidth(AValue: Integer);
|
|
begin
|
|
if FFixedColWidth = AValue then exit;
|
|
FFixedColWidth := AValue;
|
|
if FHeaderCount > 0 then
|
|
begin
|
|
ColWidths[0] := GetFixedColWidth;
|
|
FTopLeft := CalcTopLeft(false);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetFrozenBorderPen(AValue: TPen);
|
|
begin
|
|
FFrozenBorderPen.Assign(AValue);
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetFrozenCols(AValue: Integer);
|
|
begin
|
|
FFrozenCols := AValue;
|
|
if Worksheet <> nil then begin
|
|
Worksheet.LeftPaneWidth := FFrozenCols;
|
|
if (FFrozenCols > 0) or (FFrozenRows > 0) then
|
|
Worksheet.Options := Worksheet.Options + [soHasFrozenPanes]
|
|
else
|
|
Worksheet.Options := Worksheet.Options - [soHasFrozenPanes];
|
|
end;
|
|
Setup;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetFrozenRows(AValue: Integer);
|
|
begin
|
|
FFrozenRows := AValue;
|
|
if Worksheet <> nil then begin
|
|
Worksheet.TopPaneHeight := FFrozenRows;
|
|
if (FFrozenCols > 0) or (FFrozenRows > 0) then
|
|
Worksheet.Options := Worksheet.Options + [soHasFrozenPanes]
|
|
else
|
|
Worksheet.Options := Worksheet.Options - [soHasFrozenPanes];
|
|
end;
|
|
Setup;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetHorAlignment(ACol, ARow: Integer;
|
|
AValue: TsHorAlignment);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteHorAlignment(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetHorAlignments(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsHorAlignment);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetHorAlignment(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetHyperlink(ACol, ARow: Integer;
|
|
AValue: String);
|
|
var
|
|
p: Integer;
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
if AValue <> '' then
|
|
begin
|
|
p := pos('|', AValue);
|
|
if p > 0 then
|
|
Worksheet.WriteHyperlink(cell, copy(AValue, 1, p-1), copy(AValue, p+1, MaxInt))
|
|
else
|
|
Worksheet.WriteHyperlink(cell, AValue);
|
|
end else
|
|
Worksheet.RemoveHyperlink(cell);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetNumberFormat(ACol, ARow: Integer; AValue: String);
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
Worksheet.WriteNumberFormat(GetWorksheetRow(ARow), GetWorksheetCol(ACol), nfCustom, AValue);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetNumberFormats(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: String);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetNumberFormat(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetPageBreakPen(AValue: TPen);
|
|
begin
|
|
FPageBreakPen.Assign(AValue);
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetReadFormulas(AValue: Boolean);
|
|
var
|
|
optns: TsWorkbookOptions;
|
|
begin
|
|
FReadFormulas := AValue;
|
|
if Assigned(WorkbookSource) then
|
|
begin
|
|
optns := WorkbookSource.Options;
|
|
if FReadFormulas then
|
|
Include(optns, boReadFormulas)
|
|
else
|
|
Exclude(optns, boReadFormulas);
|
|
WorkbookSource.Options := optns;
|
|
if FInternalWorkbookSource <> nil then
|
|
FInternalWorkbookSource.Options := optns;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetRowHeights(ARow: Integer; AValue: Integer);
|
|
begin
|
|
if GetRowHeights(ARow) = AValue then
|
|
exit;
|
|
inherited RowHeights[ARow] := AValue;
|
|
HeaderSized(false, ARow);
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetSelPen(AValue: TsSelPen);
|
|
begin
|
|
FSelPen.Assign(AValue);
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
{@@ Shows/hides formulas in the grid when AutoCalc is off }
|
|
procedure TsCustomWorksheetGrid.SetShowFormulas(AValue: Boolean);
|
|
begin
|
|
if AValue = FShowFormulas then
|
|
exit;
|
|
FShowFormulas := AValue;
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
{@@ Shows/hides the worksheet's grid lines }
|
|
procedure TsCustomWorksheetGrid.SetShowGridLines(AValue: Boolean);
|
|
begin
|
|
if AValue = GetShowGridLines then
|
|
Exit;
|
|
|
|
if AValue then
|
|
Options := Options + [goHorzLine, goVertLine]
|
|
else
|
|
Options := Options - [goHorzLine, goVertLine];
|
|
|
|
if Worksheet <> nil then
|
|
if AValue then
|
|
Worksheet.Options := Worksheet.Options + [soShowGridLines]
|
|
else
|
|
Worksheet.Options := Worksheet.Options - [soShowGridLines];
|
|
end;
|
|
|
|
{@@ Shows/hides the worksheet's row and column headers. }
|
|
procedure TsCustomWorksheetGrid.SetShowHeaders(AValue: Boolean);
|
|
var
|
|
hdrCount: Integer;
|
|
begin
|
|
if AValue = GetShowHeaders then Exit;
|
|
|
|
// Avoid crash if selected cell is at 0/0
|
|
hdrCount := ord(AValue);
|
|
if hdrCount > 0 then
|
|
begin
|
|
if Col < hdrCount then Col := hdrCount;
|
|
if Row < hdrCount then Row := hdrCount;
|
|
end;
|
|
|
|
FHeaderCount := hdrCount;
|
|
|
|
if Worksheet <> nil then
|
|
if AValue then
|
|
Worksheet.Options := Worksheet.Options + [soShowHeaders]
|
|
else
|
|
Worksheet.Options := Worksheet.Options - [soShowHeaders];
|
|
|
|
Setup;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetShowPageBreaks(AValue: Boolean);
|
|
begin
|
|
if FShowPageBreaks = AValue then
|
|
exit;
|
|
FShowPageBreaks := AValue;
|
|
InvalidateGrid;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetTextRotation(ACol, ARow: Integer;
|
|
AValue: TsTextRotation);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteTextRotation(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetTextRotations(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsTextRotation);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetTextRotation(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetVertAlignment(ACol, ARow: Integer;
|
|
AValue: TsVertAlignment);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if Assigned(Worksheet) then
|
|
begin
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteVertAlignment(cell, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetVertAlignments(
|
|
ALeft, ATop, ARight, ABottom: Integer; AValue: TsVertAlignment);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetVertAlignment(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetWordwrap(ACol, ARow: Integer;
|
|
AValue: Boolean);
|
|
var
|
|
cell: PCell;
|
|
begin
|
|
if not Assigned(Worksheet) then
|
|
exit;
|
|
|
|
BeginUpdate;
|
|
try
|
|
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
|
|
Worksheet.WriteWordwrap(cell, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetWordwraps(ALeft, ATop, ARight, ABottom: Integer;
|
|
AValue: Boolean);
|
|
var
|
|
c,r: Integer;
|
|
begin
|
|
EnsureOrder(ALeft, ARight);
|
|
EnsureOrder(ATop, ABottom);
|
|
BeginUpdate;
|
|
try
|
|
for c := ALeft to ARight do
|
|
for r := ATop to ABottom do
|
|
SetWordwrap(c, r, AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.SetZoomFactor(AValue: Double);
|
|
begin
|
|
if (AValue <> GetZoomFactor) and Assigned(Worksheet) then begin
|
|
BeginUpdate;
|
|
try
|
|
Worksheet.ZoomFactor := abs(AValue);
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TsCustomWorksheetGrid.ValidateInput(var Msg: TLMessage);
|
|
var
|
|
grid: TsCustomWorksheetGrid;
|
|
errmsg: String;
|
|
ed: TWinControl;
|
|
begin
|
|
if TObject(Msg.lParam) is TsCustomWorksheetGrid then begin
|
|
grid := TsCustomWorksheetGrid(Msg.lParam);
|
|
if not ValidFormula(FEditText, errmsg) then begin
|
|
MessageDlg(errmsg, mtError, [mbOK], 0);
|
|
FRefocusing := grid; // Avoid an endless loop
|
|
grid.SetFocus; // Set focus back
|
|
if grid.EditorMode then begin
|
|
ed := grid.EditorByStyle(cbsAuto);
|
|
if ed is TCustomEdit then TCustomEdit(ed).SelStart := FRefocusingSelStart;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ Checks whether the provided expression is a valid spreadsheet formula.
|
|
If not, a descriptive error message is returned in "AErrMsg", and the function
|
|
result becomes @false.
|
|
|
|
Decimal separator and date/time formats are expected to be localized.
|
|
In contrast to the Office applications, however, formula names are never
|
|
localized in fpspreadsheet. }
|
|
function TsCustomWorksheetGrid.ValidFormula(AExpression: String;
|
|
out AErrMsg: String): Boolean;
|
|
var
|
|
parser: TsSpreadsheetParser;
|
|
begin
|
|
Result := true;
|
|
if Assigned(Worksheet) and (AExpression <> '') and (AExpression[1] = '=') then
|
|
begin
|
|
parser := TsSpreadsheetParser.Create(Worksheet);
|
|
try
|
|
try
|
|
parser.Expression[fdLocalized] := AExpression;
|
|
except
|
|
on E: Exception do begin
|
|
AErrMsg := E.Message;
|
|
Result := false;
|
|
end;
|
|
end;
|
|
finally
|
|
parser.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
initialization
|
|
{$I ../../resource/fpsvisual.lrs} // contains the DragCopy cursor
|
|
|
|
fpsutils.ScreenPixelsPerInch := Screen.PixelsPerInch;
|
|
FillPatternStyle := fsNoFill;
|
|
|
|
RegisterPropertyToSkip(TsCustomWorksheetGrid, 'ColWidths', 'taken from worksheet', '');
|
|
RegisterPropertyToSkip(TsCustomWorksheetGrid, 'RowHeights', 'taken from worksheet', '');
|
|
|
|
crDragCopy := 1; //201705;
|
|
Screen.Cursors[crDragCopy] := LoadCursorFromLazarusResource('cur_dragcopy');
|
|
|
|
|
|
finalization
|
|
FreeAndNil(FillPatternBitmap);
|
|
FreeAndNil(DragborderBitmap);
|
|
|
|
end.
|