1797 lines
55 KiB
ObjectPascal
1797 lines
55 KiB
ObjectPascal
unit TB2Toolbar;
|
|
|
|
{$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/TB2Toolbar.pas,v 1.126 2008/06/23 18:05:47 jr Exp $
|
|
}
|
|
|
|
interface
|
|
|
|
{$I TB2Ver.inc}
|
|
|
|
uses
|
|
{$IFDEF JR_D9} Types, {$ENDIF}
|
|
LCLIntf, LCLType, LMessages, Messages, SysUtils, Classes, Graphics, Controls, Forms, ImgList,
|
|
Menus, ActnList,
|
|
TB2Item, TB2Dock;
|
|
|
|
type
|
|
TTBCustomToolbar = class;
|
|
TTBChevronItem = class;
|
|
TTBChevronItemClass = class of TTBChevronItem;
|
|
|
|
TTBToolbarViewClass = class of TTBToolbarView;
|
|
TTBToolbarView = class(TTBView)
|
|
private
|
|
FToolbar: TTBCustomToolbar;
|
|
protected
|
|
procedure AutoSize(AWidth, AHeight: Integer); override;
|
|
procedure DoUpdatePositions(var ASize: TPoint); override;
|
|
function GetChevronItem: TTBCustomItem; override;
|
|
function GetMDIButtonsItem: TTBCustomItem; override;
|
|
function GetMDISystemMenuItem: TTBCustomItem; override;
|
|
public
|
|
constructor Create(AOwner: TComponent; AParentView: TTBView;
|
|
AParentItem: TTBCustomItem; AWindow: TWinControl;
|
|
AIsToolbar, ACustomizing, AUsePriorityList: Boolean); override;
|
|
function GetFont: TFont; override;
|
|
procedure InvalidatePositions; override;
|
|
end;
|
|
|
|
TTBChevronPriorityForNewItems = (tbcpHighest, tbcpLowest);
|
|
|
|
TTBCustomToolbar = class(TTBCustomDockableWindow, ITBItems)
|
|
private
|
|
FBaseSize: TPoint;
|
|
FChevronItem: TTBChevronItem;
|
|
FChevronMoveItems: Boolean;
|
|
FChevronPriorityForNewItems: TTBChevronPriorityForNewItems;
|
|
FDisableAlignArrange: Integer;
|
|
FFloatingWidth: Integer;
|
|
FIgnoreMouseLeave: Boolean;
|
|
FItem: TTBRootItem;
|
|
FLastWrappedLines: Integer;
|
|
FMenuBar: Boolean;
|
|
FOnShortCut: TShortCutEvent;
|
|
FProcessShortCuts: Boolean;
|
|
FMainWindowHookInstalled: Boolean;
|
|
FShrinkMode: TTBShrinkMode;
|
|
FSizeData: TObject;
|
|
FSystemFont: Boolean;
|
|
FUpdateActions: Boolean;
|
|
|
|
procedure CancelHover;
|
|
function CalcChevronOffset(const ADock: TTBDock;
|
|
const AOrientation: TTBViewOrientation): Integer;
|
|
function CalcWrapOffset(const ADock: TTBDock): Integer;
|
|
function CreateWrapper(Index: Integer; Ctl: TControl): TTBControlItem;
|
|
function FindWrapper(Ctl: TControl): TTBControlItem;
|
|
function GetChevronHint: String;
|
|
function GetImages: TCustomImageList;
|
|
function GetItems: TTBCustomItem;
|
|
function GetLinkSubitems: TTBCustomItem;
|
|
function GetOptions: TTBItemOptions;
|
|
procedure InstallMainWindowHook;
|
|
function IsChevronHintStored: Boolean;
|
|
class function MainWindowHook(var Message: TMessage): Boolean; {$IFDEF CLR} static; {$ENDIF}
|
|
procedure SetChevronHint(const Value: String);
|
|
procedure SetChevronMoveItems(Value: Boolean);
|
|
procedure SetChevronPriorityForNewItems(Value: TTBChevronPriorityForNewItems);
|
|
procedure SetFloatingWidth(Value: Integer);
|
|
procedure SetImages(Value: TCustomImageList);
|
|
procedure SetLinkSubitems(Value: TTBCustomItem);
|
|
procedure SetMainWindowHook;
|
|
procedure SetMenuBar(Value: Boolean);
|
|
procedure SetOptions(Value: TTBItemOptions);
|
|
procedure SetProcessShortCuts(Value: Boolean);
|
|
procedure SetShrinkMode(Value: TTBShrinkMode);
|
|
procedure SetSystemFont(Value: Boolean);
|
|
procedure UninstallMainWindowHook;
|
|
procedure UpdateViewProperties;
|
|
|
|
procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
|
|
{$IFNDEF CLR}
|
|
procedure CMControlChange(var Message: TCMControlChange); message CM_CONTROLCHANGE;
|
|
procedure CMControlListChange(var Message: TCMControlListChange); message CM_CONTROLLISTCHANGE;
|
|
{$ENDIF}
|
|
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
|
|
procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
|
|
procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;
|
|
procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
|
|
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
|
|
procedure CMShowHintChanged(var Message: TMessage); message CM_SHOWHINTCHANGED;
|
|
procedure CMWinIniChange(var Message: TWMWinIniChange); message CM_WININICHANGE;
|
|
procedure WMCancelMode(var Message: TWMCancelMode); message WM_CANCELMODE;
|
|
procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
|
|
procedure WMMouseLeave(var Message: TMessage); message WM_MOUSELEAVE;
|
|
procedure WMNCMouseMove(var Message: TWMNCMouseMove); message WM_NCMOUSEMOVE;
|
|
{$IFNDEF JR_D5}
|
|
procedure WMRButtonUp(var Message: TWMRButtonUp); message WM_RBUTTONUP;
|
|
{$ENDIF}
|
|
procedure WMSetCursor(var Message: TWMSetCursor); message WM_SETCURSOR;
|
|
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
|
|
protected
|
|
FMDIButtonsItem: TTBCustomItem;
|
|
FMDISystemMenuItem: TTBCustomItem;
|
|
FView: TTBToolbarView;
|
|
procedure AlignControls(AControl: TControl; var Rect: TRect); override;
|
|
procedure BuildPotentialSizesList(SizesList: TList); dynamic;
|
|
{$IFDEF CLR}
|
|
procedure ControlChange(Inserting: Boolean; Child: TControl); override;
|
|
procedure ControlListChange(Inserting: Boolean; Child: TControl); override;
|
|
{$ENDIF}
|
|
procedure ControlExistsAtPos(const P: TPoint; var ControlExists: Boolean);
|
|
override;
|
|
function DoArrange(CanMoveControls: Boolean; PreviousDockType: TTBDockType;
|
|
NewFloating: Boolean; NewDock: TTBDock): TPoint; override;
|
|
{$IFDEF JR_D5}
|
|
procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
|
|
{$ENDIF}
|
|
procedure GetBaseSize(var ASize: TPoint); override;
|
|
procedure GetMinBarSize(var MinimumSize: TPoint);
|
|
procedure GetMinShrinkSize(var AMinimumSize: Integer); override;
|
|
function GetShrinkMode: TTBShrinkMode; override;
|
|
function GetChevronItemClass: TTBChevronItemClass; dynamic;
|
|
function GetItemClass: TTBRootItemClass; dynamic;
|
|
function GetViewClass: TTBToolbarViewClass; dynamic;
|
|
procedure Loaded; override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer); override;
|
|
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
|
|
procedure Paint; override;
|
|
procedure ResizeBegin(ASizeHandle: TTBSizeHandle); override;
|
|
procedure ResizeTrack(var Rect: TRect; const OrigRect: TRect); override;
|
|
procedure ResizeTrackAccept; override;
|
|
procedure ResizeEnd; override;
|
|
procedure SetChildOrder(Child: TComponent; Order: Integer); override;
|
|
|
|
property SystemFont: Boolean read FSystemFont write SetSystemFont default True;
|
|
property OnShortCut: TShortCutEvent read FOnShortCut write FOnShortCut;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure BeginUpdate;
|
|
procedure EndUpdate;
|
|
procedure CreateWrappersForAllControls;
|
|
procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
|
|
procedure GetTabOrderList(List: TList); override;
|
|
procedure InitiateAction; override;
|
|
function IsShortCut(var Message: TWMKey): Boolean;
|
|
function KeyboardOpen(Key: Char; RequirePrimaryAccel: Boolean): Boolean;
|
|
procedure ReadPositionData(const Data: TTBReadPositionData); override;
|
|
procedure WritePositionData(const Data: TTBWritePositionData); override;
|
|
|
|
property ChevronHint: String read GetChevronHint write SetChevronHint stored IsChevronHintStored;
|
|
property ChevronMoveItems: Boolean read FChevronMoveItems write SetChevronMoveItems default True;
|
|
property ChevronPriorityForNewItems: TTBChevronPriorityForNewItems read FChevronPriorityForNewItems
|
|
write SetChevronPriorityForNewItems default tbcpHighest;
|
|
property FloatingWidth: Integer read FFloatingWidth write SetFloatingWidth default 0;
|
|
property Images: TCustomImageList read GetImages write SetImages;
|
|
property Items: TTBRootItem read FItem;
|
|
property LinkSubitems: TTBCustomItem read GetLinkSubitems write SetLinkSubitems;
|
|
property Options: TTBItemOptions read GetOptions write SetOptions default [];
|
|
property MenuBar: Boolean read FMenuBar write SetMenuBar default False;
|
|
property ProcessShortCuts: Boolean read FProcessShortCuts write SetProcessShortCuts default False;
|
|
property ShrinkMode: TTBShrinkMode read FShrinkMode write SetShrinkMode default tbsmChevron;
|
|
property UpdateActions: Boolean read FUpdateActions write FUpdateActions default True;
|
|
property View: TTBToolbarView read FView;
|
|
published
|
|
property Hint stored False; { Hint is set dynamically; don't save it }
|
|
end;
|
|
|
|
TTBToolbar = class(TTBCustomToolbar)
|
|
published
|
|
property ActivateParent;
|
|
property Align;
|
|
property Anchors;
|
|
property AutoResize;
|
|
property BorderStyle;
|
|
property Caption;
|
|
property ChevronHint;
|
|
property ChevronMoveItems;
|
|
property ChevronPriorityForNewItems;
|
|
property CloseButton;
|
|
property CloseButtonWhenDocked;
|
|
property Color;
|
|
property CurrentDock;
|
|
property DefaultDock;
|
|
property DockableTo;
|
|
property DockMode;
|
|
property DockPos;
|
|
property DockRow;
|
|
property DragHandleStyle;
|
|
property FloatingMode;
|
|
property FloatingWidth;
|
|
property Font;
|
|
property FullSize;
|
|
property HideWhenInactive;
|
|
property Images;
|
|
property Items;
|
|
property LastDock;
|
|
property LinkSubitems;
|
|
property MenuBar;
|
|
property Options;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ProcessShortCuts;
|
|
property Resizable;
|
|
property ShowCaption;
|
|
property ShowHint;
|
|
property ShrinkMode;
|
|
property SmoothDrag;
|
|
property Stretch;
|
|
property SystemFont;
|
|
property TabOrder;
|
|
property UpdateActions;
|
|
property UseLastDock;
|
|
property Visible;
|
|
|
|
property OnClose;
|
|
property OnCloseQuery;
|
|
{$IFDEF JR_D5}
|
|
property OnContextPopup;
|
|
{$ENDIF}
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnMouseDown;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMove;
|
|
property OnRecreated;
|
|
property OnRecreating;
|
|
property OnDockChanged;
|
|
property OnDockChanging;
|
|
property OnDockChangingHidden;
|
|
property OnResize;
|
|
property OnShortCut;
|
|
property OnVisibleChanged;
|
|
end;
|
|
|
|
{ TTBChevronItem & TTBChevronItemViewer }
|
|
|
|
TTBChevronItem = class(TTBCustomItem)
|
|
private
|
|
FToolbar: TTBCustomToolbar;
|
|
function GetDefaultHint: String;
|
|
protected
|
|
function GetChevronParentView: TTBView; override;
|
|
function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
end;
|
|
|
|
TTBChevronItemViewer = class(TTBItemViewer)
|
|
protected
|
|
procedure Paint(const Canvas: TCanvas; const ClientAreaRect: TRect;
|
|
IsSelected, IsPushed, UseDisabledShadow: Boolean); override;
|
|
end;
|
|
|
|
const
|
|
tbChevronSize = 12;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF CLR} System.Runtime.InteropServices, System.Text, {$ENDIF}
|
|
TB2Consts, TB2Common, TB2Hook;
|
|
|
|
const
|
|
{ Constants for TTBCustomToolbar-specific registry values. Do not localize! }
|
|
rvFloatRightX = 'FloatRightX';
|
|
DockTypeToOrientation: array[TTBDockType] of TTBViewOrientation =
|
|
(tbvoHorizontal, tbvoFloating, tbvoHorizontal, tbvoVertical);
|
|
|
|
type
|
|
{ Used internally by the TTBCustomToolbar.Resize* methods }
|
|
TToolbarSizeData = class
|
|
public
|
|
SizeHandle: TTBSizeHandle;
|
|
NewSizes: TList; { List of valid new sizes. Items are casted into TSmallPoints }
|
|
OrigWidth, OrigHeight, NCXDiff: Integer;
|
|
CurRightX: Integer;
|
|
DisableSensCheck, OpSide: Boolean;
|
|
DistanceToSmallerSize, DistanceToLargerSize: Integer;
|
|
end;
|
|
|
|
|
|
procedure HookProc(Code: THookProcCode; Wnd: HWND; WParam: WPARAM;
|
|
LParam: LPARAM);
|
|
var
|
|
Msg: {$IFNDEF CLR} PMsg {$ELSE} TMsg {$ENDIF};
|
|
MainForm: TForm;
|
|
begin
|
|
{ Work around an annoying Windows or VCL bug. If you close all MDI child
|
|
windows, the MDI client window gets the focus, and when it has the focus,
|
|
pressing Alt+[char] doesn't send a WM_SYSCOMMAND message to the form for
|
|
some reason. It seems silly to have to use a hook for this, but I don't
|
|
see a better workaround.
|
|
Also, route Alt+- to the main form so that when an MDI child form is
|
|
maximized, Alt+- brings up the TB2k MDI system menu instead of the
|
|
system's. }
|
|
if Code = hpGetMessage then begin
|
|
{$IFNDEF CLR}
|
|
Msg := PMsg(LParam);
|
|
{$ELSE}
|
|
Msg := TMsg(Marshal.PtrToStructure(IntPtr(LParam), TypeOf(TMsg)));
|
|
{$ENDIF}
|
|
if (Msg.message = WM_SYSCHAR) and (Msg.hwnd <> 0) then begin
|
|
{ Note: On Windows NT/2000/XP, even though we install the hook using
|
|
SetWindowsHookExW, Msg.wParam may either be an ANSI character or a
|
|
Unicode character, due to an apparent bug on these platforms. It is
|
|
an ANSI character when the message passes through a separate
|
|
SetWindowsHookExA-installed WH_GETMESSAGE hook first, and that hook
|
|
calls us via CallNextHookEx. Windows apparently "forgets" to convert
|
|
the character from ANSI back to Unicode in this case.
|
|
We can't convert the character code because there seems to be no way
|
|
to detect whether it is ANSI or Unicode. So we can't really do much
|
|
with Msg.wParam, apart from comparing it against character codes that
|
|
are the same between ANSI and Unicode, such as '-'. }
|
|
MainForm := Application.MainForm;
|
|
if Assigned(MainForm) and MainForm.HandleAllocated and (GetCapture = 0) and
|
|
((Msg.hwnd = MainForm.ClientHandle) or
|
|
((Msg.wParam = Ord('-')) and (MainForm.ClientHandle <> 0) and
|
|
IsChild(MainForm.ClientHandle, Msg.hwnd))) then begin
|
|
{ Redirect the message to the main form.
|
|
Note: Unfortunately, due to a bug in Windows NT 4.0 (and not
|
|
2000/XP/9x/Me), modifications to the message don't take effect if
|
|
another WH_GETMESSAGE hook has been installed above this one.
|
|
(The bug is that CallNextHookEx copies lParam^ to a local buffer, but
|
|
does not propogate the changes made by the hook back to lParam^ when
|
|
it returns.) I don't know of any clean workaround, other than to
|
|
ensure other WH_GETMESSAGE hooks are installed *before*
|
|
Toolbar2000's. }
|
|
Msg.hwnd := MainForm.Handle;
|
|
{$IFDEF CLR}
|
|
Marshal.StructureToPtr(TObject(Msg), IntPtr(LParam), False);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TTBChevronItem.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
FToolbar := AOwner as TTBCustomToolbar;
|
|
ItemStyle := ItemStyle + [tbisSubMenu, tbisNoAutoOpen];
|
|
Hint := GetDefaultHint;
|
|
Caption := EscapeAmpersands(GetShortHint(Hint));
|
|
end;
|
|
|
|
function TTBChevronItem.GetChevronParentView: TTBView;
|
|
begin
|
|
Result := FToolbar.FView;
|
|
end;
|
|
|
|
function TTBChevronItem.GetDefaultHint: String;
|
|
begin
|
|
Result := STBChevronItemMoreButtonsHint;
|
|
end;
|
|
|
|
function TTBChevronItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass;
|
|
begin
|
|
Result := TTBChevronItemViewer;
|
|
end;
|
|
|
|
procedure TTBChevronItemViewer.Paint(const Canvas: TCanvas;
|
|
const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean);
|
|
const
|
|
HorzPattern: array[0..15] of Byte =
|
|
($CC, 0, $66, 0, $33, 0, $66, 0, $CC, 0, 0, 0, 0, 0, 0, 0);
|
|
VertPattern: array[0..15] of Byte =
|
|
($88, 0, $D8, 0, $70, 0, $20, 0, $88, 0, $D8, 0, $70, 0, $20, 0);
|
|
var
|
|
DC: HDC;
|
|
R2: TRect;
|
|
TempBmp: TBitmap;
|
|
|
|
procedure DrawPattern(const Color, Offset: Integer);
|
|
begin
|
|
SelectObject(DC, GetSysColorBrush(Color));
|
|
BitBlt(DC, R2.Left, R2.Top + Offset, R2.Right - R2.Left,
|
|
R2.Bottom - R2.Top, TempBmp.Canvas.Handle, 0, 0, $00E20746 {ROP_DSPDxax});
|
|
end;
|
|
|
|
begin
|
|
DC := Canvas.Handle;
|
|
R2 := ClientAreaRect;
|
|
if Item.Enabled then begin
|
|
if IsPushed then
|
|
DrawEdge(DC, R2, BDR_SUNKENOUTER, BF_RECT)
|
|
else if IsSelected and not(csDesigning in Item.ComponentState) then
|
|
DrawEdge(DC, R2, BDR_RAISEDINNER, BF_RECT);
|
|
end;
|
|
|
|
if View.Orientation <> tbvoVertical then begin
|
|
R2.Top := 4;
|
|
R2.Bottom := R2.Top + 5;
|
|
Inc(R2.Left, 2);
|
|
R2.Right := R2.Left + 8;
|
|
end
|
|
else begin
|
|
R2.Left := R2.Right - 9;
|
|
R2.Right := R2.Left + 5;
|
|
Inc(R2.Top, 2);
|
|
R2.Bottom := R2.Top + 8;
|
|
end;
|
|
if IsPushed then
|
|
OffsetRect(R2, 1, 1);
|
|
TempBmp := TBitmap.Create;
|
|
try
|
|
if View.Orientation = tbvoVertical then
|
|
TempBmp.Handle := CreateMonoBitmap(8, 8, VertPattern)
|
|
else
|
|
TempBmp.Handle := CreateMonoBitmap(8, 8, HorzPattern);
|
|
SetTextColor(DC, clBlack);
|
|
SetBkColor(DC, clWhite);
|
|
if Item.Enabled then
|
|
DrawPattern(COLOR_BTNTEXT, 0)
|
|
else begin
|
|
DrawPattern(COLOR_BTNHIGHLIGHT, 1);
|
|
DrawPattern(COLOR_BTNSHADOW, 0);
|
|
end;
|
|
finally
|
|
TempBmp.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TTBToolbarView }
|
|
|
|
constructor TTBToolbarView.Create(AOwner: TComponent; AParentView: TTBView;
|
|
AParentItem: TTBCustomItem; AWindow: TWinControl;
|
|
AIsToolbar, ACustomizing, AUsePriorityList: Boolean);
|
|
begin
|
|
FToolbar := AOwner as TTBCustomToolbar;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBToolbarView.AutoSize(AWidth, AHeight: Integer);
|
|
begin
|
|
FToolbar.FBaseSize := BaseSize;
|
|
if FToolbar.IsAutoResized then
|
|
FToolbar.ChangeSize(AWidth, AHeight);
|
|
end;
|
|
|
|
procedure TTBToolbarView.DoUpdatePositions(var ASize: TPoint);
|
|
begin
|
|
{ Reset CurrentSize because it probably won't be valid after positions
|
|
are recalculated [2001-06-24] }
|
|
FToolbar.CurrentSize := 0;
|
|
FToolbar.GetMinBarSize(ASize);
|
|
{ On FullSize toolbars, increase ASize.X/Y to WrapOffset so that
|
|
right-aligned items always appear at the right edge even when the toolbar
|
|
isn't wrapping }
|
|
if FToolbar.FullSize then begin
|
|
if (Orientation = tbvoHorizontal) and (ASize.X < WrapOffset) then
|
|
ASize.X := WrapOffset
|
|
else if (Orientation = tbvoVertical) and (ASize.Y < WrapOffset) then
|
|
ASize.Y := WrapOffset;
|
|
end;
|
|
{ Increment FDisableAlignArrange so that we don't recursively arrange when
|
|
CalculatePositions moves controls }
|
|
Inc(FToolbar.FDisableAlignArrange);
|
|
try
|
|
inherited;
|
|
finally
|
|
Dec(FToolbar.FDisableAlignArrange);
|
|
end;
|
|
end;
|
|
|
|
procedure TTBToolbarView.InvalidatePositions;
|
|
begin
|
|
{ Reset CurrentSize because it probably won't be valid after positions
|
|
are recalculated [2001-06-04] }
|
|
FToolbar.CurrentSize := 0;
|
|
inherited;
|
|
end;
|
|
|
|
function TTBToolbarView.GetFont: TFont;
|
|
begin
|
|
if not FToolbar.SystemFont then
|
|
Result := FToolbar.Font
|
|
else
|
|
Result := inherited GetFont;
|
|
end;
|
|
|
|
function TTBToolbarView.GetChevronItem: TTBCustomItem;
|
|
begin
|
|
Result := FToolbar.FChevronItem;
|
|
end;
|
|
|
|
function TTBToolbarView.GetMDIButtonsItem: TTBCustomItem;
|
|
begin
|
|
Result := FToolbar.FMDIButtonsItem;
|
|
end;
|
|
|
|
function TTBToolbarView.GetMDISystemMenuItem: TTBCustomItem;
|
|
begin
|
|
Result := FToolbar.FMDISystemMenuItem;
|
|
end;
|
|
|
|
|
|
{ TTBCustomToolbar }
|
|
|
|
type
|
|
{}TTBCustomItemAccess = class(TTBCustomItem);
|
|
TTBItemViewerAccess = class(TTBItemViewer);
|
|
|
|
constructor TTBCustomToolbar.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
ControlStyle := ControlStyle + [csAcceptsControls, csActionClient] -
|
|
[csCaptureMouse];
|
|
DockableWindowStyles := DockableWindowStyles - [tbdsResizeEightCorner,
|
|
tbdsResizeClipCursor];
|
|
FItem := GetItemClass.Create(Self);
|
|
FItem.ParentComponent := Self;
|
|
FChevronItem := GetChevronItemClass.Create(Self);
|
|
FChevronItem.LinkSubitems := FItem;
|
|
FChevronMoveItems := True;
|
|
FView := GetViewClass.Create(Self, nil, FItem, Self, True, False,
|
|
not(csDesigning in ComponentState));
|
|
FView.BackgroundColor := clBtnFace;
|
|
FUpdateActions := True;
|
|
FShrinkMode := tbsmChevron;
|
|
FSystemFont := True;
|
|
Color := clBtnFace;
|
|
SetBounds(Left, Top, 23, 22);{}
|
|
end;
|
|
|
|
destructor TTBCustomToolbar.Destroy;
|
|
begin
|
|
{ Call Destroying to ensure csDestroying is in ComponentState now. Only
|
|
needed for Delphi 4 and earlier since Delphi 5 calls Destroying in
|
|
TComponent.BeforeDestruction }
|
|
Destroying;
|
|
UninstallHookProc(Self, HookProc);
|
|
UninstallMainWindowHook;
|
|
FreeAndNil(FView);
|
|
FreeAndNil(FChevronItem);
|
|
FreeAndNil(FItem);
|
|
inherited;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetItems: TTBCustomItem;
|
|
begin
|
|
Result := FItem;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetItemClass: TTBRootItemClass;
|
|
begin
|
|
Result := TTBRootItem;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetViewClass: TTBToolbarViewClass;
|
|
begin
|
|
Result := TTBToolbarView;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetChevronItemClass: TTBChevronItemClass;
|
|
begin
|
|
Result := TTBChevronItem;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CreateWrappersForAllControls;
|
|
{ Create wrappers for any controls that don't already have them }
|
|
var
|
|
L: TList;
|
|
I, J, C: Integer;
|
|
begin
|
|
if ControlCount = 0 then
|
|
Exit;
|
|
L := TList.Create;
|
|
try
|
|
L.Capacity := ControlCount;
|
|
for I := 0 to ControlCount-1 do
|
|
L.Add(Controls[I]);
|
|
C := FItem.Count-1;
|
|
for I := 0 to C do
|
|
if FItem[I] is TTBControlItem then begin
|
|
J := L.IndexOf(TTBControlItem(FItem[I]).Control);
|
|
if J <> -1 then
|
|
L[J] := nil;
|
|
end;
|
|
for I := 0 to L.Count-1 do
|
|
if Assigned(L[I]) then
|
|
CreateWrapper(FItem.Count, TControl(L[I]));
|
|
finally
|
|
L.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.Loaded;
|
|
begin
|
|
CreateWrappersForAllControls;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.GetChildren(Proc: TGetChildProc; Root: TComponent);
|
|
begin
|
|
TTBCustomItemAccess(TTBCustomItem(FItem)).GetChildren(Proc, Root);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetChildOrder(Child: TComponent; Order: Integer);
|
|
begin
|
|
if Child is TTBCustomItem then
|
|
TTBCustomItemAccess(TTBCustomItem(FItem)).SetChildOrder(Child, Order);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.AlignControls(AControl: TControl; var Rect: TRect);
|
|
{ VCL calls this whenever any child controls in the toolbar are moved, sized,
|
|
inserted, etc., and also when the toolbar is resized. }
|
|
begin
|
|
if FDisableAlignArrange = 0 then
|
|
Arrange;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.InitiateAction;
|
|
begin
|
|
inherited;
|
|
{}{ also add this to popupmenu(?) }
|
|
{ Update visible top-level items }
|
|
if FUpdateActions then
|
|
FView.InitiateActions;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMColorChanged(var Message: TMessage);
|
|
begin
|
|
{ Synchronize FView.BackgroundColor with the new color }
|
|
if Assigned(FView) then
|
|
FView.BackgroundColor := Color;
|
|
inherited;
|
|
end;
|
|
|
|
function TTBCustomToolbar.CreateWrapper(Index: Integer;
|
|
Ctl: TControl): TTBControlItem;
|
|
var
|
|
I: Integer;
|
|
S: String;
|
|
begin
|
|
Result := TTBControlItem.Create(Owner);
|
|
Result.Control := Ctl;
|
|
if (csDesigning in ComponentState) and Assigned(Owner) then begin
|
|
{ Needs a name for compatibility with form inheritance }
|
|
I := 1;
|
|
while True do begin
|
|
S := Format('TBControlItem%d', [I]);
|
|
if Owner.FindComponent(S) = nil then
|
|
Break;
|
|
Inc(I);
|
|
end;
|
|
Result.Name := S;
|
|
end;
|
|
FItem.Insert(Index, Result);
|
|
end;
|
|
|
|
function TTBCustomToolbar.FindWrapper(Ctl: TControl): TTBControlItem;
|
|
var
|
|
I: Integer;
|
|
Item: TTBCustomItem;
|
|
begin
|
|
Result := nil;
|
|
for I := 0 to FItem.Count-1 do begin
|
|
Item := FItem[I];
|
|
if (Item is TTBControlItem) and
|
|
(TTBControlItem(Item).Control = Ctl) then begin
|
|
Result := TTBControlItem(Item);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$IFNDEF CLR}
|
|
procedure TTBCustomToolbar.CMControlChange(var Message: TCMControlChange);
|
|
{$ELSE}
|
|
procedure TTBCustomToolbar.ControlChange(Inserting: Boolean; Child: TControl);
|
|
{$ENDIF}
|
|
{ A CM_CONTROLCHANGE handler must be used instead of a CM_CONTROLLISTCHANGE
|
|
handler because when a CM_CONTROLLISTCHANGE message is sent it is relayed to
|
|
*all* parents. CM_CONTROLCHANGE messages are only sent to the immediate
|
|
parent. }
|
|
|
|
procedure HandleControlChange(Inserting: Boolean; Child: TControl);
|
|
begin
|
|
{ Don't automatically create TTBControlItem wrappers if the component
|
|
is loading or being updated to reflect changes in an ancestor form,
|
|
because wrappers will be streamed in }
|
|
if Inserting and not(csLoading in ComponentState) and
|
|
not(csUpdating in ComponentState) and
|
|
(FindWrapper(Child) = nil) then
|
|
CreateWrapper(FItem.Count, Child);
|
|
end;
|
|
|
|
begin
|
|
inherited;
|
|
{$IFNDEF CLR}
|
|
HandleControlChange(Message.Inserting, Message.Control);
|
|
{$ELSE}
|
|
HandleControlChange(Inserting, Child);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFNDEF CLR}
|
|
procedure TTBCustomToolbar.CMControlListChange(var Message: TCMControlListChange);
|
|
{$ELSE}
|
|
procedure TTBCustomToolbar.ControlListChange(Inserting: Boolean; Child: TControl);
|
|
{$ENDIF}
|
|
{ Don't handle deletions inside CM_CONTROLCHANGE because CM_CONTROLCHANGE is
|
|
sent *before* a control begins removing itself from its parent. (It used
|
|
to handle both insertions and deletions inside CM_CONTROLCHANGE but this
|
|
caused AV's.) }
|
|
|
|
procedure HandleControlListChange(Inserting: Boolean; Child: TControl);
|
|
var
|
|
Item: TTBControlItem;
|
|
begin
|
|
if not Inserting and Assigned(FItem) then begin
|
|
while True do begin
|
|
Item := FindWrapper(Child);
|
|
if Item = nil then Break;
|
|
{ The control is being removed the control, not necessarily destroyed,
|
|
so set DontFreeControl to True }
|
|
Item.DontFreeControl := True;
|
|
Item.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
inherited;
|
|
{$IFNDEF CLR}
|
|
HandleControlListChange(Message.Inserting, Message.Control);
|
|
{$ELSE}
|
|
HandleControlListChange(Inserting, Child);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMHintShow(var Message: TCMHintShow);
|
|
{ Since the items on a toolbar aren't "real" controls, it needs a CM_HINTSHOW
|
|
handler for their hints to be displayed. }
|
|
begin
|
|
FView.HandleHintShowMessage(Message);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMShowHintChanged(var Message: TMessage);
|
|
begin
|
|
inherited;
|
|
if ShowHint then
|
|
FView.Style := FView.Style + [vsAlwaysShowHints]
|
|
else
|
|
FView.Style := FView.Style - [vsAlwaysShowHints];
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMGetObject(var Message: TMessage);
|
|
begin
|
|
if not FView.HandleWMGetObject(Message) then
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMSetCursor(var Message: TWMSetCursor);
|
|
var
|
|
P: TPoint;
|
|
Viewer: TTBItemViewer;
|
|
Cursor: HCURSOR;
|
|
begin
|
|
if not(csDesigning in ComponentState) and
|
|
(Message.CursorWnd = WindowHandle) and
|
|
(Smallint(Message.HitTest) = HTCLIENT) then begin
|
|
{ Note: This should not change the selection, because we can receive this
|
|
message during a modal loop if a user sets "Screen.Cursor := crDefault"
|
|
inside a submenu's OnClick handler (which really isn't recommended, as
|
|
it won't necessarily restore the cursor we set originally). }
|
|
GetCursorPos(P);
|
|
P := ScreenToClient(P);
|
|
Viewer := FView.ViewerFromPoint(P);
|
|
if Assigned(Viewer) then begin
|
|
Cursor := 0;
|
|
Dec(P.X, Viewer.BoundsRect.Left);
|
|
Dec(P.Y, Viewer.BoundsRect.Top);
|
|
TTBItemViewerAccess(Viewer).GetCursor(P, Cursor);
|
|
if Cursor <> 0 then begin
|
|
SetCursor(Cursor);
|
|
Message.Result := 1;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMSysCommand(var Message: TWMSysCommand);
|
|
var
|
|
ConvertedKey: Char;
|
|
begin
|
|
if FMenuBar and CanFocus then
|
|
with Message do
|
|
if (CmdType and $FFF0 = SC_KEYMENU) and (Key <> VK_SPACE) and
|
|
(GetCapture = 0) then begin
|
|
{$IFNDEF CLR}
|
|
{$IFDEF JR_WIDESTR}
|
|
{ Under Unicode Win32 VCL, no conversion required }
|
|
WideChar(ConvertedKey) := WideChar(Key);
|
|
{$ELSE}
|
|
if Win32Platform = VER_PLATFORM_WIN32_NT then begin
|
|
{ On Windows NT 4/2000/XP, Key is a wide character, so we have to
|
|
convert it. Pressing Alt+N in a Russian input locale, for example,
|
|
results in a Key value of $0442.
|
|
This could perhaps be considered a bug in Windows NT since the
|
|
character codes in other messages such as WM_SYSCHAR aren't left
|
|
in Unicode form.
|
|
The conversion isn't done with the system code page, but rather
|
|
with the code page of the currently active input locale, like
|
|
Windows does when sending WM_(SYS)CHAR messages. }
|
|
if WideCharToMultiByte(GetInputLocaleCodePage, 0, @WideChar(Key), 1,
|
|
@AnsiChar(ConvertedKey), 1, nil, nil) <> 1 then
|
|
Exit; { shouldn't fail, but if it does, we can't continue }
|
|
end
|
|
else begin
|
|
{ On Windows 95/98/Me, Key is not a wide character. }
|
|
AnsiChar(ConvertedKey) := AnsiChar(Key);
|
|
end;
|
|
{$ENDIF}
|
|
{$ELSE}
|
|
if Marshal.SystemDefaultCharSize = 2 then begin
|
|
{ Strings are Unicode on .NET, so no need to downconvert to ANSI }
|
|
ConvertedKey := WideChar(Key);
|
|
end
|
|
else begin
|
|
{ On Windows 98/Me, we have to convert ANSI->Unicode, using the
|
|
code page of the currently active input locale }
|
|
ConvertedKey := Encoding.GetEncoding(GetInputLocaleCodePage).
|
|
GetChars([Byte(Key)])[0];
|
|
end;
|
|
{$ENDIF}
|
|
if not KeyboardOpen(ConvertedKey, False) then begin
|
|
if Key = Ord('-') then Exit;
|
|
MessageBeep(0);
|
|
end;
|
|
Result := 1;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.Paint;
|
|
var
|
|
R: TRect;
|
|
begin
|
|
{ Draw dotted border in design mode on undocked toolbars }
|
|
if not Docked and (csDesigning in ComponentState) then
|
|
with Canvas do begin
|
|
R := ClientRect;
|
|
Pen.Style := psDot;
|
|
Pen.Color := clBtnShadow;
|
|
Brush.Style := bsClear;
|
|
Rectangle(R.Left, R.Top, R.Right, R.Bottom);
|
|
Pen.Style := psSolid;
|
|
end;
|
|
FView.DrawSubitems(Canvas);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMDialogKey(var Message: TCMDialogKey);
|
|
begin
|
|
if not(csDesigning in ComponentState) and
|
|
(Message.CharCode = VK_MENU) and FMenuBar and CanFocus then
|
|
FView.SetAccelsVisibility(True);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMDialogChar(var Message: TCMDialogChar);
|
|
begin
|
|
{ On toolbars that aren't menu bars, handle CM_DIALOGCHAR instead of
|
|
WM_SYSCOMMAND.
|
|
Note: We have to check for csDesigning because on Delphi 2005/2006 we get
|
|
CM_DIALOG* messages if Alt+[key] is pressed while a form with a toolbar is
|
|
open in the embedded designer, and a tab other than Design is currently
|
|
selected (e.g., Code). }
|
|
if not(csDesigning in ComponentState) and
|
|
not FMenuBar and CanFocus and (Message.CharCode <> 0) then
|
|
if KeyboardOpen(Chr(Message.CharCode), True) then begin
|
|
Message.Result := 1;
|
|
Exit;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CancelHover;
|
|
begin
|
|
if not MouseCapture then
|
|
FView.UpdateSelection(Point(Low(Integer), Low(Integer)), True);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMMouseLeave(var Message: TMessage);
|
|
begin
|
|
CancelHover;
|
|
inherited;
|
|
end;
|
|
|
|
{$IFDEF JR_D5}
|
|
procedure TTBCustomToolbar.DoContextPopup(MousePos: TPoint;
|
|
var Handled: Boolean);
|
|
begin
|
|
CancelHover;
|
|
inherited;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFNDEF JR_D5}
|
|
{ Delphi 4 and earlier don't have a DoContextPopup method; we instead have to
|
|
trap WM_RBUTTONUP to determine if a popup menu (might) be displayed }
|
|
procedure TTBCustomToolbar.WMRButtonUp(var Message: TWMRButtonUp);
|
|
begin
|
|
CancelHover;
|
|
inherited;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TTBCustomToolbar.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
P: TPoint;
|
|
Item: TTBCustomItem;
|
|
begin
|
|
if not(csDesigning in ComponentState) then begin
|
|
P := ClientToScreen(Point(X, Y));
|
|
FView.UpdateSelection(P, True);
|
|
if Assigned(FView.Selected) then begin
|
|
Item := FView.Selected.Item;
|
|
if not(tboLongHintInMenuOnly in Item.EffectiveOptions) then
|
|
Hint := Item.Hint
|
|
else
|
|
Hint := '';
|
|
end
|
|
else
|
|
Hint := '';
|
|
end;
|
|
{ Call TrackMouseEvent to be sure that we are notified when the mouse leaves
|
|
the window. We won't get a CM_MOUSELEAVE message if the mouse moves
|
|
directly from the toolbar to another application's window }
|
|
CallTrackMouseEvent(Handle, TME_LEAVE);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMCancelMode(var Message: TWMCancelMode);
|
|
begin
|
|
inherited;
|
|
{ We can receive a WM_CANCELMODE message during a modal loop if a dialog
|
|
pops up. Respond by hiding menus to make it look like the modal loop
|
|
has returned, even though it really hasn't yet.
|
|
Note: Similar code in TTBModalHandler.WndProc. }
|
|
if vsModal in FView.State then
|
|
FView.CancelMode;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMMouseLeave(var Message: TMessage);
|
|
begin
|
|
{ A WM_MOUSELEAVE handler is necessary because the control won't get a
|
|
CM_MOUSELEAVE message if the user presses Alt+Space. Also, CM_MOUSELEAVE
|
|
messages are also not sent if the application is in a
|
|
Application.ProcessMessages loop. }
|
|
if not FIgnoreMouseLeave then
|
|
CancelHover;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WMNCMouseMove(var Message: TWMNCMouseMove);
|
|
begin
|
|
Hint := '';
|
|
CancelHover;
|
|
inherited;
|
|
end;
|
|
|
|
function TTBCustomToolbar.KeyboardOpen(Key: Char;
|
|
RequirePrimaryAccel: Boolean): Boolean;
|
|
var
|
|
I: TTBItemViewer;
|
|
IsOnlyItemWithAccel: Boolean;
|
|
begin
|
|
Result := False;
|
|
{ Sanity check: Bail out early if re-entered }
|
|
if vsModal in FView.State then
|
|
Exit;
|
|
I := nil;
|
|
FView.SetAccelsVisibility(True);
|
|
try
|
|
if Key = #0 then begin
|
|
I := FView.FirstSelectable;
|
|
if I = nil then Exit;
|
|
FView.Selected := I;
|
|
FView.EnterToolbarLoop([]);
|
|
end
|
|
else begin
|
|
I := FView.NextSelectableWithAccel(nil, Key, RequirePrimaryAccel,
|
|
IsOnlyItemWithAccel);
|
|
if (I = nil) or not I.Item.Enabled then
|
|
Exit;
|
|
if IsOnlyItemWithAccel then begin
|
|
FView.Selected := I;
|
|
FView.EnterToolbarLoop([tbetExecuteSelected]);
|
|
end
|
|
else if FMenuBar then begin
|
|
FView.Selected := I;
|
|
FView.EnterToolbarLoop([]);
|
|
end
|
|
else
|
|
Exit;
|
|
end;
|
|
Result := True;
|
|
finally
|
|
if Assigned(I) then
|
|
FView.SetAccelsVisibility(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
var
|
|
OldParent: TWinControl;
|
|
P: TPoint;
|
|
Item: TTBCustomItem;
|
|
begin
|
|
OldParent := Parent;
|
|
inherited;
|
|
if Parent <> OldParent then
|
|
{ if the inherited handler (TTBDockableWindow.MouseDown) changed the Parent
|
|
(due to the toolbar moving between docks), nothing else should be done }
|
|
Exit;
|
|
if not(csDesigning in ComponentState) and (Button = mbLeft) then begin
|
|
P := ClientToScreen(Point(X, Y));
|
|
FView.UpdateSelection(P, True);
|
|
if Assigned(FView.Selected) then begin
|
|
Item := FView.Selected.Item;
|
|
if not(tbisClicksTransparent in TTBCustomItemAccess(Item).ItemStyle) then begin
|
|
FIgnoreMouseLeave := True;
|
|
try
|
|
FView.EnterToolbarLoop([tbetMouseDown]);
|
|
finally
|
|
FIgnoreMouseLeave := False;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMFontChanged(var Message: TMessage);
|
|
begin
|
|
inherited;
|
|
if not FSystemFont then
|
|
Arrange;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetChevronHint: String;
|
|
begin
|
|
Result := FChevronItem.Hint;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetChevronHint(const Value: String);
|
|
begin
|
|
FChevronItem.Hint := Value;
|
|
FChevronItem.Caption := EscapeAmpersands(GetShortHint(Value));
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetChevronMoveItems(Value: Boolean);
|
|
begin
|
|
if FChevronMoveItems <> Value then begin
|
|
FChevronMoveItems := Value;
|
|
FView.UsePriorityList := Value and not(csDesigning in ComponentState);
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetChevronPriorityForNewItems(Value: TTBChevronPriorityForNewItems);
|
|
begin
|
|
FChevronPriorityForNewItems := Value;
|
|
FView.NewViewersGetHighestPriority := (Value = tbcpHighest);
|
|
end;
|
|
|
|
function TTBCustomToolbar.IsChevronHintStored: Boolean;
|
|
begin
|
|
Result := (FChevronItem.Hint <> FChevronItem.GetDefaultHint);
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetImages: TCustomImageList;
|
|
begin
|
|
Result := FItem.SubMenuImages;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetImages(Value: TCustomImageList);
|
|
begin
|
|
FItem.SubMenuImages := Value;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetLinkSubitems: TTBCustomItem;
|
|
begin
|
|
Result := FItem.LinkSubitems;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetLinkSubitems(Value: TTBCustomItem);
|
|
begin
|
|
FItem.LinkSubitems := Value;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetMenuBar(Value: Boolean);
|
|
begin
|
|
if FMenuBar <> Value then begin
|
|
FMenuBar := Value;
|
|
if Value then begin
|
|
ControlStyle := ControlStyle + [csMenuEvents];
|
|
FView.Style := FView.Style + [vsMenuBar, vsUseHiddenAccels];
|
|
end
|
|
else begin
|
|
ControlStyle := ControlStyle - [csMenuEvents];
|
|
FView.Style := FView.Style - [vsMenuBar, vsUseHiddenAccels];
|
|
end;
|
|
if not(csLoading in ComponentState) then begin
|
|
FullSize := Value;
|
|
if Value then
|
|
ShrinkMode := tbsmWrap
|
|
else
|
|
ShrinkMode := tbsmChevron;
|
|
CloseButton := not Value;
|
|
ProcessShortCuts := Value;
|
|
end;
|
|
if Value and not(csDesigning in ComponentState) then
|
|
InstallHookProc(Self, HookProc, [hpGetMessage])
|
|
else
|
|
UninstallHookProc(Self, HookProc);
|
|
SetMainWindowHook;
|
|
end;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetOptions: TTBItemOptions;
|
|
begin
|
|
Result := FItem.Options;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetOptions(Value: TTBItemOptions);
|
|
begin
|
|
FItem.Options := Value;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetProcessShortCuts(Value: Boolean);
|
|
begin
|
|
if FProcessShortCuts <> Value then begin
|
|
FProcessShortCuts := Value;
|
|
SetMainWindowHook;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetSystemFont(Value: Boolean);
|
|
begin
|
|
if FSystemFont <> Value then begin
|
|
FSystemFont := Value;
|
|
Arrange;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetShrinkMode(Value: TTBShrinkMode);
|
|
begin
|
|
if FShrinkMode <> Value then begin
|
|
FShrinkMode := Value;
|
|
if Docked then
|
|
CurrentDock.ArrangeToolbars
|
|
else if not Floating then
|
|
Arrange;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetFloatingWidth(Value: Integer);
|
|
begin
|
|
if FFloatingWidth <> Value then begin
|
|
FFloatingWidth := Value;
|
|
if Floating then begin
|
|
UpdateViewProperties;
|
|
Arrange;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TTBCustomToolbar.CalcWrapOffset(const ADock: TTBDock): Integer;
|
|
begin
|
|
if ADock = nil then
|
|
Result := FFloatingWidth
|
|
else begin
|
|
if FShrinkMode = tbsmWrap then begin
|
|
if not(ADock.Position in [dpLeft, dpRight]) then
|
|
Result := ADock.Width - ADock.NonClientWidth - NonClientWidth
|
|
else
|
|
Result := ADock.Height - ADock.NonClientHeight - NonClientHeight;
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
function TTBCustomToolbar.CalcChevronOffset(const ADock: TTBDock;
|
|
const AOrientation: TTBViewOrientation): Integer;
|
|
begin
|
|
if (FShrinkMode = tbsmChevron) and Docked and (ADock = CurrentDock) then begin
|
|
Result := CurrentSize;
|
|
{ Subtract non-client size }
|
|
if AOrientation <> tbvoVertical then
|
|
Dec(Result, NonClientWidth)
|
|
else
|
|
Dec(Result, NonClientHeight);
|
|
if Result < 0 then
|
|
Result := 0; { in case CurrentSize wasn't properly initialized yet }
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.UpdateViewProperties;
|
|
var
|
|
DT: TTBDockType;
|
|
begin
|
|
DT := TBGetDockTypeOf(CurrentDock, Floating);
|
|
FView.Orientation := DockTypeToOrientation[DT];
|
|
FView.ChevronSize := tbChevronSize;
|
|
if Assigned(CurrentDock) or Floating then begin
|
|
FView.ChevronOffset := CalcChevronOffset(CurrentDock, FView.Orientation);
|
|
FView.WrapOffset := CalcWrapOffset(CurrentDock);
|
|
end
|
|
else begin
|
|
FView.ChevronOffset := 0;
|
|
FView.WrapOffset := 0;
|
|
{ Only enable chevron/wrapping when the width of the toolbar is fixed }
|
|
if not AutoResize or ((akLeft in Anchors) and (akRight in Anchors)) then begin
|
|
if FShrinkMode = tbsmChevron then
|
|
FView.ChevronOffset := Width - NonClientWidth
|
|
else if FShrinkMode = tbsmWrap then
|
|
FView.WrapOffset := Width - NonClientWidth;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{}{DOCKING STUFF}
|
|
|
|
procedure TTBCustomToolbar.ReadPositionData(const Data: TTBReadPositionData);
|
|
begin
|
|
inherited;
|
|
with Data do
|
|
FloatingWidth := ReadIntProc(Name, rvFloatRightX, 0, ExtraData);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.WritePositionData(const Data: TTBWritePositionData);
|
|
begin
|
|
inherited;
|
|
with Data do
|
|
WriteIntProc(Name, rvFloatRightX, FFloatingWidth, ExtraData);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.GetMinBarSize(var MinimumSize: TPoint);
|
|
var
|
|
WH: Integer;
|
|
begin
|
|
MinimumSize.X := 0;
|
|
MinimumSize.Y := 0;
|
|
if Docked then begin
|
|
WH := CurrentDock.GetMinRowSize(EffectiveDockRow, Self);
|
|
if not(CurrentDock.Position in [dpLeft, dpRight]) then
|
|
MinimumSize.Y := WH
|
|
else
|
|
MinimumSize.X := WH;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.GetBaseSize(var ASize: TPoint);
|
|
begin
|
|
FView.ValidatePositions;
|
|
ASize := FBaseSize;
|
|
end;
|
|
|
|
function TTBCustomToolbar.DoArrange(CanMoveControls: Boolean;
|
|
PreviousDockType: TTBDockType; NewFloating: Boolean; NewDock: TTBDock): TPoint;
|
|
var
|
|
DT: TTBDockType;
|
|
O: TTBViewOrientation;
|
|
TempBaseSize: TPoint;
|
|
begin
|
|
//outputdebugstring (pchar(format('%s.DoArrange(%d)', [Name, Ord(CanMoveControls)])));
|
|
if CanMoveControls then begin
|
|
UpdateViewProperties;
|
|
Result := FView.UpdatePositions;
|
|
end
|
|
else begin
|
|
DT := TBGetDockTypeOf(NewDock, NewFloating);
|
|
O := DockTypeToOrientation[DT];
|
|
Result.X := 0;
|
|
Result.Y := 0;
|
|
FView.CalculatePositions(False, O, CalcWrapOffset(NewDock),
|
|
CalcChevronOffset(NewDock, O), tbChevronSize, TempBaseSize, Result,
|
|
FLastWrappedLines);
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.ControlExistsAtPos(const P: TPoint;
|
|
var ControlExists: Boolean);
|
|
var
|
|
P2: TPoint;
|
|
begin
|
|
inherited;
|
|
if not ControlExists and not(csDesigning in ComponentState) then begin
|
|
P2 := ClientToScreen(P);
|
|
FView.UpdateSelection(P2, True);
|
|
if Assigned(FView.Selected) and
|
|
not(tbisClicksTransparent in TTBCustomItemAccess(FView.Selected.Item).ItemStyle) then
|
|
ControlExists := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.BuildPotentialSizesList(SizesList: TList);
|
|
var
|
|
Margins: TRect;
|
|
MinX, SaveWrapX: Integer;
|
|
X, PrevWrappedLines: Integer;
|
|
S: TPoint;
|
|
S2: TSmallPoint;
|
|
begin
|
|
View.GetMargins(tbvoFloating, Margins);
|
|
MinX := Margins.Left + Margins.Right;
|
|
SaveWrapX := FFloatingWidth;
|
|
try
|
|
{ Add the widest size to the list }
|
|
FFloatingWidth := 0;
|
|
S := DoArrange(False, dtNotDocked, True, nil);
|
|
SizesList.Add(TListItemType(PointToSmallPoint(S)));
|
|
{ Calculate and add rest of sizes to the list }
|
|
PrevWrappedLines := 1;
|
|
X := S.X-1;
|
|
while X >= MinX do begin
|
|
FFloatingWidth := X;
|
|
S := DoArrange(False, dtNotDocked, True, nil);
|
|
if S.X > X then { if it refuses to go any smaller }
|
|
Break
|
|
else
|
|
if X = S.X then begin
|
|
S2 := PointToSmallPoint(S);
|
|
if FLastWrappedLines <> PrevWrappedLines then
|
|
SizesList.Add(TListItemType(S2))
|
|
else
|
|
SizesList[SizesList.Count-1] := TListItemType(S2);
|
|
PrevWrappedLines := FLastWrappedLines;
|
|
Dec(X);
|
|
end
|
|
else
|
|
X := S.X;
|
|
end;
|
|
finally
|
|
FFloatingWidth := SaveWrapX;
|
|
end;
|
|
end;
|
|
|
|
function CompareSizesX(Item1, Item2: TListItemType): Integer;
|
|
begin
|
|
{ Sorts in descending order }
|
|
Result := TSmallPoint(Item2).X - TSmallPoint(Item1).X;
|
|
end;
|
|
|
|
function CompareSizesY(Item1, Item2: TListItemType): Integer;
|
|
begin
|
|
{ Sorts in descending order }
|
|
Result := TSmallPoint(Item2).Y - TSmallPoint(Item1).Y;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.ResizeBegin(ASizeHandle: TTBSizeHandle);
|
|
const
|
|
MaxDistance = 12;
|
|
var
|
|
I, NewSize: Integer;
|
|
S, N: TSmallPoint;
|
|
P: TPoint;
|
|
begin
|
|
inherited;
|
|
|
|
FSizeData := TToolbarSizeData.Create;
|
|
|
|
with TToolbarSizeData(FSizeData) do begin
|
|
SizeHandle := ASizeHandle;
|
|
OrigWidth := Parent.Width;
|
|
OrigHeight := Parent.Height;
|
|
NCXDiff := ClientToScreen(Point(0, 0)).X - Parent.Left;
|
|
CurRightX := FFloatingWidth;
|
|
DisableSensCheck := False;
|
|
OpSide := False;
|
|
|
|
NewSizes := TList.Create;
|
|
BuildPotentialSizesList(NewSizes);
|
|
for I := 0 to NewSizes.Count-1 do begin
|
|
P := SmallPointToPoint(TSmallPoint(NewSizes[I]));
|
|
AddFloatingNCAreaToSize(P);
|
|
NewSizes[I] := TListItemType(PointToSmallPoint(P));
|
|
end;
|
|
if ASizeHandle in [twshTop, twshBottom] then
|
|
NewSizes.Sort(CompareSizesY)
|
|
else
|
|
NewSizes.Sort(CompareSizesX);
|
|
|
|
{ Calculate distance in pixels to the nearest potential sizes smaller and
|
|
larger than the current size, up to a maximum of MaxDistance pixels. }
|
|
DistanceToSmallerSize := 0;
|
|
DistanceToLargerSize := 0;
|
|
for I := 0 to NewSizes.Count-1 do begin
|
|
S := TSmallPoint(NewSizes[I]);
|
|
if (S.X = OrigWidth) and (S.Y = OrigHeight) then begin
|
|
if I > 0 then begin
|
|
N := TSmallPoint(NewSizes[I-1]);
|
|
if ASizeHandle in [twshLeft, twshRight] then
|
|
NewSize := N.X - S.X
|
|
else
|
|
NewSize := N.Y - S.Y;
|
|
if NewSize > MaxDistance then
|
|
NewSize := MaxDistance;
|
|
DistanceToLargerSize := NewSize;
|
|
end;
|
|
if I < NewSizes.Count-1 then begin
|
|
N := TSmallPoint(NewSizes[I+1]);
|
|
if ASizeHandle in [twshLeft, twshRight] then
|
|
NewSize := S.X - N.X
|
|
else
|
|
NewSize := S.Y - N.Y;
|
|
if NewSize > MaxDistance then
|
|
NewSize := MaxDistance;
|
|
DistanceToSmallerSize := NewSize;
|
|
end;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.ResizeTrack(var Rect: TRect; const OrigRect: TRect);
|
|
var
|
|
Pos: TPoint;
|
|
NewOpSide: Boolean;
|
|
Reverse: Boolean;
|
|
I: Integer;
|
|
P: TSmallPoint;
|
|
begin
|
|
inherited;
|
|
|
|
with TToolbarSizeData(FSizeData) do begin
|
|
Pos.X := Rect.Right - Rect.Left;
|
|
Pos.Y := Rect.Bottom - Rect.Top;
|
|
|
|
{ Like Office, don't change from the original size until the mouse is moved
|
|
a reasonable distance left/up or right/down. Without this, dragging the
|
|
mouse just one pixel in either direction would cause the toolbar to
|
|
change sizes. }
|
|
if SizeHandle in [twshLeft, twshRight] then
|
|
NewOpSide := Pos.X < OrigWidth
|
|
else
|
|
NewOpSide := Pos.Y < OrigHeight;
|
|
if (not DisableSensCheck) or (OpSide <> NewOpSide) then begin
|
|
DisableSensCheck := False;
|
|
OpSide := NewOpSide;
|
|
if SizeHandle in [twshLeft, twshRight] then begin
|
|
if (Pos.X > OrigWidth-DistanceToSmallerSize) and (Pos.X < OrigWidth+DistanceToLargerSize) then
|
|
Pos.X := OrigWidth;
|
|
end
|
|
else begin
|
|
if (Pos.Y > OrigHeight-DistanceToSmallerSize) and (Pos.Y < OrigHeight+DistanceToLargerSize) then
|
|
Pos.Y := OrigHeight;
|
|
end;
|
|
end;
|
|
|
|
Rect := OrigRect;
|
|
|
|
if SizeHandle in [twshLeft, twshRight] then
|
|
Reverse := Pos.X > OrigWidth
|
|
else
|
|
Reverse := Pos.Y > OrigHeight;
|
|
if not Reverse then
|
|
I := 0
|
|
else
|
|
I := NewSizes.Count-1;
|
|
while True do begin
|
|
P := TSmallPoint(NewSizes[I]);
|
|
if SizeHandle in [twshLeft, twshRight] then begin
|
|
if (not Reverse and ((I = NewSizes.Count-1) or (Pos.X >= P.X))) or
|
|
(Reverse and ((I = 0) or (Pos.X <= P.X))) then begin
|
|
if I = 0 then
|
|
CurRightX := 0
|
|
else
|
|
CurRightX := P.X - NCXDiff*2;
|
|
if SizeHandle = twshRight then
|
|
Rect.Right := Rect.Left + P.X
|
|
else
|
|
Rect.Left := Rect.Right - P.X;
|
|
Rect.Bottom := Rect.Top + P.Y;
|
|
DisableSensCheck := not EqualRect(Rect, OrigRect);
|
|
Break;
|
|
end;
|
|
end
|
|
else begin
|
|
if (not Reverse and ((I = NewSizes.Count-1) or (Pos.Y >= P.Y))) or
|
|
(Reverse and ((I = 0) or (Pos.Y <= P.Y))) then begin
|
|
if I = NewSizes.Count-1 then
|
|
CurRightX := 0
|
|
else
|
|
CurRightX := P.X - NCXDiff*2;
|
|
if SizeHandle = twshBottom then
|
|
Rect.Bottom := Rect.Top + P.Y
|
|
else
|
|
Rect.Top := Rect.Bottom - P.Y;
|
|
Rect.Right := Rect.Left + P.X;
|
|
DisableSensCheck := not EqualRect(Rect, OrigRect);
|
|
Break;
|
|
end;
|
|
end;
|
|
if not Reverse then
|
|
Inc(I)
|
|
else
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.ResizeTrackAccept;
|
|
begin
|
|
inherited;
|
|
FloatingWidth := TToolbarSizeData(FSizeData).CurRightX;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.ResizeEnd;
|
|
begin
|
|
inherited;
|
|
if Assigned(FSizeData) then begin
|
|
with TToolbarSizeData(FSizeData) do
|
|
FreeAndNil(NewSizes);
|
|
FreeAndNil(FSizeData);
|
|
end;
|
|
end;
|
|
|
|
function TTBCustomToolbar.GetShrinkMode: TTBShrinkMode;
|
|
begin
|
|
Result := FShrinkMode;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.GetMinShrinkSize(var AMinimumSize: Integer);
|
|
var
|
|
I: TTBItemViewer;
|
|
begin
|
|
I := FView.HighestPriorityViewer;
|
|
if Assigned(I) then begin
|
|
if not(CurrentDock.Position in [dpLeft, dpRight]) then
|
|
AMinimumSize := I.BoundsRect.Right - I.BoundsRect.Left
|
|
else
|
|
AMinimumSize := I.BoundsRect.Bottom - I.BoundsRect.Top;
|
|
end;
|
|
if not(CurrentDock.Position in [dpLeft, dpRight]) then
|
|
Inc(AMinimumSize, NonClientWidth)
|
|
else
|
|
Inc(AMinimumSize, NonClientHeight);
|
|
Inc(AMinimumSize, tbChevronSize);
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.BeginUpdate;
|
|
begin
|
|
FView.BeginUpdate;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.EndUpdate;
|
|
begin
|
|
FView.EndUpdate;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.GetTabOrderList(List: TList);
|
|
var
|
|
CtlList: TList;
|
|
I, J: Integer;
|
|
CtlI, CtlJ: TWinControl;
|
|
begin
|
|
inherited;
|
|
{ Remove off-edge items and their children from List }
|
|
CtlList := TList.Create;
|
|
try
|
|
FView.GetOffEdgeControlList(CtlList);
|
|
for I := 0 to CtlList.Count-1 do begin
|
|
CtlI := TWinControl(CtlList[I]);
|
|
J := 0;
|
|
while J < List.Count do begin
|
|
CtlJ := TWinControl(List[J]);
|
|
if (CtlJ = CtlI) or CtlI.ContainsControl(CtlJ) then
|
|
List.Delete(J)
|
|
else
|
|
Inc(J);
|
|
end;
|
|
end;
|
|
finally
|
|
CtlList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.CMWinIniChange(var Message: TWMWinIniChange);
|
|
begin
|
|
inherited;
|
|
if {$IFNDEF CLR}TMessage{$ENDIF}(Message).WParam = SPI_SETNONCLIENTMETRICS then begin
|
|
TBInitToolbarSystemFont;
|
|
Arrange;
|
|
end;
|
|
end;
|
|
|
|
function TTBCustomToolbar.IsShortCut(var Message: TWMKey): Boolean;
|
|
begin
|
|
Result := False;
|
|
if Assigned(FOnShortCut) then
|
|
FOnShortCut(Message, Result);
|
|
Result := Result or FItem.IsShortCut(Message);
|
|
end;
|
|
|
|
var
|
|
HookCount: Integer;
|
|
HookList: TList;
|
|
|
|
class function TTBCustomToolbar.MainWindowHook(var Message: TMessage): Boolean;
|
|
|
|
function GetActiveForm: TCustomForm;
|
|
var
|
|
Wnd: HWND;
|
|
Ctl: TWinControl;
|
|
begin
|
|
{ Note: We don't use Screen.ActiveCustomForm because when an EXE calls a
|
|
DLL that shows a modal form, Screen.ActiveCustomForm doesn't change in
|
|
the EXE; it remains set to the last form that was active in the EXE.
|
|
Use FindControl(GetActiveWindow) instead to avoid this problem; it will
|
|
return nil when a form in another module is active. }
|
|
Result := nil;
|
|
Wnd := GetActiveWindow;
|
|
if Wnd <> 0 then begin
|
|
Ctl := FindControl(Wnd);
|
|
if Assigned(Ctl) and (Ctl is TCustomForm) then
|
|
Result := TCustomForm(Ctl);
|
|
end;
|
|
end;
|
|
|
|
function HandleShortCutOnForm(const Form: TCustomForm): Boolean;
|
|
var
|
|
I: Integer;
|
|
Toolbar: TTBCustomToolbar;
|
|
{$IFDEF CLR}
|
|
KeyMsg: TWMKey;
|
|
{$ENDIF}
|
|
begin
|
|
Result := False;
|
|
if Form = nil then
|
|
Exit;
|
|
for I := 0 to HookList.Count-1 do begin
|
|
Toolbar := TTBCustomToolbar(HookList[I]);
|
|
if Toolbar.ProcessShortCuts and
|
|
(TBGetToolWindowParentForm(Toolbar) = Form) and
|
|
IsWindowEnabled(Form.Handle) then begin
|
|
{$IFNDEF CLR}
|
|
if Toolbar.IsShortCut(TWMKey(Message)) then begin
|
|
{$ELSE}
|
|
KeyMsg := TWMKey.Create(Message);
|
|
if Toolbar.IsShortCut(KeyMsg) then begin
|
|
{$ENDIF}
|
|
Message.Result := 1;
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TraverseControls(Container: TWinControl): Boolean;
|
|
var
|
|
I: Integer;
|
|
Control: TControl;
|
|
begin
|
|
Result := False;
|
|
if Container.Showing then
|
|
for I := 0 to Container.ControlCount - 1 do begin
|
|
Control := Container.Controls[I];
|
|
if Control.Visible and Control.Enabled then begin
|
|
if (csMenuEvents in Control.ControlStyle) and
|
|
((Control is TTBDock) or (Control is TTBCustomToolbar)) and
|
|
(Control.Perform(WM_SYSCOMMAND, TMessage(Message).WParam,
|
|
TMessage(Message).LParam) <> 0) or (Control is TWinControl) and
|
|
TraverseControls(TWinControl(Control)) then begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ActiveForm: TCustomForm;
|
|
ActiveMDIChild: TForm;
|
|
begin
|
|
Result := False;
|
|
if (Message.Msg = CM_APPKEYDOWN) and Assigned(HookList) then begin
|
|
{ Process shortcuts on toolbars. Search forms in this order:
|
|
1. If the active form is an MDI parent form, the active MDI child form
|
|
if it has the focus.
|
|
2. The active form.
|
|
3. The main form. }
|
|
ActiveForm := GetActiveForm;
|
|
if Assigned(ActiveForm) and (ActiveForm is TForm) and
|
|
(TForm(ActiveForm).FormStyle = fsMDIForm) then begin
|
|
ActiveMDIChild := TForm(ActiveForm).ActiveMDIChild;
|
|
{ Don't search the child form if a control on the MDI parent form is
|
|
currently focused (i.e. Screen.ActiveCustomForm <> ActiveMDIChild) }
|
|
if Assigned(ActiveMDIChild) and
|
|
(Screen.ActiveCustomForm = ActiveMDIChild) and
|
|
HandleShortCutOnForm(ActiveMDIChild) then begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
if HandleShortCutOnForm(ActiveForm) then
|
|
Result := True
|
|
else begin
|
|
if (Application.MainForm <> ActiveForm) and
|
|
HandleShortCutOnForm(Application.MainForm) then
|
|
Result := True;
|
|
end;
|
|
end
|
|
else if Message.Msg = CM_APPSYSCOMMAND then begin
|
|
{ Handle "Alt or Alt+[key] pressed on secondary form" case. If there's a
|
|
menu bar on the active form we want the keypress to go to it instead of
|
|
to the main form (the VCL's default handling). }
|
|
ActiveForm := GetActiveForm;
|
|
if Assigned(ActiveForm) and IsWindowEnabled(ActiveForm.Handle) and
|
|
IsWindowVisible(ActiveForm.Handle) and TraverseControls(ActiveForm) then begin
|
|
Message.Result := 1;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.SetMainWindowHook;
|
|
begin
|
|
if (ProcessShortCuts or MenuBar) and not(csDesigning in ComponentState) then
|
|
InstallMainWindowHook
|
|
else
|
|
UninstallMainWindowHook;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.InstallMainWindowHook;
|
|
begin
|
|
if FMainWindowHookInstalled then
|
|
Exit;
|
|
if HookCount = 0 then
|
|
Application.HookMainWindow(MainWindowHook);
|
|
Inc(HookCount);
|
|
AddToList(HookList, Self);
|
|
FMainWindowHookInstalled := True;
|
|
end;
|
|
|
|
procedure TTBCustomToolbar.UninstallMainWindowHook;
|
|
begin
|
|
if not FMainWindowHookInstalled then
|
|
Exit;
|
|
FMainWindowHookInstalled := False;
|
|
RemoveFromList(HookList, Self);
|
|
Dec(HookCount);
|
|
if HookCount = 0 then
|
|
Application.UnhookMainWindow(MainWindowHook);
|
|
end;
|
|
|
|
end.
|