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 interface
uses uses
LMessages, LResources, LCLVersion,
Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList, Classes, Graphics, SysUtils, Controls, StdCtrls, ComCtrls, ValEdit, ActnList,
LResources, LCLVersion, fpstypes, fpspreadsheet;
fpstypes, fpspreadsheet; //, {%H-}fpsAllFormats;
const
{@@ User-defined message for input validation }
UM_VALIDATEINPUT = LM_USER + 100;
type type
{@@ Event handler procedure for displaying a message if an error or {@@ Event handler procedure for displaying a message if an error or
@ -236,16 +240,25 @@ type
FWorkbookSource: TsWorkbookSource; FWorkbookSource: TsWorkbookSource;
FShowHTMLText: Boolean; FShowHTMLText: Boolean;
FOldText: String; FOldText: String;
FFormulaError: Boolean;
FRefocusing: TObject;
FRefocusingCol, FRefocusingRow: Cardinal;
function GetSelectedCell: PCell; function GetSelectedCell: PCell;
function GetWorkbook: TsWorkbook; function GetWorkbook: TsWorkbook;
function GetWorksheet: TsWorksheet; function GetWorksheet: TsWorksheet;
procedure SetWorkbookSource(AValue: TsWorkbookSource); procedure SetWorkbookSource(AValue: TsWorkbookSource);
procedure ValidateInput(var Msg: TLMessage); message UM_VALIDATEINPUT;
procedure WMKillFocus(var AMessage: TLMKillFocus); message LM_KILLFOCUS;
protected protected
FEditText: String;
function CanEditCell(ACell: PCell): Boolean; overload; function CanEditCell(ACell: PCell): Boolean; overload;
function CanEditCell(ARow, ACol: Cardinal): Boolean; overload; function CanEditCell(ARow, ACol: Cardinal): Boolean; overload;
procedure CheckFormula;
procedure DoEnter; override; 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 Notification(AComponent: TComponent; Operation: TOperation); override;
procedure ShowCell(ACell: PCell); virtual; procedure ShowCell(ACell: PCell); virtual;
public public
@ -1886,21 +1899,6 @@ begin
Result := CanEditCell(cell); Result := CanEditCell(cell);
end; 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; procedure TsCellEdit.DoEnter;
begin begin
if Worksheet = nil then if Worksheet = nil then
@ -1913,31 +1911,70 @@ begin
Abort; Abort;
end; end;
if FRefocusing = self then
FRefocusing := nil;
inherited; inherited;
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
EditingDone is called when the user presses the RETURN key to finish editing, DoExit is called when the edit has lost focus. Posts a message to the end of
or the TAB key which removes focus from the control, or clicks somewhere else the message queue in order to complete the focus change operation and to
The edited text is written to the worksheet which tries to figure out the trigger validation of the edit afterwards (which will restore the edit as
data type. In particular, if the text begins with an equal sign ("=") then focused control if validation fails.
the text is assumed to be a formula.
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; procedure TsCellEdit.EditingDone;
var var
s: String; s: String;
cell: PCell; cell: PCell;
begin begin
if Worksheet = nil then if (Worksheet = nil) then
exit; exit;
try if not ValidFormula(Text, s) then begin
Checkformula; MessageDlg(s, mtError, [mbOK], 0);
except exit;
on E:Exception do begin
MessageDlg(E.Message, mtError, [mbOk], 0);
exit;
end;
end; end;
cell := Worksheet.GetCell(Worksheet.ActiveCellRow, Worksheet.ActiveCellCol); cell := Worksheet.GetCell(Worksheet.ActiveCellRow, Worksheet.ActiveCellCol);
@ -1994,16 +2031,35 @@ end;
procedure TsCellEdit.KeyDown(var Key: Word; Shift : TShiftState); procedure TsCellEdit.KeyDown(var Key: Word; Shift : TShiftState);
var var
selpos: Integer; selpos: Integer;
errMsg: String;
begin begin
if Key = VK_ESCAPE then begin FFormulaError := false;
selpos := SelStart; case Key of
Lines.Text := FOldText; VK_RETURN:
SelStart := selpos; if not ValidFormula(Text, errMsg) then begin
exit; 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; end;
inherited; inherited;
end; end;
procedure TsCellEdit.KeyUp(var Key: Word; Shift : TShiftState);
begin
if FFormulaError and (Key = VK_RETURN) then
Key := 0;
inherited;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Notification message received from the WorkbookSource telling which item Notification message received from the WorkbookSource telling which item
of the spreadsheet has changed. of the spreadsheet has changed.
@ -2019,7 +2075,7 @@ procedure TsCellEdit.ListenerNotification(
var var
cell: PCell; cell: PCell;
begin begin
if (FWorkbookSource = nil) then if (FWorkbookSource = nil) or (FRefocusing = self) then
exit; exit;
if (lniSelection in AChangedItems) or if (lniSelection in AChangedItems) or
@ -2124,6 +2180,62 @@ begin
ReadOnly := not CanEditCell(ACell); ReadOnly := not CanEditCell(ACell);
end; 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 } { TsCellIndicator }

View File

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