diff --git a/heidisql.lpi b/heidisql.lpi
index 765d4e5c..52cd8370 100644
--- a/heidisql.lpi
+++ b/heidisql.lpi
@@ -328,6 +328,10 @@
+
+
+
+
diff --git a/heidisql.lpr b/heidisql.lpr
index 7285f3e6..b48c414b 100644
--- a/heidisql.lpr
+++ b/heidisql.lpr
@@ -21,7 +21,7 @@ uses
exportgrid, usermanager, selectdbobject, reformatter, searchreplace,
connections, jsonregistry, sqlhelp, updatecheck, insertfiles, texteditor,
customize_highlighter, preferences, table_editor, view, routine_editor,
- trigger_editor, event_editor, tabletools, bineditor;
+ trigger_editor, event_editor, tabletools, bineditor, grideditlinks;
{$R *.res}
{.$R resources.rc}
diff --git a/source/connections.pas b/source/connections.pas
index fcfa7ec9..995073e8 100644
--- a/source/connections.pas
+++ b/source/connections.pas
@@ -238,7 +238,7 @@ type
implementation
-uses Main, apphelpers, dbstructures.sqlite;
+uses Main, apphelpers, dbstructures.sqlite, grideditlinks;
{$I const.inc}
@@ -870,7 +870,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, True, nil);
+ EditLink := TInplaceEditorLink.Create(Sender as TVirtualStringTree, True, nil);
end;
diff --git a/source/extra_controls.pas b/source/extra_controls.pas
index 360975a0..a5955bad 100644
--- a/source/extra_controls.pas
+++ b/source/extra_controls.pas
@@ -8,7 +8,7 @@ uses
Classes, SysUtils, Forms, Types, StdCtrls, Clipbrd, apphelpers,
Graphics, Dialogs, ImgList, ComCtrls, Generics.Collections, Generics.Defaults,
ExtCtrls, laz.VirtualTrees, RegExpr, Controls, EditBtn, Menus,
- GraphUtil, Math;
+ GraphUtil, Math, LCLIntf;
type
// Form with a sizegrip in the lower right corner, without the need for a statusbar
@@ -37,7 +37,7 @@ type
class function ScaleSize(x: Extended; Control: TControl): Integer; overload;
class procedure PageControlTabHighlight(PageControl: TPageControl);
property PixelsPerInchDesigned: Integer read FPixelsPerInchDesigned;
- procedure ShowPopup(ClickedControl: TControl; PopupMenu: TPopupMenu);
+ class procedure ShowPopup(ClickedControl: TControl; PopupMenu: TPopupMenu);
end;
// Modern file-open-dialog with high DPI support and encoding selector
@@ -93,7 +93,7 @@ type
property OnExit: TNotifyEvent read FOnExit write FOnExit;
end;}
- {TExtComboBox = class(TComboBox)
+ TExtComboBox = class(TComboBox)
private
FcbHintIndex: Integer;
FHintWindow: THintWindow;
@@ -102,7 +102,7 @@ type
procedure DropDown; override;
procedure CloseUp; override;
procedure InitiateAction; override;
- end;}
+ end;
{TExtHintWindow = class(THintWindow)
private
@@ -476,7 +476,7 @@ begin
end;;
end;
-procedure TExtForm.ShowPopup(ClickedControl: TControl; PopupMenu: TPopupMenu);
+class procedure TExtForm.ShowPopup(ClickedControl: TControl; PopupMenu: TPopupMenu);
begin
PopupMenu.Popup(ClickedControl.ClientOrigin.X, ClickedControl.ClientOrigin.Y + ClickedControl.Height);
end;
@@ -696,7 +696,7 @@ end;}
{ TExtComboBox }
-{procedure TExtComboBox.Change;
+procedure TExtComboBox.Change;
var
P: TPoint;
HintRect: TRect;
@@ -747,7 +747,7 @@ begin
FcbHintIndex := ItemIndex;
Change;
end;
-end;}
+end;
diff --git a/source/grideditlinks.pas b/source/grideditlinks.pas
new file mode 100644
index 00000000..acb218bd
--- /dev/null
+++ b/source/grideditlinks.pas
@@ -0,0 +1,1857 @@
+unit grideditlinks;
+
+// The editor links, instanciated by VirtualTree.CreateEditor
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ Forms, Graphics, Messages, laz.VirtualTrees, ComCtrls, SysUtils, Classes,
+ StdCtrls, ExtCtrls, CheckLst, Controls, Types, Dialogs, Menus, MaskEdit, DateUtils, Math,
+ dbconnection, dbstructures, apphelpers, texteditor, bineditor,
+ StrUtils, UITypes, RegExpr, extra_controls, EditBtn, LCLType, LCLIntf;
+
+type
+ // Radio buttons and checkboxes which do not pass key to their parent control
+ // so a OnKeyDown event using has the chance to end editing.
+ {TAllKeysRadioButton = class(TRadioButton)
+ procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
+ end;}
+ TAllKeysRadioButton = TRadioButton;
+ {TAllKeysCheckBox = class(TCheckBox)
+ procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
+ end;}
+ TAllKeysCheckBox = TCheckBox;
+
+ TBaseGridEditorLink = class(TInterfacedObject, IVTEditLink)
+ private
+ FInstanceId: Integer;
+ FParentForm: TWinControl; // A back reference to the main form
+ FTree: TVirtualStringTree; // A back reference to the tree calling.
+ FNode: PVirtualNode; // The node to be edited.
+ FColumn: TColumnIndex; // The column of the node.
+ FCellText: String; // Original cell text value
+ FCellFont: TFont; // Cosmetic
+ FCellBackground: TColor;
+ FMainControl: TWinControl; // The editor's most important component
+ FStopping: Boolean; // Set to True when the edit link requests stopping the edit action.
+ FLastKeyDown: Integer; // Set in OnKeyDown on the editor's main control
+ FLastShiftState: TShiftState;
+ FOldWindowProc: TWndMethod; // Temporary switched to TempWindowProc to be able to catch Tab key
+ FTableColumn: TTableColumn;
+ FModified: Boolean;
+ FAllowEdit: Boolean;
+ FBeginEditTime: Cardinal;
+ procedure Log(Msg: String);
+ //procedure TempWindowProc(var Message: TMessage);
+ procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+ procedure DoEndEdit(Sender: TObject);
+ procedure DoCancelEdit(Sender: TObject);
+ function GetCellRect(InnerTextBounds: Boolean): TRect;
+ public
+ // The table column of the cell being edited. Mostly used in data grids.
+ property TableColumn: TTableColumn read 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; Col: TTableColumn); overload; virtual;
+ destructor Destroy; override;
+ property Tree: TVirtualStringTree read FTree;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall;
+ function BeginEdit: Boolean; virtual; stdcall;
+ function CancelEdit: Boolean; virtual; stdcall;
+ function EndEdit: Boolean; virtual; stdcall; abstract;
+ function EndEditHelper(NewText: String): Boolean;
+ function GetBounds: TRect; virtual; stdcall; // Normally useless and unused
+ procedure ProcessMessage(var Message: TMessage); stdcall;
+ procedure SetBounds(R: TRect); virtual; stdcall; abstract;
+ end;
+
+ THexEditorLink = class(TBaseGridEditorLink)
+ private
+ FForm: TfrmBinEditor;
+ public
+ MaxLength: Integer;
+ TitleText: String;
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function CancelEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+ TDateTimeEditorLink = class(TBaseGridEditorLink)
+ private
+ FPanel: TPanel;
+ FMaskEdit: TMaskEdit;
+ FTimer: TTimer;
+ FModifyOffset: Integer;
+ FTimerCalls: Integer;
+ FUpDown: TUpDown;
+ procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+ procedure DoKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
+ procedure UpDownChangingEx(Sender: TObject; var AllowChange: Boolean;
+ NewValue: SmallInt; Direction: TUpDownDirection);
+ procedure UpDownMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+ procedure DoOnTimer(Sender: TObject);
+ procedure ModifyDate(Offset: Integer);
+ procedure TextChange(Sender: TObject);
+ function MicroSecondsPrecision: Integer;
+ public
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+ TEnumEditorLink = class(TBaseGridEditorLink)
+ private
+ FCombo: TExtComboBox;
+ procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+ procedure DoSelect(Sender: TObject);
+ public
+ ValueList, DisplayList: TStringList;
+ AllowCustomText: Boolean;
+ ItemMustExist: Boolean;
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+ TSetEditorLink = class(TBaseGridEditorLink)
+ private
+ FPanel: TPanel;
+ FCheckList: TCheckListBox;
+ FBtnOK, FBtnCancel: TButton;
+ FEndTimer: TTimer;
+ procedure BtnOkClick(Sender: TObject);
+ procedure BtnCancelClick(Sender: TObject);
+ public
+ ValueList: TStringList;
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+ // Inplace editor with button
+ TInplaceEditorLink = class(TBaseGridEditorLink)
+ private
+ FPanel: TPanel;
+ FEdit: TEdit;
+ FButton: TButton;
+ FMaxLength: Integer;
+ procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+ procedure ButtonClick(Sender: TObject);
+ public
+ ButtonVisible: Boolean;
+ TitleText: String;
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ property MaxLength: Integer read FMaxLength write FMaxLength;
+ end;
+
+ TColumnDefaultEditorLink = class(TBaseGridEditorLink)
+ private
+ FPanel: TPanel;
+ FRadioNothing, FRadioText, FRadioNULL, FRadioExpression, FRadioAutoInc: TAllKeysRadioButton;
+ FlblOnUpdate: TLabel;
+ FTextEdit: TEditButton;
+ FTextDropDown: TPopupMenu;
+ FExpressionEdit: TComboBox;
+ FOnUpdateEdit: TComboBox;
+ FBtnOK, FBtnCancel: TButton;
+ FEndTimer: TTimer;
+ procedure RadioClick(Sender: TObject);
+ procedure EditChange(Sender: TObject);
+ procedure EditButtonClick(Sender: TObject);
+ procedure EditDropDownClick(Sender: TObject);
+ procedure BtnOkClick(Sender: TObject);
+ procedure BtnCancelClick(Sender: TObject);
+ public
+ DefaultType, OnUpdateType: TColumnDefaultType;
+ DefaultText, OnUpdateText: String;
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+ TDataTypeEditorLink = class(TBaseGridEditorLink)
+ private
+ FTreeSelect: TVirtualStringTree;
+ FMemoHelp: TMemo;
+ procedure DoTreeSelectGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
+ procedure DoTreeSelectInitNode(Sender: TBaseVirtualTree;
+ ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
+ procedure DoTreeSelectInitChildren(Sender: TBaseVirtualTree;
+ Node: PVirtualNode; var ChildCount: Cardinal);
+ procedure DoTreeSelectHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode);
+ procedure DoTreeSelectPaintText(Sender: TBaseVirtualTree;
+ const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
+ TextType: TVSTTextType);
+ procedure DoTreeSelectFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode:
+ PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
+ procedure DoTreeSelectFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
+ public
+ constructor Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn); override;
+ destructor Destroy; override;
+ function BeginEdit: Boolean; override;
+ function EndEdit: Boolean; override;
+ function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; override;
+ procedure SetBounds(R: TRect); override;
+ end;
+
+
+var
+ ActiveGridEditor: TBaseGridEditorLink=nil;
+
+implementation
+
+uses
+ main;
+
+
+{procedure TAllKeysRadioButton.WMGetDlgCode(var Msg: TMessage);
+begin
+ inherited;
+ Msg.Result := Msg.Result or DLGC_WANTALLKEYS;
+end;}
+
+
+{procedure TAllKeysCheckBox.WMGetDlgCode(var Msg: TMessage);
+begin
+ inherited;
+ Msg.Result := Msg.Result or DLGC_WANTALLKEYS;
+end;}
+
+procedure TBaseGridEditorLink.Log(Msg: String);
+begin
+ MainForm.LogSQL('#'+FInstanceId.ToString+': '+Msg, lcDebug);
+end;
+
+
+constructor TBaseGridEditorLink.Create;
+begin
+ raise Exception.CreateFmt(_('Wrong constructor called: %s.%s. Instead, please call the overloaded version %s.%s.'),
+ [Self.ClassName, 'Create', Self.ClassName, 'Create(VirtualStringTree)']);
+end;
+
+constructor TBaseGridEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited Create;
+ FInstanceId := Random(100);
+ FTree := Tree;
+ // Enable mouse scrolling, plus ensure the editor component
+ // is not partly hidden when it pops up in a bottom cell
+ FParentForm := GetParentForm(FTree);
+ // Avoid flicker
+ FParentForm.Repaint;
+ FMainControl := nil;
+ FModified := False;
+ FAllowEdit := AllowEdit;
+ ActiveGridEditor := Self;
+ FTableColumn := Col;
+end;
+
+destructor TBaseGridEditorLink.Destroy;
+var
+ NewColumn, FirstCol, LastCol: TColumnIndex;
+ NewNode: PVirtualNode;
+ DoPrev: Boolean;
+begin
+ ActiveGridEditor := nil;
+ if Assigned(FMainControl) then begin
+ FMainControl.WindowProc := FOldWindowProc;
+ FMainControl := nil;
+ end;
+ if FLastKeyDown = VK_TAB then begin
+ DoPrev := ssShift in FLastShiftState;
+ // Advance to next/previous visible column/node.
+ NewNode := FNode;
+ NewColumn := FColumn;
+ FirstCol := FTree.Header.Columns.GetFirstVisibleColumn;
+ LastCol := FTree.Header.Columns.GetLastVisibleColumn;
+ while true do begin
+ // Find a column for the current node which can be focused.
+ if DoPrev then begin
+ if NewColumn = FirstCol then begin
+ NewColumn := LastCol;
+ NewNode := FTree.GetPreviousVisible(NewNode);
+ end else
+ NewColumn := FTree.Header.Columns.GetPreviousVisibleColumn(NewColumn);
+ end else begin
+ if NewColumn = LastCol then begin
+ NewColumn := FirstCol;
+ NewNode := FTree.GetNextVisible(NewNode);
+ end else
+ NewColumn := FTree.Header.Columns.GetNextVisibleColumn(NewColumn);
+ end;
+ if not Assigned(NewNode) then
+ Break;
+ if not FTree.CanEdit(NewNode, NewColumn) then
+ Continue;
+ FTree.ClearSelection;
+ FTree.Selected[NewNode] := True;
+ FTree.EditNode(NewNode, NewColumn);
+ Break;
+ end;
+ end;
+ inherited;
+end;
+
+function TBaseGridEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
+var
+ FCellTextBounds: TRect;
+ HasNulls: Boolean;
+begin
+ Result := not FStopping;
+ if not Result then
+ Exit;
+ FNode := Node;
+ FColumn := Column;
+ FCellFont := TFont.Create;
+ FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText);
+ apphelpers.RemoveNullChars(FCellText, HasNulls);
+ if HasNulls and FAllowEdit then begin
+ FAllowEdit := False;
+ end;
+
+ // Not all editors have a connection assigned, e.g. session manager tree
+ if Assigned(FTableColumn) then begin
+ FCellFont.Color := DatatypeCategories[FTableColumn.DataType.Category].Color;
+ end;
+ FCellBackground := FTree.Header.Columns[FColumn].Color;
+ if Assigned(FMainControl) then begin
+ FOldWindowProc := FMainControl.WindowProc;
+ //FMainControl.WindowProc := TempWindowProc;
+ TExtForm.FixControls(FMainControl);
+ end;
+ // Adjust editor position and allow repainting mainform
+ SetBounds(FCellTextBounds);
+ if not IsWine then
+ FParentForm.Repaint;
+end;
+
+function TBaseGridEditorLink.BeginEdit: Boolean;
+begin
+ Result := not FStopping;
+ FBeginEditTime := GetTickCount;
+end;
+
+function TBaseGridEditorLink.CancelEdit: Boolean;
+begin
+ Result := not FStopping;
+ if Result then begin
+ FStopping := True;
+ FTree.CancelEditNode;
+ if FTree.CanFocus then
+ FTree.SetFocus;
+ end;
+end;
+
+function TBaseGridEditorLink.EndEditHelper(NewText: String): Boolean;
+begin
+ Result := not FStopping;
+ if FStopping then Exit;
+ FStopping := True;
+ if FModified and FAllowEdit then
+ FTree.Text[FNode, FColumn] := NewText;
+ if FTree.CanFocus and (FLastKeyDown <> VK_TAB) then
+ FTree.SetFocus;
+end;
+
+{procedure TBaseGridEditorLink.TempWindowProc(var Message: TMessage);
+begin
+ case Message.Msg of
+ WM_CHAR: // Catch hotkeys
+ if not (TWMChar(Message).CharCode = VK_TAB) then
+ FOldWindowProc(Message);
+ WM_GETDLGCODE: // "WantTabs" mode for main control
+ Message.Result := Message.Result or DLGC_WANTARROWS or DLGC_WANTALLKEYS or DLGC_WANTTAB;
+ else begin
+ try
+ FOldWindowProc(Message);
+ except
+ // EAccessViolation occurring in some cases
+ on E:Exception do begin
+ Log(E.Message+' Message CharCode:'+TWMChar(Message).CharCode.ToString+' Msg:'+Message.Msg.ToString);
+ end;
+ end;
+ end;
+ end;
+end;}
+
+procedure TBaseGridEditorLink.ProcessMessage(var Message: TMessage);
+begin
+ if (FMainControl <> nil) and FMainControl.HandleAllocated then
+ FMainControl.WindowProc(Message);
+end;
+
+function TBaseGridEditorLink.GetBounds: TRect; stdcall;
+begin
+ // Only important if the editor resizes itself.
+ Result := Rect(0, 0, 0, 0);
+end;
+
+function TBaseGridEditorLink.GetCellRect(InnerTextBounds: Boolean): TRect;
+var
+ Text: String;
+ CellBounds, TextBounds: TRect;
+ Ghosted: Boolean;
+ ImageIndex: Integer;
+ f: TFont;
+begin
+ // Return the cell's rectangle, relative to the parent form.
+ f := TFont.Create;
+ FTree.GetTextInfo(FNode, FColumn, f, TextBounds, Text);
+ CellBounds := FTree.GetDisplayRect(FNode, FColumn, False);
+
+ Inc(CellBounds.Left, Integer(FTree.GetNodeLevel(FNode)) * (Integer(FTree.Indent)+FTree.TextMargin));
+ if (toShowRoot in FTree.TreeOptions.PaintOptions)
+ and (FColumn = FTree.Header.MainColumn) then begin
+ // Reserve space for plus or minus button
+ Inc(CellBounds.Left, FTree.Indent);
+ end;
+ if Assigned(FTree.Images) and Assigned(FTree.OnGetImageIndex) then begin
+ // Reserve space for image
+ ImageIndex := -1;
+ FTree.OnGetImageIndex(FTree, FNode, ikNormal, FColumn, Ghosted, ImageIndex);
+ if ImageIndex > -1 then
+ Inc(CellBounds.Left, FTree.Images.Width+2);
+ end;
+ TextBounds.Left := CellBounds.Left + 2*FTree.TextMargin;
+
+ if InnerTextBounds then begin
+ // Inner bounds are considered to be relative to the outer cell bounds
+ Result := Rect(TextBounds.Left-CellBounds.Left,
+ TextBounds.Top-CellBounds.Top,
+ CellBounds.Right-CellBounds.Left, // Far right edge of cell, not of text
+ CellBounds.Bottom-CellBounds.Top
+ );
+ end else begin
+ // Recalculate top left corner of rectangle, so it is relative to the parent form (which is FParentForm)
+ Result := CellBounds;
+ OffsetRect(Result,
+ FTree.ClientOrigin.X - FParentForm.ClientOrigin.X,
+ FTree.ClientOrigin.Y - FParentForm.ClientOrigin.Y
+ );
+ Dec(Result.Bottom, 1);
+ end;
+end;
+
+procedure TBaseGridEditorLink.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+begin
+ FLastKeyDown := Key;
+ FLastShiftState := Shift;
+ case Key of
+ // Cancel by Escape
+ VK_ESCAPE: FTree.CancelEditNode;
+ // Apply changes and end editing by [Ctrl +] Enter or Tab
+ VK_RETURN, VK_TAB: FTree.EndEditNode;
+ end;
+end;
+
+procedure TBaseGridEditorLink.DoEndEdit(Sender: TObject);
+begin
+ FTree.EndEditNode;
+end;
+
+procedure TBaseGridEditorLink.DoCancelEdit(Sender: TObject);
+begin
+ FTree.CancelEditNode;
+end;
+
+
+
+
+constructor THexEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+end;
+
+destructor THexEditorLink.Destroy;
+begin
+ inherited;
+ FForm.Close;
+ FreeAndNil(FForm);
+end;
+
+
+function THexEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if not Result then
+ Exit;
+
+ // Create the text editor form
+ FForm := TfrmBinEditor.Create(Ftree);
+ FForm.SetFont(FCellFont);
+ FForm.SetText(FCellText);
+ FForm.SetTitleText(TitleText);
+ FForm.SetMaxLength(MaxLength);
+ FForm.memoText.ReadOnly := not FAllowEdit;
+end;
+
+
+function THexEditorLink.BeginEdit: Boolean; stdcall;
+begin
+ Result := inherited BeginEdit;
+ if Result then
+ FForm.ShowModal;
+end;
+
+
+function THexEditorLink.CancelEdit: Boolean;
+begin
+ Result := inherited CancelEdit;
+ if Result then
+ FForm.Close;
+end;
+
+
+function THexEditorLink.EndEdit: Boolean; stdcall;
+begin
+ FForm.Close;
+ FModified := FForm.Modified;
+ Result := EndEditHelper(FForm.GetText);
+end;
+
+
+procedure THexEditorLink.SetBounds(R: TRect); stdcall;
+begin
+ // Not in use, form's position is centered on mainform
+end;
+
+
+
+{ DateTime editor }
+
+constructor TDateTimeEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+
+ FPanel := TPanel.Create(FParentForm);
+ FPanel.Parent := FParentForm;
+ FPanel.Hide;
+ FPanel.ParentBackground := False;
+ FPanel.BevelOuter := bvNone;
+ FPanel.OnExit := DoEndEdit;
+
+ FMaskEdit := TMaskEdit.Create(FPanel);
+ FMaskEdit.Parent := FPanel;
+ FMaskEdit.ParentColor := True;
+ FMaskEdit.BorderStyle := bsNone;
+ FMaskEdit.OnKeyDown := DoKeyDown;
+ FMaskEdit.OnKeyUp := DoKeyUp;
+ FMaskEdit.OnChange := TextChange;
+ FMainControl := FMaskEdit;
+
+ FUpDown := TUpDown.Create(FPanel);
+ FUpDown.Parent := FPanel;
+ FUpDown.OnChangingEx := UpDownChangingEx;
+ FUpDown.OnMouseUp := UpDownMouseUp;
+
+ FTimer := TTimer.Create(FMaskEdit);
+ FTimer.Interval := 50;
+ FTimer.OnTimer := DoOnTimer;
+ FTimer.Enabled := False;
+end;
+
+
+destructor TDateTimeEditorLink.Destroy;
+begin
+ AppSettings.WriteInt(asDateTimeEditorCursorPos, FMaskEdit.SelStart, IntToStr(Integer(FTableColumn.DataType.Category)));
+ FreeAndNil(FTimer);
+ FreeAndNil(FUpDown);
+ FreeAndNil(FMaskEdit);
+ FreeAndNil(FPanel);
+ inherited;
+end;
+
+
+function TDateTimeEditorLink.BeginEdit: Boolean; stdcall;
+begin
+ Result := inherited BeginEdit;
+ if Result then begin
+ FPanel.Show;
+ FMaskEdit.SetFocus;
+ // Focus very last segment of date
+ FMaskEdit.SelStart := AppSettings.ReadInt(asDateTimeEditorCursorPos, IntToStr(Integer(FTableColumn.DataType.Category)));
+ FMaskEdit.SelLength := 1;
+ end;
+end;
+
+
+function TDateTimeEditorLink.EndEdit: Boolean;
+begin
+ Result := EndEditHelper(Trim(FMaskEdit.Text));
+end;
+
+
+function TDateTimeEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
+var
+ MinColWidth,
+ ForceTextLen: Integer;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if not Result then
+ Exit;
+ FMaskEdit.ReadOnly := not FAllowEdit;
+ case FTableColumn.DataType.Index of
+ dbdtDate:
+ FMaskEdit.EditMask := '0000-00-00;1; ';
+ dbdtDatetime, dbdtDatetime2, dbdtTimestamp, dbdtInt, dbdtBigint: begin
+ if MicroSecondsPrecision > 0 then
+ FMaskEdit.EditMask := '0000-00-00 00\:00\:00.'+StringOfChar('0', MicroSecondsPrecision)+';1; '
+ else
+ FMaskEdit.EditMask := '0000-00-00 00\:00\:00;1; ';
+ end;
+ dbdtTime: begin
+ ForceTextLen := 10;
+ if MicroSecondsPrecision > 0 then begin
+ FMaskEdit.EditMask := '#900\:00\:00.'+StringOfChar('0', MicroSecondsPrecision)+';1; ';
+ Inc(ForceTextLen, MicroSecondsPrecision + 1);
+ end else
+ FMaskEdit.EditMask := '#900\:00\:00;1; ';
+ while Length(FCellText) < ForceTextLen do
+ FCellText := ' ' + FCellText;
+ end;
+ dbdtYear:
+ FMaskEdit.EditMask := '0000;1; ';
+ end;
+ FMaskEdit.Text := FCellText;
+ FModified := False;
+ FMaskEdit.Font.Assign(FCellFont);
+ FPanel.Color := FCellBackground;
+ // Auto-enlarge current tree column so the text in the edit is not cut
+ MinColWidth := FTree.Canvas.TextWidth(FCellText) + FTree.TextMargin + FUpDown.Width + 5;
+ if FTree.Header.Columns[FColumn].Width < MinColWidth then
+ FTree.Header.Columns[FColumn].Width := MinColWidth;
+end;
+
+
+procedure TDateTimeEditorLink.SetBounds(R: TRect); stdcall;
+var
+ EditRect: TRect;
+ OldSelStart, OldSelLen: Integer;
+begin
+ FPanel.BoundsRect := GetCellRect(False);
+
+ FUpDown.Left := FPanel.Width - FUpDown.Width;
+ FUpDown.Height := FPanel.Height;
+
+ EditRect := GetCellRect(True);
+ EditRect.Right := FUpDown.Left;
+ FMaskEdit.BoundsRect := EditRect;
+
+ OldSelStart := FMaskEdit.SelStart;
+ OldSelLen := FMaskEdit.SelLength;
+ FMaskEdit.SelStart := 0;
+ FMaskEdit.SelStart := OldSelStart;
+ FMaskEdit.SelLength := OldSelLen;
+end;
+
+
+procedure TDateTimeEditorLink.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+begin
+ inherited DoKeyDown(Sender, Key, Shift);
+ if (Key in [VK_UP, VK_DOWN]) and (not FTimer.Enabled) then begin
+ if Key = VK_UP then FModifyOffset := 1
+ else FModifyOffset := -1;
+ FTimerCalls := 0;
+ DoOnTimer(Sender);
+ FTimer.Enabled := True;
+ end;
+end;
+
+
+procedure TDateTimeEditorLink.DoKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
+begin
+ FTimer.Enabled := False;
+end;
+
+
+procedure TDateTimeEditorLink.UpDownChangingEx(Sender: TObject; var AllowChange: Boolean;
+ NewValue: SmallInt; Direction: TUpDownDirection);
+begin
+ if FTimer.Enabled then
+ Exit;
+ if Direction = updUp then FModifyOffset := 1
+ else FModifyOffset := -1;
+ FTimerCalls := 0;
+ DoOnTimer(Sender);
+ FTimer.Enabled := True;
+end;
+
+
+procedure TDateTimeEditorLink.UpDownMouseUp(Sender: TObject; Button: TMouseButton;
+ Shift: TShiftState; X, Y: Integer);
+begin
+ FTimer.Enabled := False;
+end;
+
+
+procedure TDateTimeEditorLink.DoOnTimer(Sender: TObject);
+var
+ DelayCalls: Integer;
+begin
+ Inc(FTimerCalls);
+ // short delay before counting up/down
+ DelayCalls := 350 Div FTimer.Interval;
+ if (FTimerCalls > DelayCalls) or (not (Sender is TTimer)) then
+ ModifyDate(FModifyOffset);
+ // Speed up counting in steps
+ if FTimerCalls in [DelayCalls*5, DelayCalls*10] then begin
+ if FModifyOffset > 0 then
+ Inc(FModifyOffset, 3)
+ else
+ Dec(FModifyOffset, 3);
+ end;
+end;
+
+
+procedure TDateTimeEditorLink.ModifyDate(Offset: Integer);
+var
+ dt: TDateTime;
+ d: TDate;
+ i, MaxSeconds, MinSeconds: Int64;
+ text: String;
+ OldSelStart, OldSelLength,
+ ms, DotPos: Integer;
+
+ function TimeToSeconds(Str: String): Int64;
+ var
+ Hours: String;
+ Seconds: Int64;
+ begin
+ Hours := Trim(Copy(Str, 1, 4));
+ Result := MakeInt(Hours) * 60 * 60;
+ Seconds := MakeInt(Copy(Str, 6, 2)) * 60 + MakeInt(Copy(Str, 9, 2));
+ if (Result < 0) or ((Result = 0) and (Hours[1]='-')) then
+ Dec(Result, Seconds)
+ else
+ Inc(Result, Seconds);
+ end;
+
+ function SecondsToTime(Seconds: Int64): String;
+ var
+ HoursNum, Minutes: Int64;
+ Hours: String;
+ begin
+ HoursNum := Abs(Seconds div (60*60));
+ Hours := IntToStr(HoursNum);
+ if Length(Hours) = 1 then
+ Hours := '0' + Hours;
+ if Seconds < 0 then
+ Hours := '-' + Hours;
+ Seconds := Abs(Seconds) mod (60*60);
+ Minutes := Seconds div 60;
+ Seconds := Seconds mod 60;
+ Result := Format('%4s:%.2u:%.2u', [Hours, Minutes, Seconds]);
+ end;
+begin
+ try
+ // Detect microseconds part of value if any
+ if MicroSecondsPrecision > 0 then begin
+ DotPos := Length(FMaskEdit.Text) - Pos('.', ReverseString(FMaskEdit.Text)) + 2;
+ ms := MakeInt(Copy(FMaskEdit.Text, DotPos, Length(FMaskEdit.Text)));
+ end else
+ ms := 0;
+
+ case FTableColumn.DataType.Index of
+ dbdtYear: begin
+ i := MakeInt(FMaskEdit.Text);
+ i := i + Offset;
+ text := IntToStr(i);
+ end;
+
+ dbdtDate: begin
+ d := StrToDate(FMaskEdit.Text);
+ // De- or increase focused date segment
+ case FMaskEdit.SelStart of
+ 0..3: d := IncYear(d, Offset);
+ 5,6: d := IncMonth(d, Offset);
+ 8..10: d := IncDay(d, Offset);
+ end;
+ text := DateToStr(d);
+ end;
+
+ dbdtDateTime, dbdtDateTime2, dbdtTimestamp, dbdtInt, dbdtBigint: begin
+ dt := StrToDateTime(FMaskEdit.Text);
+ case FMaskEdit.SelStart of
+ 0..3: dt := IncYear(dt, Offset);
+ 5,6: dt := IncMonth(dt, Offset);
+ 8,9: dt := IncDay(dt, Offset);
+ 11,12: dt := IncHour(dt, Offset);
+ 14,15: dt := IncMinute(dt, Offset);
+ 17..19: dt := IncSecond(dt, Offset);
+ 20..26: Inc(ms, Offset);
+ end;
+ text := DateTimeToStr(dt);
+ if Length(text) = 10 then
+ text := text + ' 00:00:00';
+ if MicroSecondsPrecision > 0 then
+ text := text + '.' + Format('%.'+IntToStr(MicroSecondsPrecision)+'d', [ms]);
+ end;
+
+ dbdtTime: begin
+ i := TimeToSeconds(FMaskEdit.Text);
+ case FMaskEdit.SelStart of
+ 0..3: Inc(i, Offset*60*60);
+ 5,6: Inc(i, Offset*60);
+ 8,9: Inc(i, Offset);
+ 10..16: Inc(ms, Offset);
+ end;
+ // Stop at max and min values. See http://dev.mysql.com/doc/refman/5.0/en/time.html
+ MaxSeconds := 839*60*60-1;
+ MinSeconds := -(MaxSeconds);
+ if i > MaxSeconds then
+ i := MaxSeconds;
+ if i < MinSeconds then
+ i := MinSeconds;
+ text := SecondsToTime(i);
+ if MicroSecondsPrecision > 0 then
+ text := text + '.' + Format('%.'+IntToStr(MicroSecondsPrecision)+'d', [ms]);
+ end;
+
+ else text := '';
+ end;
+
+ if text <> '' then begin
+ OldSelStart := FMaskEdit.SelStart;
+ OldSelLength := FMaskEdit.SelLength;
+ FMaskEdit.Text := text;
+ FMaskEdit.SelStart := OldSelStart;
+ FMaskEdit.SelLength := OldSelLength;
+ end;
+ except
+ on E:EConvertError do begin
+ // Ignore any DateToStr exception. Should only appear in cases where the users
+ // enters invalid dates
+ end else
+ raise;
+ end;
+end;
+
+
+procedure TDateTimeEditorLink.TextChange;
+begin
+ FModified := True;
+end;
+
+
+function TDateTimeEditorLink.MicroSecondsPrecision: Integer;
+var
+ rx: TRegExpr;
+begin
+ if not FTableColumn.LengthSet.IsEmpty then
+ Result := MakeInt(FTableColumn.LengthSet)
+ else begin
+ // Find default length of supported microseconds in datatype definition
+ // See dbstructures
+ rx := TRegExpr.Create;
+ rx.Expression := '\.([^\.]+)$';
+ if rx.Exec(FTableColumn.DataType.Format) then
+ Result := rx.MatchLen[1]
+ else
+ Result := 0;
+ rx.Free;
+ end;
+ // No microseconds for UNIX timestamp columns
+ if FTableColumn.DataType.Index in [dbdtInt, dbdtBigint] then
+ Result := 0;
+end;
+
+
+
+{ Enum editor }
+
+constructor TEnumEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+ AllowCustomText := False;
+ ItemMustExist := False;
+ FCombo := TExtComboBox.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;
+ DisplayList := TStringList.Create;
+ FMainControl := FCombo;
+end;
+
+
+destructor TEnumEditorLink.Destroy;
+begin
+ FCombo.Free;
+ inherited;
+end;
+
+
+function TEnumEditorLink.BeginEdit: Boolean; stdcall;
+begin
+ Result := inherited BeginEdit;
+ if Result then begin
+ FCombo.Show;
+ FCombo.SetFocus;
+ end;
+end;
+
+
+function TEnumEditorLink.EndEdit: Boolean; stdcall;
+var
+ NewText: String;
+begin
+ if AllowCustomText and FAllowEdit and (not ItemMustExist) then
+ NewText := FCombo.Text
+ else if (ValueList.Count > 0) and (FCombo.ItemIndex > -1) then
+ NewText := ValueList[FCombo.ItemIndex]
+ else
+ NewText := '';
+ FModified := NewText <> FCellText;
+ Result := EndEditHelper(NewText);
+end;
+
+
+function TEnumEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if Result then begin
+ if DisplayList.Count = ValueList.Count then
+ FCombo.Items.AddStrings(DisplayList)
+ else
+ FCombo.Items.AddStrings(ValueList);
+ FCombo.ItemIndex := ValueList.IndexOf(FCellText);
+ if AllowCustomText and FAllowEdit then begin
+ FCombo.Style := csDropDown;
+ FCombo.Text := FCellText;
+ end else begin
+ // Set style to OwnerDraw, otherwise we wouldn't be able to adjust the combo's height
+ FCombo.Style := csOwnerDrawFixed;
+ end;
+ end;
+end;
+
+
+procedure TEnumEditorLink.SetBounds(R: TRect); stdcall;
+begin
+ FCombo.BoundsRect := GetCellRect(False);
+end;
+
+
+procedure TEnumEditorLink.DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
+begin
+ // Work around a magic automatic TAB key arriving the editor if the user got
+ // into this cell via TAB. Only seen for a TComboBox with style=csDropDown.
+ // See issue #2809
+ if (not AllowCustomText) and (GetTickCount-FBeginEditTime > 200) then
+ inherited;
+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; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+ ValueList := TStringList.Create;
+
+ FPanel := TPanel.Create(FParentForm);
+ FPanel.Hide;
+ FPanel.Parent := FParentForm;
+ FPanel.ParentBackground := False;
+ FPanel.Height := TExtForm.ScaleSize(150, FParentForm);
+ FPanel.OnExit := DoEndEdit;
+
+ FCheckList := TCheckListBox.Create(FPanel);
+ FCheckList.Parent := FPanel;
+ FCheckList.OnKeyDown := DoKeyDown;
+ FMainControl := FCheckList;
+
+ 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;
+
+ FEndTimer := TTimer.Create(FPanel);
+ FEndTimer.Interval := 50;
+ FEndTimer.Enabled := False;
+end;
+
+
+destructor TSetEditorLink.Destroy;
+begin
+ FreeAndNil(FPanel);
+ inherited;
+end;
+
+
+function TSetEditorLink.BeginEdit: Boolean; stdcall;
+begin
+ Result := inherited BeginEdit;
+ if Result then begin
+ FPanel.Show;
+ FCheckList.SetFocus;
+ end;
+end;
+
+
+function TSetEditorLink.EndEdit: Boolean; stdcall;
+var
+ newtext: String;
+ i: Integer;
+begin
+ Result := not FStopping;
+ if FStopping 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);
+ FModified := newText <> FCellText;
+ Result := EndEditHelper(newText);
+end;
+
+
+function TSetEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
+var
+ SelValues: TStringList;
+ i: Integer;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if not Result then
+ Exit;
+
+ FCheckList.Font.Assign(FCellFont);
+ FCheckList.Items.Assign(ValueList);
+ SelValues := TStringList.Create;
+ SelValues.Delimiter := ',';
+ SelValues.StrictDelimiter := True;
+ 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;
+
+
+procedure TSetEditorLink.SetBounds(R: TRect); stdcall;
+const
+ margin = 5;
+begin
+ R := GetCellRect(False);
+ FPanel.Top := R.Top;
+ FPanel.Left := R.Left;
+ FPanel.Width := R.Width;
+
+ FBtnOk.Width := (FPanel.Width - 3*margin) div 2;
+ FBtnOk.Left := margin;
+ FBtnOk.Height := TExtForm.ScaleSize(24, FParentForm);
+ FBtnOk.Top := FPanel.Height - 2*margin - FBtnOk.Height;
+ FBtnOk.Enabled := FAllowEdit;
+
+ 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 := FBtnOk.Top - margin - FCheckList.Top;
+ // FCheckList.Enabled := FAllowEdit; // crashes with "cannot focus if disabled"
+end;
+
+
+procedure TSetEditorLink.BtnOkClick(Sender: TObject);
+begin
+ // Timer based click on OK button, to prevent crash when theming is active
+ FEndTimer.OnTimer := DoEndEdit;
+ FEndTimer.Enabled := True;
+end;
+
+
+procedure TSetEditorLink.BtnCancelClick(Sender: TObject);
+begin
+ // Timer based click on Cancel button, to prevent crash when theming is active
+ FEndTimer.OnTimer := DoCancelEdit;
+ FEndTimer.Enabled := True;
+end;
+
+
+
+{ TInplaceEditorLink }
+
+constructor TInplaceEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+ ButtonVisible := false;
+
+ FPanel := TPanel.Create(FParentForm);
+ FPanel.Parent := FParentForm;
+ FPanel.Hide;
+ FPanel.ParentBackground := False;
+ FPanel.BevelOuter := bvNone;
+ FPanel.OnExit := DoEndEdit;
+
+ FEdit := TEdit.Create(FPanel);
+ FEdit.Parent := FPanel;
+ FEdit.ParentColor := True;
+ FEdit.BorderStyle := bsNone;
+ FEdit.OnKeyDown := DoKeyDown;
+ FMainControl := FEdit;
+
+ FButton := TButton.Create(FPanel);
+ FButton.Parent := FPanel;
+ FButton.TabStop := False;
+ FButton.Caption := '…';
+ FButton.Hint := _('Edit text in popup editor ...');
+ FButton.ShowHint := True;
+ FButton.OnClick := ButtonClick;
+end;
+
+destructor TInplaceEditorLink.Destroy;
+begin
+ if not ((csDestroying in FPanel.ComponentState) or (csCreating in FPanel.ControlState)) then begin
+ FEdit.Free;
+ FButton.Free;
+ FPanel.Free;
+ end;
+ inherited;
+end;
+
+function TInplaceEditorLink.BeginEdit: Boolean;
+begin
+ Result := inherited BeginEdit;
+ if Result then begin
+ FButton.Visible := ButtonVisible;
+ SetBounds(Rect(0, 0, 0, 0));
+ if (Length(FEdit.Text) > SIZE_KB) or (ScanLineBreaks(FEdit.Text) <> lbsNone) then
+ ButtonClick(FTree)
+ else begin
+ FPanel.Show;
+ FEdit.SetFocus;
+ end;
+ end;
+end;
+
+
+function TInplaceEditorLink.EndEdit: Boolean;
+var
+ NewText: String;
+begin
+ Result := not FStopping;
+ if FStopping then Exit;
+ NewText := FEdit.Text;
+ FModified := NewText <> FCellText;
+ Result := EndEditHelper(NewText);
+end;
+
+procedure TInplaceEditorLink.DoKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+begin
+ inherited DoKeyDown(Sender, Key, Shift);
+ if Key = VK_F2 then
+ ButtonClick(FButton);
+end;
+
+procedure TInplaceEditorLink.ButtonClick(Sender: TObject);
+var
+ Editor: TfrmTextEditor;
+begin
+ if not FButton.Visible then Exit; // Button was invisible, but hotkey was pressed
+ Editor := TfrmTextEditor.Create(FTree);
+ Editor.SetFont(MainForm.SynMemoQuery.Font);
+ Editor.SetText(FEdit.Text);
+ if FEdit.HandleAllocated then begin
+ Editor.MemoText.SelStart := FEdit.SelStart;
+ Editor.MemoText.SelEnd := FEdit.SelStart + FEdit.SelLength;
+ end;
+ Editor.SetTitleText(TitleText);
+ Editor.Modified := FEdit.Modified;
+ Editor.SetMaxLength(FMaxLength);
+ Editor.TableColumn := FTableColumn;
+ Editor.MemoText.ReadOnly := not FAllowEdit;
+ if Editor.ShowModal = mrYes then begin
+ FEdit.Text := Editor.GetText;
+ DoEndEdit(Sender);
+ end
+ else begin
+ DoCancelEdit(Sender);
+ end;
+ Editor.Free;
+end;
+
+function TInplaceEditorLink.PrepareEdit(Tree: TBaseVirtualTree;
+ Node: PVirtualNode; Column: TColumnIndex): Boolean;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if not Result then
+ Exit;
+
+ FEdit.ReadOnly := not FAllowEdit;
+ FEdit.Font.Assign(FCellFont);
+ FEdit.Font.Color := GetThemeColor(clWindowText);
+ FPanel.Color := FCellBackground;
+ FEdit.Text := FCellText;
+ FEdit.Modified := False;
+end;
+
+procedure TInplaceEditorLink.SetBounds(R: TRect);
+begin
+ if not FStopping then begin
+ // Position edit control according to cell text bounds
+ FPanel.BoundsRect := GetCellRect(False);
+ R := GetCellRect(True);
+ if FButton.Visible then
+ Dec(R.Right, TExtForm.ScaleSize(20, FPanel));
+ FEdit.BoundsRect := R;
+
+ FButton.BoundsRect := Rect(FEdit.BoundsRect.Right, 0, FPanel.Width, FPanel.Height);
+ end;
+end;
+
+
+
+{ Column default editor }
+
+constructor TColumnDefaultEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+var
+ SQLFunc: TSQLFunction;
+ m: Integer;
+begin
+ inherited;
+
+ // Margin between controls and to edge of panel
+ m := TExtForm.ScaleSize(5, FParentForm);
+
+ FPanel := TPanel.Create(FParentForm);
+ FPanel.Hide;
+ FPanel.Parent := FParentForm;
+ FPanel.OnExit := DoEndEdit;
+ FPanel.ParentBackground := False;
+ FPanel.Color := GetThemeColor(clWindow);
+ //FPanel.BevelKind := bkFlat;
+ FPanel.BevelOuter := bvNone;
+ FPanel.DoubleBuffered := True; // Avoid flicker?
+ FMainControl := FPanel;
+
+ FRadioNothing := TAllKeysRadioButton.Create(FPanel);
+ FRadioNothing.Parent := FPanel;
+ FRadioNothing.Top := m;
+ FRadioNothing.Left := m;
+ FRadioNothing.Width := FRadioNothing.Parent.Width - 2 * FRadioNothing.Left;
+ FRadioNothing.OnClick := RadioClick;
+ FRadioNothing.OnKeyDown := DoKeyDown;
+ FRadioNothing.Caption := _('No default value');
+
+ FRadioText := TAllKeysRadioButton.Create(FPanel);
+ FRadioText.Parent := FPanel;
+ FRadioText.Top := FRadioNothing.Top + FRadioNothing.Height + m;;
+ FRadioText.Left := m;
+ FRadioText.Width := FRadioText.Parent.Width - 2 * FRadioText.Left;
+ FRadioText.OnClick := RadioClick;
+ FRadioText.OnKeyDown := DoKeyDown;
+ FRadioText.Caption := _('Custom text')+':';
+
+ FTextDropDown := TPopupMenu.Create(FPanel);
+
+ FTextEdit := TEditButton.Create(FPanel);
+ FTextEdit.Parent := FPanel;
+ FTextEdit.Top := FRadioText.Top + FRadioText.Height + m;
+ FTextEdit.Left := 2*m;
+ FTextEdit.Width := FTextEdit.Parent.Width - 2*FTextEdit.Left;
+ FTextEdit.OnChange := EditChange;
+ FTextEdit.Images := Tree.Images;
+ FTextEdit.ImageIndex := 75; // Drop down arrow
+ FTextEdit.OnButtonClick := EditButtonClick;
+ //FTextEdit.RightButton.DropDownMenu := FTextDropDown;
+
+ FRadioNull := TAllKeysRadioButton.Create(FPanel);
+ FRadioNull.Parent := FPanel;
+ FRadioNull.Top := FTextEdit.Top + FTextEdit.Height + 2*m;
+ FRadioNull.Left := m;
+ FRadioNull.Width := FRadioNull.Parent.Width - 2 * FRadioNull.Left;
+ FRadioNull.OnClick := RadioClick;
+ FRadioNull.OnKeyDown := DoKeyDown;
+ FRadioNull.Caption := 'NULL';
+
+ FRadioExpression := TAllKeysRadioButton.Create(FPanel);
+ FRadioExpression.Parent := FPanel;
+ FRadioExpression.Top := FRadioNull.Top + FRadioNull.Height + m;
+ FRadioExpression.Left := m;
+ FRadioExpression.Width := FRadioExpression.Parent.Width - 2 * FRadioExpression.Left;
+ FRadioExpression.OnClick := RadioClick;
+ FRadioExpression.OnKeyDown := DoKeyDown;
+ FRadioExpression.Caption := _('Expression')+':';
+
+ FExpressionEdit := TComboBox.Create(FPanel);
+ FExpressionEdit.Parent := FPanel;
+ FExpressionEdit.Top := FRadioExpression.Top + FRadioExpression.Height + m;
+ FExpressionEdit.Left := 2*m;
+ FExpressionEdit.Width := FExpressionEdit.Parent.Width - 2*FExpressionEdit.Left;
+ FExpressionEdit.OnChange := EditChange;
+ FExpressionEdit.DropDownCount := 20;
+ for SQLFunc in FTableColumn.Connection.SQLFunctions do begin
+ FExpressionEdit.Items.Add(SQLFunc.Name + SQLFunc.Declaration);
+ end;
+
+ FlblOnUpdate := TLabel.Create(FPanel);
+ FlblOnUpdate.Parent := FPanel;
+ FlblOnUpdate.Top := FExpressionEdit.Top + FExpressionEdit.Height + m;
+ FlblOnUpdate.Left := 2*m;
+ FlblOnUpdate.Width := FlblOnUpdate.Parent.Width - 2*FlblOnUpdate.Left;
+ FlblOnUpdate.Caption := _('On update') + ':';
+
+ FOnUpdateEdit := TComboBox.Create(FPanel);
+ FOnUpdateEdit.Parent := FPanel;
+ FOnUpdateEdit.Top := FlblOnUpdate.Top + FlblOnUpdate.Height + m;
+ FOnUpdateEdit.Left := 2*m;
+ FOnUpdateEdit.Width := FOnUpdateEdit.Parent.Width - 2*FOnUpdateEdit.Left;
+ FOnUpdateEdit.OnChange := EditChange;
+ FOnUpdateEdit.DropDownCount := 20;
+ for SQLFunc in FTableColumn.Connection.SQLFunctions do begin
+ FOnUpdateEdit.Items.Add(SQLFunc.Name + SQLFunc.Declaration);
+ end;
+
+ FRadioAutoInc := TAllKeysRadioButton.Create(FPanel);
+ FRadioAutoInc.Parent := FPanel;
+ FRadioAutoInc.Top := FOnUpdateEdit.Top + FOnUpdateEdit.Height + m;
+ FRadioAutoInc.Left := m;
+ FRadioAutoInc.Width := FRadioAutoInc.Parent.Width - 2 * FRadioAutoInc.Left;
+ FRadioAutoInc.OnClick := RadioClick;
+ FRadioAutoInc.OnKeyDown := DoKeyDown;
+ FRadioAutoInc.Caption := Col.AutoIncName;
+
+ FBtnOk := TButton.Create(FPanel);
+ FBtnOk.Parent := FPanel;
+ FBtnOk.Width := TExtForm.ScaleSize(60, FParentForm);
+ FBtnOk.Top := FRadioAutoInc.Top + FRadioAutoInc.Height + m;
+ FBtnOk.Left := FPanel.Width - 3*m - 2*FBtnOk.Width - 2*FPanel.BorderWidth;
+ FBtnOk.OnClick := BtnOkClick;
+ FBtnOk.Default := True;
+ FBtnOk.Caption := _('OK');
+
+ FBtnCancel := TButton.Create(FPanel);
+ FBtnCancel.Parent := FPanel;
+ FBtnCancel.Top := FBtnOk.Top;
+ FBtnCancel.Width := FBtnOk.Width;
+ FBtnCancel.Left := FBtnOk.Left + FBtnOk.Width + m;
+ FBtnCancel.OnClick := BtnCancelClick;
+ FBtnCancel.Cancel := True;
+ FBtnCancel.Caption := _('Cancel');
+
+ FEndTimer := TTimer.Create(FPanel);
+ FEndTimer.Interval := 50;
+ FEndTimer.Enabled := False;
+
+ // Set outer panel (minimum) dimensions. Width is set in .SetBounds()
+ FPanel.Height := 2*FPanel.BorderWidth + FBtnOk.Top + FBtnOk.Height + 2*m;
+ FPanel.Constraints.MinWidth := 2*m + FBtnOK.Width + m + FBtnCancel.Width + 2*m;
+
+ // Set anchors for all controls, so they are sticky when resizing the underlying column width
+ FRadioNothing.Anchors := [akLeft, akTop, akRight];
+ FRadioText.Anchors := [akLeft, akTop, akRight];
+ FTextEdit.Anchors := [akLeft, akTop, akRight, akBottom];
+ FRadioNull.Anchors := [akLeft, akBottom, akRight];
+ FRadioExpression.Anchors := [akLeft, akBottom, akRight];
+ FExpressionEdit.Anchors := [akLeft, akBottom, akRight];
+ FOnUpdateEdit.Anchors := [akLeft, akBottom, akRight];
+ FRadioAutoInc.Anchors := [akLeft, akBottom, akRight];
+ FBtnOk.Anchors := [akBottom, akRight];
+ FBtnCancel.Anchors := FBtnOk.Anchors;
+end;
+
+
+destructor TColumnDefaultEditorLink.Destroy;
+begin
+ FPanel.Free;
+ inherited;
+end;
+
+
+function TColumnDefaultEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
+var
+ ValueList, SelectedValues: TStringList;
+ i: Integer;
+ Item: TMenuItem;
+begin
+ inherited PrepareEdit(Tree, Node, Column);
+
+ // Check relevant radio button
+ FRadioNothing.Checked := DefaultType = cdtNothing;
+ FRadioText.Checked := DefaultType = cdtText;
+ FRadioNull.Checked := DefaultType = cdtNull;
+ FRadioExpression.Checked := DefaultType = cdtExpression;
+ FRadioAutoInc.Checked := DefaultType = cdtAutoInc;
+
+ if FRadioText.Checked then begin
+ FTextEdit.Text := DefaultText;
+ end;
+
+ if FRadioExpression.Checked then begin
+ FExpressionEdit.Text := DefaultText;
+ end;
+
+ FOnUpdateEdit.Text := OnUpdateText;
+
+ // Disable non working default options per data type
+ FRadioAutoInc.Enabled := FRadioAutoInc.Checked or (FTableColumn.DataType.Category = dtcInteger);
+
+ // Provide items with a check mark for ENUM and SET columns
+ if FTableColumn.DataType.Index in [dbdtEnum, dbdtSet] then begin
+ FTextEdit.Button.Enabled := True;
+ ValueList := FTableColumn.ValueList;
+ SelectedValues := Explode(',', FTextEdit.Text);
+
+ for i:=0 to ValueList.Count-1 do begin
+ Item := TMenuItem.Create(FTextDropDown);
+ Item.Caption := ValueList[i];
+ Item.RadioItem := FTableColumn.DataType.Index = dbdtEnum;
+ Item.Checked := SelectedValues.IndexOf(Item.Caption) > -1;
+ Item.OnClick := EditDropDownClick;
+ FTextDropDown.Items.Add(Item);
+ end;
+
+ ValueList.Free;
+ SelectedValues.Free;
+ end;
+
+ Result := True;
+end;
+
+
+procedure TColumnDefaultEditorLink.SetBounds(R: TRect); stdcall;
+var
+ CellRect: TRect;
+ P: TPoint;
+ Room: Integer;
+begin
+ CellRect := GetCellRect(False);
+ FPanel.Left := CellRect.Left;
+ FPanel.Top := CellRect.Top;
+ FPanel.Width := CellRect.Width;
+
+ // Reposition editor so it's not outside the main form
+ P := FParentForm.ClientToScreen(FPanel.BoundsRect.TopLeft);
+ Room := FParentForm.BoundsRect.Bottom - 8 {Borderwidth} - (P.Y + FPanel.Height);
+ if Room < 0 then
+ FPanel.Top := CellRect.Top + Room;
+ P := FParentForm.ClientToScreen(FPanel.BoundsRect.BottomRight);
+ Room := FParentForm.BoundsRect.Right - 8 {Borderwidth} - P.X;
+ if Room < 0 then
+ FPanel.Left := CellRect.Left + Room;
+end;
+
+
+function TColumnDefaultEditorLink.BeginEdit: Boolean; stdcall;
+begin
+ Result := not FStopping;
+ if Result then begin
+ FPanel.Show;
+ if FRadioNothing.Checked then FRadioNothing.SetFocus
+ else if FRadioText.Checked then FTextEdit.SetFocus
+ else if FRadioNull.Checked then FRadioNull.SetFocus
+ else if FRadioExpression.Checked then FExpressionEdit.SetFocus
+ else if FRadioAutoInc.Checked then FRadioAutoInc.SetFocus;
+ end;
+end;
+
+
+function TColumnDefaultEditorLink.EndEdit: Boolean; stdcall;
+var
+ Col: PTableColumn;
+begin
+ Result := not FStopping;
+ if Result then begin
+ FStopping := True;
+ Col := FTree.GetNodeData(FNode);
+
+ if FRadioNothing.Checked then
+ Col.DefaultType := cdtNothing
+ else if FRadioText.Checked then
+ Col.DefaultType := cdtText
+ else if FRadioNull.Checked then
+ Col.DefaultType := cdtNull
+ else if FRadioExpression.Checked then
+ Col.DefaultType := cdtExpression
+ else if FRadioAutoInc.Checked then
+ Col.DefaultType := cdtAutoInc
+ else
+ Col.DefaultType := cdtText;
+
+ case Col.DefaultType of
+ cdtNothing: Col.DefaultText := '';
+ cdtText: Col.DefaultText := FTextEdit.Text;
+ cdtNull: Col.DefaultText := 'NULL';
+ cdtExpression: Col.DefaultText := FExpressionEdit.Text;
+ cdtAutoInc: Col.DefaultText := Col.AutoIncName;
+ end;
+
+ if FOnUpdateEdit.Text <> '' then
+ Col.OnUpdateType := cdtExpression
+ else
+ Col.OnUpdateType := cdtNothing;
+ Col.OnUpdateText := FOnUpdateEdit.Text;
+
+ FTree.Text[FNode, FColumn] := Col.DefaultText;
+ if FTree.CanFocus then
+ FTree.SetFocus;
+ end;
+end;
+
+
+procedure TColumnDefaultEditorLink.RadioClick(Sender: TObject);
+begin
+ if not FRadioText.Checked then
+ FTextEdit.Color := clBtnFace
+ else begin
+ FTextEdit.Color := clWindow;
+ if FTextEdit.CanFocus then
+ FTextEdit.SetFocus;
+ end;
+ if not FRadioExpression.Checked then
+ FExpressionEdit.Color := clBtnFace
+ else begin
+ FExpressionEdit.Color := clWindow;
+ if FExpressionEdit.CanFocus then
+ FExpressionEdit.SetFocus;
+ end;
+ FModified := True;
+end;
+
+
+procedure TColumnDefaultEditorLink.EditChange(Sender: TObject);
+begin
+ if Sender = FTextEdit then
+ FRadioText.Checked := True
+ else if Sender = FExpressionEdit then
+ FRadioExpression.Checked := True;
+ FModified := True;
+end;
+
+
+procedure TColumnDefaultEditorLink.EditButtonClick(Sender: TObject);
+begin
+ TExtForm.ShowPopup(FTextEdit.Button, FTextDropDown);
+end;
+
+procedure TColumnDefaultEditorLink.EditDropDownClick(Sender: TObject);
+var
+ Item: TMenuItem;
+ NewValue: String;
+begin
+ // ENUM or SET value clicked in drop down menu
+ Item := Sender as TMenuItem;
+ Item.Checked := not Item.Checked;
+ NewValue := '';
+ for Item in Item.GetParentMenu.Items do begin
+ if Item.Checked then
+ NewValue := NewValue + StripHotkey(Item.Caption) + ',';
+ end;
+ if not NewValue.IsEmpty then
+ Delete(NewValue, Length(NewValue), 1);
+ FTextEdit.Text := NewValue;
+ FModified := True;
+end;
+
+procedure TColumnDefaultEditorLink.BtnOkClick(Sender: TObject);
+begin
+ // Timer based click on OK button, to prevent crash when theming is active
+ FEndTimer.OnTimer := DoEndEdit;
+ FEndTimer.Enabled := True;
+end;
+
+procedure TColumnDefaultEditorLink.BtnCancelClick(Sender: TObject);
+begin
+ // Timer based click on Cancel button, to prevent crash when theming is active
+ FEndTimer.OnTimer := DoCancelEdit;
+ FEndTimer.Enabled := True;
+end;
+
+
+
+
+{ Datatype selector }
+constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree; AllowEdit: Boolean; Col: TTableColumn);
+begin
+ inherited;
+
+ FTreeSelect := TVirtualStringTree.Create(FParentForm);
+ FTreeSelect.Hide;
+ FTreeSelect.TreeOptions.PaintOptions := FTreeSelect.TreeOptions.PaintOptions
+ - [toShowTreeLines, toShowButtons, toShowRoot]
+ + [toHotTrack, toUseExplorerTheme, toHideTreeLinesIfThemed];
+ FTreeSelect.TreeOptions.SelectionOptions := FTreeSelect.TreeOptions.SelectionOptions
+ + [toFullRowSelect];
+ FTreeSelect.Header.Columns.Add;
+ FTreeSelect.Parent := FParentForm;
+ FTreeSelect.TextMargin := 0;
+ FTreeSelect.BorderStyle := bsNone;
+ //FTreeSelect.BevelKind := bkFlat;
+ //FTreeSelect.BevelInner := bvNone;
+ FTreeSelect.IncrementalSearch := isAll;
+ FTreeSelect.RootNodeCount := Length(DatatypeCategories);
+ FTreeSelect.OnGetText := DoTreeSelectGetText;
+ FTreeSelect.OnInitNode := DoTreeSelectInitNode;
+ FTreeSelect.OnInitChildren := DoTreeSelectInitChildren;
+ FTreeSelect.OnKeyDown := DoKeyDown;
+ FTreeSelect.OnHotChange := DoTreeSelectHotChange;
+ FTreeSelect.OnPaintText := DoTreeSelectPaintText;
+ FTreeSelect.OnExit := DoEndEdit;
+ // See further events in PrepareEdit
+ FixVT(FTreeSelect);
+ FMainControl := FTreeSelect;
+
+ FMemoHelp := TMemo.Create(FParentForm);
+ FMemoHelp.Hide;
+ FMemoHelp.Parent := FParentForm;
+ FMemoHelp.Color := clInfoBk;
+ FMemoHelp.Font.Color := clInfoText;
+ //FMemoHelp.BevelKind := bkFlat;
+ {if TStyleManager.IsCustomStyleActive then begin
+ FMemoHelp.BorderStyle := bsSingle;
+ FMemoHelp.BevelInner := bvNone;
+ end else begin
+ FMemoHelp.BorderStyle := bsNone;
+ FMemoHelp.BevelInner := bvSpace;
+ end;}
+end;
+
+
+destructor TDataTypeEditorLink.Destroy;
+begin
+ FreeAndNil(FTreeSelect);
+ FreeAndNil(FMemoHelp);
+ inherited;
+end;
+
+
+function TDataTypeEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
+var
+ dt: TDBDatatype;
+ CatNode, TypeNode: PVirtualNode;
+begin
+ Result := inherited PrepareEdit(Tree, Node, Column);
+ if not Result then
+ Exit;
+ // Just use font name and size, avoid bold style and white color
+ FTreeSelect.Font.Name := FCellFont.Name;
+ FTreeSelect.Font.Size := FCellFont.Size;
+
+ // Find and select current datatype in tree
+ dt := FTableColumn.Connection.GetDataTypeByName(FCellText, False, FTableColumn.Name);
+ CatNode := FTreeSelect.GetFirst;
+ while Assigned(CatNode) do begin
+ // Since recent update to VT 5.2.1 we need to initialize root nodes by hand for some reason:
+ FTreeSelect.ReinitNode(CatNode, True);
+ if CatNode.Index = Cardinal(dt.Category) then begin
+ TypeNode := FTreeSelect.GetFirstChild(CatNode);
+ while Assigned(TypeNode) do begin
+ if FTreeSelect.Text[TypeNode, 0] = FCellText then begin
+ FTreeSelect.FocusedNode := TypeNode;
+ FTreeSelect.Selected[TypeNode] := True;
+ break;
+ end;
+ TypeNode := FTreeSelect.GetNextSibling(TypeNode);
+ end;
+ end;
+ CatNode := FTreeSelect.GetNextSibling(CatNode);
+ end;
+ FTreeSelect.Header.AutoFitColumns(False, smaUseColumnOption, 0, 0);
+ if Assigned(FTreeSelect.FocusedNode) then
+ FTreeSelect.ScrollIntoView(FTreeSelect.FocusedNode, True);
+ FTreeSelect.OnFocusChanging := DoTreeSelectFocusChanging;
+ FTreeSelect.OnFocusChanged := DoTreeSelectFocusChanged;
+ FTreeSelect.OnClick := DoEndEdit;
+end;
+
+
+function TDataTypeEditorLink.BeginEdit: Boolean;
+begin
+ Result := inherited BeginEdit;
+ if Result then begin
+ FTreeSelect.Show;
+ FTreeSelect.SetFocus;
+ end;
+end;
+
+
+function TDataTypeEditorLink.EndEdit: Boolean;
+begin
+ if Assigned(FTreeSelect.FocusedNode) then
+ Result := EndEditHelper(FTreeSelect.Text[FTreeSelect.FocusedNode, 0])
+ else
+ Result := FTree.CancelEditNode;
+end;
+
+
+procedure TDataTypeEditorLink.SetBounds(R: TRect);
+var
+ CellRect: TRect;
+ TreeHeight: Integer;
+begin
+ // Set position of tree. As the tree's parent is mainform, not listcolumns, add listcolumn's x + y positions
+ CellRect := GetCellRect(False);
+ // Do not exceed lower edge of mainform, as that portion would be hidden
+ TreeHeight := Min(250, FParentForm.ClientHeight-CellRect.Top-10);
+ FTreeSelect.SetBounds(CellRect.Left,
+ CellRect.Top,
+ FTreeSelect.Header.Columns[0].Width + GetSystemMetrics(SM_CXVSCROLL) + 5,
+ TreeHeight);
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectInitNode(Sender: TBaseVirtualTree;
+ ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
+begin
+ // First level nodes always expanded
+ if Sender.GetNodeLevel(Node) = 0 then
+ InitialStates := InitialStates + [ivsExpanded, ivsHasChildren];
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectInitChildren(Sender: TBaseVirtualTree;
+ Node: PVirtualNode; var ChildCount: Cardinal);
+var
+ i: Integer;
+begin
+ // Tell number of datatypes per category
+ ChildCount := 0;
+ if Sender.GetNodeLevel(Node) = 0 then for i:=0 to High(FTableColumn.Connection.Datatypes) do begin
+ if FTableColumn.Connection.Datatypes[i].Category = TDBDatatypeCategoryIndex(Node.Index) then
+ Inc(ChildCount);
+ end;
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
+ Column: TColumnIndex; TextType: TVSTTextType; var CellText: String);
+var
+ i: Integer;
+ Counter: Cardinal;
+begin
+ // Get cell text
+ case Sender.GetNodeLevel(Node) of
+ 0: CellText := DatatypeCategories[TDBDatatypeCategoryIndex(Node.Index)].Name;
+ 1: begin
+ Counter := 0;
+ for i:=0 to High(FTableColumn.Connection.Datatypes) do begin
+ if FTableColumn.Connection.Datatypes[i].Category = TDBDatatypeCategoryIndex(Node.Parent.Index) then begin
+ Inc(Counter);
+ if Counter = Node.Index+1 then begin
+ CellText := FTableColumn.Connection.Datatypes[i].Name;
+ break;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectHotChange(Sender: TBaseVirtualTree; OldNode, NewNode: PVirtualNode);
+var
+ R: TRect;
+ NodeText: String;
+ bmp: TBitMap;
+begin
+ // Display help box for hovered datatype
+ FMemoHelp.Clear;
+ if Assigned(NewNode) and (Sender.GetNodeLevel(NewNode) = 1) then begin
+ R := FTreeSelect.GetDisplayRect(NewNode, 0, False);
+ NodeText := FTreeSelect.Text[NewNode, 0];
+ FMemoHelp.Width := Min(250, FTreeSelect.Left);
+ FMemoHelp.Left := FTreeSelect.Left - FMemoHelp.Width + (Integer(FTreeSelect.Indent) Div 2);
+ FMemoHelp.Top := FTreeSelect.Top + R.Top + 3;
+ FMemoHelp.Text := FTableColumn.Connection.GetDatatypeByName(NodeText, False, FTableColumn.Name).Description;
+ // Calc height of memo
+ bmp := TBitMap.Create;
+ bmp.Canvas.Font.Assign(FMemoHelp.Font);
+ R := Rect(0, 0, FMemoHelp.Width-10, 0);
+ DrawText(bmp.Canvas.Handle, PChar(FMemoHelp.Text), Length(FMemoHelp.Text), R, DT_WORDBREAK or DT_CALCRECT);
+ FreeAndNil(bmp);
+ FMemoHelp.Height := R.Bottom + 8;
+ FMemoHelp.Show;
+ end;
+ if FMemoHelp.GetTextLen = 0 then
+ FMemoHelp.Hide;
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectPaintText(Sender: TBaseVirtualTree;
+ const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
+ TextType: TVSTTextType);
+begin
+ // Give datatype column specific color, as set in preferences
+ case Sender.GetNodeLevel(Node) of
+ 0: TargetCanvas.Font.Style := TargetCanvas.Font.Style + [fsBold];
+ 1: if not (vsSelected in Node.States) then
+ TargetCanvas.Font.Color := DatatypeCategories[TDBDatatypeCategoryIndex(Node.Parent.Index)].Color;
+ end;
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectFocusChanging(Sender: TBaseVirtualTree; OldNode, NewNode:
+ PVirtualNode; OldColumn, NewColumn: TColumnIndex; var Allowed: Boolean);
+var
+ JumpToNode: PVirtualNode;
+begin
+ // Allow only 2nd level datatypes to be focused, not their category
+ Allowed := Sender.GetNodeLevel(NewNode) = 1;
+ if not Allowed then begin
+ JumpToNode := nil;
+ if FLastKeyDown = VK_UP then
+ JumpToNode := Sender.GetPrevious(NewNode)
+ else if FLastKeyDown = VK_DOWN then
+ JumpToNode := Sender.GetNext(NewNode);
+ if Assigned(JumpToNode) then
+ Sender.FocusedNode := JumpToNode;
+ end;
+end;
+
+
+procedure TDataTypeEditorLink.DoTreeSelectFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
+begin
+ FModified := True;
+end;
+
+
+end.
diff --git a/source/insertfiles.pas b/source/insertfiles.pas
index ad8b89be..ff6b2d7d 100644
--- a/source/insertfiles.pas
+++ b/source/insertfiles.pas
@@ -99,7 +99,7 @@ type
implementation
-uses main;
+uses main, grideditlinks;
const
ColColname = 0;
@@ -260,12 +260,12 @@ procedure TfrmInsertFiles.ListColumnsCreateEditor(Sender: TBaseVirtualTree; Node
Column: TColumnIndex; out EditLink: IVTEditLink);
var
Grid: TVirtualStringTree;
- //EnumEditor: TEnumEditorLink;
+ EnumEditor: TEnumEditorLink;
begin
// Start cell editor
Grid := Sender as TVirtualStringTree;
if Column = ColValue then begin
- {EnumEditor := TEnumEditorLink.Create(Grid, True, nil);
+ EnumEditor := TEnumEditorLink.Create(Grid, True, nil);
EnumEditor.AllowCustomText := True;
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.Text := 'NULL'+CRLF+
@@ -281,7 +281,7 @@ begin
'UPPER(''%filenname%'')'+CRLF+
'UNIX_TIMESTAMP(''%filedatetime%'')'+CRLF+
'ENCODE(''%filename%'', ''password'')';
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end;
end;
diff --git a/source/main.pas b/source/main.pas
index 1c004410..1de2929b 100644
--- a/source/main.pas
+++ b/source/main.pas
@@ -1421,7 +1421,7 @@ implementation
uses
FileInfo, winpeimagereader, elfreader, machoreader, About, data_sorting, column_selection, loaddata, editvar,
copytable, csv_detector, exportgrid, usermanager, selectdbobject, reformatter, connections, sqlhelp, updatecheck,
- insertfiles, texteditor, preferences, table_editor, view, routine_editor, trigger_editor, event_editor;
+ insertfiles, texteditor, preferences, table_editor, view, routine_editor, trigger_editor, event_editor, grideditlinks;
{$R *.lfm}
@@ -10848,11 +10848,11 @@ const
ForeignItemsLimit: Integer = 10000;
var
VT: TVirtualStringTree;
- {HexEditor: THexEditorLink;
+ HexEditor: THexEditorLink;
DateTimeEditor: TDateTimeEditorLink;
EnumEditor: TEnumEditorLink;
SetEditor: TSetEditorLink;
- InplaceEditor: TInplaceEditorLink;}
+ InplaceEditor: TInplaceEditorLink;
TypeCat: TDBDatatypeCategoryIndex;
ForeignKey: TForeignKey;
TblColumn: TTableColumn;
@@ -10912,7 +10912,7 @@ begin
ForeignResults := Conn.GetResults(SQL);
if ForeignResults.RecordCount < ForeignItemsLimit then begin
- {EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
+ EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
EditLink := EnumEditor;
DisplayHex := (not actBlobAsText.Checked) and (ForeignResults.DataType(0).Category in [dtcBinary, dtcSpatial]);
while not ForeignResults.Eof do begin
@@ -10927,7 +10927,7 @@ begin
EnumEditor.DisplayList.Add(ForeignResults.Col(0)+': '+ForeignResults.Col(1));
end;
ForeignResults.Next;
- end;}
+ end;
end else begin
LogSQL(f_('Connected table has too many rows. Foreign key drop-down is limited to %d items.', [ForeignItemsLimit]), lcInfo);
end;
@@ -10941,11 +10941,11 @@ begin
FGridEditFunctionMode := FGridEditFunctionMode or Results.IsFunction(ResultCol);
if FGridEditFunctionMode then begin
- {EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
+ EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
for SQLFunc in Conn.SQLFunctions do
EnumEditor.ValueList.Add(SQLFunc.Name + SQLFunc.Declaration);
EnumEditor.AllowCustomText := True;
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end;
TypeCat := Results.DataType(ResultCol).Category;
@@ -10953,20 +10953,20 @@ begin
if Assigned(EditLink) then
// Editor was created above, do nothing now
else if (Results.DataType(ResultCol).Index in [dbdtEnum, dbdtBool]) and AppSettings.ReadBool(asFieldEditorEnum) then begin
- {EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
+ EnumEditor := TEnumEditorLink.Create(VT, AllowEdit, TblColumn);
EnumEditor.ValueList := Results.ValueList(ResultCol);
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end else if (TypeCat = dtcText) or ((TypeCat in [dtcBinary, dtcSpatial]) and actBlobAsText.Checked) then begin
- {InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit, TblColumn);
+ InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit, TblColumn);
InplaceEditor.MaxLength := Results.MaxLength(ResultCol);
InplaceEditor.TitleText := Results.ColumnOrgNames[ResultCol];
InplaceEditor.ButtonVisible := True;
- EditLink := InplaceEditor;}
+ EditLink := InplaceEditor;
end else if (TypeCat in [dtcBinary, dtcSpatial]) and AppSettings.ReadBool(asFieldEditorBinary) then begin
- {HexEditor := THexEditorLink.Create(VT, AllowEdit, TblColumn);
+ HexEditor := THexEditorLink.Create(VT, AllowEdit, TblColumn);
HexEditor.MaxLength := Results.MaxLength(ResultCol);
HexEditor.TitleText := Results.ColumnOrgNames[ResultCol];
- EditLink := HexEditor;}
+ EditLink := HexEditor;
end else if (TypeCat = dtcTemporal)
and AppSettings.ReadBool(asFieldEditorDatetime)
and Assigned(TblColumn) // Editor crashes without a column object (on joins), see #1024
@@ -10988,22 +10988,22 @@ begin
NowText := NowText + '.' + StringOfChar('0', MicroSecondsPrecision);
VT.Text[Node, Column] := NowText;
end;
- {DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit, TblColumn);
- EditLink := DateTimeEditor;}
+ DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit, TblColumn);
+ EditLink := DateTimeEditor;
end else if AppSettings.ReadBool(asFieldEditorDatetime)
and HandleUnixTimestampColumn(Sender, Column)
and Assigned(TblColumn) // see above
then begin
- {DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit, TblColumn);
- EditLink := DateTimeEditor;}
+ DateTimeEditor := TDateTimeEditorLink.Create(VT, AllowEdit, TblColumn);
+ EditLink := DateTimeEditor;
end else if (Results.DataType(ResultCol).Index = dbdtSet) and AppSettings.ReadBool(asFieldEditorSet) then begin
- {SetEditor := TSetEditorLink.Create(VT, AllowEdit, TblColumn);
+ SetEditor := TSetEditorLink.Create(VT, AllowEdit, TblColumn);
SetEditor.ValueList := Results.ValueList(ResultCol);
- EditLink := SetEditor;}
+ EditLink := SetEditor;
end else begin
- {InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit, TblColumn);
+ InplaceEditor := TInplaceEditorLink.Create(VT, AllowEdit, TblColumn);
InplaceEditor.ButtonVisible := False;
- EditLink := InplaceEditor;}
+ EditLink := InplaceEditor;
end;
Sender.FocusedNode := Node;
Sender.FocusedColumn := Column;
@@ -14541,13 +14541,13 @@ end;
procedure TMainForm.treeQueryHelpersCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
var
- //InplaceEditor: TInplaceEditorLink;
+ InplaceEditor: TInplaceEditorLink;
VT: TVirtualStringTree;
begin
VT := Sender as TVirtualStringTree;
- //InplaceEditor := TInplaceEditorLink.Create(VT, True, nil);
- //InplaceEditor.ButtonVisible := true;
- //EditLink := InplaceEditor;
+ InplaceEditor := TInplaceEditorLink.Create(VT, True, nil);
+ InplaceEditor.ButtonVisible := true;
+ EditLink := InplaceEditor;
end;
diff --git a/source/routine_editor.pas b/source/routine_editor.pas
index dd274366..a1ed2d5f 100644
--- a/source/routine_editor.pas
+++ b/source/routine_editor.pas
@@ -96,7 +96,7 @@ type
implementation
-uses main, dbstructures, dbstructures.mysql;
+uses main, dbstructures, dbstructures.mysql, grideditlinks;
{$R *.lfm}
@@ -389,14 +389,14 @@ procedure TfrmRoutineEditor.listParametersCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
var
VT: TLazVirtualStringTree;
- //EnumEditor: TEnumEditorLink;
+ EnumEditor: TEnumEditorLink;
Datatype: String;
DBDatatype: TDBDatatype;
begin
VT := Sender as TLazVirtualStringTree;
if Column = 1 then
EditLink := TStringEditLink.Create
- {else if Column = 2 then begin
+ else if Column = 2 then begin
EnumEditor := TEnumEditorLink.Create(VT, True, nil);
EnumEditor.AllowCustomText := True;
EnumEditor.ValueList := TStringList.Create;
@@ -414,7 +414,7 @@ begin
EnumEditor.ValueList.Add('OUT');
EnumEditor.ValueList.Add('INOUT');
EditLink := EnumEditor;
- end;}
+ end;
end;
diff --git a/source/table_editor.pas b/source/table_editor.pas
index 4eaad962..55ff60d5 100644
--- a/source/table_editor.pas
+++ b/source/table_editor.pas
@@ -254,7 +254,7 @@ type
implementation
-uses main;
+uses main, grideditlinks;
{$R *.lfm}
@@ -1730,56 +1730,56 @@ procedure TfrmTableEditor.listColumnsCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
var
VT: TLazVirtualStringTree;
- //EnumEditor: TEnumEditorLink;
- //DefaultEditor: TColumnDefaultEditorLink;
- //DatatypeEditor: TDatatypeEditorLink;
+ EnumEditor: TEnumEditorLink;
+ DefaultEditor: TColumnDefaultEditorLink;
+ DatatypeEditor: TDatatypeEditorLink;
Col: PTableColumn;
- //Edit: TInplaceEditorLink;
+ Edit: TInplaceEditorLink;
begin
// Start cell editor
VT := Sender as TLazVirtualStringTree;
Col := Sender.GetNodeData(Node);
case Column of
ColNumDatatype: begin // Datatype pulldown
- {DatatypeEditor := TDatatypeEditorLink.Create(VT, True, Col^);
- EditLink := DataTypeEditor;}
+ DatatypeEditor := TDatatypeEditorLink.Create(VT, True, Col^);
+ EditLink := DataTypeEditor;
end;
ColNumCollation: begin // Collation pulldown
- {EnumEditor := TEnumEditorLink.Create(VT, True, Col^);
+ EnumEditor := TEnumEditorLink.Create(VT, True, Col^);
EnumEditor.AllowCustomText := True;
EnumEditor.ItemMustExist := True;
EnumEditor.ValueList := TStringList.Create;
EnumEditor.ValueList.Text := DBObject.Connection.CollationList.Text;
EnumEditor.ValueList.Sort;
EnumEditor.ValueList.Insert(0, '');
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end;
ColNumDefault: begin
- {DefaultEditor := TColumnDefaultEditorLink.Create(VT, True, Col^);
+ DefaultEditor := TColumnDefaultEditorLink.Create(VT, True, Col^);
DefaultEditor.DefaultType := Col.DefaultType;
DefaultEditor.DefaultText := Col.DefaultText;
DefaultEditor.OnUpdateType := Col.OnUpdateType;
DefaultEditor.OnUpdateText := Col.OnUpdateText;
- EditLink := DefaultEditor;}
+ EditLink := DefaultEditor;
end;
ColNumVirtuality: begin // Virtuality pulldown
- {EnumEditor := TEnumEditorLink.Create(VT, True, Col^);
+ EnumEditor := TEnumEditorLink.Create(VT, True, Col^);
EnumEditor.ValueList := TStringList.Create;
if DBObject.Connection.Parameters.IsMariaDB then
EnumEditor.ValueList.CommaText := ',VIRTUAL,PERSISTENT'
else
EnumEditor.ValueList.CommaText := ',VIRTUAL,STORED';
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end
else begin
- {Edit := TInplaceEditorLink.Create(VT, True, Col^);
+ Edit := TInplaceEditorLink.Create(VT, True, Col^);
Edit.TitleText := VT.Header.Columns[Column].Text;
Edit.ButtonVisible := True;
- EditLink := Edit;}
+ EditLink := Edit;
end;
end;
end;
@@ -2128,25 +2128,25 @@ procedure TfrmTableEditor.listCheckConstraintsCreateEditor(
out EditLink: IVTEditLink);
var
VT: TLazVirtualStringTree;
- //Edit: TInplaceEditorLink;
- //EnumEditor: TEnumEditorLink;
+ Edit: TInplaceEditorLink;
+ EnumEditor: TEnumEditorLink;
SQLFunc: TSQLFunction;
begin
// Edit check constraint
VT := Sender as TLazVirtualStringTree;
case Column of
0: begin
- {Edit := TInplaceEditorLink.Create(VT, True, nil);
+ Edit := TInplaceEditorLink.Create(VT, True, nil);
Edit.TitleText := VT.Header.Columns[Column].Text;
Edit.ButtonVisible := True;
- EditLink := Edit;}
+ EditLink := Edit;
end;
1: begin
- {EnumEditor := TEnumEditorLink.Create(VT, True, nil);
+ EnumEditor := TEnumEditorLink.Create(VT, True, nil);
for SQLFunc in DBObject.Connection.SQLFunctions do
EnumEditor.ValueList.Add(SQLFunc.Name + SQLFunc.Declaration);
EnumEditor.AllowCustomText := True;
- EditLink := EnumEditor;}
+ EditLink := EnumEditor;
end;
end;
end;
@@ -2242,7 +2242,7 @@ procedure TfrmTableEditor.treeIndexesCreateEditor(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
var
VT: TLazVirtualStringTree;
- //EnumEditor: TEnumEditorLink;
+ EnumEditor: TEnumEditorLink;
Level: Cardinal;
ColNode: PVirtualNode;
Col: PTableColumn;
@@ -2250,7 +2250,7 @@ begin
// Start cell editor
VT := Sender as TLazVirtualStringTree;
Level := (Sender as TLazVirtualStringTree).GetNodeLevel(Node);
- {if (Level = 0) and (Column = 1) then begin
+ if (Level = 0) and (Column = 1) then begin
// Index type pulldown
EnumEditor := TEnumEditorLink.Create(VT, True, nil);
EnumEditor.ValueList := TStringList.Create;
@@ -2277,7 +2277,7 @@ begin
EnumEditor.ValueList := Explode(',', ',ASC,DESC');
EditLink := EnumEditor;
end else
- EditLink := TInplaceEditorLink.Create(VT, True, nil);}
+ EditLink := TInplaceEditorLink.Create(VT, True, nil);
end;
@@ -2807,8 +2807,8 @@ procedure TfrmTableEditor.listForeignKeysCreateEditor(
out EditLink: IVTEditLink);
var
VT: TLazVirtualStringTree;
- //EnumEditor: TEnumEditorLink;
- //SetEditor: TSetEditorLink;
+ EnumEditor: TEnumEditorLink;
+ SetEditor: TSetEditorLink;
DBObjects: TDBObjectList;
Key: TForeignKey;
ColNode: PVirtualNode;
@@ -2819,7 +2819,7 @@ var
begin
// Init grid editor in foreign key list
VT := Sender as TLazVirtualStringTree;
- {case Column of
+ case Column of
0: EditLink := TInplaceEditorLink.Create(VT, True, nil);
1: begin
SetEditor := TSetEditorLink.Create(VT, True, nil);
@@ -2858,7 +2858,7 @@ begin
EnumEditor.ValueList := Explode(',', DBObject.Connection.GetSQLSpecifity(spForeignKeyEventAction));
EditLink := EnumEditor;
end;
- end;}
+ end;
end;