You've already forked lazarus-ccr
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:
@ -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,31 +1911,70 @@ 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);
|
||||
exit;
|
||||
end;
|
||||
if not ValidFormula(Text, s) then begin
|
||||
MessageDlg(s, mtError, [mbOK], 0);
|
||||
exit;
|
||||
end;
|
||||
|
||||
cell := Worksheet.GetCell(Worksheet.ActiveCellRow, Worksheet.ActiveCellCol);
|
||||
@ -1994,16 +2031,35 @@ end;
|
||||
procedure TsCellEdit.KeyDown(var Key: Word; Shift : TShiftState);
|
||||
var
|
||||
selpos: Integer;
|
||||
errMsg: String;
|
||||
begin
|
||||
if Key = VK_ESCAPE then begin
|
||||
selpos := SelStart;
|
||||
Lines.Text := FOldText;
|
||||
SelStart := selpos;
|
||||
exit;
|
||||
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;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Notification message received from the WorkbookSource telling which item
|
||||
of the spreadsheet has changed.
|
||||
@ -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 }
|
||||
|
@ -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,9 +1404,9 @@ begin
|
||||
VK_LEFT, VK_RIGHT:
|
||||
if GetFastEntry then begin
|
||||
IntSel:=
|
||||
((Key=VK_LEFT) and not AtStart) or
|
||||
((Key=VK_RIGHT) and not AtEnd);
|
||||
if not IntSel then begin
|
||||
((AKey = VK_LEFT) and not AtStart) or
|
||||
((AKey = VK_RIGHT) and not AtEnd);
|
||||
if not IntSel then begin
|
||||
doGridKeyDown;
|
||||
end;
|
||||
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
|
||||
|
Reference in New Issue
Block a user