993 lines
29 KiB
ObjectPascal

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.