{%MainUnit ../menus.pp} {****************************************************************************** TMenu ****************************************************************************** ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } {------------------------------------------------------------------------------ Method: TMenu.Create Params: AOwner: the owner of the class Returns: Nothing Constructor for the class. ------------------------------------------------------------------------------} constructor TMenu.Create(AOwner: TComponent); begin FItems := TMenuItem.Create(Self); FItems.FAutoLineReduction := maAutomatic; FItems.FOnChange := @MenuChanged; FItems.FMenu := Self; FImageChangeLink := TChangeLink.Create; FImageChangeLink.OnChange := @ImageListChange; FBidiMode := bdLeftToRight; FParentBidiMode := True; ParentBidiModeChanged(AOwner); inherited Create(AOwner); end; {------------------------------------------------------------------------------ procedure TMenu.SetImages(const AValue: TCustomImageList); Creates the handle ( = object). ------------------------------------------------------------------------------} procedure TMenu.SetImages(const AValue: TCustomImageList); begin if FImages <> nil then begin FImages.UnRegisterChanges(FImageChangeLink); FImages.RemoveFreeNotification(Self); end; FImages := AValue; if FImages <> nil then begin FImages.FreeNotification(Self); FImages.RegisterChanges(FImageChangeLink); end; FItems.UpdateImages(true); end; procedure TMenu.SetImagesWidth(const aImagesWidth: Integer); begin if FImagesWidth = aImagesWidth then Exit; FImagesWidth := aImagesWidth; FItems.UpdateImages; end; procedure TMenu.SetBiDiMode(const AValue: TBiDiMode); begin if FBidiMode=AValue then exit; FBidiMode:=AValue; FParentBiDiMode := False; if not (csLoading in ComponentState) then BidiModeChanged; end; procedure TMenu.SetParentBiDiMode(const AValue: Boolean); begin if FParentBiDiMode = AValue then exit; FParentBiDiMode := AValue; if not (csLoading in ComponentState) then ParentBidiModeChanged; end; class procedure TMenu.WSRegisterClass; begin inherited WSRegisterClass; RegisterMenu; end; procedure TMenu.CMParentBiDiModeChanged(var Message: TLMessage); begin ParentBidiModeChanged; end; procedure TMenu.CMAppShowMenuGlyphChanged(var Message: TLMessage); begin FItems.UpdateImages; end; function TMenu.GetAutoLineReduction: TMenuAutoFlag; begin case FItems.AutoLineReduction of maParent, maAutomatic: Result := maAutomatic; //cannot return maParent for TMenu.AutoLineReduction maManual: Result := maManual; end; end; procedure TMenu.BidiModeChanged; begin if HandleAllocated then TWSMenuClass(WidgetSetClass).SetBiDiMode(Self, UseRightToLeftAlignment, UseRightToLeftReading); end; procedure TMenu.ParentBidiModeChanged(AOwner: TComponent); begin if FParentBidiMode then begin //Take the value from the Owner //i can not use parent because TPopupMenu.Parent = nil if (AOwner<>nil) and (AOwner is TCustomForm) and not (csDestroying in AOwner.ComponentState) then begin BiDiMode := TCustomForm(AOwner).BiDiMode; FParentBiDiMode := True; end; end; end; procedure TMenu.ParentBidiModeChanged; begin ParentBidiModeChanged(Owner); end; {------------------------------------------------------------------------------ procedure TMenu.SetParent(const AValue: TComponent); ------------------------------------------------------------------------------} procedure TMenu.SetParent(const AValue: TComponent); begin if FParent = AValue then Exit; FParent := AValue; if (FParent = nil) and (Items <> nil) and Items.HandleAllocated then begin // disconnect from the form DestroyHandle; end end; procedure TMenu.ImageListChange(Sender: TObject); begin if Sender = Images then UpdateItems; end; procedure TMenu.SetAutoLineReduction(AValue: TMenuAutoFlag); begin FItems.AutoLineReduction := AValue; end; {------------------------------------------------------------------------------ Method: TMenu.CreateHandle Params: None Returns: Nothing Creates the handle ( = object). ------------------------------------------------------------------------------} procedure TMenu.CreateHandle; begin FItems.Handle := TWSMenuClass(WidgetSetClass).CreateHandle(Self); FItems.CheckChildrenHandles; end; procedure TMenu.DestroyHandle; begin //debugln(['TMenu.DestroyHandle ',DbgSName(Self),' ',Items<>nil]); Items.DestroyHandle; end; procedure TMenu.DoChange(Source: TMenuItem; Rebuild: Boolean); begin if Assigned(FOnChange) then FOnChange(Self, Source, Rebuild); end; {------------------------------------------------------------------------------ Method: TMenu.Destroy Params: None Returns: Nothing Destructor for the class. ------------------------------------------------------------------------------} destructor TMenu.Destroy; begin FreeAndNil(FItems); FreeAndNil(FImageChangeLink); inherited Destroy; end; {------------------------------------------------------------------------------ Function: TMenu.FindItem Params: Returns: the menu item with the shortcut ------------------------------------------------------------------------------} function TMenu.FindItem(AValue: PtrInt; Kind: TFindItemKind): TMenuItem; function Find(Item: TMenuItem): TMenuItem; var I: Integer; {$IFDEF UseAltKeysForMenuItems} Key: Word; Shift: TShiftState; {$ENDIF} begin Result := nil; //DebugLn(['Find ',dbgsName(Item),' Item.ShortCut=',dbgs(Item.ShortCut),' ',dbgs(TShortCut(AValue))]); if Item = nil then exit; if ((Kind = fkCommand) and (AValue = Item.Command)) or ((Kind = fkHandle) and (HMenu(AValue) = Item.FHandle)) or ((Kind = fkShortCut) and (AValue = Item.ShortCut)) then begin Result := Item; Exit; end; {$IFDEF UseAltKeysForMenuItems} if (Kind = fkShortCut) and (Item.IsInMenuBar) then begin // ToDo: check if parent is currently visible // item caption is currently visible -> check caption for ShortCutToKey(TShortCut(AValue),Key,Shift); if (Shift=[ssAlt]) and IsAccel(Key,Item.Caption) then begin Result := Item; exit; end; end; {$ENDIF} for I := 0 to Item.GetCount - 1 do begin Result := Find(Item[I]); if Assigned(Result) then Exit; end; end; begin Result := Find(Items); end; function TMenu.GetHelpContext(AValue: PtrInt; ByCommand: Boolean): THelpContext; const FindKind: array[Boolean] of TFindItemKind = (fkHandle, fkCommand); var Item: TMenuItem; begin Result := 0; Item := FindItem(AValue, FindKind[ByCommand]); if Item <> nil then Result := Item.HelpContext; end; function TMenu.IsShortcut(var Message: TLMKey): boolean; procedure HandleItem(Item: TMenuItem); begin if Item = nil then Exit; HandleItem(Item.Parent); if FShortcutHandled and Item.Enabled then begin Item.InitiateActions; Item.Click; end else FShortcutHandled := False; end; var Item: TMenuItem; Shortcut: TShortcut; ShiftState: TShiftState; begin ShiftState := KeyDataToShiftState(Message.KeyData); Shortcut := Menus.ShortCut(Message.CharCode, ShiftState); Item := FindItem(Shortcut, fkShortcut); Result := not (csDesigning in ComponentState) and (Item <> nil); //DebugLn(['TMenu.IsShortcut ',dbgsName(Self),' Result=',Result,' Message.CharCode=',Message.CharCode,' ShiftState=',dbgs(ShiftState)]); if Result then begin //DebugLn(['TMenu.IsShortcut ',dbgsName(Self),' Result=',Result,' Message.CharCode=',Message.CharCode,' ShiftState=',dbgs(ShiftState)]); FShortcutHandled := True; HandleItem(Item); Result := FShortcutHandled; //debugln(['TMenu.IsShortcut ',Result]); DumpStack; end; end; {------------------------------------------------------------------------------ Function: TMenu.GetHandle Params: none Returns: Handle of the menu The handle will be created if not already allocated. ------------------------------------------------------------------------------} function TMenu.GetHandle: HMENU; begin HandleNeeded(); Result := FItems.Handle; end; {------------------------------------------------------------------------------ Function: TMenu.GetChildren Params: proc - procedure which has to be called for every item root - root component Returns: nothing Helper function for streaming. ------------------------------------------------------------------------------} procedure TMenu.GetChildren(Proc: TGetChildProc; Root: TComponent); var i: integer; begin for i := 0 to FItems.Count - 1 do Proc(TComponent(FItems[i])); end; procedure TMenu.MenuChanged(Sender: TObject; Source: TMenuItem; Rebuild: Boolean); begin if ComponentState * [csLoading, csDestroying] = [] then DoChange(Source, Rebuild); end; procedure TMenu.AssignTo(Dest: TPersistent); begin if Dest is TMenu then Menu_Copy(Self, Dest as TMenu) else inherited AssignTo(Dest); end; procedure TMenu.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opRemove) then begin if (AComponent = FImages) then Images := nil else if AComponent=FItems then begin raise Exception.Create(''); // someone is stealing my end; end; end; procedure TMenu.SetChildOrder(Child: TComponent; Order: Integer); begin (Child as TMenuItem).MenuIndex := Order; end; procedure TMenu.UpdateItems; begin // TODO: implement end; {------------------------------------------------------------------------------ Function: TMenu.HandleAllocated Params: None Returns: True if handle is allocated Checks if a handle is allocated. I.E. if the control is created ------------------------------------------------------------------------------} function TMenu.HandleAllocated : Boolean; begin Result := FItems.HandleAllocated; end; {------------------------------------------------------------------------------ Method: TMenu.HandleNeeded Params: AOwner: the owner of the class Returns: Nothing Description of the procedure for the class. ------------------------------------------------------------------------------} procedure TMenu.HandleNeeded; begin if not HandleAllocated then CreateHandle; 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.IsBiDiModeStored: Boolean; begin Result := not FParentBidiMode; end; {------------------------------------------------------------------------------ Function: TMenu.IsRightToLeft Params: Returns: ------------------------------------------------------------------------------} function TMenu.IsRightToLeft : Boolean; Begin Result := BidiMode <> bdLeftToRight; end; function TMenu.UseRightToLeftAlignment : Boolean; begin Result := (BiDiMode = bdRightToLeft); end; function TMenu.UseRightToLeftReading : Boolean; begin Result := (BiDiMode <> bdLeftToRight); end; // included by menus.pp