mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
1684 lines
53 KiB
ObjectPascal
1684 lines
53 KiB
ObjectPascal
unit grideditlinks;
|
||
|
||
// The editor links, instanciated by VirtualTree.CreateEditor
|
||
|
||
interface
|
||
|
||
uses
|
||
Windows, Forms, Graphics, Messages, VirtualTrees, ComCtrls, SysUtils, Classes,
|
||
StdCtrls, ExtCtrls, CheckLst, Controls, Types, Dialogs, Menus, Mask, DateUtils, Math,
|
||
dbconnection, mysql_structures, helpers, texteditor, bineditor, gnugettext,
|
||
StrUtils, System.UITypes;
|
||
|
||
type
|
||
// Radio buttons and checkboxes which do not pass <Enter> key to their parent control
|
||
// so a OnKeyDown event using <Enter> has the chance to end editing.
|
||
TAllKeysRadioButton = class(TRadioButton)
|
||
procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
|
||
end;
|
||
TAllKeysCheckBox = class(TCheckBox)
|
||
procedure WMGetDlgCode(var Msg: TMessage); message WM_GETDLGCODE;
|
||
end;
|
||
|
||
TBaseGridEditorLink = class(TInterfacedObject, IVTEditLink)
|
||
private
|
||
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;
|
||
FBeginEditTime: Cardinal;
|
||
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
|
||
property TableColumn: TTableColumn read FTableColumn write FTableColumn; // The table column of the cell being edited. Mostly used in data grids.
|
||
constructor Create; overload; // The original constructor, not used any more, throws an exception if you do
|
||
constructor Create(Tree: TVirtualStringTree); overload; virtual; // The right constructor, we need the Tree reference
|
||
destructor Destroy; override;
|
||
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;
|
||
constructor Create(Tree: TVirtualStringTree); 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: Integer; 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); 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: TComboBox;
|
||
procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||
public
|
||
ValueList, DisplayList: TStringList;
|
||
AllowCustomText: Boolean;
|
||
constructor Create(Tree: TVirtualStringTree); 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;
|
||
public
|
||
ValueList: TStringList;
|
||
constructor Create(Tree: TVirtualStringTree); 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;
|
||
FTextEditor: TfrmTextEditor;
|
||
FMaxLength: Integer;
|
||
procedure DoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||
procedure ButtonClick(Sender: TObject);
|
||
public
|
||
ButtonVisible: Boolean;
|
||
constructor Create(Tree: TVirtualStringTree); 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;
|
||
property MaxLength: Integer read FMaxLength write FMaxLength;
|
||
end;
|
||
|
||
TColumnDefaultEditorLink = class(TBaseGridEditorLink)
|
||
private
|
||
FPanel: TPanel;
|
||
FRadioNothing, FRadioText, FRadioNULL, FRadioCurTS, FRadioAutoInc: TAllKeysRadioButton;
|
||
FCheckCurTS: TAllKeysCheckbox;
|
||
FCustomEdit: TButtonedEdit;
|
||
FCustomDropDown: TPopupMenu;
|
||
FBtnOK, FBtnCancel: TButton;
|
||
procedure RadioClick(Sender: TObject);
|
||
procedure CustomEditChange(Sender: TObject);
|
||
procedure CustomDropDownClick(Sender: TObject);
|
||
public
|
||
DefaultType: TColumnDefaultType;
|
||
DefaultText: String;
|
||
constructor Create(Tree: TVirtualStringTree); 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); 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;
|
||
|
||
|
||
|
||
implementation
|
||
|
||
|
||
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;
|
||
|
||
|
||
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);
|
||
begin
|
||
inherited Create;
|
||
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;
|
||
SendMessage(FParentForm.Handle, WM_SETREDRAW, 0, 0);
|
||
FModified := False;
|
||
end;
|
||
|
||
destructor TBaseGridEditorLink.Destroy;
|
||
var
|
||
NewColumn, FirstCol, LastCol: TColumnIndex;
|
||
NewNode: PVirtualNode;
|
||
DoPrev: Boolean;
|
||
begin
|
||
inherited;
|
||
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.GetPreviousColumn(NewColumn);
|
||
end else begin
|
||
if NewColumn = LastCol then begin
|
||
NewColumn := FirstCol;
|
||
NewNode := FTree.GetNextVisible(NewNode);
|
||
end else
|
||
NewColumn := FTree.Header.Columns.GetNextColumn(NewColumn);
|
||
end;
|
||
if not Assigned(NewNode) then
|
||
Break;
|
||
FTree.FocusedNode := NewNode;
|
||
FTree.FocusedColumn := NewColumn;
|
||
if not FTree.EditNode(FTree.FocusedNode, FTree.FocusedColumn) then
|
||
FTree.SetFocus;
|
||
Break;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TBaseGridEditorLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
|
||
var
|
||
FCellTextBounds: TRect;
|
||
begin
|
||
Result := not FStopping;
|
||
if not Result then
|
||
Exit;
|
||
FNode := Node;
|
||
FColumn := Column;
|
||
FCellFont := TFont.Create;
|
||
FTree.GetTextInfo(FNode, FColumn, FCellFont, FCellTextBounds, FCellText);
|
||
// 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;
|
||
end;
|
||
// Adjust editor position and allow repainting mainform
|
||
SetBounds(FCellTextBounds);
|
||
SendMessage(FParentForm.Handle, WM_SETREDRAW, 1, 0);
|
||
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 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
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TBaseGridEditorLink.ProcessMessage(var Message: TMessage);
|
||
begin
|
||
if Assigned(FMainControl) 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);
|
||
begin
|
||
inherited Create(Tree);
|
||
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.SetMaxLength(MaxLength);
|
||
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);
|
||
begin
|
||
inherited Create(Tree);
|
||
|
||
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;
|
||
case FTableColumn.DataType.Index of
|
||
dtDate:
|
||
FMaskEdit.EditMask := '0000-00-00;1; ';
|
||
dtDatetime, dtTimestamp, dtInt, dtBigint: 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;
|
||
dtTime: 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;
|
||
dtYear:
|
||
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: Integer; 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
|
||
dtYear: begin
|
||
i := MakeInt(FMaskEdit.Text);
|
||
i := i + Offset;
|
||
text := IntToStr(i);
|
||
end;
|
||
|
||
dtDate: 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;
|
||
|
||
dtDateTime, dtTimestamp, dtInt, dtBigint: 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;
|
||
|
||
dtTime: 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;
|
||
begin
|
||
Result := MakeInt(FTableColumn.LengthSet);
|
||
// No microseconds for UNIX timestamp columns
|
||
if FTableColumn.DataType.Index in [dtInt, dtBigint] then
|
||
Result := 0;
|
||
end;
|
||
|
||
|
||
|
||
{ Enum editor }
|
||
|
||
constructor TEnumEditorLink.Create(Tree: TVirtualStringTree);
|
||
begin
|
||
inherited Create(Tree);
|
||
AllowCustomText := False;
|
||
FCombo := TComboBox.Create(FParentForm);
|
||
FCombo.Hide;
|
||
FCombo.Parent := FParentForm;
|
||
FCombo.OnKeyDown := DoKeyDown;
|
||
FCombo.OnExit := DoEndEdit;
|
||
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 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;
|
||
var
|
||
i: Integer;
|
||
Items: TStringList;
|
||
begin
|
||
Result := inherited PrepareEdit(Tree, Node, Column);
|
||
if Result then begin
|
||
if DisplayList.Count = ValueList.Count then
|
||
Items := DisplayList
|
||
else
|
||
Items := ValueList;
|
||
for i:=0 to Items.Count - 1 do
|
||
FCombo.Items.Add(Items[i]);
|
||
if AllowCustomText 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;
|
||
FCombo.ItemIndex := ValueList.IndexOf(FCellText);
|
||
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;
|
||
|
||
|
||
|
||
{ SET editor }
|
||
|
||
constructor TSetEditorLink.Create(Tree: TVirtualStringTree);
|
||
begin
|
||
inherited Create(Tree);
|
||
ValueList := TStringList.Create;
|
||
|
||
FPanel := TPanel.Create(FParentForm);
|
||
FPanel.Hide;
|
||
FPanel.Parent := FParentForm;
|
||
FPanel.ParentBackground := False;
|
||
FPanel.Height := 150;
|
||
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 := DoEndEdit;
|
||
|
||
FBtnCancel := TButton.Create(FPanel);
|
||
FBtnCancel.Parent := FPanel;
|
||
FBtnCancel.Caption := _('Cancel');
|
||
FBtnCancel.OnClick := DoCancelEdit;
|
||
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;
|
||
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 := 24;
|
||
FBtnOk.Top := FPanel.Height - 2*margin - FBtnOk.Height;
|
||
|
||
FBtnCancel.Width := FBtnOk.Width;
|
||
FBtnCancel.Left := 2*margin + FBtnOk.Width;
|
||
FBtnCancel.Height := FBtnOk.Height;
|
||
FBtnCancel.Top := FBtnOk.Top;
|
||
|
||
FCheckList.Top := margin;
|
||
FCheckList.Left := margin;
|
||
FCheckList.Width := FPanel.Width - 2*margin;
|
||
FCheckList.Height := FBtnOk.Top - margin - FCheckList.Top;
|
||
end;
|
||
|
||
|
||
|
||
{ TInplaceEditorLink }
|
||
|
||
constructor TInplaceEditorLink.Create(Tree: TVirtualStringTree);
|
||
begin
|
||
inherited Create(Tree);
|
||
ButtonVisible := false;
|
||
FTextEditor := nil;
|
||
|
||
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 := '<27>';
|
||
FButton.Hint := _('Edit text in popup editor ...');
|
||
FButton.ShowHint := True;
|
||
FButton.OnClick := ButtonClick;
|
||
end;
|
||
|
||
destructor TInplaceEditorLink.Destroy;
|
||
begin
|
||
if Assigned(FTextEditor) then
|
||
FTextEditor.Release;
|
||
if not (csDestroying in FPanel.ComponentState) then
|
||
FPanel.Free;
|
||
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*100) or (ScanLineBreaks(FEdit.Text) <> lbsNone) then
|
||
ButtonClick(FTree)
|
||
else begin
|
||
FPanel.Show;
|
||
FEdit.SetFocus;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TInplaceEditorLink.CancelEdit: Boolean;
|
||
begin
|
||
Result := inherited CancelEdit;
|
||
if Result then begin
|
||
if Assigned(FTextEditor) then
|
||
FTextEditor.Close;
|
||
end;
|
||
end;
|
||
|
||
function TInplaceEditorLink.EndEdit: Boolean;
|
||
var
|
||
NewText: String;
|
||
begin
|
||
Result := not FStopping;
|
||
if FStopping then Exit;
|
||
if Assigned(FTextEditor) then begin
|
||
NewText := FTextEditor.GetText;
|
||
FModified := FTextEditor.Modified;
|
||
FTextEditor.Close;
|
||
end else begin
|
||
NewText := FEdit.Text;
|
||
FModified := FEdit.Modified;
|
||
end;
|
||
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);
|
||
begin
|
||
if not FButton.Visible then Exit; // Button was invisible, but hotkey was pressed
|
||
FTextEditor := TfrmTextEditor.Create(FTree);
|
||
FTextEditor.SetFont(FEdit.Font);
|
||
FTextEditor.SetText(FEdit.Text);
|
||
FTextEditor.Modified := FEdit.Modified;
|
||
FTextEditor.SetMaxLength(Self.FMaxLength);
|
||
FTextEditor.ShowModal;
|
||
end;
|
||
|
||
function TInplaceEditorLink.PrepareEdit(Tree: TBaseVirtualTree;
|
||
Node: PVirtualNode; Column: TColumnIndex): Boolean;
|
||
begin
|
||
Result := inherited PrepareEdit(Tree, Node, Column);
|
||
if not Result then
|
||
Exit;
|
||
|
||
FEdit.Font.Assign(FCellFont);
|
||
FEdit.Font.Color := 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, 20);
|
||
FEdit.BoundsRect := R;
|
||
|
||
FButton.BoundsRect := Rect(FEdit.BoundsRect.Right, 0, FPanel.Width, FPanel.Height);
|
||
end;
|
||
end;
|
||
|
||
|
||
|
||
{ Column default editor }
|
||
|
||
constructor TColumnDefaultEditorLink.Create(Tree: TVirtualStringTree);
|
||
const
|
||
m = 3;
|
||
begin
|
||
inherited Create(Tree);
|
||
|
||
FPanel := TPanel.Create(FParentForm);
|
||
FPanel.Hide;
|
||
FPanel.Parent := FParentForm;
|
||
FPanel.OnExit := DoEndEdit;
|
||
FPanel.Width := 200;
|
||
FPanel.ParentBackground := False;
|
||
FPanel.Color := clWindow;
|
||
FPanel.BevelKind := bkFlat;
|
||
FPanel.BevelOuter := bvNone;
|
||
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')+':';
|
||
|
||
FCustomDropDown := TPopupMenu.Create(FPanel);
|
||
|
||
FCustomEdit := TButtonedEdit.Create(FPanel);
|
||
FCustomEdit.Parent := FPanel;
|
||
FCustomEdit.Top := FRadioText.Top + FRadioText.Height + m;
|
||
FCustomEdit.Left := 2*m;
|
||
FCustomEdit.Width := FCustomEdit.Parent.Width - FCustomEdit.Left - m;
|
||
FCustomEdit.OnChange := CustomEditChange;
|
||
FCustomEdit.Images := Tree.Images;
|
||
FCustomEdit.RightButton.ImageIndex := 75; // Drop down arrow
|
||
FCustomEdit.RightButton.DropDownMenu := FCustomDropDown;
|
||
|
||
FRadioNull := TAllKeysRadioButton.Create(FPanel);
|
||
FRadioNull.Parent := FPanel;
|
||
FRadioNull.Top := FCustomEdit.Top + FCustomEdit.Height + 2*m;
|
||
FRadioNull.Left := m;
|
||
FRadioNull.Width := FRadioNull.Parent.Width - 2 * FRadioNull.Left;
|
||
FRadioNull.OnClick := RadioClick;
|
||
FRadioNull.OnKeyDown := DoKeyDown;
|
||
FRadioNull.Caption := 'NULL';
|
||
|
||
FRadioCurTS := TAllKeysRadioButton.Create(FPanel);
|
||
FRadioCurTS.Parent := FPanel;
|
||
FRadioCurTS.Top := FRadioNull.Top + FRadioNull.Height + m;
|
||
FRadioCurTS.Left := m;
|
||
FRadioCurTS.Width := FRadioCurTS.Parent.Width - 2 * FRadioCurTS.Left;
|
||
FRadioCurTS.OnClick := RadioClick;
|
||
FRadioCurTS.OnKeyDown := DoKeyDown;
|
||
FRadioCurTS.Caption := 'CURRENT_TIMESTAMP';
|
||
|
||
FCheckCurTS := TAllKeysCheckbox.Create(FPanel);
|
||
FCheckCurTS.Parent := FPanel;
|
||
FCheckCurTS.Top := FRadioCurTS.Top + FRadioCurTS.Height + m;
|
||
FCheckCurTS.Left := m;
|
||
FCheckCurTS.Width := FCheckCurTS.Parent.Width - 2 * FCheckCurTS.Left;
|
||
FCheckCurTS.OnClick := RadioClick;
|
||
FCheckCurTS.OnKeyDown := DoKeyDown;
|
||
FCheckCurTS.Caption := 'ON UPDATE CURRENT_TIMESTAMP';
|
||
|
||
FRadioAutoInc := TAllKeysRadioButton.Create(FPanel);
|
||
FRadioAutoInc.Parent := FPanel;
|
||
FRadioAutoInc.Top := FCheckCurTS.Top + FCheckCurTS.Height + m;
|
||
FRadioAutoInc.Left := m;
|
||
FRadioAutoInc.Width := FRadioAutoInc.Parent.Width - 2 * FRadioAutoInc.Left;
|
||
FRadioAutoInc.OnClick := RadioClick;
|
||
FRadioAutoInc.OnKeyDown := DoKeyDown;
|
||
FRadioAutoInc.Caption := 'AUTO_INCREMENT';
|
||
|
||
FBtnOk := TButton.Create(FPanel);
|
||
FBtnOk.Parent := FPanel;
|
||
FBtnOk.Width := 60;
|
||
FBtnOk.Top := FRadioAutoInc.Top + FRadioAutoInc.Height + m;
|
||
FBtnOk.Left := FPanel.Width - 2*m - 2*FBtnOk.Width;
|
||
FBtnOk.OnClick := DoEndEdit;
|
||
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 := DoCancelEdit;
|
||
FBtnCancel.Cancel := True;
|
||
FBtnCancel.Caption := _('Cancel');
|
||
|
||
FPanel.Height := FBtnOk.Top + FBtnOk.Height + m;
|
||
FRadioNothing.Anchors := [akLeft, akTop, akRight];
|
||
FRadioText.Anchors := [akLeft, akTop, akRight];
|
||
FCustomEdit.Anchors := [akLeft, akTop, akRight, akBottom];
|
||
FRadioNull.Anchors := [akLeft, akBottom, akRight];
|
||
FRadioCurTS.Anchors := [akLeft, akBottom, akRight];
|
||
FCheckCurTS.Anchors := [akLeft, akBottom, akRight];
|
||
FRadioAutoInc.Anchors := [akLeft, akBottom, akRight];
|
||
FBtnOk.Anchors := [akBottom, akRight];
|
||
FBtnCancel.Anchors := FBtnOk.Anchors;
|
||
FPanel.Width := GetParentForm(FPanel).Canvas.TextWidth(FCheckCurTS.Caption) + 2*FCheckCurTS.Left + 24;
|
||
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 in [cdtText, cdtTextUpdateTS];
|
||
FRadioNull.Checked := DefaultType in [cdtNull, cdtNullUpdateTS];
|
||
FRadioCurTS.Checked := DefaultType in [cdtCurTS, cdtCurTSUpdateTS];
|
||
FCheckCurTS.Checked := DefaultType in [cdtTextUpdateTS, cdtNullUpdateTS, cdtCurTSUpdateTS];
|
||
FRadioAutoInc.Checked := DefaultType = cdtAutoInc;
|
||
|
||
if FRadioText.Checked then
|
||
FCustomEdit.Text := DefaultText;
|
||
|
||
// Disable non working default options per data type
|
||
// But leave checked option enabled, regardless of if that is a valid default option or not
|
||
FRadioCurTS.Enabled := FRadioCurTS.Checked or (FTableColumn.DataType.Index = dtTimestamp);
|
||
FCheckCurTS.Enabled := FCheckCurTS.Checked or FRadioCurTS.Enabled;
|
||
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 [dtEnum, dtSet] then begin
|
||
FCustomEdit.RightButton.Visible := True;
|
||
ValueList := FTableColumn.ValueList;
|
||
SelectedValues := Explode(',', FCustomEdit.Text);
|
||
|
||
for i:=0 to ValueList.Count-1 do begin
|
||
Item := TMenuItem.Create(FCustomDropDown);
|
||
Item.Caption := ValueList[i];
|
||
Item.RadioItem := FTableColumn.DataType.Index = dtEnum;
|
||
Item.Checked := SelectedValues.IndexOf(Item.Caption) > -1;
|
||
Item.OnClick := CustomDropDownClick;
|
||
FCustomDropDown.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;
|
||
|
||
// 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 FRadioText.SetFocus
|
||
else if FRadioNull.Checked then FRadioNull.SetFocus
|
||
else if FRadioCurTS.Checked then FRadioCurTS.SetFocus
|
||
else if FRadioAutoInc.Checked then FRadioAutoInc.SetFocus;
|
||
end;
|
||
end;
|
||
|
||
|
||
function TColumnDefaultEditorLink.EndEdit: Boolean; stdcall;
|
||
var
|
||
newText: String;
|
||
newDefaultType: TColumnDefaultType;
|
||
begin
|
||
Result := not FStopping;
|
||
if Result then begin
|
||
FStopping := True;
|
||
if FRadioNothing.Checked then
|
||
newDefaultType := cdtNothing
|
||
else if FRadioText.Checked and FCheckCurTS.Checked then
|
||
newDefaultType := cdtTextUpdateTS
|
||
else if FRadioText.Checked then
|
||
newDefaultType := cdtText
|
||
else if FRadioNull.Checked and FCheckCurTS.Checked then
|
||
newDefaultType := cdtNullUpdateTS
|
||
else if FRadioNull.Checked then
|
||
newDefaultType := cdtNull
|
||
else if FRadioCurTS.Checked and FCheckCurTS.Checked then
|
||
newDefaultType := cdtCurTSUpdateTS
|
||
else if FRadioCurTS.Checked then
|
||
newDefaultType := cdtCurTS
|
||
else if FRadioAutoInc.Checked then
|
||
newDefaultType := cdtAutoInc
|
||
else
|
||
newDefaultType := cdtText;
|
||
|
||
case newDefaultType of
|
||
cdtNothing: newText := '';
|
||
cdtText, cdtTextUpdateTS: newText := FCustomEdit.Text;
|
||
cdtNull, cdtNullUpdateTS: newText := 'NULL';
|
||
cdtCurTS, cdtCurTSUpdateTS: newText := 'CURRENT_TIMESTAMP';
|
||
cdtAutoInc: newText := 'AUTO_INCREMENT';
|
||
end;
|
||
newText := IntToStr(Integer(newDefaultType)) + newText;
|
||
if newtext <> IntToStr(Integer(DefaultType)) + DefaultText then
|
||
FTree.Text[FNode, FColumn] := newtext;
|
||
if FTree.CanFocus then
|
||
FTree.SetFocus;
|
||
end;
|
||
end;
|
||
|
||
|
||
procedure TColumnDefaultEditorLink.RadioClick(Sender: TObject);
|
||
begin
|
||
if not FRadioText.Checked then
|
||
FCustomEdit.Color := clBtnFace
|
||
else begin
|
||
FCustomEdit.Color := clWindow;
|
||
if FCustomEdit.CanFocus then
|
||
FCustomEdit.SetFocus;
|
||
end;
|
||
FCheckCurTS.Enabled := not FRadioNothing.Checked;
|
||
FModified := True;
|
||
end;
|
||
|
||
|
||
procedure TColumnDefaultEditorLink.CustomEditChange(Sender: TObject);
|
||
begin
|
||
FRadioText.Checked := True;
|
||
FModified := True;
|
||
end;
|
||
|
||
|
||
procedure TColumnDefaultEditorLink.CustomDropDownClick(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 IsEmpty(NewValue) then
|
||
Delete(NewValue, Length(NewValue), 1);
|
||
FCustomEdit.Text := NewValue;
|
||
FModified := True;
|
||
end;
|
||
|
||
|
||
|
||
|
||
{ Datatype selector }
|
||
constructor TDataTypeEditorLink.Create(Tree: TVirtualStringTree);
|
||
begin
|
||
inherited Create(Tree);
|
||
|
||
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.BorderStyle := bsNone;
|
||
FMemoHelp.BevelKind := bkFlat;
|
||
FMemoHelp.BevelInner := bvNone;
|
||
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);
|
||
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).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 + 2;
|
||
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.
|