Introduce read-only mode for grid editors. Closes #631

This commit is contained in:
Ansgar Becker
2020-02-06 12:29:35 +01:00
parent 568e1a6d8b
commit 56e541a478
8 changed files with 100 additions and 58 deletions

View File

@ -737,7 +737,7 @@ procedure Tconnform.ListSessionsCreateEditor(Sender: TBaseVirtualTree; Node: PVi
Column: TColumnIndex; out EditLink: IVTEditLink);
begin
// Use our own text editor to rename a session
EditLink := TInplaceEditorLink.Create(Sender as TVirtualStringTree);
EditLink := TInplaceEditorLink.Create(Sender as TVirtualStringTree, True);
end;

View File

@ -706,6 +706,7 @@ type
function HasResult: Boolean; virtual; abstract;
function GetWhereClause: String;
procedure CheckEditable;
function IsEditable: Boolean;
procedure DeleteRow;
function InsertRow: Int64;
procedure SetCol(Column: Integer; NewText: String; Null: Boolean; IsFunction: Boolean);
@ -7989,6 +7990,19 @@ begin
end;
end;
function TDBQuery.IsEditable: Boolean;
begin
try
CheckEditable;
Result := True;
except
on E:EDbError do begin
FConnection.Log(lcDebug, E.Message);
Result := False;
end;
end;
end;
function TDBQuery.GetWhereClause: String;
var

View File

