Files
HeidiSQL/source/grideditlinks.pas

611 lines
15 KiB
ObjectPascal

unit grideditlinks;
// The editor links, instanciated by VirtualTree.CreateEditor
interface
uses Windows, Forms, Graphics, messages, VirtualTrees, texteditor, bineditor, ComCtrls, SysUtils, Classes,
mysql_structures, Main, ChildWin, helpers, TntStdCtrls, WideStrings, StdCtrls, ExtCtrls, TntCheckLst;
type
TMemoEditorLink = class(TInterfacedObject, IVTEditLink)
private
FForm: TMemoEditor;
FTree: TCustomVirtualStringTree; // A back reference to the tree calling.
FNode: PVirtualNode; // The node to be edited.
FColumn: TColumnIndex; // The column of the node.
FTextBounds: TRect; // Smallest rectangle around the text.
FStopping: Boolean; // Set to True when the edit link requests stopping the edit action.
public
FieldType: Integer;
MaxLength: Integer;
constructor Create;
destructor Destroy; override;
function BeginEdit: Boolean; virtual; stdcall;
function CancelEdit: Boolean; virtual; stdcall;
function EndEdit: Boolean; virtual; stdcall;
function GetBounds: TRect; virtual; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
procedure SetBounds(R: TRect); virtual; stdcall;
end;
TDateTimeEditorLink = class(TInterfacedObject, IVTEditLink)
private
FPicker: TDateTimePicker;
FTree: TCustomVirtualStringTree;
FNode: PVirtualNode;
FColumn: TColumnIndex;
FStopping: Boolean;
FModified: Boolean;
procedure PickerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure PickerChange(Sender: TObject);
public
DataType: Byte; // @see mysql_structures
constructor Create;
destructor Destroy; override;
function BeginEdit: Boolean; virtual; stdcall;
function CancelEdit: Boolean; virtual; stdcall;
function EndEdit: Boolean; virtual; stdcall;
function GetBounds: TRect; virtual; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
procedure SetBounds(R: TRect); virtual; stdcall;
end;
type
TEnumEditorLink = class(TInterfacedObject, IVTEditLink)
private
FCombo: TTnTComboBox;
FTree: TCustomVirtualStringTree;
FNode: PVirtualNode;
FColumn: TColumnIndex;
FStopping: Boolean;
procedure ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
public
ValueList: TWideStringList;
constructor Create;
destructor Destroy; override;
function BeginEdit: Boolean; virtual; stdcall;
function CancelEdit: Boolean; virtual; stdcall;
function EndEdit: Boolean; virtual; stdcall;
function GetBounds: TRect; virtual; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
procedure SetBounds(R: TRect); virtual; stdcall;
end;
type
TSetEditorLink = class(TInterfacedObject, IVTEditLink)
private
FPanel: TPanel;
FCheckList: TTNTCheckListBox;
FBtnOK, FBtnCancel: TButton;
FTree: TCustomVirtualStringTree;
FNode: PVirtualNode;
FColumn: TColumnIndex;
FTextBounds: TRect;
FStopping: Boolean;
procedure CheckListKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure BtnOkClick(Sender: TObject);
procedure BtnCancelClick(Sender: TObject);
public
ValueList: TWideStringList;
constructor Create;
destructor Destroy; override;
function BeginEdit: Boolean; virtual; stdcall;
function CancelEdit: Boolean; virtual; stdcall;
function EndEdit: Boolean; virtual; stdcall;
function GetBounds: TRect; virtual; stdcall;
function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
procedure ProcessMessage(var Message: TMessage); virtual; stdcall;
procedure SetBounds(R: TRect); virtual; stdcall;
end;
implementation
constructor TMemoEditorLink.Create;
begin
inherited;
FForm := nil;
end;
destructor TMemoEditorLink.Destroy;
begin
inherited;
FreeAndNil(FForm);
end;
function TMemoEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
// Retrieves the true text bounds from the owner tree.
var
IsBinary: Boolean;
Text: WideString;
F: TFont;
begin
Result := Tree is TCustomVirtualStringTree;
if not Result then
exit;
FTree := Tree as TVirtualStringTree;
FNode := Node;
FColumn := Column;
// Initial size, font and text (ANSI) of the node.
F := TFont.Create;
FTree.GetTextInfo(Node, Column, F, FTextBounds, Text);
IsBinary := MainForm.ChildWin.FDataGridResult.Columns[Column].IsBinary;
// Get wide text of the node.
Text := FTree.Text[FNode, FColumn];
// Create the text editor form
if IsBinary then FForm := TfrmBinEditor.Create(Ftree)
else FForm := TfrmTextEditor.Create(Ftree);
FForm.SetFont(F);
FForm.SetText(Text);
FForm.SetMaxLength(MaxLength);
end;
function TMemoEditorLink.BeginEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then
FForm.ShowModal;
end;
function TMemoEditorLink.CancelEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then begin
FStopping := True;
FForm.Close;
FTree.CancelEditNode;
FTree.SetFocus;
end;
end;
function TMemoEditorLink.EndEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then try
FStopping := True;
if FForm.GetText <> FTree.Text[FNode, FColumn] then
FTree.Text[FNode, FColumn] := FForm.GetText;
FForm.Close;
FTree.SetFocus;
except
FStopping := False;
raise;
end;
end;
function TMemoEditorLink.GetBounds: TRect; stdcall;
begin
Result := FForm.BoundsRect;
end;
procedure TMemoEditorLink.ProcessMessage(var Message: TMessage); stdcall;
begin
end;
procedure TMemoEditorLink.SetBounds(R: TRect); stdcall;
begin
// Not in use, form's position is centered on mainform
end;
{ DateTime editor }
constructor TDateTimeEditorLink.Create;
begin
inherited;
end;
destructor TDateTimeEditorLink.Destroy;
begin
inherited;
FPicker.Free;
end;
function TDateTimeEditorLink.BeginEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then
FPicker.Show;
end;
function TDateTimeEditorLink.CancelEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then begin
FStopping := True;
FPicker.Hide;
FTree.CancelEditNode;
FTree.SetFocus;
end;
end;
function TDateTimeEditorLink.EndEdit: Boolean; stdcall;
var
newtext: WideString;
begin
Result := not FStopping;
if Not Result then
Exit;
if FModified then begin
case DataType of
tpDATE:
newtext := FormatDateTime(ShortDateFormat, FPicker.Date);
tpDATETIME, tpTIMESTAMP:
newtext := FormatDateTime(ShortDateFormat+' '+LongTimeFormat, FPicker.DateTime);
tpTIME:
newtext := FormatDateTime(LongTimeFormat, FPicker.Time);
end;
if newtext <> FTree.Text[FNode, FColumn] then
FTree.Text[FNode, FColumn] := newtext;
end;
FPicker.Hide;
FTree.SetFocus;
end;
function TDateTimeEditorLink.GetBounds: TRect; stdcall;
begin
Result := FPicker.BoundsRect;
end;
function TDateTimeEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
var
Text: WideString;
begin
Result := Tree is TCustomVirtualStringTree;
if not Result then
Exit;
Ftree := Tree as TCustomVirtualStringTree;
FNode := Node;
FColumn := Column;
FPicker := TDateTimePicker.Create(Tree);
FPicker.Parent := FTree;
FPicker.OnKeyDown := PickerKeyDown;
DateSeparator := '-';
TimeSeparator := ':';
ShortDateFormat := 'yyyy-MM-dd';
LongTimeFormat := 'HH:mm:ss';
case DataType of
tpDATE: begin
FPicker.Kind := dtkDate;
FPicker.Format := ShortDateFormat;
end;
tpDATETIME, tpTIMESTAMP: begin
FPicker.Kind := dtkDate;
FPicker.Format := ShortDateFormat + ' ' + LongTimeFormat;
end;
tpTIME: begin
FPicker.Kind := dtkTime;
FPicker.Format := LongTimeFormat;
end;
end;
Text := FTree.Text[Node, Column];
try
FPicker.DateTime := StrToDateTime(FTree.Text[Node, Column]);
except
FPicker.DateTime := Now;
end;
FModified := False;
FPicker.SetFocus;
FPicker.OnChange := PickerChange;
end;
procedure TDateTimeEditorLink.ProcessMessage(var Message: TMessage); stdcall;
begin
end;
procedure TDateTimeEditorLink.SetBounds(R: TRect); stdcall;
begin
FPicker.BoundsRect := R;
end;
procedure TDateTimeEditorLink.PickerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
// Cancel by Escape
VK_ESCAPE: FTree.CancelEditNode;
// Apply changes and end editing by [Ctrl +] Enter
VK_RETURN: FTree.EndEditNode;
end;
end;
procedure TDateTimeEditorLink.PickerChange(Sender: TObject);
begin
FModified := True;
end;
{ Enum editor }
constructor TEnumEditorLink.Create;
begin
inherited;
end;
destructor TEnumEditorLink.Destroy;
begin
inherited;
FCombo.Free;
end;
function TEnumEditorLink.BeginEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then
FCombo.Show;
end;
function TEnumEditorLink.CancelEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then begin
FStopping := True;
FCombo.Hide;
FTree.CancelEditNode;
FTree.SetFocus;
end;
end;
function TEnumEditorLink.EndEdit: Boolean; stdcall;
var
newtext: WideString;
begin
Result := not FStopping;
if Not Result then
Exit;
newText := FCombo.Text;
if newtext <> FTree.Text[FNode, FColumn] then
FTree.Text[FNode, FColumn] := newtext;
FCombo.Hide;
FTree.SetFocus;
end;
function TEnumEditorLink.GetBounds: TRect; stdcall;
begin
Result := FCombo.BoundsRect;
end;
function TEnumEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
var
i: Integer;
begin
Result := Tree is TCustomVirtualStringTree;
if not Result then
Exit;
Ftree := Tree as TCustomVirtualStringTree;
FNode := Node;
FColumn := Column;
FCombo := TTnTComboBox.Create(Tree);
FCombo.Parent := FTree;
// Set style to OwnerDraw, otherwise we wouldn't be able to adjust the combo's height
FCombo.Style := csOwnerDrawFixed;
for i := 0 to ValueList.Count - 1 do
FCombo.Items.Add(ValueList[i]);
FCombo.ItemIndex := FCombo.Items.IndexOf(FTree.Text[FNode, FColumn]);
FCombo.SetFocus;
FCombo.OnKeyDown := ComboKeyDown;
end;
procedure TEnumEditorLink.ProcessMessage(var Message: TMessage); stdcall;
begin
end;
procedure TEnumEditorLink.SetBounds(R: TRect); stdcall;
begin
FCombo.BoundsRect := R;
FCombo.ItemHeight := R.Bottom - R.Top - 4;
end;
procedure TEnumEditorLink.ComboKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
// Cancel by Escape
VK_ESCAPE: FTree.CancelEditNode;
// Apply changes and end editing by [Ctrl +] Enter
VK_RETURN: FTree.EndEditNode;
end;
end;
{ SET editor }
constructor TSetEditorLink.Create;
begin
inherited;
end;
destructor TSetEditorLink.Destroy;
begin
inherited;
FCheckList.Free;
FPanel.Free;
end;
function TSetEditorLink.BeginEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then
FPanel.Show;
end;
function TSetEditorLink.CancelEdit: Boolean; stdcall;
begin
Result := not FStopping;
if Result then begin
FStopping := True;
FPanel.Hide;
FTree.CancelEditNode;
FTree.SetFocus;
end;
end;
function TSetEditorLink.EndEdit: Boolean; stdcall;
var
newtext: WideString;
i: Integer;
begin
Result := not FStopping;
if Not Result then
Exit;
newText := '';
for i := 0 to FCheckList.Items.Count - 1 do
if FCheckList.Checked[i] then newText := newText + FCheckList.Items[i] + ',';
Delete(newText, Length(newText), 1);
if newtext <> FTree.Text[FNode, FColumn] then
FTree.Text[FNode, FColumn] := newtext;
FPanel.Hide;
FTree.SetFocus;
end;
function TSetEditorLink.GetBounds: TRect; stdcall;
begin
Result := FPanel.BoundsRect;
end;
function TSetEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
var
F: TFont;
SelValues: TWideStringList;
Text: WideString;
begin
Result := Tree is TCustomVirtualStringTree;
if not Result then
Exit;
Ftree := Tree as TCustomVirtualStringTree;
FNode := Node;
FColumn := Column;
// Initial size, font and text of the node.
F := TFont.Create;
FTree.GetTextInfo(Node, Column, F, FTextBounds, Text);
SelValues := TWideStringList.Create;
SelValues.Delimiter := ',';
SelValues.StrictDelimiter := True;
SelValues.DelimitedText := Text;
FPanel := TPanel.Create(Tree);
FPanel.Parent := FTree;
FPanel.Left := FTextBounds.Left;
FPanel.Top := FTextBounds.Top;
FCheckList := TTNTCheckListBox.Create(FPanel);
FCheckList.Parent := FPanel;
FCheckList.Font := F;
FCheckList.Items.Assign(ValueList);
ToggleCheckListBox(FCheckList, True, SelValues);
FCheckList.SetFocus;
FCheckList.OnKeyDown := CheckListKeyDown;
FBtnOk := TButton.Create(FPanel);
FBtnOk.Parent := FPanel;
FBtnOk.Caption := 'OK';
FBtnOk.OnClick := BtnOkClick;
FBtnCancel := TButton.Create(FPanel);
FBtnCancel.Parent := FPanel;
FBtnCancel.Caption := 'Cancel';
FBtnCancel.OnClick := BtnCancelClick;
end;
procedure TSetEditorLink.ProcessMessage(var Message: TMessage); stdcall;
begin
end;
procedure TSetEditorLink.SetBounds(R: TRect); stdcall;
const
margin = 3;
begin
FPanel.Top := R.Top;
FPanel.Left := R.Left;
FPanel.Width := R.Right - R.Left;
FPanel.Height := 200;
FBtnOk.Width := (FPanel.Width - 3*margin) div 2;
FBtnOk.Left := margin;
FBtnOk.Height := 24;
FBtnOk.Top := FPanel.Height - margin - FBtnOk.Height;
FBtnCancel.Width := FBtnOk.Width;
FBtnCancel.Left := 2*margin + FBtnOk.Width;
FBtnCancel.Height := FBtnOk.Height;
FBtnCancel.Top := FBtnOk.Top;
FCheckList.Top := margin;
FCheckList.Left := margin;
FCheckList.Width := FPanel.Width - 2*margin;
FCheckList.Height := FPanel.Height - 3*margin - FBtnOk.Height;
end;
procedure TSetEditorLink.CheckListKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
// Cancel by Escape
VK_ESCAPE: FTree.CancelEditNode;
// Apply changes and end editing by [Ctrl +] Enter
VK_RETURN: FTree.EndEditNode;
end;
end;
procedure TSetEditorLink.BtnOkClick(Sender: TObject);
begin
FTree.EndEditNode;
end;
procedure TSetEditorLink.BtnCancelClick(Sender: TObject);
begin
FTree.CancelEditNode;
end;
end.