diff --git a/Demos/Advanced/Editors.pas b/Demos/Advanced/Editors.pas index d89ff33f5..2a5686a03 100644 --- a/Demos/Advanced/Editors.pas +++ b/Demos/Advanced/Editors.pas @@ -7,8 +7,8 @@ interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, - StdCtrls, VirtualTrees, ExtDlgs, ImgList, Buttons, ExtCtrls, ComCtrls, - Mask; + StdCtrls, ExtDlgs, ImgList, Buttons, ExtCtrls, ComCtrls, Mask, + VirtualTrees, VirtualTrees.EditLink; type // Describes the type of value a property tree node stores in its data property. @@ -34,25 +34,19 @@ TPropertyData = record end; // Our own edit link to implement several different node editors. - TPropertyEditLink = class(TInterfacedObject, IVTEditLink) - private - FEdit: TWinControl; // One of the property editor classes. - FTree: TVirtualStringTree; // A back reference to the tree calling. - FNode: PVirtualNode; // The node being edited. - FColumn: Integer; // The column of the node being edited. + + // Base class for TPropertyEditLink and TGridEditLink implementing key handling + TBasePropertyEditLink = class(TWinControlEditLink) protected procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure SetBounds(R: TRect); override; stdcall; + end; + + TPropertyEditLink = class(TBasePropertyEditLink) public - destructor Destroy; override; - - function BeginEdit: Boolean; stdcall; - function CancelEdit: Boolean; stdcall; - function EndEdit: Boolean; stdcall; - function GetBounds: TRect; stdcall; - function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; - procedure ProcessMessage(var Message: TMessage); stdcall; - procedure SetBounds(R: TRect); stdcall; + procedure DoEndEdit(var Result: Boolean); override; + procedure DoPrepareEdit(var Result: Boolean); override; end; //---------------------------------------------------------------------------------------------------------------------- @@ -176,35 +170,17 @@ TGridData = class // Our own edit link to implement several different node editors. TGridEditLink = class(TPropertyEditLink, IVTEditLink) public - function EndEdit: Boolean; stdcall; - function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; + procedure DoEndEdit(var Result: Boolean); override; + procedure DoPrepareEdit(var Result: Boolean); override; end; //---------------------------------------------------------------------------------------------------------------------- implementation -uses - PropertiesDemo, GridDemo; - -//----------------- TPropertyEditLink ---------------------------------------------------------------------------------- - -// This implementation is used in VST3 to make a connection beween the tree -// and the actual edit window which might be a simple edit, a combobox -// or a memo etc. +//----------------- TBasePropertyEditLink ---------------------------------------------------------------------------------- -destructor TPropertyEditLink.Destroy; - -begin - //FEdit.Free; casues issue #357. Fix: - if FEdit.HandleAllocated then - PostMessage(FEdit.Handle, CM_RELEASE, 0, 0); - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TPropertyEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +procedure TBasePropertyEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var CanAdvance: Boolean; @@ -244,7 +220,9 @@ procedure TPropertyEditLink.EditKeyDown(Sender: TObject; var Key: Word; Shift: T end; end; -procedure TPropertyEditLink.EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBasePropertyEditLink.EditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_ESCAPE: @@ -257,26 +235,21 @@ procedure TPropertyEditLink.EditKeyUp(Sender: TObject; var Key: Word; Shift: TSh //---------------------------------------------------------------------------------------------------------------------- -function TPropertyEditLink.BeginEdit: Boolean; - -begin - Result := True; - FEdit.Show; - FEdit.SetFocus; -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBasePropertyEditLink.SetBounds(R: TRect); -function TPropertyEditLink.CancelEdit: Boolean; +var + Dummy: Integer; begin - Result := True; - FEdit.Hide; + // Since we don't want to activate grid extensions in the tree (this would influence how the selection is drawn) + // we have to set the edit's width explicitly to the width of the column. + FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right); + FEdit.BoundsRect := R; end; -//---------------------------------------------------------------------------------------------------------------------- +//----------------- TPropertyEditLink ---------------------------------------------------------------------------------- -function TPropertyEditLink.EndEdit: Boolean; +procedure TPropertyEditLink.DoEndEdit(var Result: Boolean); var Data: PPropertyData; @@ -284,14 +257,14 @@ function TPropertyEditLink.EndEdit: Boolean; S: UnicodeString; begin - Result := True; + inherited; Data := FNode.GetData(); - if FEdit is TComboBox then - S := TComboBox(FEdit).Text + if Edit is TComboBox then + S := TComboBox(Edit).Text else begin - GetWindowText(FEdit.Handle, Buffer, 1024); + GetWindowText(Edit.Handle, Buffer, 1024); S := Buffer; end; @@ -301,34 +274,20 @@ function TPropertyEditLink.EndEdit: Boolean; Data.Changed := True; FTree.InvalidateNode(FNode); end; - FEdit.Hide; FTree.SetFocus; end; //---------------------------------------------------------------------------------------------------------------------- -function TPropertyEditLink.GetBounds: TRect; - -begin - Result := FEdit.BoundsRect; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TPropertyEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; +procedure TPropertyEditLink.DoPrepareEdit(var Result: Boolean); var Data: PPropertyData; begin - Result := True; - FTree := Tree as TVirtualStringTree; - FNode := Node; - FColumn := Column; + inherited; // determine what edit type actually is needed - FEdit.Free; - FEdit := nil; Data := Node.GetData(); case Data.ValueType of vtString: @@ -421,32 +380,9 @@ function TPropertyEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNod end; end; -//---------------------------------------------------------------------------------------------------------------------- - -procedure TPropertyEditLink.ProcessMessage(var Message: TMessage); - -begin - FEdit.WindowProc(Message); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TPropertyEditLink.SetBounds(R: TRect); - -var - Dummy: Integer; - -begin - // Since we don't want to activate grid extensions in the tree (this would influence how the selection is drawn) - // we have to set the edit's width explicitly to the width of the column. - FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right); - FEdit.BoundsRect := R; -end; - //---------------- TGridEditLink --------------------------------------------------------------------------------------- -function TGridEditLink.EndEdit: Boolean; - +procedure TGridEditLink.DoEndEdit(var Result: Boolean); var Data: TGridData; Buffer: array[0..1024] of Char; @@ -454,11 +390,11 @@ function TGridEditLink.EndEdit: Boolean; I: Integer; begin - Result := True; + inherited; Data := FNode.GetData(); - if FEdit is TComboBox then + if Edit is TComboBox then begin - S := TComboBox(FEdit).Text; + S := TComboBox(Edit).Text; if S <> Data.Value[FColumn - 1] then begin Data.Value[FColumn - 1] := S; @@ -466,9 +402,9 @@ function TGridEditLink.EndEdit: Boolean; end; end else - if FEdit is TMaskEdit then + if Edit is TMaskEdit then begin - I := StrToInt(Trim(TMaskEdit(FEdit).EditText)); + I := StrToInt(Trim(TMaskEdit(Edit).EditText)); if I <> Data.Value[FColumn - 1] then begin Data.Value[FColumn - 1] := I; @@ -477,7 +413,7 @@ function TGridEditLink.EndEdit: Boolean; end else begin - GetWindowText(FEdit.Handle, Buffer, 1024); + GetWindowText(Edit.Handle, Buffer, 1024); S := Buffer; if S <> Data.Value[FColumn - 1] then begin @@ -488,26 +424,20 @@ function TGridEditLink.EndEdit: Boolean; if Data.Changed then FTree.InvalidateNode(FNode); - FEdit.Hide; end; //---------------------------------------------------------------------------------------------------------------------- -function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; +procedure TGridEditLink.DoPrepareEdit(var Result: Boolean); var Data: TGridData; begin - Result := True; - FTree := Tree as TVirtualStringTree; - FNode := Node; - FColumn := Column; + inherited; // Determine what edit type actually is needed. - FEdit.Free; - FEdit := nil; - Data := FTree.GetNodeData(Node); - case Data.ValueType[FColumn - 1] of + Data := Tree.GetNodeData(Node); + case Data.ValueType[Column - 1] of vtString: begin FEdit := TEdit.Create(nil); @@ -515,7 +445,7 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C begin Visible := False; Parent := Tree; - Text := Data.Value[FColumn - 1]; + Text := Data.Value[Column - 1]; OnKeyDown := EditKeyDown; OnKeyUp := EditKeyUp; end; @@ -527,10 +457,10 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C begin Visible := False; Parent := Tree; - Text := Data.Value[FColumn - 1]; + Text := Data.Value[Column - 1]; // Here you would usually do a lookup somewhere to get // values for the combobox. We only add some dummy values. - case FColumn of + case Column of 2: begin Items.Add('John'); @@ -558,7 +488,7 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C Visible := False; Parent := Tree; EditMask := '9999;0; '; - Text := Data.Value[FColumn - 1]; + Text := Data.Value[Column - 1]; OnKeyDown := EditKeyDown; OnKeyUp := EditKeyUp; end; @@ -570,7 +500,7 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C begin Visible := False; Parent := Tree; - Text := Data.Value[FColumn - 1]; + Text := Data.Value[Column - 1]; OnKeyDown := EditKeyDown; OnKeyUp := EditKeyUp; end; @@ -584,8 +514,8 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C begin Visible := False; Parent := Tree; - Text := Data.Value[FColumn - 1]; - Items.Add(Data.Value[FColumn - 1]); + Text := Data.Value[Column - 1]; + Items.Add(Data.Value[Column - 1]); OnKeyDown := EditKeyDown; OnKeyUp := EditKeyUp; end; @@ -602,7 +532,7 @@ function TGridEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; C CalColors.TitleBackColor := clBtnShadow; CalColors.TitleTextColor := clBlack; CalColors.TrailingTextColor := clBtnFace; - Date := StrToDate(Data.Value[FColumn - 1]); + Date := StrToDate(Data.Value[Column - 1]); OnKeyDown := EditKeyDown; OnKeyUp := EditKeyUp; end; diff --git a/Source/VirtualTrees.EditLink.pas b/Source/VirtualTrees.EditLink.pas index 8d399468b..0a64ce7f0 100644 --- a/Source/VirtualTrees.EditLink.pas +++ b/Source/VirtualTrees.EditLink.pas @@ -1,4 +1,7 @@ -unit VirtualTrees.EditLink; +unit VirtualTrees.EditLink; + +// Base class for inplace node editors implementing IVTEditLink interface +// and default node editor. interface @@ -49,34 +52,111 @@ TVTEdit = class(TCustomEdit) property PasswordChar; end; - TStringEditLink = class(TInterfacedObject, IVTEditLink) - private - FEdit : TVTEdit; //A normal custom edit control. - protected + 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. - FAlignment : TAlignment; - FTextBounds : TRect; //Smallest rectangle around the text. FStopping : Boolean; //Set to True when the edit link requests stopping the edit action. - procedure SetEdit(const Value : TVTEdit); //Setter for the FEdit member; - public - constructor Create; virtual; - destructor Destroy; override; - property Alignment : TAlignment read FAlignment; - property Node : PVirtualNode read FNode; //[IPK] Make FNode accessible - property Column : TColumnIndex read FColumn; //[IPK] Make Column(Index) accessible + 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; - property Edit : TVTEdit read FEdit write SetEdit; function EndEdit : Boolean; virtual; stdcall; - function GetBounds : TRect; 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; - procedure SetBounds(R : TRect); virtual; stdcall; - property Stopping : Boolean read FStopping; + 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 @@ -91,15 +171,15 @@ implementation type TCustomVirtualStringTreeCracker = class(TCustomVirtualStringTree); - //----------------- TVTEdit -------------------------------------------------------------------------------------------- +//----------------- TVTEdit -------------------------------------------------------------------------------------------- - //Implementation of a generic node caption editor. +//Implementation of a generic node caption editor. constructor TVTEdit.Create(Link : TStringEditLink); begin inherited Create(nil); if not Assigned(Link) then - raise EArgumentException.Create('Paramter Link must not be nil.'); + raise EArgumentException.Create('Parameter Link must not be nil.'); ShowHint := False; ParentShowHint := False; //This assignment increases the reference count for the interface. @@ -147,7 +227,7 @@ procedure TVTEdit.CMAutoAdjust(var Message : TMessage); procedure TVTEdit.CMExit(var Message : TMessage); begin if Assigned(FLink) and not FLink.Stopping then - with FLink, TCustomVirtualStringTreeCracker(FTree) do + with TCustomVirtualStringTreeCracker(FLink.Tree) do begin if (toAutoAcceptEditChange in TreeOptions.StringOptions) then DoEndEdit @@ -191,10 +271,10 @@ procedure TVTEdit.WMDestroy(var Message : TWMDestroy); //pending changes. if Assigned(FLink) and not FLink.Stopping and not (csRecreating in Self.ControlState) then begin - with FLink, TCustomVirtualStringTreeCracker(FTree) do + with TCustomVirtualStringTreeCracker(FLink.Tree) do begin if (toAutoAcceptEditChange in TreeOptions.StringOptions) and Modified then - Text[FNode, FColumn] := FEdit.Text; + Text[FLink.Node, FLink.Column] := FLink.Edit.Text; end; FLink := nil; FRefLink := nil; @@ -443,146 +523,290 @@ procedure TVTEdit.Release; PostMessage(Handle, CM_RELEASE, 0, 0); end; -//----------------- TStringEditLink ------------------------------------------------------------------------------------ +//----------------- TBaseEditLink ------------------------------------------------------------------------------------ -constructor TStringEditLink.Create; - begin - inherited; - FEdit := TVTEdit.Create(Self); - with FEdit do - begin - Visible := False; - BorderStyle := bsSingle; - AutoSize := False; - end; +procedure TBaseEditLink.SetEdit(const Value : TControl); +begin + if Assigned(FEdit) then + FEdit.Free; + FEdit := Value; end; //---------------------------------------------------------------------------------------------------------------------- -destructor TStringEditLink.Destroy; +function TBaseEditLink.BeginEdit : Boolean; +//Notifies the edit link that editing can start now. descendants may cancel node edit +//by returning False. + begin - if Assigned(FEdit) then - FEdit.Release; - inherited; + Result := not FStopping; + if Result then + DoBeginEdit(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.BeginEdit : Boolean; -//Notifies the edit link that editing can start now. descendants may cancel node edit -//by returning False. +function TBaseEditLink.CancelEdit : Boolean; + +// Performs edit cancelling. begin Result := not FStopping; if Result then begin - FEdit.Show; - FEdit.SelectAll; - FEdit.SetFocus; - FEdit.AutoAdjustSize; + // Let descendants cancel the cancel + DoCancelEdit(Result); + if not Result then + Exit; + FStopping := True; + FTree.CancelEditNode; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TStringEditLink.SetEdit(const Value : TVTEdit); +function TBaseEditLink.EndEdit : Boolean; + +// Performs edit ending. + begin - if Assigned(FEdit) then - FEdit.Free; - FEdit := Value; + Result := not FStopping; + if Result then + begin + // Let descendants cancel the end + DoEndEdit(Result); + if not Result then + Exit; + FStopping := True; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.CancelEdit : Boolean; +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 := not FStopping; + 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 - FStopping := True; - FEdit.Hide; - FTree.CancelEditNode; - FEdit.ClearLink; - FEdit.ClearRefLink; + Edit.Show; + Edit.SetFocus; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.EndEdit : Boolean; +function TWinControlEditLink.CancelEdit: Boolean; begin - Result := not FStopping; + Result := inherited; if Result then - try - FStopping := True; - if FEdit.Modified then - FTree.Text[FNode, FColumn] := FEdit.Text; - FEdit.Hide; - FEdit.ClearLink; - FEdit.ClearRefLink; - except - FStopping := False; - raise; - end; + begin + Edit.Hide; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.GetBounds : TRect; +function TWinControlEditLink.GetBounds : TRect; begin Result := FEdit.BoundsRect; end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean; -//Retrieves the true text bounds from the owner tree. +procedure TWinControlEditLink.ProcessMessage(var Message : TMessage); +begin + FEdit.WindowProc(Message); +end; -var - Text : string; +//---------------------------------------------------------------------------------------------------------------------- + +function TWinControlEditLink.EndEdit: Boolean; begin - Result := Tree is TCustomVirtualStringTree; + Result := inherited; if Result then begin - if not Assigned(FEdit) then - begin - FEdit := TVTEdit.Create(Self); - FEdit.Visible := False; - FEdit.BorderStyle := bsSingle; - end; - FEdit.AutoSize := True; - FTree := Tree as TCustomVirtualStringTree; - FNode := Node; - FColumn := Column; - FEdit.Parent := Tree; - //Initial size, font and text of the node. - FTree.GetTextInfo(Node, Column, FEdit.Font, FTextBounds, Text); - FEdit.Font.Color := clWindowText; - FEdit.RecreateWnd; - FEdit.AutoSize := False; - FEdit.Text := Text; + Edit.Hide; + end; +end; - if Column <= NoColumn then - begin - FEdit.BidiMode := FTree.BidiMode; - FAlignment := TCustomVirtualStringTreeCracker(FTree).Alignment; - end - else - begin - FEdit.BidiMode := FTree.Header.Columns[Column].BidiMode; - FAlignment := FTree.Header.Columns[Column].Alignment; - end; +//----------------- TStringEditLink ------------------------------------------------------------------------------------ - if FEdit.BidiMode <> bdLeftToRight then - ChangeBidiModeAlignment(FAlignment); +constructor TStringEditLink.Create; +begin + inherited; + FEdit := TVTEdit.Create(Self); + with Edit do + begin + Visible := False; + BorderStyle := bsSingle; + AutoSize := False; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TStringEditLink.ProcessMessage(var Message : TMessage); +function TStringEditLink.GetEdit: TVTEdit; begin - FEdit.WindowProc(Message); + 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; //---------------------------------------------------------------------------------------------------------------------- @@ -598,10 +822,10 @@ procedure TStringEditLink.SetBounds(R : TRect); begin //Check if the provided rect height is smaller than the edit control height. Height := R.Bottom - R.Top; - if Height < FEdit.ClientHeight then + 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 := FEdit.CalcMinHeight - Height; + tOffset := Edit.CalcMinHeight - Height; if tOffset > 0 then Inc(R.Bottom, tOffset); end; @@ -619,17 +843,17 @@ procedure TStringEditLink.SetBounds(R : TRect); end; if R.Right > FTree.ClientWidth then R.Right := FTree.ClientWidth; - FEdit.BoundsRect := R; + 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 := FEdit.ClientRect; + 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 (FEdit.BidiMode = bdLeftToRight) or (FAlignment = taRightJustify) and (FEdit.BidiMode <> bdLeftToRight)) then + ((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); @@ -643,7 +867,7 @@ procedure TStringEditLink.SetBounds(R : TRect); else lOffset := offsets[ofsText] - offsets[ofsMargin] + 1; //Apply the offset. - if FEdit.BidiMode = bdLeftToRight then + if Edit.BidiMode = bdLeftToRight then Inc(R.Left, lOffset) else Dec(R.Right, lOffset); @@ -655,14 +879,14 @@ procedure TStringEditLink.SetBounds(R : TRect); InflateRect(R, - TCustomVirtualStringTreeCracker(FTree).TextMargin + lOffset, lOffset); if not (vsMultiline in FNode.States) then begin - tOffset := FTextBounds.Top - FEdit.Top; + 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(FEdit.Handle, EM_SETRECTNP, 0, LPARAM(@R)); + SendMessage(Edit.Handle, EM_SETRECTNP, 0, LPARAM(@R)); end; end;