@ -36,6 +36,7 @@ type
FOldWindowProc: TWndMethod; // Temporary switched to TempWindowProc to be able to catch Tab key
FTableColumn: TTableColumn;
FModified: Boolean;
FAllowEdit: Boolean;
FBeginEditTime: Cardinal;
procedure TempWindowProc(var Message: TMessage);
procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
@ -43,9 +44,12 @@ type
procedure DoCancelEdit(Sender: TObject);
function GetCellRect(InnerTextBounds: Boolean): TRect;
public
property TableColumn: TTableColumn read FTableColumn write FTableColumn; // The table column of the cell being edited. Mostly used in data grids.
constructor Create; overload; // The original constructor, not used any more, throws an exception if you do
constructor Create(Tree: TVirtualStringTree); overload; virtual; // The right constructor, we need the Tree reference
// The table column of the cell being edited. Mostly used in data grids.
property TableColumn: TTableColumn read FTableColumn write FTableColumn;
// The original constructor, not used any more, throws an exception if you do
constructor Create; overload;
// The right constructor, we need the Tree reference
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); overload; virtual;
destructor Destroy; override;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
function BeginEdit: Boolean; virtual; stdcall;
@ -63,7 +67,7 @@ type
public
MaxLength: Integer;
TitleText: String;
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function CancelEdit: Boolean; override;
@ -90,7 +94,7 @@ type
procedure TextChange(Sender: TObject);
function MicroSecondsPrecision: Integer;
public
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function EndEdit: Boolean; override;
@ -102,10 +106,11 @@ type
private
FCombo: TComboBox;
procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure DoSelect(Sender: TObject);
public
ValueList, DisplayList: TStringList;
AllowCustomText: Boolean;
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function EndEdit: Boolean; override;
@ -123,7 +128,7 @@ type
procedure BtnCancelClick(Sender: TObject);
public
ValueList: TStringList;
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function EndEdit: Boolean; override;
@ -144,7 +149,7 @@ type
public
ButtonVisible: Boolean;
TitleText: String;
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function CancelEdit: Boolean; override;
@ -173,7 +178,7 @@ type
public
DefaultType, OnUpdateType: TColumnDefaultType;
DefaultText, OnUpdateText: String;
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function EndEdit: Boolean; override;
@ -199,7 +204,7 @@ type
PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
procedure DoTreeSelectFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
public
constructor Create(Tree: TVirtualStringTree); override;
constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean); override;
destructor Destroy; override;
function BeginEdit: Boolean; override;
function EndEdit: Boolean; override;
@ -232,7 +237,7 @@ begin
[Self.ClassName, 'Create', Self.ClassName, 'Create(VirtualStringTree)']);
end;
constructor TBaseGridEditorLink.Create(Tree: TVirtualStringTree);
constructor TBaseGridEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create;
FTree := Tree;
@ -243,6 +248,7 @@ begin
FParentForm.Repaint;
SendMessage(FParentForm.Handle, WM_SETREDRAW, 0, 0);
FModified := False;
FAllowEdit := AllowEdit;
end;
destructor TBaseGridEditorLink.Destroy;
@ -333,7 +339,7 @@ begin
Result := not FStopping;
if FStopping then Exit;
FStopping := True;
if FModified then
if FModified and FAllowEdit then
FTree.Text[FNode, FColumn] := NewText;
if FTree.CanFocus and (FLastKeyDown <> VK_TAB) then
FTree.SetFocus;
@ -440,9 +446,9 @@ end;
constructor THexEditorLink.Create(Tree: TVirtualStringTree);
constructor THexEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
end;
destructor THexEditorLink.Destroy;
@ -465,6 +471,7 @@ begin
FForm.SetText(FCellText);
FForm.SetTitleText(TitleText);
FForm.SetMaxLength(MaxLength);
FForm.memoText.ReadOnly := not FAllowEdit;
end;
@ -501,9 +508,9 @@ end;
{ DateTime editor }
constructor TDateTimeEditorLink.Create(Tree: TVirtualStringTree);
constructor TDateTimeEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
FPanel := TPanel.Create(FParentForm);
FPanel.Parent := FParentForm;
@ -519,6 +526,7 @@ begin
FMaskEdit.OnKeyDown := DoKeyDown;
FMaskEdit.OnKeyUp := DoKeyUp;
FMaskEdit.OnChange := TextChange;
FMaskEdit.ReadOnly := not FAllowEdit;
FMainControl := FMaskEdit;
FUpDown := TUpDown.Create(FPanel);
@ -840,15 +848,16 @@ end;
{ Enum editor }
constructor TEnumEditorLink.Create(Tree: TVirtualStringTree);
constructor TEnumEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
AllowCustomText := False;
FCombo := TComboBox.Create(FParentForm);
FCombo.Hide;
FCombo.Parent := FParentForm;
FCombo.OnKeyDown := DoKeyDown;
FCombo.OnExit := DoEndEdit;
FCombo.OnSelect := DoSelect;
// Show some more than the default 8 items
FCombo.DropDownCount := 16;
ValueList := TStringList.Create;
@ -878,7 +887,7 @@ function TEnumEditorLink.EndEdit: Boolean; stdcall;
var
NewText: String;
begin
if AllowCustomText then
if AllowCustomText and FAllowEdit then
NewText := FCombo.Text
else if (ValueList.Count > 0) and (FCombo.ItemIndex > -1) then
NewText := ValueList[FCombo.ItemIndex]
@ -902,7 +911,7 @@ begin
Items := ValueList;
for i:=0 to Items.Count - 1 do
FCombo.Items.Add(Items[i]);
if AllowCustomText then begin
if AllowCustomText and FAllowEdit then begin
FCombo.Style := csDropDown;
FCombo.Text := FCellText;
end else begin
@ -930,12 +939,21 @@ begin
end;
procedure TEnumEditorLink.DoSelect(Sender: TObject);
begin
// Read only mode?
if not FAllowEdit then begin
FCombo.ItemIndex := ValueList.IndexOf(FCellText);
end;
end;
{ SET editor }
constructor TSetEditorLink.Create(Tree: TVirtualStringTree);
constructor TSetEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
ValueList := TStringList.Create;
FPanel := TPanel.Create(FParentForm);
@ -1016,6 +1034,7 @@ begin
SelValues.DelimitedText := FCellText;
for i:=0 to FCheckList.Items.Count-1 do begin
FCheckList.Checked[i] := SelValues.IndexOf(FCheckList.Items[i]) > -1;
FCheckList.ItemEnabled[i] := FAllowEdit;
end;
SelValues.Free;
end;
@ -1034,6 +1053,7 @@ begin
FBtnOk.Left := margin;
FBtnOk.Height := 24;
FBtnOk.Top := FPanel.Height - 2*margin - FBtnOk.Height;
FBtnOk.Enabled := FAllowEdit;
FBtnCancel.Width := FBtnOk.Width;
FBtnCancel.Left := 2*margin + FBtnOk.Width;
@ -1044,6 +1064,7 @@ begin
FCheckList.Left := margin;
FCheckList.Width := FPanel.Width - 2*margin;
FCheckList.Height := FBtnOk.Top - margin - FCheckList.Top;
// FCheckList.Enabled := FAllowEdit; // crashes with "cannot focus if disabled"
end;
@ -1066,9 +1087,9 @@ end;
{ TInplaceEditorLink }
constructor TInplaceEditorLink.Create(Tree: TVirtualStringTree);
constructor TInplaceEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
ButtonVisible := false;
FTextEditor := nil;
@ -1084,6 +1105,7 @@ begin
FEdit.ParentColor := True;
FEdit.BorderStyle := bsNone;
FEdit.OnKeyDown := DoKeyDown;
FEdit.ReadOnly := not FAllowEdit;
FMainControl := FEdit;
FButton := TButton.Create(FPanel);
@ -1162,6 +1184,7 @@ begin
FTextEditor.SetTitleText(TitleText);
FTextEditor.Modified := FEdit.Modified;
FTextEditor.SetMaxLength(Self.FMaxLength);
FTextEditor.memoText.ReadOnly := not FAllowEdit;
FTextEditor.ShowModal;
end;
@ -1197,13 +1220,13 @@ end;
{ Column default editor }
constructor TColumnDefaultEditorLink.Create(Tree: TVirtualStringTree);
constructor TColumnDefaultEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
const
m = 5;
var
i: Integer;
begin
inherited Create(Tree);
inherited;
FPanel := TPanel.Create(FParentForm);
FPanel.Hide;
@ -1547,9 +1570,9 @@ end;
{ Datatype selector }
constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree);
constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean);
begin
inherited Create(Tree);
inherited;
FTreeSelect := TVirtualStringTree.Create(FParentForm);
FTreeSelect.Hide;

