Files
HeidiSQL/components/virtualtreeview/Source/VirtualTrees.EditLink.pas

894 lines
30 KiB
ObjectPascal

unit VirtualTrees.EditLink;
// Base class for inplace node editors implementing IVTEditLink interface
// and default node editor.
interface
uses
WinApi.Messages,
System.Types,
System.Classes,
Vcl.Controls,
Vcl.StdCtrls,
VirtualTrees,
VirtualTrees.Types;
type
//Edit support Classes.
TStringEditLink = class;
TVTEdit = class(TCustomEdit)
private
procedure CMAutoAdjust(var Message : TMessage); message CM_AUTOADJUST;
procedure CMExit(var Message : TMessage); message CM_EXIT;
procedure CMRelease(var Message : TMessage); message CM_RELEASE;
procedure CNCommand(var Message : TWMCommand); message CN_COMMAND;
procedure WMChar(var Message : TWMChar); message WM_CHAR;
procedure WMDestroy(var Message : TWMDestroy); message WM_DESTROY;
procedure WMGetDlgCode(var Message : TWMGetDlgCode); message WM_GETDLGCODE;
procedure WMKeyDown(var Message : TWMKeyDown); message WM_KEYDOWN;
protected
FRefLink : IVTEditLink;
FLink : TStringEditLink;
procedure AutoAdjustSize; virtual;
function CalcMinHeight : Integer; virtual;
procedure CreateParams(var Params : TCreateParams); override;
function GetTextSize : TSize; virtual;
procedure KeyPress(var Key : Char); override;
public
constructor Create(Link : TStringEditLink); reintroduce;
procedure ClearLink;
procedure ClearRefLink;
procedure Release; virtual;
property AutoSelect;
property AutoSize;
property BorderStyle;
property CharCase;
property HideSelection;
property MaxLength;
property OEMConvert;
property PasswordChar;
end;
TBaseEditLink = class;
TEditLinkEditEvent = procedure (Sender: TBaseEditLink; var Result: Boolean) of object;
TEditLinkPrepareEditEvent = procedure (Sender: TBaseEditLink; var Edit: TControl; var Result: Boolean) of object;
// Most abstract base class for implementing IVTEditLink.
// Knows almost nothing about associated Edit control and doesn't perform any
// actions on it. Contains some properties that are not used directly but could
// be useful in descendant classes. Follows general extension approach - all
// IVTEditLink methods are virtual and most of them call DoXXX virtual methods
// which in turn call event handlers so these extension options possible:
// - overriding main API methods to run additional actions before, after or
// instead of basic class code.
// (+) Lesser modification of existing classes
// (-) Event handlers are already launched after calling parent method
// (-) It's critical to check Result of parent method and exit immediately
// on False - this value means no action is done.
// (-) Returning Result is necessary
// - overriding DoXXX methods to run additional actions inside basic class code
// (+) No need in returning - lesser boilerplate code
// (-) Should call inherited to launch event handlers (OK if not using them)
// - assign event handlers in end-user code
// (+) Access to external classes with data to copy to EditLink editor.
// (-) Lesser encapsulation
TBaseEditLink = class(TInterfacedObject, IVTEditLink)
strict protected
FEdit: TControl; // One of the property editor classes.
FTree : TCustomVirtualStringTree; //A back reference to the tree calling.
FNode : PVirtualNode; //The node to be edited.
FColumn : TColumnIndex; //The column of the node.
FStopping : Boolean; //Set to True when the edit link requests stopping the edit action.
FAlignment : TAlignment;
FBiDiMode: TBiDiMode;
// custom event handlers
FOnPrepareEdit: TEditLinkPrepareEditEvent;
FOnBeginEdit,
FOnEndEdit,
FOnCancelEdit: TEditLinkEditEvent;
procedure SetEdit(const Value : TControl); //Setter for the FEdit member;
public
// IVTEditLink API
function BeginEdit : Boolean; virtual; stdcall;
function CancelEdit : Boolean; virtual; stdcall;
function EndEdit : Boolean; virtual; stdcall;
function GetBounds : TRect; virtual; stdcall; abstract;
function PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean; virtual; stdcall;
procedure ProcessMessage(var Message : TMessage); virtual; stdcall; abstract;
procedure SetBounds(R : TRect); virtual; stdcall; abstract;
// Methods to plug custom actions into main ones. In base class only call event handlers.
// Descendants may modify Result to cancel further flow.
procedure DoBeginEdit(var Result: Boolean); virtual;
procedure DoCancelEdit(var Result: Boolean); virtual;
procedure DoEndEdit(var Result: Boolean); virtual;
procedure DoPrepareEdit(var Result: Boolean); virtual;
property Alignment : TAlignment read FAlignment;
property BiDiMode: TBiDiMode read FBiDiMode;
property Column : TColumnIndex read FColumn; //[IPK] Make Column(Index) accessible
property Node : PVirtualNode read FNode; //[IPK] Make FNode accessible
property Tree : TCustomVirtualStringTree read FTree;
property Stopping : Boolean read FStopping;
property OnBeginEdit: TEditLinkEditEvent read FOnBeginEdit write FOnBeginEdit;
property OnCancelEdit: TEditLinkEditEvent read FOnCancelEdit write FOnCancelEdit;
property OnEndEdit: TEditLinkEditEvent read FOnEndEdit write FOnEndEdit;
property OnPrepareEdit: TEditLinkPrepareEditEvent read FOnPrepareEdit write FOnPrepareEdit;
end;
// Edit link that has TWinControl-based Edit. Performs visibility and focus actions,
// transfers window messages to Edit control.
TWinControlEditLink = class(TBaseEditLink)
protected
function GetEdit: TWinControl; //Getter for the FEdit member;
procedure SetEdit(const Value : TWinControl); //Setter for the FEdit member;
public
destructor Destroy; override;
function BeginEdit : Boolean; override; stdcall;
function CancelEdit : Boolean; override; stdcall;
function EndEdit : Boolean; override; stdcall;
function GetBounds : TRect; override; stdcall;
procedure ProcessMessage(var Message : TMessage); override; stdcall;
property Edit : TWinControl read GetEdit write SetEdit;
end;
// Edit link that implements default node text editor.
TStringEditLink = class(TWinControlEditLink)
protected
FTextBounds : TRect; //Smallest rectangle around the text.
function GetEdit: TVTEdit; //Getter for the FEdit member;
procedure SetEdit(const Value : TVTEdit); //Setter for the FEdit member;
public
constructor Create;
function BeginEdit : Boolean; override; stdcall;
function CancelEdit : Boolean; override; stdcall;
function EndEdit : Boolean; override; stdcall;
function PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean; override; stdcall;
procedure SetBounds(R : TRect); override; stdcall;
property Edit : TVTEdit read GetEdit write SetEdit;
end;
implementation
uses
WinApi.Windows,
System.SysUtils,
System.Math,
Vcl.Graphics,
Vcl.Forms;
type
TCustomVirtualStringTreeCracker = class(TCustomVirtualStringTree);
//----------------- TVTEdit --------------------------------------------------------------------------------------------
//Implementation of a generic node caption editor.
constructor TVTEdit.Create(Link : TStringEditLink);
begin
inherited Create(nil);
if not Assigned(Link) then
raise EArgumentException.Create('Parameter Link must not be nil.');
ShowHint := False;
ParentShowHint := False;
//This assignment increases the reference count for the interface.
FRefLink := Link;
//This reference is used to access the link.
FLink := Link;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.ClearLink;
begin
FLink := nil
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.ClearRefLink;
begin
FRefLink := nil
end;
//----------------------------------------------------------------------------------------------------------------------
function TVTEdit.CalcMinHeight : Integer;
var
textHeight : Integer;
begin
//Get the actual text height.
textHeight := GetTextSize.cy;
//The minimal height is the actual text height in pixels plus the the non client area.
Result := textHeight + (Height - ClientHeight);
//Also, proportionally to the text size, additional pixel(s) needs to be added for the caret.
Result := Result + Trunc(textHeight * 0.05);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.CMAutoAdjust(var Message : TMessage);
begin
AutoAdjustSize;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.CMExit(var Message : TMessage);
begin
if Assigned(FLink) and not FLink.Stopping then
with TCustomVirtualStringTreeCracker(FLink.Tree) do
begin
if (toAutoAcceptEditChange in TreeOptions.StringOptions) then
DoEndEdit
else
DoCancelEdit;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.CMRelease(var Message : TMessage);
begin
Free;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.CNCommand(var Message : TWMCommand);
begin
if Assigned(FLink) and Assigned(FLink.Tree) and (Message.NotifyCode = EN_UPDATE) and not (vsMultiline in FLink.Node.States) then
//Instead directly calling AutoAdjustSize it is necessary on Win9x/Me to decouple this notification message
//and eventual resizing. Hence we use a message to accomplish that.
AutoAdjustSize()
else
inherited;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.WMChar(var Message : TWMChar);
begin
if not (Message.CharCode in [VK_ESCAPE, VK_TAB]) then
inherited;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.WMDestroy(var Message : TWMDestroy);
begin
//If editing stopped by other means than accept or cancel then we have to do default processing for
//pending changes.
if Assigned(FLink) and not FLink.Stopping and not (csRecreating in Self.ControlState) then
begin
with TCustomVirtualStringTreeCracker(FLink.Tree) do
begin
if (toAutoAcceptEditChange in TreeOptions.StringOptions) and Modified then
Text[FLink.Node, FLink.Column] := FLink.Edit.Text;
end;
FLink := nil;
FRefLink := nil;
end;
inherited;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.WMGetDlgCode(var Message : TWMGetDlgCode);
begin
inherited;
Message.Result := Message.Result or DLGC_WANTALLKEYS or DLGC_WANTTAB or DLGC_WANTARROWS;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.WMKeyDown(var Message : TWMKeyDown);
//Handles some control keys.
var
Shift : TShiftState;
EndEdit : Boolean;
Tree : TBaseVirtualTree;
NextNode : PVirtualNode;
ColumnCandidate : Integer;
EditOptions : TVTEditOptions;
Column : TVirtualTreeColumn;
begin
Tree := FLink.Tree;
case Message.CharCode of
VK_ESCAPE :
begin
TCustomVirtualStringTreeCracker(Tree).DoCancelEdit;
end;
VK_RETURN :
begin
EndEdit := not (vsMultiline in FLink.Node.States);
if not EndEdit then
begin
//If a multiline node is being edited the finish editing only if Ctrl+Enter was pressed,
//otherwise allow to insert line breaks into the text.
Shift := KeyDataToShiftState(Message.KeyData);
EndEdit := ssCtrl in Shift;
end;
if EndEdit then
begin
Tree := FLink.Tree;
FLink.Tree.InvalidateNode(FLink.Node);
NextNode := Tree.GetNextVisible(FLink.Node, True);
TCustomVirtualStringTreeCracker(FLink.Tree).DoEndEdit;
//get edit options for column as priority. If column has toDefaultEdit
//use global edit options for tree
EditOptions := TCustomVirtualStringTreeCracker(Tree).TreeOptions.EditOptions; //default
ColumnCandidate := - 1;
if Tree.Header.Columns.Count > 0 then //are there any columns?
begin
Column := Tree.Header.Columns[Tree.FocusedColumn];
if Column.EditOptions <> toDefaultEdit then
EditOptions := Column.EditOptions;
//next column candidate for toVerticalEdit and toHorizontalEdit
if Column.EditNextColumn <> - 1 then
ColumnCandidate := Column.EditNextColumn;
end;
case EditOptions of
toDefaultEdit :
TCustomVirtualStringTreeCracker(Tree).TrySetFocus;
toVerticalEdit :
if NextNode <> nil then
begin
Tree.FocusedNode := NextNode;
//for toVerticalEdit ColumnCandidate is also proper,
//select ColumnCandidate column in row below
if ColumnCandidate <> - 1 then
begin
Tree.FocusedColumn := ColumnCandidate;
TCustomVirtualStringTreeCracker(Tree).EditColumn := ColumnCandidate;
end;
if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then
TCustomVirtualStringTreeCracker(Tree).DoEdit;
end;
toHorizontalEdit :
begin
if ColumnCandidate = - 1 then
begin
//for toHorizontalEdit if property EditNextColumn is not used
//try to use just next column
ColumnCandidate := Tree.FocusedColumn + 1;
while (ColumnCandidate < Tree.Header.Columns.Count) and not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) do
Inc(ColumnCandidate);
end
else if not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) then
ColumnCandidate := Tree.Header.Columns.Count; //omit "focus/edit column" (see below)
if ColumnCandidate < Tree.Header.Columns.Count then
begin
Tree.FocusedColumn := ColumnCandidate;
TCustomVirtualStringTreeCracker(Tree).EditColumn := ColumnCandidate;
TCustomVirtualStringTreeCracker(Tree).DoEdit;
end;
end;
end;
end;
end;
VK_UP :
begin
if not (vsMultiline in FLink.Node.States) then
Message.CharCode := VK_LEFT;
inherited;
end;
VK_DOWN :
begin
if not (vsMultiline in FLink.Node.States) then
Message.CharCode := VK_RIGHT;
inherited;
end;
VK_TAB :
begin
if Tree.IsEditing then
begin
Tree.InvalidateNode(FLink.Node);
if ssShift in KeyDataToShiftState(Message.KeyData) then
NextNode := Tree.GetPreviousVisible(FLink.Node, True)//Shift+Tab goes to previous mode
else
NextNode := Tree.GetNextVisible(FLink.Node, True);
Tree.EndEditNode;
//check NextNode, otherwise we got AV
if NextNode <> nil then
begin
//Continue editing next node
Tree.ClearSelection();
Tree.Selected[NextNode] := True;
if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then
TCustomVirtualStringTreeCracker(Tree).DoEdit;
end;
end;
end;
Ord('A') :
begin
if Tree.IsEditing and ([ssCtrl] = KeyboardStateToShiftState) then
begin
Self.SelectAll();
Message.CharCode := 0;
end;
end;
else
inherited;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.AutoAdjustSize;
//Changes the size of the edit to accomodate as much as possible of its text within its container window.
//NewChar describes the next character which will be added to the edit's text.
var
Size : TSize;
begin
if not (vsMultiline in FLink.Node.States) and not (toGridExtensions in TCustomVirtualStringTreeCracker(FLink.Tree).TreeOptions.MiscOptions { see issue #252 } ) then
begin
//avoid flicker
SendMessage(Handle, WM_SETREDRAW, 0, 0);
try
Size := GetTextSize;
Inc(Size.cx, 2 * TCustomVirtualStringTreeCracker(FLink.Tree).TextMargin);
//Repaint associated node if the edit becomes smaller.
if Size.cx < Width then
FLink.Tree.Invalidate();
if FLink.Alignment = taRightJustify then
FLink.SetBounds(Rect(Left + Width - Size.cx, Top, Left + Width, Top + Max(Size.cy, Height)))
else
FLink.SetBounds(Rect(Left, Top, Left + Size.cx, Top + Max(Size.cy, Height)));
finally
SendMessage(Handle, WM_SETREDRAW, 1, 0);
end;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.CreateParams(var Params : TCreateParams);
begin
inherited;
if not Assigned(FLink.Node) then
exit; //Prevent AV exceptions occasionally seen in code below
//Only with multiline style we can use the text formatting rectangle.
//This does not harm formatting as single line control, if we don't use word wrapping.
with Params do
begin
Style := Style or ES_MULTILINE;
if vsMultiline in FLink.Node.States then
Style := Style and not (ES_AUTOHSCROLL or WS_HSCROLL) or WS_VSCROLL or ES_AUTOVSCROLL;
if tsUseThemes in FLink.Tree.TreeStates then
begin
Style := Style and not WS_BORDER;
ExStyle := ExStyle or WS_EX_CLIENTEDGE;
end
else
begin
Style := Style or WS_BORDER;
ExStyle := ExStyle and not WS_EX_CLIENTEDGE;
end;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TVTEdit.GetTextSize : TSize;
var
DC : HDC;
LastFont : THandle;
begin
DC := GetDC(Handle);
LastFont := SelectObject(DC, Font.Handle);
try
//Read needed space for the current text.
GetTextExtentPoint32(DC, PChar(Text + 'yG'), Length(Text) + 2, Result);
finally
SelectObject(DC, LastFont);
ReleaseDC(Handle, DC);
end;
end;
procedure TVTEdit.KeyPress(var Key : Char);
begin
if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.Node.States) then
Key := #0; //Filter out return keys as they will be added to the text, avoids #895
inherited;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TVTEdit.Release;
begin
if HandleAllocated then
PostMessage(Handle, CM_RELEASE, 0, 0);
end;
//----------------- TBaseEditLink ------------------------------------------------------------------------------------
procedure TBaseEditLink.SetEdit(const Value : TControl);
begin
if Assigned(FEdit) then
FEdit.Free;
FEdit := Value;
end;
//----------------------------------------------------------------------------------------------------------------------
function TBaseEditLink.BeginEdit : Boolean;
//Notifies the edit link that editing can start now. descendants may cancel node edit
//by returning False.
begin
Result := not FStopping;
if Result then
DoBeginEdit(Result);
end;
//----------------------------------------------------------------------------------------------------------------------
function TBaseEditLink.CancelEdit : Boolean;
// Performs edit cancelling.
begin
Result := not FStopping;
if Result then
begin
// Let descendants cancel the cancel
DoCancelEdit(Result);
if not Result then
Exit;
FStopping := True;
FTree.CancelEditNode;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TBaseEditLink.EndEdit : Boolean;
// Performs edit ending.
begin
Result := not FStopping;
if Result then
begin
// Let descendants cancel the end
DoEndEdit(Result);
if not Result then
Exit;
FStopping := True;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TBaseEditLink.PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean;
// Performs general init: assign Tree, Node, Column, other properties; destroys previous
// edit instance.
begin
Result := Tree is TCustomVirtualStringTree;
if not Result then Exit; // should not happen
FTree := Tree as TCustomVirtualStringTree;
FNode := Node;
FColumn := Column;
if Column <= NoColumn then
begin
FBidiMode := FTree.BidiMode;
FAlignment := TCustomVirtualStringTreeCracker(FTree).Alignment;
end
else
begin
FBidiMode := FTree.Header.Columns[Column].BidiMode;
FAlignment := FTree.Header.Columns[Column].Alignment;
end;
SetEdit(nil); // always dispose edit
DoPrepareEdit(Result);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseEditLink.DoBeginEdit(var Result: Boolean);
begin
if Assigned(OnBeginEdit) then
OnBeginEdit(Self, Result);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseEditLink.DoCancelEdit(var Result: Boolean);
begin
if Assigned(OnCancelEdit) then
OnCancelEdit(Self, Result);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseEditLink.DoEndEdit(var Result: Boolean);
begin
if Assigned(OnEndEdit) then
OnEndEdit(Self, Result);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TBaseEditLink.DoPrepareEdit(var Result: Boolean);
begin
if Assigned(OnPrepareEdit) then
OnPrepareEdit(Self, FEdit, Result);
end;
//----------------- TWinControlEditLink ------------------------------------------------------------------------------------
destructor TWinControlEditLink.Destroy;
begin
//FEdit.Free; casues issue #357. Fix:
if Assigned(FEdit) and Edit.HandleAllocated then
PostMessage(Edit.Handle, CM_RELEASE, 0, 0);
inherited;
end;
//----------------------------------------------------------------------------------------------------------------------
function TWinControlEditLink.GetEdit: TWinControl;
begin
Result := TWinControl(FEdit);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TWinControlEditLink.SetEdit(const Value: TWinControl);
begin
inherited SetEdit(Value);
end;
//----------------------------------------------------------------------------------------------------------------------
function TWinControlEditLink.BeginEdit: Boolean;
begin
Result := inherited;
if Result then
begin
Edit.Show;
Edit.SetFocus;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TWinControlEditLink.CancelEdit: Boolean;
begin
Result := inherited;
if Result then
begin
Edit.Hide;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TWinControlEditLink.GetBounds : TRect;
begin
Result := FEdit.BoundsRect;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TWinControlEditLink.ProcessMessage(var Message : TMessage);
begin
FEdit.WindowProc(Message);
end;
//----------------------------------------------------------------------------------------------------------------------
function TWinControlEditLink.EndEdit: Boolean;
begin
Result := inherited;
if Result then
begin
Edit.Hide;
end;
end;
//----------------- TStringEditLink ------------------------------------------------------------------------------------
constructor TStringEditLink.Create;
begin
inherited;
FEdit := TVTEdit.Create(Self);
with Edit do
begin
Visible := False;
BorderStyle := bsSingle;
AutoSize := False;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TStringEditLink.GetEdit: TVTEdit;
begin
Result := TVTEdit(FEdit);
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TStringEditLink.SetEdit(const Value : TVTEdit);
begin
inherited SetEdit(Value);
end;
//----------------------------------------------------------------------------------------------------------------------
function TStringEditLink.BeginEdit : Boolean;
begin
Result := inherited;
if Result then
begin
Edit.SelectAll;
Edit.AutoAdjustSize;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TStringEditLink.CancelEdit : Boolean;
begin
Result := inherited;
if Result then
begin
Edit.ClearLink;
Edit.ClearRefLink;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TStringEditLink.EndEdit : Boolean;
begin
Result := inherited;
if Result then
try
if Edit.Modified then
FTree.Text[FNode, FColumn] := Edit.Text;
Edit.ClearLink;
Edit.ClearRefLink;
except
FStopping := False;
raise;
end;
end;
//----------------------------------------------------------------------------------------------------------------------
function TStringEditLink.PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean;
var
Text : string;
begin
Result := inherited;
if Result then
begin
Edit := TVTEdit.Create(Self);
Edit.Visible := False;
Edit.BorderStyle := bsSingle;
Edit.AutoSize := True;
Edit.Parent := Tree;
//Initial size, font and text of the node.
FTree.GetTextInfo(Node, Column, Edit.Font, FTextBounds, Text);
Edit.Font.Color := clWindowText;
Edit.RecreateWnd;
Edit.AutoSize := False;
Edit.Text := Text;
Edit.BidiMode := FBidiMode;
if Edit.BidiMode <> bdLeftToRight then
ChangeBidiModeAlignment(FAlignment);
end;
end;
//----------------------------------------------------------------------------------------------------------------------
procedure TStringEditLink.SetBounds(R : TRect);
//Sets the outer bounds of the edit control and the actual edit area in the control.
var
lOffset, tOffset, Height : Integer;
offsets : TVTOffsets;
begin
if not FStopping then
begin
//Check if the provided rect height is smaller than the edit control height.
Height := R.Bottom - R.Top;
if Height < Edit.ClientHeight then
begin
//If the height is smaller than the minimal height we must correct it, otherwise the caret will be invisible.
tOffset := Edit.CalcMinHeight - Height;
if tOffset > 0 then
Inc(R.Bottom, tOffset);
end;
//Set the edit's bounds but make sure there's a minimum width and the right border does not
//extend beyond the parent's left/right border.
if R.Left < 0 then
R.Left := 0;
if R.Right - R.Left < 30 then
begin
if FAlignment = taRightJustify then
R.Left := R.Right - 30
else
R.Right := R.Left + 30;
end;
if R.Right > FTree.ClientWidth then
R.Right := FTree.ClientWidth;
Edit.BoundsRect := R;
//The selected text shall exclude the text margins and be centered vertically.
//We have to take out the two pixel border of the edit control as well as a one pixel "edit border" the
//control leaves around the (selected) text.
R := Edit.ClientRect;
//If toGridExtensions are turned on, we can fine tune the left margin (or the right margin if RTL is on)
//of the text to exactly match the text in the tree cell.
if (toGridExtensions in TCustomVirtualStringTreeCracker(FTree).TreeOptions.MiscOptions) and
((FAlignment = taLeftJustify) and (Edit.BidiMode = bdLeftToRight) or (FAlignment = taRightJustify) and (Edit.BidiMode <> bdLeftToRight)) then
begin
//Calculate needed text area offset.
FTree.GetOffsets(FNode, offsets, ofsText, FColumn);
if FColumn = FTree.Header.MainColumn then
begin
if offsets[ofsToggleButton] < 0 then
lOffset := - (offsets[ofsToggleButton] + 2)
else
lOffset := 0;
end
else
lOffset := offsets[ofsText] - offsets[ofsMargin] + 1;
//Apply the offset.
if Edit.BidiMode = bdLeftToRight then
Inc(R.Left, lOffset)
else
Dec(R.Right, lOffset);
end;
lOffset := IfThen(vsMultiline in FNode.States, 0, 2);
if tsUseThemes in FTree.TreeStates then
Inc(lOffset);
InflateRect(R, - TCustomVirtualStringTreeCracker(FTree).TextMargin + lOffset, lOffset);
if not (vsMultiline in FNode.States) then
begin
tOffset := FTextBounds.Top - Edit.Top;
//Do not apply a negative offset, the cursor will disappear.
if tOffset > 0 then
OffsetRect(R, 0, tOffset);
end;
R.Top := Max( - 1, R.Top); //A value smaller than -1 will prevent the edit cursor from being shown by Windows, see issue #159
R.Left := Max( - 1, R.Left);
SendMessage(Edit.Handle, EM_SETRECTNP, 0, LPARAM(@R));
end;
end;
end.