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;