View File

@ -272,7 +272,7 @@ begin
// Start cell editor
Grid := Sender as TVirtualStringTree;
if Column = ColValue then begin
EnumEditor := TEnumEditorLink.Create(Grid);
EnumEditor := TEnumEditorLink.Create(Grid, True);
EnumEditor.AllowCustomText := True;
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.Text := 'NULL'+CRLF+

View File

@ -9344,9 +9344,11 @@ var
Results: TDBQuery;
RowNum: PInt64;
Timestamp: Int64;
IsNull: Boolean;
IsNull, AllowEdit: Boolean;
begin
Results := GridResult(Sender);
if not Results.IsEditable then
Exit;
RowNum := Sender.GetNodeData(Node);
Results.RecNo := RowNum^;
try
@ -9457,7 +9459,6 @@ procedure TMainForm.AnyGridEditing(Sender: TBaseVirtualTree; Node:
begin
Allowed := False;
try
GridResult(Sender).CheckEditable;
if not AnyGridEnsureFullRow(Sender as TVirtualStringTree, Node) then
ErrorDialog(_('Could not load full row data.'))
else begin
@ -9513,12 +9514,15 @@ var
RowNum: PInt64;
RefDb, RefTable: String;
RefObj: TDBObject;
AllowEdit: Boolean;
begin
VT := Sender as TVirtualStringTree;
Results := GridResult(VT);
RowNum := VT.GetNodeData(Node);
Results.RecNo := RowNum^;
Conn := Results.Connection;
// Allow editing, or leave readonly mode
AllowEdit := Results.IsEditable;
// Find foreign key values
if AppSettings.ReadBool(asForeignDropDown) and (Sender = DataGrid) then begin
@ -9561,7 +9565,7 @@ begin
ForeignResults := Conn.GetResults(SQL);
if ForeignResults.RecordCount < ForeignItemsLimit then begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, AllowEdit);
EditLink := EnumEditor;
while not ForeignResults.Eof do begin
EnumEditor.ValueList.Add(ForeignResults.Col(0));
@ -9582,7 +9586,7 @@ begin
FGridEditFunctionMode := FGridEditFunctionMode or Results.IsFunction(Column);
if FGridEditFunctionMode then begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, AllowEdit);
for idx:=Low(MySQLFunctions) to High(MySQLFunctions) do
EnumEditor.ValueList.Add(MySQLFunctions[idx].Name + MySQLFunctions[idx].Declaration);
EnumEditor.AllowCustomText := True;
@ -9593,17 +9597,17 @@ begin
if Assigned(EditLink) then
// Editor was created above, do nothing now
else if (Results.DataType(Column).Index in [dtEnum, dtBool]) and AppSettings.ReadBool(asFieldEditorEnum) then begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, AllowEdit);
EnumEditor.ValueList := Results.ValueList(Column);
EditLink := EnumEditor;
end else if (TypeCat = dtcText) or ((TypeCat in [dtcBinary, dtcSpatial]) and actBlobAsText.Checked) then begin
InplaceEditor := TInplaceEditorLink.Create(VT);
InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit);
InplaceEditor.MaxLength := Results.MaxLength(Column);
InplaceEditor.TitleText := Results.ColumnOrgNames[Column];
InplaceEditor.ButtonVisible := True;
EditLink := InplaceEditor;
end else if (TypeCat in [dtcBinary, dtcSpatial]) and AppSettings.ReadBool(asFieldEditorBinary) then begin
HexEditor := THexEditorLink.Create(VT);
HexEditor := THexEditorLink.Create(VT, AllowEdit);
HexEditor.MaxLength := Results.MaxLength(Column);
HexEditor.TitleText := Results.ColumnOrgNames[Column];
EditLink := HexEditor;
@ -9625,17 +9629,17 @@ begin
NowText := NowText + '.' + StringOfChar('0', MicroSecondsPrecision);
VT.Text[Node, Column] := NowText;
end;
DateTimeEditor := TDateTimeEditorLink.Create(VT);
DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit);
EditLink := DateTimeEditor;
end else if AppSettings.ReadBool(asFieldEditorDatetime) and HandleUnixTimestampColumn(Sender, Column) then begin
DateTimeEditor := TDateTimeEditorLink.Create(VT);
DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit);
EditLink := DateTimeEditor;
end else if (Results.DataType(Column).Index = dtSet) and AppSettings.ReadBool(asFieldEditorSet) then begin
SetEditor := TSetEditorLink.Create(VT);
SetEditor := TSetEditorLink.Create(VT, AllowEdit);
SetEditor.ValueList := Results.ValueList(Column);
EditLink := SetEditor;
end else begin
InplaceEditor := TInplaceEditorLink.Create(VT);
InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit);
InplaceEditor.ButtonVisible := False;
EditLink := InplaceEditor;
end;
@ -12706,7 +12710,7 @@ var
VT: TVirtualStringTree;
begin
VT := Sender as TVirtualStringTree;
InplaceEditor := TInplaceEditorLink.Create(VT);
InplaceEditor := TInplaceEditorLink.Create(VT, True);
InplaceEditor.ButtonVisible := true;
EditLink := InplaceEditor;
end;

View File

@ -395,7 +395,7 @@ begin
if Column = 1 then
EditLink := TStringEditLink.Create
else if Column = 2 then begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.AllowCustomText := True;
EnumEditor.ValueList := TStringList.Create;
for DBDatatype in DBObject.Connection.Datatypes do begin
@ -406,7 +406,7 @@ begin
end;
EditLink := EnumEditor;
end else if Column = 3 then begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.Add('IN');
EnumEditor.ValueList.Add('OUT');

View File

@ -1457,18 +1457,18 @@ begin
Col := Sender.GetNodeData(Node);
case Column of
2: begin // Datatype pulldown
DatatypeEditor := TDatatypeEditorLink.Create(VT);
DatatypeEditor := TDatatypeEditorLink.Create(VT, True);
EditLink := DataTypeEditor;
end;
9: begin // Collation pulldown
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.Text := DBObject.Connection.CollationList.Text;
EnumEditor.ValueList.Insert(0, '');
EditLink := EnumEditor;
end;
7: begin
DefaultEditor := TColumnDefaultEditorLink.Create(VT);
DefaultEditor := TColumnDefaultEditorLink.Create(VT, True);
DefaultEditor.DefaultType := Col.DefaultType;
DefaultEditor.DefaultText := Col.DefaultText;
DefaultEditor.OnUpdateType := Col.OnUpdateType;
@ -1476,7 +1476,7 @@ begin
EditLink := DefaultEditor;
end;
11: begin // Virtuality pulldown
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList := TStringList.Create;
if DBObject.Connection.Parameters.IsMariaDB then
EnumEditor.ValueList.CommaText := ',VIRTUAL,PERSISTENT'
@ -1485,7 +1485,7 @@ begin
EditLink := EnumEditor;
end
else begin
Edit := TInplaceEditorLink.Create(VT);
Edit := TInplaceEditorLink.Create(VT, True);
Edit.TitleText := VT.Header.Columns[Column].Text;
Edit.ButtonVisible := True;
EditLink := Edit;
@ -1798,18 +1798,18 @@ begin
Level := (Sender as TVirtualStringtree).GetNodeLevel(Node);
if (Level = 0) and (Column = 1) then begin
// Index type pulldown
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.CommaText := PKEY +','+ KEY +','+ UKEY +','+ FKEY +','+ SKEY;
EditLink := EnumEditor;
end else if (Level = 0) and (Column = 2) then begin
// Algorithm pulldown
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList := Explode(',', ',BTREE,HASH,RTREE');
EditLink := EnumEditor;
end else if (Level = 1) and (Column = 0) then begin
// Column names pulldown
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
ColNode := listColumns.GetFirst;
while Assigned(ColNode) do begin
Col := listColumns.GetNodeData(ColNode);
@ -1819,7 +1819,7 @@ begin
EnumEditor.AllowCustomText := True; // Allows adding a subpart in index parts: "TextCol(20)"
EditLink := EnumEditor;
end else
EditLink := TInplaceEditorLink.Create(VT);
EditLink := TInplaceEditorLink.Create(VT, True);
end;
@ -2295,9 +2295,9 @@ begin
// Init grid editor in foreign key list
VT := Sender as TVirtualStringTree;
case Column of
0: EditLink := TInplaceEditorLink.Create(VT);
0: EditLink := TInplaceEditorLink.Create(VT, True);
1: begin
SetEditor := TSetEditorLink.Create(VT);
SetEditor := TSetEditorLink.Create(VT, True);
ColNode := listColumns.GetFirst;
while Assigned(ColNode) do begin
PCol := listColumns.GetNodeData(ColNode);
@ -2307,7 +2307,7 @@ begin
EditLink := SetEditor;
end;
2: begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.AllowCustomText := True;
DBObjects := DBObject.Connection.GetDBObjects(DBObject.Connection.Database);
for Obj in DBObjects do begin
@ -2318,7 +2318,7 @@ begin
end;
3: begin
Key := FForeignKeys[Node.Index];
SetEditor := TSetEditorLink.Create(VT);
SetEditor := TSetEditorLink.Create(VT, True);
Obj := DBObject.Connection.FindObject(DBObject.Database, Key.ReferenceTable);
if Obj <> nil then begin
Columns := Obj.TableColumns;
@ -2329,7 +2329,7 @@ begin
EditLink := SetEditor;
end;
4, 5: begin
EnumEditor := TEnumEditorLink.Create(VT);
EnumEditor := TEnumEditorLink.Create(VT, True);
EnumEditor.ValueList.Text := 'RESTRICT'+CRLF+'CASCADE'+CRLF+'SET NULL'+CRLF+'NO ACTION';
EditLink := EnumEditor;
end;

View File

@ -68,6 +68,7 @@ type
procedure SetMaxLength(len: integer);
procedure SetFont(font: TFont);
property Modified: Boolean read FModified write SetModified;
property memoText: TLineNormalizingMemo read FmemoText;
end;