unit TB2ExtItems; {$MODE Delphi} { Toolbar2000 Copyright (C) 1998-2008 by Jordan Russell All rights reserved. The contents of this file are subject to the "Toolbar2000 License"; you may not use or distribute this file except in compliance with the "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in TB2k-LICENSE.txt or at: http://www.jrsoftware.org/files/tb2k/TB2k-LICENSE.txt Alternatively, the contents of this file may be used under the terms of the GNU General Public License (the "GPL"), in which case the provisions of the GPL are applicable instead of those in the "Toolbar2000 License". A copy of the GPL may be found in GPL-LICENSE.txt or at: http://www.jrsoftware.org/files/tb2k/GPL-LICENSE.txt If you wish to allow use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the "Toolbar2000 License", indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the "Toolbar2000 License" or the GPL. $jrsoftware: tb2k/Source/TB2ExtItems.pas,v 1.68 2008/04/10 21:51:12 jr Exp $ } interface {$I TB2Ver.inc} uses LCLIntf, LCLType, LMessages, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, CommCtrl, Menus, ActnList, TB2Item; type TTBEditItemOption = (tboUseEditWhenVertical); TTBEditItemOptions = set of TTBEditItemOption; const EditItemDefaultEditOptions = []; EditItemDefaultEditWidth = 64; type TTBEditItem = class; TTBEditItemViewer = class; TTBAcceptTextEvent = procedure(Sender: TObject; var NewText: String; var Accept: Boolean) of object; TTBBeginEditEvent = procedure(Sender: TTBEditItem; Viewer: TTBEditItemViewer; EditControl: TEdit) of object; TTBEditAction = class(TAction) private FEditOptions: TTBEditItemOptions; FEditCaption: String; FEditWidth: Integer; FOnAcceptText: TTBAcceptTextEvent; FText: String; procedure SetEditCaption(Value: String); procedure SetEditOptions(Value: TTBEditItemOptions); procedure SetEditWidth(Value: Integer); procedure SetOnAcceptText(Value: TTBAcceptTextEvent); procedure SetText(Value: String); public constructor Create(AOwner: TComponent); override; published property EditCaption: String read FEditCaption write SetEditCaption; property EditOptions: TTBEditItemOptions read FEditOptions write SetEditOptions default EditItemDefaultEditOptions; property EditWidth: Integer read FEditWidth write SetEditWidth default EditItemDefaultEditWidth; property Text: String read FText write SetText; property OnAcceptText: TTBAcceptTextEvent read FOnAcceptText write SetOnAcceptText; end; TTBEditItemActionLink = class(TTBCustomItemActionLink) protected procedure AssignClient(AClient: TObject); override; function IsEditCaptionLinked: Boolean; virtual; function IsEditOptionsLinked: Boolean; virtual; function IsEditWidthLinked: Boolean; virtual; function IsOnAcceptTextLinked: Boolean; virtual; function IsTextLinked: Boolean; virtual; procedure SetEditCaption(const Value: String); virtual; procedure SetEditOptions(Value: TTBEditItemOptions); virtual; procedure SetEditWidth(const Value: Integer); virtual; procedure SetOnAcceptText(Value: TTBAcceptTextEvent); virtual; procedure SetText(const Value: String); virtual; end; TTBEditItem = class(TTBCustomItem) private FCharCase: TEditCharCase; FEditCaption: String; FEditOptions: TTBEditItemOptions; FEditWidth: Integer; FMaxLength: Integer; FOnAcceptText: TTBAcceptTextEvent; FOnBeginEdit: TTBBeginEditEvent; FText: String; function IsEditCaptionStored: Boolean; function IsEditOptionsStored: Boolean; function IsEditWidthStored: Boolean; function IsTextStored: Boolean; procedure SetCharCase(Value: TEditCharCase); procedure SetEditCaption(Value: String); procedure SetEditOptions(Value: TTBEditItemOptions); procedure SetEditWidth(Value: Integer); procedure SetMaxLength(Value: Integer); procedure SetText(Value: String); protected procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override; procedure DoBeginEdit(Viewer: TTBEditItemViewer); virtual; function GetActionLinkClass: TTBCustomItemActionLinkClass; override; function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; override; function NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean; override; public constructor Create(AOwner: TComponent); override; procedure Clear; procedure Click; override; published property Action; property AutoCheck; property Caption; property CharCase: TEditCharCase read FCharCase write SetCharCase default ecNormal; property Checked; property DisplayMode; property EditCaption: String read FEditCaption write SetEditCaption stored IsEditCaptionStored; property EditOptions: TTBEditItemOptions read FEditOptions write SetEditOptions stored IsEditOptionsStored; property EditWidth: Integer read FEditWidth write SetEditWidth stored IsEditWidthStored; property MaxLength: Integer read FMaxLength write SetMaxLength default 0; property Enabled; property GroupIndex; property HelpContext; property Hint; property ImageIndex; property InheritOptions; property MaskOptions; property Options; property RadioItem; property ShortCut; property Text: String read FText write SetText stored IsTextStored; property Visible; property OnAcceptText: TTBAcceptTextEvent read FOnAcceptText write FOnAcceptText; property OnBeginEdit: TTBBeginEditEvent read FOnBeginEdit write FOnBeginEdit; property OnClick; property OnSelect; end; TTBEditItemViewer = class(TTBItemViewer) private FEditControl: TEdit; FEditControlStatus: set of (ecsContinueLoop, ecsAccept, ecsClose); function EditLoop(const CapHandle: HWND): Boolean; procedure EditWndProc(var Message: TMessage); procedure MouseBeginEdit; protected procedure CalcSize(const Canvas: TCanvas; var AWidth, AHeight: Integer); override; function CaptionShown: Boolean; override; function DoExecute: Boolean; override; function GetAccRole: Integer; override; function GetAccValue(var Value: WideString): Boolean; override; function GetCaptionText: String; override; procedure GetCursor(const Pt: TPoint; var ACursor: HCURSOR); override; procedure GetEditRect(var R: TRect); virtual; procedure MouseDown(Shift: TShiftState; X, Y: Integer; var MouseDownOnMenu: Boolean); override; procedure MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean); override; procedure Paint(const Canvas: TCanvas; const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean); override; function UsesSameWidth: Boolean; override; public property EditControl: TEdit read FEditControl; end; { TTBVisibilityToggleItem } TTBVisibilityToggleItem = class(TTBCustomItem) private FControl: TControl; procedure SetControl(Value: TControl); procedure UpdateProps; protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; public procedure Click; override; procedure InitiateAction; override; published property Caption; property Control: TControl read FControl write SetControl; property DisplayMode; property Enabled; property HelpContext; property Hint; property ImageIndex; property Images; property InheritOptions; property MaskOptions; property Options; property ShortCut; property Visible; property OnClick; property OnSelect; end; implementation uses TB2Common, TB2Consts; const EditMenuTextMargin = 3; EditMenuMidWidth = 4; type TControlAccess = class(TControl); TEditAccess = {$IFNDEF CLR} class(TEdit) {$ELSE} IControl {$ENDIF}; { TTBEditAction } constructor TTBEditAction.Create(AOwner: TComponent); begin inherited; FEditOptions := EditItemDefaultEditOptions; FEditWidth := EditItemDefaultEditWidth; DisableIfNoHandler := False; end; procedure TTBEditAction.SetEditCaption(Value: String); var I: Integer; begin if FEditCaption <> Value then begin for I := 0 to FClients.Count - 1 do if TBasicActionLink(FClients[I]) is TTBEditItemActionLink then TTBEditItemActionLink(FClients[I]).SetEditCaption(Value); FEditCaption := Value; Change; end; end; procedure TTBEditAction.SetEditOptions(Value: TTBEditItemOptions); var I: Integer; begin if FEditOptions <> Value then begin for I := 0 to FClients.Count - 1 do if TBasicActionLink(FClients[I]) is TTBEditItemActionLink then TTBEditItemActionLink(FClients[I]).SetEditOptions(Value); FEditOptions := Value; Change; end; end; procedure TTBEditAction.SetEditWidth(Value: Integer); var I: Integer; begin if FEditWidth <> Value then begin for I := 0 to FClients.Count - 1 do if TBasicActionLink(FClients[I]) is TTBEditItemActionLink then TTBEditItemActionLink(FClients[I]).SetEditWidth(Value); FEditWidth := Value; Change; end; end; procedure TTBEditAction.SetOnAcceptText(Value: TTBAcceptTextEvent); var I: Integer; begin {$IFNDEF CLR} if not MethodsEqual(TMethod(FOnAcceptText), TMethod(Value)) then begin {$ELSE} if @FOnAcceptText <> @Value then begin {$ENDIF} for I := 0 to FClients.Count - 1 do if TBasicActionLink(FClients[I]) is TTBEditItemActionLink then TTBEditItemActionLink(FClients[I]).SetOnAcceptText(Value); FOnAcceptText := Value; Change; end; end; procedure TTBEditAction.SetText(Value: String); var I: Integer; begin if FText <> Value then begin for I := 0 to FClients.Count - 1 do if TBasicActionLink(FClients[I]) is TTBEditItemActionLink then TTBEditItemActionLink(FClients[I]).SetText(Value); FText := Value; Change; end; end; { TTBEditItemActionLink } procedure TTBEditItemActionLink.AssignClient(AClient: TObject); begin FClient := AClient as TTBEditItem; end; function TTBEditItemActionLink.IsEditCaptionLinked: Boolean; begin if Action is TTBEditAction then Result := TTBEditItem(FClient).EditCaption = TTBEditAction(Action).EditCaption else Result := False; end; function TTBEditItemActionLink.IsEditOptionsLinked: Boolean; begin if Action is TTBEditAction then Result := TTBEditItem(FClient).EditOptions = TTBEditAction(Action).EditOptions else Result := False; end; function TTBEditItemActionLink.IsEditWidthLinked: Boolean; begin if Action is TTBEditAction then Result := TTBEditItem(FClient).EditWidth = TTBEditAction(Action).EditWidth else Result := False; end; function TTBEditItemActionLink.IsOnAcceptTextLinked: Boolean; begin if Action is TTBEditAction then {$IFNDEF CLR} Result := MethodsEqual(TMethod(TTBEditItem(FClient).OnAcceptText), TMethod(TTBEditAction(Action).OnAcceptText)) {$ELSE} Result := @TTBEditItem(FClient).OnAcceptText = @TTBEditAction(Action).OnAcceptText {$ENDIF} else Result := False; end; function TTBEditItemActionLink.IsTextLinked: Boolean; begin if Action is TTBEditAction then Result := TTBEditItem(FClient).Text = TTBEditAction(Action).Text else Result := False; end; procedure TTBEditItemActionLink.SetEditCaption(const Value: String); begin if IsEditCaptionLinked then TTBEditItem(FClient).EditCaption := Value; end; procedure TTBEditItemActionLink.SetEditOptions(Value: TTBEditItemOptions); begin if IsEditOptionsLinked then TTBEditItem(FClient).EditOptions := Value; end; procedure TTBEditItemActionLink.SetEditWidth(const Value: Integer); begin if IsEditWidthLinked then TTBEditItem(FClient).EditWidth := Value; end; procedure TTBEditItemActionLink.SetOnAcceptText(Value: TTBAcceptTextEvent); begin if IsOnAcceptTextLinked then TTBEditItem(FClient).OnAcceptText := Value; end; procedure TTBEditItemActionLink.SetText(const Value: String); begin if IsTextLinked then TTBEditItem(FClient).Text := Value; end; { TTBEditItem } constructor TTBEditItem.Create(AOwner: TComponent); begin inherited; FEditOptions := EditItemDefaultEditOptions; FEditWidth := EditItemDefaultEditWidth; end; procedure TTBEditItem.ActionChange(Sender: TObject; CheckDefaults: Boolean); begin inherited; if Action is TTBEditAction then with TTBEditAction(Sender) do begin if not CheckDefaults or (Self.EditCaption = '') then Self.EditCaption := EditCaption; if not CheckDefaults or (Self.EditOptions = []) then Self.EditOptions := EditOptions; if not CheckDefaults or (Self.Text = '') then Self.Text := Text; if not CheckDefaults or not Assigned(Self.OnAcceptText) then Self.OnAcceptText := OnAcceptText; end; end; function TTBEditItem.GetActionLinkClass: TTBCustomItemActionLinkClass; begin Result := TTBEditItemActionLink; end; function TTBEditItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass; begin if not(tboUseEditWhenVertical in EditOptions) and (AView.Orientation = tbvoVertical) then Result := inherited GetItemViewerClass(AView) else Result := TTBEditItemViewer; end; function TTBEditItem.NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean; begin Result := GetItemViewerClass(AViewer.View) <> AViewer.ClassType; end; procedure TTBEditItem.Clear; begin Text := ''; end; procedure TTBEditItem.Click; begin inherited; end; procedure TTBEditItem.DoBeginEdit(Viewer: TTBEditItemViewer); begin if Assigned(FOnBeginEdit) then FOnBeginEdit(Self, Viewer, Viewer.EditControl); end; function TTBEditItem.IsEditOptionsStored: Boolean; begin Result := (EditOptions <> EditItemDefaultEditOptions) and ((ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or not TTBEditItemActionLink(ActionLink).IsEditOptionsLinked); end; function TTBEditItem.IsEditCaptionStored: Boolean; begin Result := (ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or not TTBEditItemActionLink(ActionLink).IsEditCaptionLinked; end; function TTBEditItem.IsEditWidthStored: Boolean; begin Result := (EditWidth <> EditItemDefaultEditWidth) and ((ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or not TTBEditItemActionLink(ActionLink).IsEditWidthLinked); end; function TTBEditItem.IsTextStored: Boolean; begin Result := (ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or not TTBEditItemActionLink(ActionLink).IsTextLinked; end; procedure TTBEditItem.SetCharCase(Value: TEditCharCase); begin if FCharCase <> Value then begin FCharCase := Value; Text := Text; { update case } end; end; procedure TTBEditItem.SetEditOptions(Value: TTBEditItemOptions); begin if FEditOptions <> Value then begin FEditOptions := Value; Change(True); end; end; procedure TTBEditItem.SetEditCaption(Value: String); begin if FEditCaption <> Value then begin FEditCaption := Value; Change(True); end; end; procedure TTBEditItem.SetEditWidth(Value: Integer); begin if FEditWidth <> Value then begin FEditWidth := Value; Change(True); end; end; procedure TTBEditItem.SetMaxLength(Value: Integer); begin if FMaxLength <> Value then begin FMaxLength := Value; Change(False); end; end; procedure TTBEditItem.SetText(Value: String); begin case FCharCase of ecUpperCase: Value := {$IFNDEF CLR} AnsiUpperCase {$ELSE} UpperCase {$ENDIF} (Value); ecLowerCase: Value := {$IFNDEF CLR} AnsiLowerCase {$ELSE} LowerCase {$ENDIF} (Value); end; if FText <> Value then begin FText := Value; Change(False); end; end; { TTBEditItemViewer } procedure TTBEditItemViewer.EditWndProc(var Message: TMessage); var Item: TTBEditItem; procedure AcceptText; var S: String; Accept: Boolean; begin S := FEditControl.Text; Accept := True; if Assigned(Item.FOnAcceptText) then Item.FOnAcceptText(Self, S, Accept); if Accept then Item.Text := S; end; begin Item := TTBEditItem(Self.Item); if Message.Msg = WM_CHAR then case Word(Message.WParam) of VK_TAB: begin FEditControlStatus := [ecsAccept]; AcceptText; Exit; end; VK_RETURN: begin FEditControlStatus := [ecsAccept, ecsClose]; AcceptText; Exit; end; VK_ESCAPE: begin FEditControlStatus := []; Exit; end; end; TEditAccess(FEditControl).WndProc(Message); if Message.Msg = WM_KILLFOCUS then begin { Someone has stolen the focus from us, so 'cancel mode'. (We have to handle WM_KILLFOCUS in addition to the upstream WM_CANCELMODE handling since we don't always hold the mouse capture.) } View.CancelMode; FEditControlStatus := [ecsClose]; end; end; procedure TTBEditItemViewer.GetEditRect(var R: TRect); var Item: TTBEditItem; DC: HDC; begin Item := TTBEditItem(Self.Item); DC := GetDC(0); try SelectObject(DC, View.GetFont.Handle); R := BoundsRect; if not View.IsToolbar and (Item.EditCaption <> '') then begin Inc(R.Left, GetTextWidth(DC, Item.EditCaption, True) + EditMenuMidWidth + EditMenuTextMargin * 2); end; finally ReleaseDC(0, DC); end; end; procedure TTBEditItemViewer.CalcSize(const Canvas: TCanvas; var AWidth, AHeight: Integer); var Item: TTBEditItem; DC: HDC; begin Item := TTBEditItem(Self.Item); DC := Canvas.Handle; AWidth := Item.FEditWidth; AHeight := GetTextHeight(DC) + (EditMenuTextMargin * 2) + 1; if not IsToolbarStyle and (Item.EditCaption <> '') then begin Inc(AWidth, GetTextWidth(DC, Item.EditCaption, True) + EditMenuMidWidth + EditMenuTextMargin * 2); end; { Review: Should the height include external leading on fonts that use it, such as the default menu font on Windows Me Trad. Chinese? Office 2000 seems to insist on using Tahoma on Chinese Windows, so I'm not sure how it handles external leading on edit items. } end; function TTBEditItemViewer.CaptionShown: Boolean; begin Result := not IsToolbarStyle and inherited CaptionShown; end; function TTBEditItemViewer.GetCaptionText: String; begin Result := TTBEditItem(Item).EditCaption; end; procedure TTBEditItemViewer.Paint(const Canvas: TCanvas; const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean); const FillColors: array[Boolean] of TColor = (clBtnFace, clWindow); TextColors: array[Boolean] of TColor = (clGrayText, clWindowText); var Item: TTBEditItem; S: String; R: TRect; W: Integer; begin Item := TTBEditItem(Self.Item); R := ClientAreaRect; { Caption } if not IsToolbarStyle and (Item.EditCaption <> '') then begin S := Item.EditCaption; W := GetTextWidth(Canvas.Handle, S, True) + EditMenuTextMargin * 2; R.Right := R.Left + W; if IsSelected then Canvas.FillRect(R); Inc(R.Left, EditMenuTextMargin); DrawItemCaption(Canvas, R, S, UseDisabledShadow, DT_SINGLELINE or DT_LEFT or DT_VCENTER); R := ClientAreaRect; Inc(R.Left, W + EditMenuMidWidth); end; { Border } if IsSelected and Item.Enabled then DrawEdge(Canvas.Handle, R, BDR_SUNKENOUTER, BF_RECT); InflateRect(R, -1, -1); Canvas.Brush.Color := FillColors[not Item.Enabled]; Canvas.FrameRect(R); InflateRect(R, -1, -1); { Fill } Canvas.Brush.Color := FillColors[Item.Enabled]; Canvas.FillRect(R); InflateRect(R, -1, -1); { Text } if Item.Text <> '' then begin S := Item.Text; Canvas.Brush.Style := bsClear; { speed optimization } Canvas.Font.Color := TextColors[Item.Enabled]; DrawTextStr(Canvas.Handle, S, R, DT_SINGLELINE or DT_NOPREFIX); end; end; procedure TTBEditItemViewer.GetCursor(const Pt: TPoint; var ACursor: HCURSOR); var R: TRect; begin if not Item.Enabled then Exit; GetEditRect(R); OffsetRect(R, -BoundsRect.Left, -BoundsRect.Top); InflateRect(R, -2, -2); if PtInRect(R, Pt) then ACursor := LoadCursor(0, IDC_IBEAM); end; function TTBEditItemViewer.EditLoop(const CapHandle: HWND): Boolean; procedure ControlMessageLoop; function PointInWindow(const Wnd: HWND; const P: TPoint): Boolean; var W: HWND; begin Result := False; W := WindowFromPoint(P); if W = 0 then Exit; if W = Wnd then Result := True else if IsChild(Wnd, W) then Result := True; end; function ContinueLoop: Boolean; begin Result := (ecsContinueLoop in FEditControlStatus) and not View.IsModalEnding and FEditControl.Focused and Item.Enabled; { Note: View.IsModalEnding is checked since TTBView.CancelMode doesn't destroy popup windows; it merely hides them and calls EndModal. So if IsModalEnding returns True we can infer that CancelMode was likely called. } end; var Msg: TMsg; IsKeypadDigit: Boolean; ScanCode: Byte; V: Integer; begin try while ContinueLoop do begin { Examine the next message before popping it out of the queue } if not PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin WaitMessage; Continue; end; case Msg.message of WM_SYSKEYDOWN: begin { Exit immediately if Alt+[key] or F10 are pressed, but not Alt+Shift, Alt+`, or Alt+[keypad digit] } if not(Word(Msg.wParam) in [VK_MENU, VK_SHIFT, VK_HANJA]) then begin IsKeypadDigit := False; { This detect digits regardless of whether Num Lock is on: } ScanCode := Byte(Msg.lParam shr 16); if ScanCode <> 0 then for V := VK_NUMPAD0 to VK_NUMPAD9 do if MapVirtualKey(V, 0) = ScanCode then begin IsKeypadDigit := True; Break; end; if not IsKeypadDigit then begin FEditControlStatus := [ecsClose]; Exit; end; end; end; WM_SYSKEYUP: begin { Exit when Alt is released by itself } if Word(Msg.wParam) = VK_MENU then begin FEditControlStatus := [ecsClose]; Exit; end; end; WM_LBUTTONDOWN, WM_LBUTTONDBLCLK, WM_RBUTTONDOWN, WM_RBUTTONDBLCLK, WM_MBUTTONDOWN, WM_MBUTTONDBLCLK, WM_NCLBUTTONDOWN, WM_NCLBUTTONDBLCLK, WM_NCRBUTTONDOWN, WM_NCRBUTTONDBLCLK, WM_NCMBUTTONDOWN, WM_NCMBUTTONDBLCLK: begin { If a mouse click outside the edit control is in the queue, exit and let the upstream message loop deal with it } if Msg.hwnd <> FEditControl.Handle then Exit; end; WM_MOUSEMOVE, WM_NCMOUSEMOVE: begin if GetCapture = CapHandle then begin if PointInWindow(FEditControl.Handle, Msg.pt) then ReleaseCapture; end else if GetCapture = 0 then begin if not PointInWindow(FEditControl.Handle, Msg.pt) then SetCapture(CapHandle); end; if GetCapture = CapHandle then SetCursor(LoadCursor(0, IDC_ARROW)); end; end; { Now pop the message out of the queue } if not PeekMessage(Msg, 0, Msg.message, Msg.message, PM_REMOVE or PM_NOYIELD) then Continue; if ((Msg.message >= WM_MOUSEFIRST) and (Msg.message <= WM_MOUSELAST)) and (Msg.hwnd = CapHandle) then { discard, so that the selection doesn't get changed } else begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; finally { Make sure there are no outstanding WM_*CHAR messages } RemoveMessages(WM_CHAR, WM_DEADCHAR); RemoveMessages(WM_SYSCHAR, WM_SYSDEADCHAR); end; end; procedure RestoreEditControlWndProc; {$IFNDEF CLR} var OrigWndProc: TWndMethod; begin { NOTE: We can't assign WndProc to WindowProc directly because on Delphi 4 and 5, the compiler generates incorrect code, causing an AV at run-time } OrigWndProc := TEditAccess(FEditControl).WndProc; FEditControl.WindowProc := OrigWndProc; end; {$ELSE} begin IControl(FEditControl).RestoreWndProc; end; {$ENDIF} var Item: TTBEditItem; R: TRect; ActiveWnd, FocusWnd: HWND; begin Item := TTBEditItem(Self.Item); GetEditRect(R); if IsRectEmpty(R) then begin Result := False; Exit; end; ActiveWnd := GetActiveWindow; FocusWnd := GetFocus; { Create the edit control } InflateRect(R, -3, -3); //View.FreeNotification(Self); FEditControl := TEdit.Create(nil); try FEditControl.Visible := False; FEditControl.BorderStyle := bsNone; FEditControl.AutoSize := False; FEditControl.Font.Assign(View.GetFont); FEditControl.Text := Item.Text; FEditControl.CharCase := Item.FCharCase; FEditControl.MaxLength := Item.FMaxLength; FEditControl.BoundsRect := R; FEditControl.WindowProc := EditWndProc; FEditControl.ParentWindow := View.Window.Handle; FEditControl.SelectAll; Item.DoBeginEdit(Self); FEditControl.Visible := True; FEditControl.SetFocus; if GetActiveWindow <> ActiveWnd then { don't gray out title bar of old active window } SendMessage(ActiveWnd, WM_NCACTIVATE, 1, 0) else ActiveWnd := 0; FEditControlStatus := [ecsContinueLoop]; ControlMessageLoop; finally { Restore the original window procedure before destroying the control so it doesn't see a WM_KILLFOCUS message } RestoreEditControlWndProc; FreeAndNil(FEditControl); end; { ensure the area underneath the edit control is repainted immediately } View.Window.Update; { If app is still active, set focus to previous control and restore capture to CapHandle if another control hasn't taken it } if GetActiveWindow <> 0 then begin SetFocus(FocusWnd); if GetCapture = 0 then SetCapture(CapHandle); end; if ActiveWnd <> 0 then SendMessage(ActiveWnd, WM_NCACTIVATE, Ord(GetActiveWindow = ActiveWnd), 0); { The SetFocus call above can change the Z order of windows. If the parent window is a popup window, reassert its topmostness. } if View.Window is TTBPopupWindow then SetWindowPos(View.Window.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE); { Send an MSAA "focus" event now that we're returning to the regular modal loop } View.NotifyFocusEvent; Result := ecsClose in FEditControlStatus; if not Result and (GetCapture = CapHandle) then begin if ecsAccept in FEditControlStatus then { if we are accepting but not closing, Tab must have been pressed } View.Selected := View.NextSelectable(View.Selected, GetKeyState(VK_SHIFT) >= 0); end; end; function TTBEditItemViewer.DoExecute: Boolean; begin { Close any delay-close popup menus before entering the edit loop } View.CancelChildPopups; Result := False; if EditLoop(View.GetCaptureWnd) then begin View.EndModal; if ecsAccept in FEditControlStatus then Result := True; end; end; procedure TTBEditItemViewer.MouseBeginEdit; begin if Item.Enabled then Execute(True) else begin if (View.ParentView = nil) and not View.IsPopup then View.EndModal; end; end; procedure TTBEditItemViewer.MouseDown(Shift: TShiftState; X, Y: Integer; var MouseDownOnMenu: Boolean); begin if IsPtInButtonPart(X, Y) then { for TBX... } MouseBeginEdit else inherited; end; procedure TTBEditItemViewer.MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean); begin if IsPtInButtonPart(X, Y) then { for TBX... } MouseBeginEdit else inherited; end; function TTBEditItemViewer.UsesSameWidth: Boolean; begin Result := False; end; function TTBEditItemViewer.GetAccRole: Integer; const ROLE_SYSTEM_TEXT = $2a; { from OleAcc.h } begin Result := ROLE_SYSTEM_TEXT; end; function TTBEditItemViewer.GetAccValue(var Value: WideString): Boolean; begin Value := TTBEditItem(Item).Text; Result := True; end; { TTBToolbarVisibilityItem } procedure TTBVisibilityToggleItem.Click; begin if Assigned(FControl) then FControl.Visible := not FControl.Visible; inherited; end; procedure TTBVisibilityToggleItem.InitiateAction; begin UpdateProps; end; procedure TTBVisibilityToggleItem.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent = FControl) then Control := nil; end; procedure TTBVisibilityToggleItem.SetControl(Value: TControl); begin if FControl <> Value then begin FControl := Value; if Assigned(Value) then begin Value.FreeNotification(Self); if (Caption = '') and not(csLoading in ComponentState) then {$IFNDEF CLR} Caption := TControlAccess(Value).Caption; {$ELSE} Caption := Value.GetText; {$ENDIF} end; UpdateProps; end; end; procedure TTBVisibilityToggleItem.UpdateProps; begin if (ComponentState * [csDesigning, csLoading, csDestroying] = []) then Checked := Assigned(FControl) and FControl.Visible; end; end.