📄 menus.pas
字号:
State: TOwnerDrawState);
var
{ These are the hotkeys that the auto-hotkey system will pick from.
Change this global variable at runtime if you want to add or remove
characters from the available characters. Notice that by default we
do not do international characters. }
ValidMenuHotkeys: string = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'; { do not localize }
const
cHotkeyPrefix = '&';
cLineCaption = '-';
cDialogSuffix = '...';
{ StripHotkey removes the & escape char that marks the hotkey character(s) in
the string. When the current locale is a Far East locale, this function also
looks for and removes parens around the hotkey, common in Far East locales. }
function StripHotkey(const Text: string): string;
{ GetHotkey will return the last hotkey that StripHotkey would strip. }
function GetHotkey(const Text: string): string;
{ Similar to AnsiSameText but strips hotkeys before comparing }
function AnsiSameCaption(const Text1, Text2: string): Boolean;
implementation
uses Controls, Forms, Consts;
const
RightToLeftMenuFlag = MFT_RIGHTORDER or MFT_RIGHTJUSTIFY;
cMenuAutoFlagToItem: array [TMenuAutoFlag] of TMenuItemAutoFlag = (maAutomatic, maManual);
cItemAutoFlagToMenu: array [TMenuItemAutoFlag] of TMenuAutoFlag = (maAutomatic, maManual, maAutomatic);
cBooleanToItemAutoFlag: array [Boolean] of TMenuItemAutoFlag = (maManual, maAutomatic);
cItemAutoFlagToBoolean: array [TMenuItemAutoFlag] of Boolean = (True, False, True);
function FindPopupControl(const Pos: TPoint): TControl;
var
Window: TWinControl;
begin
Result := nil;
Window := FindVCLWindow(Pos);
if Window <> nil then
begin
Result := Window.ControlAtPos(Pos, False);
if Result = nil then Result := Window;
end;
end;
procedure Error(ResStr: PResStringRec);
function ReturnAddr: Pointer;
asm
MOV EAX,[ESP+8]
end;
begin
raise EMenuError.CreateRes(ResStr) at ReturnAddr;
end;
{ TShortCut processing routines }
function ShortCut(Key: Word; Shift: TShiftState): TShortCut;
begin
Result := 0;
if WordRec(Key).Hi <> 0 then Exit;
Result := Key;
if ssShift in Shift then Inc(Result, scShift);
if ssCtrl in Shift then Inc(Result, scCtrl);
if ssAlt in Shift then Inc(Result, scAlt);
end;
procedure ShortCutToKey(ShortCut: TShortCut; var Key: Word; var Shift: TShiftState);
begin
Key := ShortCut and not (scShift + scCtrl + scAlt);
Shift := [];
if ShortCut and scShift <> 0 then Include(Shift, ssShift);
if ShortCut and scCtrl <> 0 then Include(Shift, ssCtrl);
if ShortCut and scAlt <> 0 then Include(Shift, ssAlt);
end;
type
TMenuKeyCap = (mkcBkSp, mkcTab, mkcEsc, mkcEnter, mkcSpace, mkcPgUp,
mkcPgDn, mkcEnd, mkcHome, mkcLeft, mkcUp, mkcRight, mkcDown, mkcIns,
mkcDel, mkcShift, mkcCtrl, mkcAlt);
var
MenuKeyCaps: array[TMenuKeyCap] of string = (
SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp,
SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight,
SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt);
function GetSpecialName(ShortCut: TShortCut): string;
var
ScanCode: Integer;
KeyName: array[0..255] of Char;
begin
Result := '';
ScanCode := MapVirtualKey(WordRec(ShortCut).Lo, 0) shl 16;
if ScanCode <> 0 then
begin
GetKeyNameText(ScanCode, KeyName, SizeOf(KeyName));
GetSpecialName := KeyName;
end;
end;
function ShortCutToText(ShortCut: TShortCut): string;
var
Name: string;
begin
case WordRec(ShortCut).Lo of
$08, $09:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + WordRec(ShortCut).Lo - $08)];
$0D: Name := MenuKeyCaps[mkcEnter];
$1B: Name := MenuKeyCaps[mkcEsc];
$20..$28:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + WordRec(ShortCut).Lo - $20)];
$2D..$2E:
Name := MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + WordRec(ShortCut).Lo - $2D)];
$30..$39: Name := Chr(WordRec(ShortCut).Lo - $30 + Ord('0'));
$41..$5A: Name := Chr(WordRec(ShortCut).Lo - $41 + Ord('A'));
$60..$69: Name := Chr(WordRec(ShortCut).Lo - $60 + Ord('0'));
$70..$87: Name := 'F' + IntToStr(WordRec(ShortCut).Lo - $6F);
else
Name := GetSpecialName(ShortCut);
end;
if Name <> '' then
begin
Result := '';
if ShortCut and scShift <> 0 then Result := Result + MenuKeyCaps[mkcShift];
if ShortCut and scCtrl <> 0 then Result := Result + MenuKeyCaps[mkcCtrl];
if ShortCut and scAlt <> 0 then Result := Result + MenuKeyCaps[mkcAlt];
Result := Result + Name;
end
else Result := '';
end;
{ This function is *very* slow. Use sparingly. Return 0 if no VK code was
found for the text }
function TextToShortCut(Text: string): TShortCut;
{ If the front of Text is equal to Front then remove the matching piece
from Text and return True, otherwise return False }
function CompareFront(var Text: string; const Front: string): Boolean;
begin
Result := False;
if (Length(Text) >= Length(Front)) and
(AnsiStrLIComp(PChar(Text), PChar(Front), Length(Front)) = 0) then
begin
Result := True;
Delete(Text, 1, Length(Front));
end;
end;
var
Key: TShortCut;
Shift: TShortCut;
begin
Result := 0;
Shift := 0;
while True do
begin
if CompareFront(Text, MenuKeyCaps[mkcShift]) then Shift := Shift or scShift
else if CompareFront(Text, '^') then Shift := Shift or scCtrl
else if CompareFront(Text, MenuKeyCaps[mkcCtrl]) then Shift := Shift or scCtrl
else if CompareFront(Text, MenuKeyCaps[mkcAlt]) then Shift := Shift or scAlt
else Break;
end;
if Text = '' then Exit;
for Key := $08 to $255 do { Copy range from table in ShortCutToText }
if AnsiCompareText(Text, ShortCutToText(Key)) = 0 then
begin
Result := Key or Shift;
Exit;
end;
end;
{ Menu command managment }
var
CommandPool: TBits;
function UniqueCommand: Word;
begin
Result := CommandPool.OpenBit;
CommandPool[Result] := True;
end;
{ Used to populate or merge menus }
procedure IterateMenus(Func: Pointer; Menu1, Menu2: TMenuItem);
var
I, J: Integer;
IIndex, JIndex: Byte;
Menu1Size, Menu2Size: Integer;
Done: Boolean;
function Iterate(var I: Integer; MenuItem: TMenuItem; AFunc: Pointer): Boolean;
var
Item: TMenuItem;
begin
if MenuItem = nil then Exit;
Result := False;
while not Result and (I < MenuItem.Count) do
begin
Item := MenuItem[I];
if Item.GroupIndex > IIndex then Break;
asm
MOV EAX,Item
MOV EDX,[EBP+8]
PUSH DWORD PTR [EDX]
CALL DWORD PTR AFunc
ADD ESP,4
MOV Result,AL
end;
Inc(I);
end;
end;
begin
I := 0;
J := 0;
Menu1Size := 0;
Menu2Size := 0;
if Menu1 <> nil then Menu1Size := Menu1.Count;
if Menu2 <> nil then Menu2Size := Menu2.Count;
Done := False;
while not Done and ((I < Menu1Size) or (J < Menu2Size)) do
begin
IIndex := High(Byte);
JIndex := High(Byte);
if (I < Menu1Size) then IIndex := Menu1[I].GroupIndex;
if (J < Menu2Size) then JIndex := Menu2[J].GroupIndex;
if IIndex <= JIndex then Done := Iterate(I, Menu1, Func)
else
begin
IIndex := JIndex;
Done := Iterate(J, Menu2, Func);
end;
while (I < Menu1Size) and (Menu1[I].GroupIndex <= IIndex) do Inc(I);
while (J < Menu2Size) and (Menu2[J].GroupIndex <= IIndex) do Inc(J);
end;
end;
{ TMenuActionLink }
procedure TMenuActionLink.AssignClient(AClient: TObject);
begin
FClient := AClient as TMenuItem;
end;
function TMenuActionLink.IsAutoCheckLinked: Boolean;
begin
Result := FClient.AutoCheck = (Action as TCustomAction).AutoCheck;
end;
function TMenuActionLink.IsCaptionLinked: Boolean;
begin
Result := inherited IsCaptionLinked and
AnsiSameCaption(FClient.Caption, (Action as TCustomAction).Caption);
end;
function TMenuActionLink.IsCheckedLinked: Boolean;
begin
Result := inherited IsCheckedLinked and
(FClient.Checked = (Action as TCustomAction).Checked);
end;
function TMenuActionLink.IsEnabledLinked: Boolean;
begin
Result := inherited IsEnabledLinked and
(FClient.Enabled = (Action as TCustomAction).Enabled);
end;
function TMenuActionLink.IsHelpContextLinked: Boolean;
begin
Result := inherited IsHelpContextLinked and
(FClient.HelpContext = (Action as TCustomAction).HelpContext);
end;
function TMenuActionLink.IsHintLinked: Boolean;
begin
Result := inherited IsHintLinked and
(FClient.Hint = (Action as TCustomAction).Hint);
end;
function TMenuActionLink.IsGroupIndexLinked: Boolean;
begin
Result := FClient.RadioItem and inherited IsGroupIndexLinked and
(FClient.GroupIndex = (Action as TCustomAction).GroupIndex);
end;
function TMenuActionLink.IsImageIndexLinked: Boolean;
begin
Result := inherited IsImageIndexLinked and
(FClient.ImageIndex = (Action as TCustomAction).ImageIndex);
end;
function TMenuActionLink.IsShortCutLinked: Boolean;
begin
Result := inherited IsShortCutLinked and
(FClient.ShortCut = (Action as TCustomAction).ShortCut);
end;
function TMenuActionLink.IsVisibleLinked: Boolean;
begin
Result := inherited IsVisibleLinked and
(FClient.Visible = (Action as TCustomAction).Visible);
end;
function TMenuActionLink.IsOnExecuteLinked: Boolean;
begin
Result := inherited IsOnExecuteLinked and
(@FClient.OnClick = @Action.OnExecute);
end;
procedure TMenuActionLink.SetAutoCheck(Value: Boolean);
begin
if IsAutoCheckLinked then FClient.AutoCheck := Value;
end;
procedure TMenuActionLink.SetCaption(const Value: string);
begin
if IsCaptionLinked then FClient.Caption := Value;
end;
procedure TMenuActionLink.SetChecked(Value: Boolean);
begin
if IsCheckedLinked then FClient.Checked := Value;
end;
procedure TMenuActionLink.SetEnabled(Value: Boolean);
begin
if IsEnabledLinked then FClient.Enabled := Value;
end;
procedure TMenuActionLink.SetHelpContext(Value: THelpContext);
begin
if IsHelpContextLinked then FClient.HelpContext := Value;
end;
procedure TMenuActionLink.SetHint(const Value: string);
begin
if IsHintLinked then FClient.Hint := Value;
end;
procedure TMenuActionLink.SetImageIndex(Value: Integer);
begin
if IsImageIndexLinked then FClient.ImageIndex := Value;
end;
procedure TMenuActionLink.SetShortCut(Value: TShortCut);
begin
if IsShortCutLinked then FClient.ShortCut := Value;
end;
procedure TMenuActionLink.SetVisible(Value: Boolean);
begin
if IsVisibleLinked then FClient.Visible := Value;
end;
procedure TMenuActionLink.SetOnExecute(Value: TNotifyEvent);
begin
if IsOnExecuteLinked then FClient.OnClick := Value;
end;
{ TMenuItem }
constructor TMenuItem.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FVisible := True;
FEnabled := True;
FAutoHotkeys := maParent;
FAutoLineReduction := maParent;
FCommand := UniqueCommand;
FImageIndex := -1;
FImageChangeLink := TChangeLink.Create;
FImageChangeLink.OnChange := ImageListChange;
end;
destructor TMenuItem.Destroy;
begin
ShortCutItems.ClearItem(Self);
if FParent <> nil then
begin
FParent.Remove(Self);
FParent := nil;
end;
while Count > 0 do Items[0].Free;
if FHandle <> 0 then
begin
MergeWith(nil);
DestroyMenu(FHandle);
end;
FItems.Free;
FreeAndNil(FActionLink);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -