fpspreadsheet: Improved formula validation by TsWorksheetGrid and TsCellEdit

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6524 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-06-24 21:59:43 +00:00
parent d4679d6f14
commit c6b5ea4fa8
2 changed files with 485 additions and 124 deletions

View File

@ -27,9 +27,13 @@ unit fpspreadsheetctrls;
interface
uses
LMessages, LResources, LCLVersion,
Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList,
LResources, LCLVersion,
fpstypes, fpspreadsheet; //, {%H-}fpsAllFormats;
fpstypes, fpspreadsheet;
const
{@@ User-defined message for input validation }
UM_VALIDATEINPUT = LM_USER + 100;
type
{@@ Event handler procedure for displaying a message if an error or
@ -236,16 +240,25 @@ type
FWorkbookSource: TsWorkbookSource;
FShowHTMLText: Boolean;
FOldText: String;
FFormulaError: Boolean;
FRefocusing: TObject;
FRefocusingCol, FRefocusingRow: Cardinal;
function GetSelectedCell: PCell;
function GetWorkbook: TsWorkbook;
function GetWorksheet: TsWorksheet;
procedure SetWorkbookSource(AValue: TsWorkbookSource);
procedure ValidateInput(var Msg: TLMessage); message UM_VALIDATEINPUT;
procedure WMKillFocus(var AMessage: TLMKillFocus); message LM_KILLFOCUS;
protected
FEditText: String;
function CanEditCell(ACell: PCell): Boolean; overload;
function CanEditCell(ARow, ACol: Cardinal): Boolean; overload;
procedure CheckFormula;
procedure DoEnter; override;
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
procedure DoExit; override;
function DoValidText(const AText: String): Boolean; virtual;
function ValidFormula(AFormula: String; out AErrMsg: String): Boolean;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ShowCell(ACell: PCell); virtual;
public
@ -1886,21 +1899,6 @@ begin
Result := CanEditCell(cell);
end;
procedure TsCellEdit.CheckFormula;
var
parser: TsSpreadsheetParser;
begin
if Assigned(Worksheet) and (Text <> '') and (Text[1] = '=') then
begin
parser := TsSpreadsheetParser.Create(Worksheet);
try
parser.LocalizedExpression[Workbook.FormatSettings] := Text;
finally
parser.Free;
end;
end;
end;
procedure TsCellEdit.DoEnter;
begin
if Worksheet = nil then
@ -1913,32 +1911,71 @@ begin
Abort;
end;
if FRefocusing = self then
FRefocusing := nil;
inherited;
end;
{@@ ----------------------------------------------------------------------------
EditingDone is called when the user presses the RETURN key to finish editing,
or the TAB key which removes focus from the control, or clicks somewhere else
The edited text is written to the worksheet which tries to figure out the
data type. In particular, if the text begins with an equal sign ("=") then
the text is assumed to be a formula.
DoExit is called when the edit has lost focus. Posts a message to the end of
the message queue in order to complete the focus change operation and to
trigger validation of the edit afterwards (which will restore the edit as
focused control if validation fails.
Source of the idea:
https://community.embarcadero.com/article/technical-articles/149-tools/12766-validating-input-in-tedit-components
-------------------------------------------------------------------------------}
procedure TsCellEdit.DoExit;
begin
if FRefocusing = nil then begin
// Remember current text in editor...
FEditText := Text;
// ... as well as currently selected cell.
FRefocusingRow := Worksheet.ActiveCellRow;
FRefocusingCol := Worksheet.ActiveCellCol;
// Initiate validation of current input
PostMessage(Handle, UM_VALIDATEINPUT, 0, LParam(Self));
end;
end;
{@@ ----------------------------------------------------------------------------
Validation function for the current text in the edit control called by the
handler of the message posted in DoExit. Checks whether a formula is valid.
If not, the currently selected cell and the edit's Text are restored, and an
error message is displayed.
-------------------------------------------------------------------------------}
function TsCellEdit.DoValidText(const AText: String): Boolean;
var
err: String;
begin
Result := ValidFormula(AText, err);
if not Result then begin
Worksheet.SelectCell(FRefocusingRow, FRefocusingCol);
Text := AText; // restore orig text lost by interaction with grid
SelectAll;
MessageDlg(err, mtError, [mbOK], 0);
end;
end;
{@@ ----------------------------------------------------------------------------
EditingDone is called when the user presses the RETURN key to finish editing.
If the current Text is an invalid formula an error message is displayed and
nothing else happens. Otherwise, however, the edited text is written to the
worksheet which tries to figure out the data type.
-------------------------------------------------------------------------------}
procedure TsCellEdit.EditingDone;
var
s: String;
cell: PCell;
begin
if Worksheet = nil then
if (Worksheet = nil) then
exit;
try
Checkformula;
except
on E:Exception do begin
MessageDlg(E.Message, mtError, [mbOk], 0);
if not ValidFormula(Text, s) then begin
MessageDlg(s, mtError, [mbOK], 0);
exit;
end;
end;
cell := Worksheet.GetCell(Worksheet.ActiveCellRow, Worksheet.ActiveCellCol);
if Worksheet.IsMerged(cell) then
@ -1994,13 +2031,32 @@ end;
procedure TsCellEdit.KeyDown(var Key: Word; Shift : TShiftState);
var
selpos: Integer;
errMsg: String;
begin
if Key = VK_ESCAPE then begin
FFormulaError := false;
case Key of
VK_RETURN:
if not ValidFormula(Text, errMsg) then begin
Key := 0;
FFormulaError := true;
MessageDlg(errMsg, mtError, [mbOK], 0);
end;
VK_ESCAPE:
begin
Key := 0;
selpos := SelStart;
Lines.Text := FOldText;
SelStart := selpos;
exit;
end;
end;
inherited;
end;
procedure TsCellEdit.KeyUp(var Key: Word; Shift : TShiftState);
begin
if FFormulaError and (Key = VK_RETURN) then
Key := 0;
inherited;
end;
@ -2019,7 +2075,7 @@ procedure TsCellEdit.ListenerNotification(
var
cell: PCell;
begin
if (FWorkbookSource = nil) then
if (FWorkbookSource = nil) or (FRefocusing = self) then
exit;
if (lniSelection in AChangedItems) or
@ -2124,6 +2180,62 @@ begin
ReadOnly := not CanEditCell(ACell);
end;
{@@ ----------------------------------------------------------------------------
Validation routine of current input initiated when the edit lost focus.
If input is not correct (DoValidText) then state before focus change is
re-established.
-------------------------------------------------------------------------------}
procedure TsCellEdit.ValidateInput(var Msg: TLMessage);
var
s: String;
begin
if TControl(Msg.lParam) is TsCellEdit then begin
s := TsCellEdit(Msg.lParam).FEditText;
if not DoValidText(s) then begin
FRefocusing := TControl(Msg.lParam); // Avoid an endless loop
TWinControl(Msg.lParam).SetFocus; // Set focus back
end;
end;
end;
{@@ ----------------------------------------------------------------------------
Checks valididty of the provided formula and creates a corresponding
error message.
Returns TRUE if the provided string is a valid formula or no formula, FALSE
otherwise. In the latter case an error message string is returned as well.
-------------------------------------------------------------------------------}
function TsCellEdit.ValidFormula(AFormula: String; out AErrMsg: String): Boolean;
var
parser: TsSpreadsheetParser;
begin
Result := true;
AErrMsg := '';
if Assigned(Worksheet) and (AFormula <> '') and (AFormula[1] = '=') then
begin
parser := TsSpreadsheetParser.Create(Worksheet);
try
try
parser.LocalizedExpression[Workbook.FormatSettings] := AFormula;
except
on E: Exception do begin
AErrMsg := E.Message;
Result := false;
end;
end;
finally
parser.Free;
end;
end;
end;
procedure TsCellEdit.WMKillFocus(var AMessage: TLMKillFocus);
begin
// Override inherited behavior because we don't want to call EditingDone
// here.
end;
{------------------------------------------------------------------------------}
{ TsCellIndicator }

View File

@ -61,24 +61,46 @@ type
property JoinStyle default pjsMiter;
end;
TMultilineStringCellEditor = class(TMemo)
(*
TsSingleLineStringCellEditor = class(TEdit)
private
FGrid: TsCustomWorksheetGrid;
FCol,FRow:Integer;
FCol, FRow:Integer;
// procedure WMKillFocus(var AMessage: TLMKillFocus); message LM_KILLFOCUS;
protected
procedure WndProc(var TheMessage : TLMessage); override;
procedure Change; override;
procedure KeyDown(var Key : Word; Shift : TShiftState); override;
procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
procedure msg_SelectAll(var Msg: TGridMessage); message GM_SELECTALL;
procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
procedure KeyDown(var AKey: Word; AShift : TShiftState); override;
procedure msg_GetGrid(var AMsg: TGridMessage); message GM_GETGRID;
procedure msg_GetValue(var AMsg: TGridMessage); message GM_GETVALUE;
procedure msg_SelectAll(var AMsg: TGridMessage); message GM_SELECTALL;
procedure msg_SetGrid(var AMsg: TGridMessage); message GM_SETGRID;
procedure msg_SetPos(var AMsg: TGridMessage); message GM_SETPOS;
procedure msg_SetValue(var AMsg: TGridMessage); message GM_SETVALUE;
procedure WndProc(var AMsg: TLMessage); override;
public
constructor Create(Aowner : TComponent); override;
constructor Create(AOwner: TComponent); override;
procedure EditingDone; override;
property OnEditingDone;
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 EditText;
property OnEditingDone;
end;
@ -119,7 +141,8 @@ type
FReadOnly: Boolean;
FOnClickHyperlink: TsHyperlinkClickEvent;
FOldEditorText: String;
FMultiLineStringEditor: TMultilineStringCellEditor;
// FSingleLineStringEditor: TsSingleLineStringCellEditor;
FMultiLineStringEditor: TsMultilineStringCellEditor;
FLineMode: TsEditorLineMode;
FAllowDragAndDrop: Boolean;
FDragStartCol, FDragStartRow: Integer;
@ -127,6 +150,7 @@ type
FDragSelection: TGridRect;
FGetRowHeaderText: TsGetCellTextEvent;
FGetColHeaderText: TsGetCellTextEvent;
FRefocusing: TObject;
function CalcAutoRowHeight(ARow: Integer): Integer;
function CalcColWidthFromSheet(AWidth: Single): Integer;
function CalcRowHeightFromSheet(AHeight: Single): Integer;
@ -255,6 +279,8 @@ type
procedure DoCutToClipboard; override;
procedure DoEditorHide; override;
procedure DoEditorShow; override;
procedure DoEnter; override;
procedure DoExit; override;
procedure DoPasteFromClipboard; override;
procedure DoOnResize; override;
procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override;
@ -314,6 +340,7 @@ type
function ToPixels(AValue: Double): Integer;
procedure TopLeftChanged; override;
function TrimToCell(ACell: PCell): String;
procedure ValidateInput(var Msg: TLMessage); message UM_VALIDATEINPUT;
function ValidFormula({ACol, ARow: Integer; }AExpression: String;
out AErrMsg: String): Boolean;
procedure WMHScroll(var message: TLMHScroll); message LM_HSCROLL;
@ -1105,56 +1132,80 @@ end;
{*******************************************************************************
* TMultilineStringCellEditor *
* TsSingleLineStringCellEditor *
*******************************************************************************}
constructor TMultilineStringCellEditor.Create(Aowner: TComponent);
(*
constructor TsSingleLineStringCellEditor.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
AutoSize := false;
end;
procedure TMultilineStringCellEditor.Change;
procedure TsSingleLineStringCellEditor.Change;
begin
inherited Change;
if (FGrid <> nil) and Visible then
if (FGrid <> nil) and Visible then begin
FGrid.EditorTextChanged(FCol, FRow, Text);
end;
end;
procedure TMultilineStringCellEditor.EditingDone;
{
function TsSingleLineStringCellEditor.DoValidText(const AText: String): Boolean;
var
err: String;
begin
Result := FGrid.ValidFormula(AText, err);
if not Result then
MessageDlg(err, mtError, [mbOK], 0);
end;
}
procedure TsSingleLineStringCellEditor.EditingDone;
begin
inherited EditingDone;
if FGrid <> nil then
FGrid.EditingDone;
end;
procedure TMultilineStringCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
procedure TsSingleLineStringCellEditor.msg_GetGrid(var AMsg: TGridMessage);
begin
AMsg.Grid := FGrid;
AMsg.Options:= EO_IMPLEMENTED;
end;
procedure TsSingleLineStringCellEditor.msg_GetValue(var AMsg: TGridMessage);
begin
AMsg.Col := FCol;
AMsg.Row := FRow;
AMsg.Value := Text;
end;
procedure TsSingleLineStringCellEditor.KeyDown(var AKey: Word;
AShift: TShiftState);
function AllSelected: boolean;
begin
result := (SelLength > 0) and (SelLength = UTF8Length(Text));
Result := (SelLength > 0) and (SelLength = UTF8Length(Text));
end;
function AtStart: Boolean;
begin
Result:= (SelStart = 0);
Result := (SelStart = 0);
end;
function AtEnd: Boolean;
begin
result := ((SelStart + 1) > UTF8Length(Text)) or AllSelected;
Result := ((SelStart + 1) > UTF8Length(Text)) or AllSelected;
end;
procedure doEditorKeyDown;
procedure DoEditorKeyDown;
begin
if FGrid <> nil then
FGrid.EditorkeyDown(Self, key, shift);
FGrid.EditorkeyDown(Self, AKey, AShift);
end;
procedure doGridKeyDown;
procedure DoGridKeyDown;
begin
if FGrid <> nil then
FGrid.KeyDown(Key, shift);
FGrid.KeyDown(AKey, AShift);
end;
function GetFastEntry: boolean;
@ -1168,15 +1219,179 @@ procedure TMultilineStringCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
procedure CheckEditingKey;
begin
if (FGrid = nil) or FGrid.EditorIsReadOnly then
Key := 0;
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 TsSingleLineStringCellEditor.msg_SelectAll(var AMsg: TGridMessage);
begin
Unused(AMsg);
SelectAll;
end;
procedure TsSingleLineStringCellEditor.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 TsSingleLineStringCellEditor.msg_SetMask(var AMsg: TGridMessage);
begin
EditMask := AMsg.Value;
end;
}
procedure TsSingleLineStringCellEditor.msg_SetPos(var AMsg: TGridMessage);
begin
FCol := AMsg.Col;
FRow := AMsg.Row;
end;
procedure TsSingleLineStringCellEditor.msg_SetValue(var AMsg: TGridMessage);
begin
Text := AMsg.Value;
SelStart := UTF8Length(Text);
end;
{
procedure TsSingleLineStringCellEditor.WMKillFocus(var AMessage: TLMKillFocus);
begin
end;
}
procedure TsSingleLineStringCellEditor.WndProc(var AMsg: TLMessage);
begin
if FGrid <> nil then
case AMsg.Msg of
LM_CLEAR, LM_CUT, LM_PASTE:
begin
if FGrid.EditorIsReadOnly then
exit;
end;
end;
inherited WndProc(AMsg);
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;
(*
function TsMultiLineStringCellEditor.DoValidText(const AText: String): Boolean;
var
err: String;
begin
Result := FGrid.ValidFormula(AText, err);
if not Result then
MessageDlg(err, mtError, [mbOK], 0);
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;
msg: String;
begin
inherited KeyDown(Key,Shift);
case Key of
inherited KeyDown(AKey, AShift);
case AKey of
VK_F2:
if AllSelected then begin
SelLength := 0;
@ -1189,8 +1404,8 @@ begin
VK_LEFT, VK_RIGHT:
if GetFastEntry then begin
IntSel:=
((Key=VK_LEFT) and not AtStart) or
((Key=VK_RIGHT) and not AtEnd);
((AKey = VK_LEFT) and not AtStart) or
((AKey = VK_RIGHT) and not AtEnd);
if not IntSel then begin
doGridKeyDown;
end;
@ -1200,64 +1415,66 @@ begin
VK_ESCAPE:
begin
doGridKeyDown;
if key<>0 then begin
if AKey <> 0 then begin
Text := FGrid.FOldEditorText;
// Lines.Text := ''; // FIXME: FGrid.FEditorOldvalue;
// SetEditText(FGrid.FEditorOldValue);
FGrid.EditorHide;
end;
end;
else
doEditorKeyDown;
DoEditorKeyDown;
end;
end;
procedure TMultilineStringCellEditor.msg_GetGrid(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_GetGrid(var AMsg: TGridMessage);
begin
Msg.Grid := FGrid;
Msg.Options:= EO_IMPLEMENTED;
AMsg.Grid := FGrid;
AMsg.Options:= EO_IMPLEMENTED;
end;
procedure TMultilineStringCellEditor.msg_GetValue(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_GetValue(var AMsg: TGridMessage);
begin
Msg.Col := FCol;
Msg.Row := FRow;
Msg.Value := Text;
AMsg.Col := FCol;
AMsg.Row := FRow;
AMsg.Value := Text;
end;
procedure TMultilineStringCellEditor.msg_SelectAll(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_SelectAll(var AMsg: TGridMessage);
begin
Unused(Msg);
Unused(AMsg);
SelectAll;
end;
procedure TMultilineStringCellEditor.msg_SetGrid(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_SetGrid(var AMsg: TGridMessage);
begin
FGrid := Msg.Grid as TsCustomWorksheetGrid;
Msg.Options := EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
FGrid := AMsg.Grid as TsCustomWorksheetGrid;
AMsg.Options := EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
end;
procedure TMultilineStringCellEditor.msg_SetPos(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_SetPos(var AMsg: TGridMessage);
begin
FCol := Msg.Col;
FRow := Msg.Row;
FCol := AMsg.Col;
FRow := AMsg.Row;
end;
procedure TMultilineStringCellEditor.msg_SetValue(var Msg: TGridMessage);
procedure TsMultilineStringCellEditor.msg_SetValue(var AMsg: TGridMessage);
begin
Text := Msg.Value;
Text := AMsg.Value;
SelStart := UTF8Length(Text);
end;
(*
procedure TsMultiLineStringCellEditor.WMKillFocus(var AMessage: TLMKillFocus);
begin
end; *)
procedure TMultilineStringCellEditor.WndProc(var TheMessage: TLMessage);
procedure TsMultilineStringCellEditor.WndProc(var AMsg: TLMessage);
begin
if FGrid <> nil then
case TheMessage.Msg of
case AMsg.Msg of
LM_CLEAR, LM_CUT, LM_PASTE:
if FGrid.EditorIsReadOnly then exit;
end;
inherited WndProc(TheMessage);
inherited WndProc(AMsg);
end;
@ -1314,6 +1531,13 @@ begin
{$ENDIF}
FAllowDragAndDrop := true;
(*
FSingleLineStringEditor := TsSingleLineStringCellEditor.Create(self);
FSingleLineStringEditor.name :='SingleLineStringEditor';
FSingleLineStringEditor.Text := '';
FSingleLineStringEditor.Visible := False;
FSingleLineStringEditor.Align := alNone;
FSingleLineStringEditor.BorderStyle := bsNone; *)
dec(FRowHeightLock);
UpdateRowHeights;
@ -1330,7 +1554,8 @@ begin
FreeAndNil(FCellFont);
FreeAndNil(FSelPen);
FreeAndNil(FFrozenBorderPen);
FreeAndNil(FMultilineStringEditor);
// FreeAndNil(FSingleLineStringEditor);
FreeAndNil(FMultiLineStringEditor);
inherited Destroy;
end;
@ -2002,13 +2227,15 @@ var
msg: String;
begin
inherited;
{
// The following code is reached when an error is found in the cell formula
// being edited and another control is selected.
if (FEditText <> '') then
if not ValidFormula(FEditText, msg) then begin
if (FEditText <> '') then begin
if not lula(FEditText, msg) then
MessageDlg(msg, mtError, [mbOK], 0);
FEditText := '';
end;
}
end;
{ Make the cell editor the same size as the edited cell, in particular for
@ -2040,6 +2267,22 @@ begin
end;
end;
procedure TsCustomWorksheetGrid.DoEnter;
begin
if FRefocusing = self then
FRefocusing := nil;
inherited;
end;
procedure TsCustomWorksheetGrid.DoExit;
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
PostMessage(Handle, um_ValidateInput, 0, LParam(Self));
FGridState := gsNormal;
end;
procedure TsCustomWorksheetGrid.DoOnResize;
begin
if (csDesigning in ComponentState) and (Worksheet = nil) then
@ -3296,6 +3539,13 @@ end;
function TsCustomWorksheetGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl;
begin
(*
if (Style = cbsAuto) then
case FLineMode of
elmSingleLine : Result := FSingleLineStringEditor;
elmMultiLine : Result := FMultiLineStringEditor ;
end
*)
if (Style = cbsAuto) and (FLineMode = elmMultiLine) then
Result := FMultiLineStringEditor
else
@ -4676,7 +4926,6 @@ begin
Key := 0;
end;
end;
case Key of
VK_RIGHT:
if (aeNavigation in FAutoExpand) and (Col = ColCount-1) then
@ -5386,6 +5635,7 @@ var
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;
@ -5395,6 +5645,7 @@ begin
end;
end;
Result := inherited;
if Result and Assigned(Worksheet) and Worksheet.IsProtected then
@ -5464,22 +5715,6 @@ begin
maxRowCount := IfThen(aeDefault in FAutoExpand, DEFAULT_ROW_COUNT, 1);
ColCount := Max(GetGridCol(Worksheet.GetLastColIndex) + 1, maxColCount);
RowCount := Max(GetGridRow(Worksheet.GetLastRowIndex) + 1, maxRowCount);
(*
if aeDefault in FAutoExpand then begin
ColCount := Max(GetGridCol(Worksheet.GetLastColIndex)+1, DEFAULT_COL_COUNT); // + FHeaderCount;
RowCount := Max(GetGridRow(Worksheet.GetLastRowIndex)+1, DEFAULT_ROW_COUNT); // + FHeaderCount;
end else begin
// wp: next lines replaced by the following ones because of
// http://forum.lazarus.freepascal.org/index.php/topic,36770.msg246192.html#msg246192
// NOTE: VERY SENSITIVE LOCATION !!!
// ColCount := Max(GetGridCol(WorkSheet.GetLastColIndex), 1) + FHeaderCount;
// RowCount := Max(GetGridCol(Worksheet.GetLastRowIndex), 1) + FHeaderCount;
ColCount := Max(GetGridCol(WorkSheet.GetLastColIndex)+1, 1); // + FHeaderCount;
RowCount := Max(GetGridCol(Worksheet.GetLastRowIndex)+1, 1); // + FHeaderCount;
end;
*)
FixedCols := FFrozenCols + FHeaderCount;
FixedRows := FFrozenRows + FHeaderCount;
if ShowHeaders then begin
@ -6765,11 +7000,11 @@ begin
FLineMode := AValue;
if (FLineMode = elmMultiline) and (FMultilineStringEditor = nil) then
begin
FMultilineStringEditor := TMultilineStringCellEditor.Create(nil);
FMultilineStringEditor.name :='StringEditor';
FMultilineStringEditor.Text:='';
FMultilineStringEditor.Visible:=False;
FMultilineStringEditor.Align:=alNone;
FMultilineStringEditor := TsMultilineStringCellEditor.Create(nil);
FMultilineStringEditor.name := 'MultilineStringEditor';
FMultilineStringEditor.Text := '';
FMultilineStringEditor.Visible := False;
FMultilineStringEditor.Align := alNone;
FMultilineStringEditor.BorderStyle := bsNone;
end;
end;
@ -7053,15 +7288,29 @@ begin
BeginUpdate;
try
Worksheet.ZoomFactor := abs(AValue);
// AdaptToZoomFactor;
finally
EndUpdate;
end;
end;
end;
function TsCustomWorksheetGrid.ValidFormula(//ACol, ARow: Integer;
AExpression: String; out AErrMsg: String): Boolean;
procedure TsCustomWorksheetGrid.ValidateInput(var Msg: TLMessage);
var
grid: TsCustomWorksheetGrid;
errmsg: String;
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
end;
end;
end;
function TsCustomWorksheetGrid.ValidFormula(AExpression: String;
out AErrMsg: String): Boolean;
var
parser: TsSpreadsheetParser;
begin