📄 menus.pas
字号:
FreeAndNil(FImageChangeLink);
if FCommand <> 0 then CommandPool[FCommand] := False;
if Assigned(FBitmap) then FBitmap.Free;
inherited Destroy;
end;
const
Checks: array[Boolean] of DWORD = (MF_UNCHECKED, MF_CHECKED);
Enables: array[Boolean] of DWORD = (MF_DISABLED or MF_GRAYED, MF_ENABLED);
Breaks: array[TMenuBreak] of DWORD = (0, MF_MENUBREAK, MF_MENUBARBREAK);
Separators: array[Boolean] of DWORD = (MF_STRING, MF_SEPARATOR);
procedure TMenuItem.AppendTo(Menu: HMENU; ARightToLeft: Boolean);
const
IBreaks: array[TMenuBreak] of DWORD = (MFT_STRING, MFT_MENUBREAK, MFT_MENUBARBREAK);
IChecks: array[Boolean] of DWORD = (MFS_UNCHECKED, MFS_CHECKED);
IDefaults: array[Boolean] of DWORD = (0, MFS_DEFAULT);
IEnables: array[Boolean] of DWORD = (MFS_DISABLED or MFS_GRAYED, MFS_ENABLED);
IRadios: array[Boolean] of DWORD = (MFT_STRING, MFT_RADIOCHECK);
ISeparators: array[Boolean] of DWORD = (MFT_STRING, MFT_SEPARATOR);
IRTL: array[Boolean] of DWORD = (0, RightToLeftMenuFlag);
IOwnerDraw: array[Boolean] of DWORD = (MFT_STRING, MFT_OWNERDRAW);
var
MenuItemInfo: TMenuItemInfo;
Caption: string;
NewFlags: Integer;
IsOwnerDraw: Boolean;
ParentMenu: TMenu;
begin
if FVisible then
begin
Caption := FCaption;
if GetCount > 0 then
MenuItemInfo.hSubMenu := GetHandle
else if (FShortCut <> scNone) and ((Parent = nil) or
(Parent.Parent <> nil) or not (Parent.Owner is TMainMenu)) then
Caption := Caption + #9 + ShortCutToText(FShortCut);
if Lo(GetVersion) >= 4 then
begin
MenuItemInfo.cbSize := 44; // Required for Windows 95
MenuItemInfo.fMask := MIIM_CHECKMARKS or MIIM_DATA or MIIM_ID or
MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE;
ParentMenu := GetParentMenu;
// IsOwnerDraw := Assigned(ParentMenu) and ParentMenu.IsOwnerDraw or
IsOwnerDraw := Assigned(ParentMenu) and
(ParentMenu.OwnerDraw or (GetImageList <> nil)) or
Assigned(FBitmap) and not FBitmap.Empty;
MenuItemInfo.fType := IRadios[FRadioItem] or IBreaks[FBreak] or
ISeparators[FCaption = cLineCaption] or IRTL[ARightToLeft] or
IOwnerDraw[IsOwnerDraw];
MenuItemInfo.fState := IChecks[FChecked] or IEnables[FEnabled]
or IDefaults[FDefault];
MenuItemInfo.wID := Command;
MenuItemInfo.hSubMenu := 0;
MenuItemInfo.hbmpChecked := 0;
MenuItemInfo.hbmpUnchecked := 0;
MenuItemInfo.dwTypeData := PChar(Caption);
if GetCount > 0 then
MenuItemInfo.hSubMenu := GetHandle;
InsertMenuItem(Menu, DWORD(-1), True, MenuItemInfo);
end
else
begin
NewFlags := Breaks[FBreak] or Checks[FChecked] or Enables[FEnabled] or
Separators[FCaption = cLineCaption] or MF_BYPOSITION;
if GetCount > 0 then
InsertMenu(Menu, DWORD(-1), MF_POPUP or NewFlags, GetHandle,
PChar(FCaption))
else
InsertMenu(Menu, DWORD(-1), NewFlags, Command, PChar(Caption));
end;
end;
end;
procedure TMenuItem.PopulateMenu;
var
MenuRightToLeft: Boolean;
function AddIn(MenuItem: TMenuItem): Boolean;
begin
MenuItem.AppendTo(FHandle, MenuRightToLeft);
Result := False;
end;
begin
if (FMenu <> nil) and
(FMenu is TMainMenu) then
begin
InternalRethinkHotkeys(False);
InternalRethinkLines(False);
end;
// all menu items use BiDiMode of their root menu
MenuRightToLeft := (FMenu <> nil) and FMenu.IsRightToLeft;
IterateMenus(@AddIn, FMerged, Self);
end;
procedure TMenuItem.ReadShortCutText(Reader: TReader);
begin
ShortCut := TextToShortCut(Reader.ReadString);
end;
procedure TMenuItem.MergeWith(Menu: TMenuItem);
begin
if Assigned(Menu) and (csDestroying in Menu.ComponentState) then exit;
if FMerged <> Menu then
begin
if FMerged <> nil then
FMerged.FMergedWith := nil;
FMerged := Menu;
if FMerged <> nil then
begin
FMerged.FMergedWith := Self;
FMerged.FreeNotification(Self);
end;
RebuildHandle;
end;
end;
procedure TMenuItem.Loaded;
begin
inherited Loaded;
if Action <> nil then ActionChange(Action, True);
if FStreamedRebuild then RebuildHandle;
end;
procedure TMenuItem.RebuildHandle;
var
I: Integer;
LRepopulate: Boolean;
begin
if csDestroying in ComponentState then Exit;
if csReading in ComponentState then
FStreamedRebuild := True
else
begin
if FMergedWith <> nil then
FMergedWith.RebuildHandle
else
begin
I := GetMenuItemCount(Handle);
LRepopulate := I = 0;
while I > 0 do
begin
if GetMenuState(Handle, I - 1, MF_BYPOSITION) and MF_BITMAP = 0 then
begin
RemoveMenu(Handle, I - 1, MF_BYPOSITION);
LRepopulate := True;
end;
Dec(I);
end;
if LRepopulate then
begin
if (FParent = nil) and (FMenu is TMainMenu) and
(GetMenuItemCount(Handle) = 0) then
begin
DestroyMenu(FHandle);
FHandle := 0;
end
else
PopulateMenu;
MenuChanged(False);
end;
end;
end;
end;
procedure TMenuItem.VerifyGroupIndex(Position: Integer; Value: Byte);
var
I: Integer;
begin
for I := 0 to GetCount - 1 do
if I < Position then
begin
if Items[I].GroupIndex > Value then Error(@SGroupIndexTooLow)
end
else
{ Ripple change to menu items at Position and after }
if Items[I].GroupIndex < Value then Items[I].FGroupIndex := Value;
end;
function TMenuItem.GetHandle: HMENU;
begin
if FHandle = 0 then
begin
if Owner is TPopupMenu then
FHandle := CreatePopupMenu
else
FHandle := CreateMenu;
if FHandle = 0 then Error(@SOutOfResources);
PopulateMenu;
end;
Result := FHandle;
end;
procedure TMenuItem.DefineProperties(Filer: TFiler);
begin
inherited DefineProperties(Filer);
Filer.DefineProperty('ShortCutText', ReadShortCutText, nil, False);
end;
procedure TMenuItem.DoDrawText(ACanvas: TCanvas; const ACaption: string;
var Rect: TRect; Selected: Boolean; Flags: Longint);
var
Text: string;
R: TRect;
ParentMenu: TMenu;
begin
ParentMenu := GetParentMenu;
if (ParentMenu <> nil) and (ParentMenu.IsRightToLeft) then
begin
if Flags and DT_LEFT = DT_LEFT then
Flags := Flags and (not DT_LEFT) or DT_RIGHT
else if Flags and DT_RIGHT = DT_RIGHT then
Flags := Flags and (not DT_RIGHT) or DT_LEFT;
Flags := Flags or DT_RTLREADING;
end;
Text := ACaption;
if (Flags and DT_CALCRECT <> 0) and ((Text = '') or
(Text[1] = cHotkeyPrefix) and (Text[2] = #0)) then Text := Text + ' ';
with ACanvas do
begin
if Text = cLineCaption then
begin
if Flags and DT_CALCRECT = 0 then
begin
R := Rect;
Inc(R.Top, 4);
DrawEdge(Handle, R, EDGE_ETCHED, BF_TOP);
end;
end
else
begin
Brush.Style := bsClear;
if Default then
Font.Style := Font.Style + [fsBold];
if not Enabled then
begin
if not Selected then
begin
OffsetRect(Rect, 1, 1);
Font.Color := clBtnHighlight;
DrawText(Handle, PChar(Text), Length(Text), Rect, Flags);
OffsetRect(Rect, -1, -1);
end;
if Selected and (ColorToRGB(clHighlight) = ColorToRGB(clBtnShadow)) then
Font.Color := clBtnHighlight else
Font.Color := clBtnShadow;
end;
DrawText(Handle, PChar(Text), Length(Text), Rect, Flags);
end;
end;
end;
procedure TMenuItem.DrawItem(ACanvas: TCanvas; ARect: TRect; Selected: Boolean);
begin
if Assigned(FOnDrawItem) then
FOnDrawItem(Self, ACanvas, ARect, Selected);
end;
procedure TMenuItem.AdvancedDrawItem(ACanvas: TCanvas; ARect: TRect;
State: TOwnerDrawState; TopLevel: Boolean);
const
Alignments: array[TPopupAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
EdgeStyle: array[Boolean] of Longint = (BDR_RAISEDINNER, BDR_SUNKENOUTER);
var
ImageList: TCustomImageList;
ParentMenu: TMenu;
Alignment: TPopupAlignment;
DrawImage, DrawGlyph: Boolean;
GlyphRect, SaveRect: TRect;
DrawStyle: Longint;
Glyph: TBitmap;
OldBrushColor: TColor;
Selected: Boolean;
Win98Plus: Boolean;
Win2K: Boolean;
WinXP: Boolean;
procedure NormalDraw;
begin
with ACanvas do
begin
if WinXP then
begin
if (odSelected in State) or (odHotLight in State) then
begin
Brush.Color := clMenuHighlight;
Font.Color := clHighlightText;
end
else if TopLevel then
Brush.Color := clMenuBar
end;
//ImageList := GetImageList;
{ With XP, we need to always fill in the rect, even when selected }
if not Selected or WinXP then
FillRect(ARect);
if ParentMenu is TMenu then
Alignment := paLeft
else if ParentMenu is TPopupMenu then
Alignment := TPopupMenu(ParentMenu).Alignment
else
Alignment := paLeft;
GlyphRect.Left := ARect.Left + 1;
GlyphRect.Top := ARect.Top + 1;
if Caption = cLineCaption then
begin
FillRect(ARect);
GlyphRect.Left := 0;
GlyphRect.Right := -4;
DrawGlyph := False;
end
else
begin
DrawImage := (ImageList <> nil) and ((ImageIndex > -1) and
(ImageIndex < ImageList.Count) or Checked and ((FBitmap = nil) or
FBitmap.Empty));
if DrawImage or Assigned(FBitmap) and not FBitmap.Empty then
begin
DrawGlyph := True;
if DrawImage then
begin
GlyphRect.Right := GlyphRect.Left + ImageList.Width;
GlyphRect.Bottom := GlyphRect.Top + ImageList.Height;
end
else
begin
{ Need to add BitmapWidth/Height properties for TMenuItem if we're to
support them. Right now let's hardcode them to 16x16. }
GlyphRect.Right := GlyphRect.Left + 16;
GlyphRect.Bottom := GlyphRect.Top + 16;
end;
{ Draw background pattern brush if selected }
if Checked and not WinXP then
begin
Inc(GlyphRect.Right);
Inc(GlyphRect.Bottom);
OldBrushColor := Brush.Color;
if not (odSelected in State) then
begin
OldBrushColor := Brush.Color;
Brush.Bitmap := AllocPatternBitmap(clBtnFace, clBtnHighlight);
FillRect(GlyphRect);
end
else
begin
Brush.Color := clBtnFace;
FillRect(GlyphRect);
end;
Brush.Color := OldBrushColor;
Inc(GlyphRect.Left);
Inc(GlyphRect.Top);
end;
if DrawImage then
begin
if (ImageIndex > -1) and (ImageIndex < ImageList.Count) then
ImageList.Draw(ACanvas, GlyphRect.Left, GlyphRect.Top, ImageIndex,
Enabled)
else
begin
{ Draw a menu check }
Glyph := TBitmap.Create;
try
Glyph.Transparent := True;
Glyph.Handle := LoadBitmap(0, PChar(OBM_CHECK));
OldBrushColor := Font.Color;
Font.Color := clBtnText;
Draw(GlyphRect.Left + (GlyphRect.Right - GlyphRect.Left - Glyph.Width) div 2 + 1,
GlyphRect.Top + (GlyphRect.Bottom - GlyphRect.Top - Glyph.Height) div 2 + 1, Glyph);
Font.Color := OldBrushColor;
finally
Glyph.Free;
end;
end;
end
else
begin
SaveRect := GlyphRect;
{ Make sure image is within glyph bounds }
if FBitmap.Width < GlyphRect.Right - GlyphRect.Left then
with GlyphRect do
begin
Left := Left + ((Right - Left) - FBitmap.Width) div 2 + 1;
Right := Left + FBitmap.Width;
end;
if FBitmap.Height < GlyphRect.Bottom - GlyphRect.Top then
with GlyphRect do
begin
Top := Top + ((Bottom - Top) - FBitmap.Height) div 2 + 1;
Bottom := Top + FBitmap.Height;
end;
StretchDraw(GlyphRect, FBitmap);
GlyphRect := SaveRect;
end;
if Checked then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -