home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Delphi Visual Component Library }
- { }
- { Copyright (c) 1995,96 Borland International }
- { }
- {*******************************************************}
-
- unit Menus;
-
- {$S-,W-,R-}
- {$C PRELOAD}
-
- interface
-
- uses Windows, SysUtils, Classes, Messages;
-
- const
- scShift = $2000;
- scCtrl = $4000;
- scAlt = $8000;
- scNone = 0;
-
- type
- EMenuError = class(Exception);
- TMenu = class;
- TMenuBreak = (mbNone, mbBreak, mbBarBreak);
- TShortCut = Low(Word)..High(Word);
- TMenuChangeEvent = procedure (Sender: TObject; Rebuild: Boolean) of object;
- {$OldClassLayout On}
- TMenuItem = class(TComponent)
- private
- FCaption: string;
- FHandle: HMENU;
- FChecked: Boolean;
- FEnabled: Boolean;
- FDefault: Boolean;
- FRadioItem: Boolean;
- FVisible: Boolean;
- FGroupIndex: Byte;
- FBreak: TMenuBreak;
- FCommand: Word;
- FHelpContext: THelpContext;
- FHint: string;
- FItems: TList;
- FShortCut: TShortCut;
- FParent: TMenuItem;
- FMerged: TMenuItem;
- FMenu: TMenu;
- FOnChange: TMenuChangeEvent;
- FOnClick: TNotifyEvent;
- procedure AppendTo(Menu: HMENU);
- procedure ClearHandles;
- procedure ReadShortCutText(Reader: TReader);
- procedure MergeWith(Menu: TMenuItem);
- procedure RebuildHandle;
- procedure PopulateMenu;
- procedure SubItemChanged(Sender: TObject; Rebuild: Boolean);
- procedure WriteShortCutText(Writer: TWriter);
- procedure VerifyGroupIndex(Position: Integer; Value: Byte);
- protected
- procedure DefineProperties(Filer: TFiler); override;
- function GetHandle: HMENU;
- function GetCount: Integer;
- procedure GetChildren(Proc: TGetChildProc); override;
- function GetItem(Index: Integer): TMenuItem;
- function GetMenuIndex: Integer;
- function GetParentComponent: TComponent; override;
- procedure MenuChanged(Rebuild: Boolean); virtual;
- function HasParent: Boolean; override;
- procedure SetBreak(Value: TMenuBreak);
- procedure SetCaption(const Value: string);
- procedure SetChecked(Value: Boolean);
- procedure SetChildOrder(Child: TComponent; Order: Integer); override;
- procedure SetDefault(Value: Boolean);
- procedure SetEnabled(Value: Boolean);
- procedure SetGroupIndex(Value: Byte);
- procedure SetMenuIndex(Value: Integer);
- procedure SetParentComponent(Value: TComponent); override;
- procedure SetRadioItem(Value: Boolean);
- procedure SetShortCut(Value: TShortCut);
- procedure SetVisible(Value: Boolean);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Insert(Index: Integer; Item: TMenuItem);
- procedure Delete(Index: Integer);
- procedure Click; virtual;
- function IndexOf(Item: TMenuItem): Integer;
- procedure Add(Item: TMenuItem);
- procedure Remove(Item: TMenuItem);
- property Command: Word read FCommand;
- property Handle: HMENU read GetHandle;
- property Count: Integer read GetCount;
- property Items[Index: Integer]: TMenuItem read GetItem; default;
- property MenuIndex: Integer read GetMenuIndex write SetMenuIndex;
- property Parent: TMenuItem read FParent;
- published
- property Break: TMenuBreak read FBreak write SetBreak default mbNone;
- property Caption: string read FCaption write SetCaption;
- property Checked: Boolean read FChecked write SetChecked default False;
- property Default: Boolean read FDefault write SetDefault default False;
- property Enabled: Boolean read FEnabled write SetEnabled default True;
- property GroupIndex: Byte read FGroupIndex write SetGroupIndex default 0;
- property HelpContext: THelpContext read FHelpContext write FHelpContext default 0;
- property Hint: string read FHint write FHint;
- property RadioItem: Boolean read FRadioItem write SetRadioItem default False;
- property ShortCut: TShortCut read FShortCut write SetShortCut;
- property Visible: Boolean read FVisible write SetVisible default True;
- property OnClick: TNotifyEvent read FOnClick write FOnClick;
- end;
- {$OldClassLayout Off}
-
- TFindItemKind = (fkCommand, fkHandle, fkShortCut);
-
- TMenu = class(TComponent)
- private
- FItems: TMenuItem;
- FWindowHandle: HWND;
- FMenuImage: string;
- procedure MenuChanged(Sender: TObject; Rebuild: Boolean); virtual;
- procedure SetWindowHandle(Value: HWND);
- function UpdateImage: Boolean;
- protected
- procedure GetChildren(Proc: TGetChildProc); override;
- function GetHandle: HMENU; virtual;
- procedure SetChildOrder(Child: TComponent; Order: Integer); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function DispatchCommand(ACommand: Word): Boolean;
- function DispatchPopup(AHandle: HMENU): Boolean;
- function FindItem(Value: Integer; Kind: TFindItemKind): TMenuItem;
- function GetHelpContext(Value: Word; ByCommand: Boolean): THelpContext;
- function IsShortCut(var Message: TWMKey): Boolean;
- property Handle: HMENU read GetHandle;
- property WindowHandle: HWND read FWindowHandle write SetWindowHandle;
- published
- property Items: TMenuItem read FItems;
- end;
-
- TMainMenu = class(TMenu)
- private
- MergedMenu: TMenuItem;
- FOle2Menu: HMENU;
- FAutoMerge: Boolean;
- FReserved: Byte;
- procedure ItemChanged;
- procedure MenuChanged(Sender: TObject; Rebuild: Boolean); override;
- procedure SetAutoMerge(Value: Boolean);
- protected
- function GetHandle: HMENU; override;
- public
- procedure Merge(Menu: TMainMenu);
- procedure Unmerge(Menu: TMainMenu);
- procedure PopulateOle2Menu(SharedMenu: HMenu; Groups: array of Integer;
- var Widths: array of Longint);
- procedure GetOle2AcceleratorTable(var AccelTable: HAccel;
- var AccelCount: Integer; Groups: array of Integer);
- procedure SetOle2MenuHandle(Handle: HMENU);
- published
- property AutoMerge: Boolean read FAutoMerge write SetAutoMerge default False;
- end;
-
- TPopupAlignment = (paLeft, paRight, paCenter);
-
- TPopupMenu = class(TMenu)
- private
- FAlignment: TPopupAlignment;
- FAutoPopup: Boolean;
- FPopupComponent: TComponent;
- FOnPopup: TNotifyEvent;
- procedure DoPopup(Item: TObject);
- function GetHelpContext: THelpContext;
- procedure SetHelpContext(Value: THelpContext);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Popup(X, Y: Integer); virtual;
- property PopupComponent: TComponent read FPopupComponent write FPopupComponent;
- published
- property Alignment: TPopupAlignment read FAlignment write FAlignment default paLeft;
- property AutoPopup: Boolean read FAutoPopup write FAutoPopup default True;
- property HelpContext: THelpContext read GetHelpContext write SetHelpContext default 0;
- property OnPopup: TNotifyEvent read FOnPopup write FOnPopup;
- end;
-
- function ShortCut(Key: Word; Shift: TShiftState): TShortCut;
- procedure ShortCutToKey(ShortCut: TShortCut; var Key: Word; var Shift: TShiftState);
- function ShortCutToText(ShortCut: TShortCut): string;
- function TextToShortCut(Text: string): TShortCut;
-
- function NewMenu(Owner: TComponent; const AName: string; Items: array of TMenuItem): TMainMenu;
- function NewPopupMenu(Owner: TComponent; const AName: string;
- Alignment: TPopupAlignment; AutoPopup: Boolean; Items: array of TMenuitem): TPopupMenu;
- function NewSubMenu(const ACaption: string; hCtx: Word; const AName: string;
- Items: array of TMenuItem): TMenuItem;
- function NewItem(const ACaption: string; AShortCut: TShortCut;
- AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent; hCtx: Word;
- const AName: string): TMenuItem;
- function NewLine: TMenuItem;
-
- implementation
-
- uses Controls, Forms, Consts;
-
- procedure Error(const S: string);
- begin
- raise EMenuError.Create(S);
- end;
-
- procedure IndexError;
- begin
- Error(LoadStr(SMenuIndexError));
- 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);
-
- const
- MenuKeyCapIDs: array[TMenuKeyCap] of Word = (
- SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp,
- SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight,
- SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt);
-
- var
- MenuKeyCaps: array[TMenuKeyCap] of string;
-
- procedure LoadStrings;
- var
- I: TMenuKeyCap;
- begin
- for I := Low(TMenuKeyCap) to High(TMenuKeyCap) do
- MenuKeyCaps[I] := LoadStr(MenuKeyCapIDs[I]);
- end;
-
- 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));
- if (KeyName[1] = #0) and (KeyName[0] <> #0) then
- 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 CompareText(Copy(Text, 1, Length(Front)), 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;
-
- { TMenuItem }
-
- constructor TMenuItem.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FVisible := True;
- FEnabled := True;
- FCommand := UniqueCommand;
- end;
-
- destructor TMenuItem.Destroy;
- begin
- if FParent <> nil then
- begin
- FParent.Remove(Self);
- FParent := nil;
- end;
- if FHandle <> 0 then
- begin
- MergeWith(nil);
- DestroyMenu(FHandle);
- ClearHandles;
- end;
- while Count > 0 do Items[0].Free;
- FItems.Free;
- if FCommand <> 0 then CommandPool[FCommand] := False;
- inherited Destroy;
- end;
-
- procedure TMenuItem.ClearHandles;
-
- procedure Clear(Item: TMenuItem);
- var
- I: Integer;
- begin
- with Item do
- begin
- FHandle := 0;
- for I := 0 to GetCount - 1 do Clear(FItems[I]);
- end;
- end;
-
- begin
- Clear(Self);
- end;
-
- const
- Checks: array[Boolean] of LongInt = (MF_UNCHECKED, MF_CHECKED);
- Enables: array[Boolean] of LongInt = (MF_DISABLED or MF_GRAYED, MF_ENABLED);
- Breaks: array[TMenuBreak] of Longint = (0, MF_MENUBREAK, MF_MENUBARBREAK);
- Separators: array[Boolean] of LongInt = (MF_STRING, MF_SEPARATOR);
-
- procedure TMenuItem.AppendTo(Menu: HMENU);
- const
- IBreaks: array[TMenuBreak] of Longint = (MFT_STRING, MFT_MENUBREAK, MFT_MENUBARBREAK);
- IChecks: array[Boolean] of Longint = (MFS_UNCHECKED, MFS_CHECKED);
- IDefaults: array[Boolean] of Longint = (0, MFS_DEFAULT);
- IEnables: array[Boolean] of Longint = (MFS_DISABLED or MFS_GRAYED, MFS_ENABLED);
- IRadios: array[Boolean] of Longint = (MFT_STRING, MFT_RADIOCHECK);
- ISeparators: array[Boolean] of Longint = (MFT_STRING, MFT_SEPARATOR);
- var
- MenuItemInfo: TMenuItemInfo;
- Caption: string;
- NewFlags: Integer;
- 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 := SizeOf(TMenuItemInfo);
- MenuItemInfo.fMask := MIIM_CHECKMARKS or MIIM_DATA or MIIM_ID or
- MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE;
- MenuItemInfo.fType := IRadios[FRadioItem] or IBreaks[FBreak] or
- ISeparators[FCaption = '-'];
- 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, -1, True, MenuItemInfo);
- end
- else
- begin
- NewFlags := Breaks[FBreak] or Checks[FChecked] or Enables[FEnabled] or
- Separators[FCaption = '-'] or MF_BYPOSITION;
- if GetCount > 0 then
- InsertMenu(Menu, -1, MF_POPUP or NewFlags, GetHandle,
- PChar(FCaption))
- else
- InsertMenu(Menu, -1, NewFlags, Command, PChar(Caption));
- end;
- end;
- end;
-
- procedure TMenuItem.PopulateMenu;
-
- function AddIn(MenuItem: TMenuItem): Boolean;
- begin
- MenuItem.AppendTo(FHandle);
- Result := False;
- end;
-
- begin
- IterateMenus(@AddIn, FMerged, Self);
- end;
-
- procedure TMenuItem.ReadShortCutText(Reader: TReader);
- begin
- ShortCut := TextToShortCut(Reader.ReadString);
- end;
-
- procedure TMenuItem.MergeWith(Menu: TMenuItem);
- begin
- if FMerged <> Menu then
- begin
- FMerged := Menu;
- RebuildHandle;
- end;
- end;
-
- procedure TMenuItem.RebuildHandle;
- begin
- while GetMenuItemCount(Handle) > 0 do RemoveMenu(Handle, 0, MF_BYPOSITION);
- PopulateMenu;
- MenuChanged(False);
- 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(LoadStr(SGroupIndexTooLow))
- end
- else
- { Ripple change to menu items at Position and after }
- if Items[I].GroupIndex < Value then Items[I].FGroupIndex := Value;
- end;
-
- procedure TMenuItem.WriteShortCutText(Writer: TWriter);
- begin
- {Writer.WriteString(ShortCutToText(ShortCut));}
- 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 raise EMenuError.CreateRes(SOutOfResources);
- PopulateMenu;
- end;
- Result := FHandle;
- end;
-
- procedure TMenuItem.DefineProperties(Filer: TFiler);
- begin
- inherited DefineProperties(Filer);
- Filer.DefineProperty('ShortCutText', ReadShortCutText, WriteShortCutText, False);
- end;
-
- function TMenuItem.HasParent: Boolean;
- begin
- Result := True;
- end;
-
- procedure TMenuItem.SetBreak(Value: TMenuBreak);
- begin
- if FBreak <> Value then
- begin
- FBreak := Value;
- MenuChanged(True);
- end;
- end;
-
- procedure TMenuItem.SetCaption(const Value: string);
- begin
- if FCaption <> Value then
- begin
- FCaption := Value;
- MenuChanged(True);
- end;
- end;
-
- procedure TMenuItem.SetChecked(Value: Boolean);
- var
- I: Integer;
- Item: TMenuItem;
- begin
- if FChecked <> Value then
- begin
- if FRadioItem and (GroupIndex <> 0) and (FParent <> nil) then
- for I := 0 to FParent.Count - 1 do
- begin
- Item := FParent[I];
- if (Item <> Self) and Item.FRadioItem and (Item.GroupIndex = GroupIndex) then
- Item.Checked := False;
- end;
- FChecked := Value;
- if FParent <> nil then
- CheckMenuItem(FParent.Handle, FCommand, MF_BYCOMMAND or Checks[Value]);
- end;
- end;
-
- procedure TMenuItem.SetEnabled(Value: Boolean);
- begin
- if FEnabled <> Value then
- begin
- FEnabled := Value;
- if (Win32Platform = VER_PLATFORM_WIN32_NT) and (Count <> 0) then
- MenuChanged(True)
- else
- begin
- if FParent <> nil then
- EnableMenuItem(FParent.Handle, FCommand, MF_BYCOMMAND or Enables[Value]);
- MenuChanged(False);
- end;
- end;
- end;
-
- procedure TMenuItem.SetGroupIndex(Value: Byte);
- begin
- if FGroupIndex <> Value then
- begin
- if Parent <> nil then Parent.VerifyGroupIndex(Parent.IndexOf(Self), Value);
- FGroupIndex := Value;
- end;
- end;
-
- function TMenuItem.GetCount: Integer;
- begin
- if FItems = nil then Result := 0
- else Result := FItems.Count;
- end;
-
- function TMenuItem.GetItem(Index: Integer): TMenuItem;
- begin
- if FItems = nil then IndexError;
- Result := FItems[Index];
- end;
-
- procedure TMenuItem.SetShortCut(Value: TShortCut);
- begin
- FShortCut := Value;
- MenuChanged(True);
- end;
-
- procedure TMenuItem.SetVisible(Value: Boolean);
- begin
- FVisible := Value;
- MenuChanged(True);
- end;
-
- function TMenuItem.GetMenuIndex: Integer;
- begin
- Result := -1;
- if FParent <> nil then Result := FParent.IndexOf(Self);
- end;
-
- procedure TMenuItem.SetMenuIndex(Value: Integer);
- var
- Parent: TMenuItem;
- Count: Integer;
- begin
- if FParent <> nil then
- begin
- Count := FParent.Count;
- if Value < 0 then Value := 0;
- if Value >= Count then Value := Count - 1;
- if Value <> MenuIndex then
- begin
- Parent := FParent;
- Parent.Remove(Self);
- Parent.Insert(Value, Self);
- end;
- end;
- end;
-
- procedure TMenuItem.GetChildren(Proc: TGetChildProc);
- var
- I: Integer;
- begin
- for I := 0 to Count - 1 do Proc(Items[I]);
- end;
-
- procedure TMenuItem.SetChildOrder(Child: TComponent; Order: Integer);
- begin
- (Child as TMenuItem).MenuIndex := Order;
- end;
-
- procedure TMenuItem.SetDefault(Value: Boolean);
- var
- I: Integer;
- begin
- if FDefault <> Value then
- begin
- if Value and (FParent <> nil) then
- for I := 0 to FParent.Count - 1 do
- if FParent[I].Default then FParent[I].FDefault := False;
- FDefault := Value;
- MenuChanged(True);
- end;
- end;
-
- procedure TMenuItem.Insert(Index: Integer; Item: TMenuItem);
- begin
- if Item.FParent <> nil then
- raise EMenuError.CreateRes(SMenuReinserted);
- if FItems = nil then FItems := TList.Create;
- if (Index - 1 >= 0) and (Index - 1 < FItems.Count) then
- if Item.GroupIndex < TMenuItem(FItems[Index - 1]).GroupIndex then
- Item.GroupIndex := TMenuItem(FItems[Index - 1]).GroupIndex;
- VerifyGroupIndex(Index, Item.GroupIndex);
- FItems.Insert(Index, Item);
- Item.FParent := Self;
- Item.FOnChange := SubItemChanged;
- if FHandle <> 0 then RebuildHandle;
- MenuChanged(True);
- end;
-
- procedure TMenuItem.Delete(Index: Integer);
- var
- Cur: TMenuItem;
- begin
- if (Index < 0) or (FItems = nil) or (Index >= GetCount) then IndexError;
- Cur := FItems[Index];
- FItems.Delete(Index);
- Cur.FParent := nil;
- Cur.FOnChange := nil;
- if FHandle <> 0 then RebuildHandle;
- MenuChanged(True);
- end;
-
- procedure TMenuItem.Click;
- begin
- if FEnabled and Assigned(FOnClick) then FOnClick(Self);
- end;
-
- function TMenuItem.IndexOf(Item: TMenuItem): Integer;
- begin
- Result := -1;
- if FItems <> nil then Result := FItems.IndexOf(Item);
- end;
-
- procedure TMenuItem.Add(Item: TMenuItem);
- begin
- Insert(GetCount, Item);
- end;
-
- procedure TMenuItem.Remove(Item: TMenuItem);
- var
- I: Integer;
- begin
- I := IndexOf(Item);
- if I = -1 then raise EMenuError.CreateRes(SMenuNotFound);
- Delete(I);
- end;
-
- procedure TMenuItem.MenuChanged(Rebuild: Boolean);
- begin
- if Assigned(FOnChange) then FOnChange(Self, Rebuild);
- end;
-
- procedure TMenuItem.SubItemChanged(Sender: TObject; Rebuild: Boolean);
- begin
- if Rebuild and (FHandle <> 0) then RebuildHandle;
- if Parent <> nil then Parent.SubItemChanged(Self, False)
- else if Owner is TMainMenu then TMainMenu(Owner).ItemChanged;
- end;
-
- function TMenuItem.GetParentComponent: TComponent;
- begin
- if (FParent <> nil) and (FParent.FMenu <> nil) then
- Result := FParent.FMenu else
- Result := FParent;
- end;
-
- procedure TMenuItem.SetParentComponent(Value: TComponent);
- begin
- if FParent <> nil then FParent.Remove(Self);
- if Value <> nil then
- if Value is TMenu then
- TMenu(Value).Items.Add(Self)
- else if Value is TMenuItem then
- TMenuItem(Value).Add(Self);
- end;
-
- procedure TMenuItem.SetRadioItem(Value: Boolean);
- begin
- if FRadioItem <> Value then
- begin
- FRadioItem := Value;
- MenuChanged(True);
- end;
- end;
-
- { TMenu }
-
- constructor TMenu.Create(AOwner: TComponent);
- begin
- FItems := TMenuItem.Create(Self);
- FItems.FOnChange := MenuChanged;
- FItems.FMenu := Self;
- inherited Create(AOwner);
- end;
-
- destructor TMenu.Destroy;
- begin
- FItems.Free;
- inherited Destroy;
- end;
-
- procedure TMenu.GetChildren(Proc: TGetChildProc);
- begin
- FItems.GetChildren(Proc);
- end;
-
- function TMenu.GetHandle: HMENU;
- begin
- Result := FItems.GetHandle;
- end;
-
- procedure TMenu.SetChildOrder(Child: TComponent; Order: Integer);
- begin
- FItems.SetChildOrder(Child, Order);
- end;
-
- function TMenu.FindItem(Value: Integer; Kind: TFindItemKind): TMenuItem;
- var
- FoundItem: TMenuItem;
-
- function Find(Item: TMenuItem): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- if ((Kind = fkCommand) and (Value = Item.Command)) or
- ((Kind = fkHandle) and (Value = Item.FHandle)) or
- ((Kind = fkShortCut) and (Value = Item.ShortCut)) then
- begin
- FoundItem := Item;
- Result := True;
- Exit;
- end
- else
- for I := 0 to Item.GetCount - 1 do
- if Find(Item[I]) then
- begin
- Result := True;
- Exit;
- end;
- end;
-
- begin
- FoundItem := nil;
- IterateMenus(@Find, Items.FMerged, Items);
- Result := FoundItem;
- end;
-
- function TMenu.GetHelpContext(Value: Word; ByCommand: Boolean): THelpContext;
- var
- Item: TMenuItem;
- Kind: TFindItemKind;
- begin
- Result := 0;
- Kind := fkHandle;
- if ByCommand then Kind := fkCommand;
- Item := FindItem(Value, Kind);
- while (Item <> nil) and (Item.FHelpContext = 0) do
- Item := Item.FParent;
- if Item <> nil then Result := Item.FHelpContext;
- end;
-
- function TMenu.DispatchCommand(ACommand: Word): Boolean;
- var
- Item: TMenuItem;
- begin
- Result := False;
- Item := FindItem(ACommand, fkCommand);
- if Item <> nil then
- begin
- Item.Click;
- Result := True;
- end;
- end;
-
- function TMenu.DispatchPopup(AHandle: HMENU): Boolean;
- var
- Item: TMenuItem;
- begin
- Result := False;
- Item := FindItem(AHandle, fkHandle);
- if Item <> nil then
- begin
- Item.Click;
- Result := True;
- end;
- end;
-
- function TMenu.IsShortCut(var Message: TWMKey): Boolean;
- type
- TClickResult = (crDisabled, crClicked, crShortCutMoved);
- const
- AltMask = $20000000;
- var
- ShortCut: TShortCut;
- ShortCutItem: TMenuItem;
- ClickResult: TClickResult;
-
- function DoClick(Item: TMenuItem): TClickResult;
- begin
- Result := crClicked;
- if Item.Parent <> nil then Result := DoClick(Item.Parent);
- if Result = crClicked then
- if Item.Enabled then
- try
- Item.Click;
- if ShortCutItem.ShortCut <> ShortCut then
- Result := crShortCutMoved;
- except
- Application.HandleException(Self);
- end
- else Result := crDisabled;
- end;
-
- begin
- Result := False;
- if FWindowHandle <> 0 then
- begin
- ShortCut := Byte(Message.CharCode);
- if GetKeyState(VK_SHIFT) < 0 then Inc(ShortCut, scShift);
- if GetKeyState(VK_CONTROL) < 0 then Inc(ShortCut, scCtrl);
- if Message.KeyData and AltMask <> 0 then Inc(ShortCut, scAlt);
- repeat
- ClickResult := crDisabled;
- ShortCutItem := FindItem(ShortCut, fkShortCut);
- if ShortCutItem <> nil then ClickResult := DoClick(ShortCutItem);
- until ClickResult <> crShortCutMoved;
- Result := ShortCutItem <> nil;
- end;
- end;
-
- function TMenu.UpdateImage: Boolean;
- var
- Image: array[0..511] of Char;
-
- procedure BuildImage(Menu: HMENU);
- var
- P, ImageEnd: PChar;
- I, C: Integer;
- State: Word;
- begin
- C := GetMenuItemCount(Menu);
- P := Image;
- ImageEnd := @Image[SizeOf(Image) - 5];
- I := 0;
- while (I < C) and (P < ImageEnd) do
- begin
- GetMenuString(Menu, I, P, ImageEnd - P, MF_BYPOSITION);
- P := StrEnd(P);
- State := GetMenuState(Menu, I, MF_BYPOSITION);
- if State and MF_DISABLED <> 0 then P := StrECopy(P, '$');
- if State and MF_MENUBREAK <> 0 then P := StrECopy(P, '@');
- if State and MF_GRAYED <> 0 then P := StrECopy(P, '#');
- P := StrECopy(P, ';');
- Inc(I);
- end;
- end;
-
- begin
- Result := False;
- Image[0] := #0;
- if FWindowHandle <> 0 then BuildImage(Handle);
- if (FMenuImage = '') or (StrComp(PChar(FMenuImage), Image) <> 0) then
- begin
- Result := True;
- FMenuImage := Image;
- end;
- end;
-
- procedure TMenu.SetWindowHandle(Value: HWND);
- begin
- FWindowHandle := Value;
- UpdateImage;
- end;
-
- procedure TMenu.MenuChanged(Sender: TObject; Rebuild: Boolean);
- begin
- end;
-
- { TMainMenu }
-
- procedure TMainMenu.SetAutoMerge(Value: Boolean);
- begin
- if FAutoMerge <> Value then
- begin
- FAutoMerge := Value;
- if FWindowHandle <> 0 then
- SendMessage(FWindowHandle, CM_MENUCHANGED, 0, 0);
- end;
- end;
-
- procedure TMainMenu.MenuChanged(Sender: TObject; Rebuild: Boolean);
- begin
- if (FWindowHandle <> 0) and UpdateImage then DrawMenuBar(FWindowHandle);
- end;
-
- procedure TMainMenu.Merge(Menu: TMainMenu);
- begin
- if Menu <> nil then
- FItems.MergeWith(Menu.FItems) else
- FItems.MergeWith(nil);
- end;
-
- procedure TMainMenu.Unmerge(Menu: TMainMenu);
- begin
- if (Menu <> nil) and (FItems.FMerged = Menu.FItems) then
- FItems.MergeWith(nil);
- end;
-
- procedure TMainMenu.ItemChanged;
- begin
- MenuChanged(nil, False);
- if FWindowHandle <> 0 then
- SendMessage(FWindowHandle, CM_MENUCHANGED, 0, 0);
- end;
-
- function TMainMenu.GetHandle: HMENU;
- begin
- if FOle2Menu <> 0 then
- Result := FOle2Menu else
- Result := inherited GetHandle;
- end;
-
- procedure TMainMenu.GetOle2AcceleratorTable(var AccelTable: HAccel;
- var AccelCount: Integer; Groups: array of Integer);
- var
- NumAccels: Integer;
- AccelList, AccelPtr: PAccel;
-
- procedure ProcessAccels(Item: TMenuItem);
- var
- I: Integer;
- Virt: Byte;
- begin
- if Item.ShortCut <> 0 then
- if AccelPtr <> nil then
- begin
- Virt := FNOINVERT or FVIRTKEY;
- if Item.ShortCut and scCtrl <> 0 then Virt := Virt or FCONTROL;
- if Item.ShortCut and scAlt <> 0 then Virt := Virt or FALT;
- if Item.ShortCut and scShift <> 0 then Virt := Virt or FSHIFT;
- AccelPtr^.fVirt := Virt;
- AccelPtr^.key := Item.ShortCut and $FF;
- AccelPtr^.cmd := Item.Command;
- Inc(AccelPtr);
- end else
- Inc(NumAccels)
- else
- for I := 0 to Item.GetCount - 1 do ProcessAccels(Item[I]);
- end;
-
- function ProcessAccelItems(Item: TMenuItem): Boolean;
- var
- I: Integer;
- begin
- for I := 0 to High(Groups) do
- if Item.GroupIndex = Groups[I] then
- begin
- ProcessAccels(Item);
- Break;
- end;
- Result := False;
- end;
-
- begin
- NumAccels := 0;
- AccelPtr := nil;
- IterateMenus(@ProcessAccelItems, Items.FMerged, Items);
- AccelTable := 0;
- if NumAccels <> 0 then
- begin
- GetMem(AccelList, NumAccels * SizeOf(TAccel));
- AccelPtr := AccelList;
- IterateMenus(@ProcessAccelItems, Items.FMerged, Items);
- AccelTable := CreateAcceleratorTable(AccelList^, NumAccels);
- FreeMem(AccelList);
- end;
- AccelCount := NumAccels;
- end;
-
- { Similar to regular TMenuItem.PopulateMenus except that it only adds
- the specified groups to the menu handle }
-
- procedure TMainMenu.PopulateOle2Menu(SharedMenu: HMenu;
- Groups: array of Integer; var Widths: array of Longint);
- var
- NumGroups: Integer;
- J: Integer;
-
- function AddOle2(Item: TMenuItem): Boolean;
- var
- I: Integer;
- begin
- for I := 0 to NumGroups do
- begin
- if Item.GroupIndex = Groups[I] then
- begin
- Inc(Widths[Item.GroupIndex]);
- Item.AppendTo(SharedMenu);
- end;
- end;
- Result := False;
- end;
-
- begin
- NumGroups := High(Groups);
- for J := 0 to High(Widths) do Widths[J] := 0;
- IterateMenus(@AddOle2, Items.FMerged, Items);
- end;
-
- procedure TMainMenu.SetOle2MenuHandle(Handle: HMENU);
- begin
- FOle2Menu := Handle;
- ItemChanged;
- end;
-
- { TPopupMenu }
-
- type
- TPopupList = class(TList)
- private
- procedure WndProc(var Message: TMessage);
- public
- Window: HWND;
- procedure Add(Popup: TPopupMenu);
- procedure Remove(Popup: TPopupMenu);
- end;
-
- var
- PopupList: TPopupList;
-
- procedure TPopupList.WndProc(var Message: TMessage);
- var
- I: Integer;
- MenuItem: TMenuItem;
- FindKind: TFindItemKind;
- ContextID: Integer;
- begin
- try
- case Message.Msg of
- WM_COMMAND:
- for I := 0 to Count - 1 do
- if TPopupMenu(Items[I]).DispatchCommand(Message.wParam) then Exit;
- WM_INITMENUPOPUP:
- for I := 0 to Count - 1 do
- with TWMInitMenuPopup(Message) do
- if TPopupMenu(Items[I]).DispatchPopup(MenuPopup) then Exit;
- WM_MENUSELECT:
- with TWMMenuSelect(Message) do
- begin
- FindKind := fkCommand;
- if MenuFlag and MF_POPUP <> 0 then FindKind := fkHandle;
- for I := 0 to Count - 1 do
- begin
- MenuItem := TPopupMenu(Items[I]).FindItem(IDItem, FindKind);
- if MenuItem <> nil then
- begin
- Application.Hint := MenuItem.Hint;
- Exit;
- end;
- end;
- Application.Hint := '';
- end;
- WM_HELP:
- with PHelpInfo(Message.LParam)^ do
- begin
- for I := 0 to Count - 1 do
- if TPopupMenu(Items[I]).Handle = hItemHandle then
- begin
- ContextID := TMenu(Items[I]).GetHelpContext(iCtrlID, True);
- if ContextID = 0 then
- ContextID := TMenu(Items[I]).GetHelpContext(hItemHandle, False);
- if Screen.ActiveForm = nil then Exit;
- if (biHelp in Screen.ActiveForm.BorderIcons) then
- Application.HelpCommand(HELP_CONTEXTPOPUP, ContextID)
- else
- Application.HelpContext(ContextID);
- Exit;
- end;
- end;
- end;
- with Message do Result := DefWindowProc(Window, Msg, wParam, lParam);
- except
- Application.HandleException(Self);
- end;
- end;
-
- procedure TPopupList.Add(Popup: TPopupMenu);
- begin
- if Count = 0 then Window := AllocateHWnd(WndProc);
- inherited Add(Popup);
- end;
-
- procedure TPopupList.Remove(Popup: TPopupMenu);
- begin
- inherited Remove(Popup);
- if Count = 0 then DeallocateHWnd(Window);
- end;
-
- constructor TPopupMenu.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FItems.OnClick := DoPopup;
- FWindowHandle := Application.Handle;
- FAutoPopup := True;
- PopupList.Add(Self);
- end;
-
- destructor TPopupMenu.Destroy;
- begin
- PopupList.Remove(Self);
- inherited Destroy;
- end;
-
- procedure TPopupMenu.DoPopup(Item: TObject);
- begin
- if Assigned(FOnPopup) then FOnPopup(Item);
- end;
-
- function TPopupMenu.GetHelpContext: THelpContext;
- begin
- Result := FItems.HelpContext;
- end;
-
- procedure TPopupMenu.SetHelpContext(Value: THelpContext);
- begin
- FItems.HelpContext := Value;
- end;
-
- procedure TPopupMenu.Popup(X, Y: Integer);
- const
- Flags: array[TPopupAlignment] of Word = (TPM_LEFTALIGN, TPM_RIGHTALIGN,
- TPM_CENTERALIGN);
- begin
- DoPopup(Self);
- TrackPopupMenu(FItems.Handle, Flags[FAlignment] or TPM_RIGHTBUTTON, X, Y,
- 0 { reserved}, PopupList.Window, nil);
- end;
-
- { Menu building functions }
-
- procedure InitMenuItems(AMenu: TMenu; Items: array of TMenuItem);
- var
- I: Integer;
-
- procedure SetOwner(Item: TMenuItem);
- var
- I: Integer;
- begin
- if Item.Owner = nil then AMenu.Owner.InsertComponent(Item);
- for I := 0 to Item.Count - 1 do
- SetOwner(Item[I]);
- end;
-
- begin
- for I := Low(Items) to High(Items) do
- begin
- SetOwner(Items[I]);
- AMenu.FItems.Add(Items[I]);
- end;
- end;
-
- function NewMenu(Owner: TComponent; const AName: string; Items: array of TMenuItem): TMainMenu;
- begin
- Result := TMainMenu.Create(Owner);
- Result.Name := AName;
- InitMenuItems(Result, Items);
- end;
-
- function NewPopupMenu(Owner: TComponent; const AName: string;
- Alignment: TPopupAlignment; AutoPopup: Boolean; Items: array of TMenuItem): TPopupMenu;
- begin
- Result := TPopupMenu.Create(Owner);
- Result.Name := AName;
- Result.AutoPopup := AutoPopup;
- Result.Alignment := Alignment;
- InitMenuItems(Result, Items);
- end;
-
- function NewSubMenu(const ACaption: string; hCtx: Word; const AName: string;
- Items: array of TMenuItem): TMenuItem;
- var
- I: Integer;
- begin
- Result := TMenuItem.Create(nil);
- for I := Low(Items) to High(Items) do
- Result.Add(Items[I]);
- Result.Caption := ACaption;
- Result.HelpContext := hCtx;
- Result.Name := AName;
- end;
-
- function NewItem(const ACaption: string; AShortCut: TShortCut;
- AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent; hCtx: Word;
- const AName: string): TMenuItem;
- begin
- Result := TMenuItem.Create(nil);
- with Result do
- begin
- Caption := ACaption;
- ShortCut := AShortCut;
- OnClick := AOnClick;
- HelpContext := hCtx;
- Checked := AChecked;
- Enabled := AEnabled;
- Name := AName;
- end;
- end;
-
- function NewLine: TMenuItem;
- begin
- Result := TMenuItem.Create(nil);
- Result.Caption := '-';
- end;
-
- begin
- RegisterClasses([TMenuItem]);
- LoadStrings;
- CommandPool := TBits.Create;
- PopupList := TPopupList.Create;
- end.
-