public static void Recreate(CommandManager manager, CommandEntrySet entrySet, HashSet <object> ignoreCommands) { if (manager == null) { throw new ArgumentException("manager"); } if (MacMainMenu.manager != null) { MacMainMenu.manager.CommandActivating -= OnCommandActivating; } MacMainMenu.manager = manager; MacMainMenu.manager.CommandActivating += OnCommandActivating; if (rootMenu == IntPtr.Zero) { rootMenu = HIToolbox.CreateMenu(idSeq++, GetName(entrySet), 0); CreateChildren(rootMenu, entrySet, ignoreCommands); InstallRootMenu(); } else { Destroy(false); CreateChildren(rootMenu, entrySet, ignoreCommands); } }
public static void AddAppMenuItems(CommandManager manager, params object [] cmdIds) { //FIXME: we assume we get first pick of cmdIDs HIMenuItem mnu = HIToolbox.GetMenuItem((uint)CarbonCommandID.Hide); appMenu = mnu.MenuRef; var appMenuId = HIToolbox.GetMenuID(appMenu); for (int i = cmdIds.Length - 1; i >= 0; i--) { var cmdId = cmdIds[i]; if (cmdId == Command.Separator) { HIToolbox.InsertMenuSeparator(mnu.MenuRef, 0); continue; } Command cmd = manager.GetCommand(cmdId); if (cmd == null) { MonoDevelop.Core.LoggingService.LogError("Null command in Mac app menu for ID {0}", cmdId); continue; } uint macCmdId = GetNewMenuItemId(cmd); ushort pos = HIToolbox.InsertMenuItem(mnu.MenuRef, (cmd.Text ?? "").Replace("_", ""), 0, 0, macCmdId); SetMenuAccelerator(new HIMenuItem(mnu.MenuRef, pos), cmd.AccelKey); menuIdMap[cmdId] = appMenuId; } }
static void BuildDynamicSubMenu(IntPtr rootMenu, IntPtr parentMenu, ushort index, uint macCmdID, CommandInfoSet cinfoSet) { IntPtr menuRef = HIToolbox.CreateMenu(idSeq++, GetCleanCommandText(cinfoSet), MenuAttributes.CondenseSeparators); objectsToDestroyOnMenuClose.Add(new DestructableMenu(menuRef)); HIToolbox.CheckResult(HIToolbox.SetMenuItemHierarchicalMenu(parentMenu, index, menuRef)); ushort count = (ushort)cinfoSet.CommandInfos.Count; for (ushort i = 1, j = 0; i <= count; i++) { CommandInfo ci = cinfoSet.CommandInfos[j++]; if (ci.IsArraySeparator) { HIToolbox.AppendMenuSeparator(menuRef); } else { HIToolbox.AppendMenuItem(menuRef, ci.Text, 0, macCmdID); UpdateMenuItem(rootMenu, menuRef, ref i, ref count, macCmdID, ci); objectsToDestroyOnMenuClose.Add(ci.DataItem); uint refcon = (uint)objectsToDestroyOnMenuClose.Count; HIToolbox.SetMenuItemReferenceConstant(new HIMenuItem(menuRef, i), refcon); } } }
internal static void RunMenuCommand(CarbonCommandID commandID) { var item = HIToolbox.GetMenuItem((uint)commandID); var cmd = new CarbonHICommand((uint)commandID, item); Carbon.ProcessHICommand(ref cmd); }
public static void Recreate(CommandManager manager, CommandEntrySet entrySet) { if (manager == null) { throw new ArgumentException("manager"); } if (MacMainMenu.manager != null) { MacMainMenu.manager.CommandActivating -= OnCommandActivating; } MacMainMenu.manager = manager; MacMainMenu.manager.CommandActivating += OnCommandActivating; if (rootMenu == IntPtr.Zero) { rootMenu = HIToolbox.CreateMenu(idSeq++, GetName(entrySet), 0); AddMenuItems(new HIMenuItem(rootMenu, 0), entrySet); InstallRootMenu(); } else { Destroy(false); AddMenuItems(new HIMenuItem(rootMenu, 0), entrySet); } }
public static void Destroy(bool removeRoot) { if (mainMenus.Count > 0) { foreach (IntPtr ptr in mainMenus) { HIToolbox.DeleteMenu(ptr); CoreFoundation.Release(ptr); } DestroyOldMenuObjects(); mainMenus.Clear(); linkCommands.Clear(); idSeq = 1; } HIToolbox.ClearMenuBar(); if (removeRoot && rootMenu != IntPtr.Zero) { HIToolbox.DeleteMenu(rootMenu); CoreFoundation.Release(rootMenu); HIToolbox.CheckResult(HIToolbox.SetRootMenu(IntPtr.Zero)); Carbon.RemoveEventHandler(commandHandlerRef); Carbon.RemoveEventHandler(openingHandlerRef); Carbon.RemoveEventHandler(closedHandlerRef); commandHandlerRef = openingHandlerRef = rootMenu = IntPtr.Zero; idSeq = 0; } }
static void InstallRootMenu() { HIToolbox.CheckResult(HIToolbox.SetRootMenu(rootMenu)); openingHandlerRef = Carbon.InstallApplicationEventHandler(HandleMenuOpening, CarbonEventMenu.Opening); closedHandlerRef = Carbon.InstallApplicationEventHandler(HandleMenuClosed, CarbonEventMenu.Closed); commandHandlerRef = Carbon.InstallApplicationEventHandler(HandleMenuCommand, CarbonEventCommand.Process); }
public void Destroy() { if (Ref != IntPtr.Zero) { HIToolbox.DeleteMenu(Ref); CoreFoundation.Release(Ref); Ref = IntPtr.Zero; } }
static CarbonEventHandlerStatus HandleMenuCommand(IntPtr callRef, IntPtr eventRef, IntPtr userData) { try { CarbonHICommand hiCmd = GetCarbonHICommand(eventRef); uint refCon = HIToolbox.GetMenuItemReferenceConstant(hiCmd.MenuItem); //link commands if (hiCmd.CommandID == linkCommandId) { string url = ""; try { url = linkCommands[(int)refCon]; MacPlatformService.OpenUrl(url); } catch (Exception ex) { Gtk.Application.Invoke(delegate { MonoDevelop.Ide.MessageService.ShowException(ex, MonoDevelop.Core.GettextCatalog.GetString("Could not open the url {0}", url)); }); } DestroyOldMenuObjects(); return(CarbonEventHandlerStatus.Handled); } //normal commands object cmdID = GetCommandID(hiCmd); if (cmdID != null) { if (refCon > 0) { object data = objectsToDestroyOnMenuClose[(int)refCon - 1]; //need to return before we execute the command, so that the menu unhighlights Gtk.Application.Invoke(delegate { manager.DispatchCommand(cmdID, data, CommandSource.MainMenu); }); } else { Gtk.Application.Invoke(delegate { manager.DispatchCommand(cmdID, CommandSource.MainMenu); }); } DestroyOldMenuObjects(); return(CarbonEventHandlerStatus.Handled); } } catch (Exception ex) { MonoDevelop.Core.LoggingService.LogError("Unhandled error handling menu command", ex); } return(CarbonEventHandlerStatus.NotHandled); }
static uint[] GetMenuCommandIDs(IntPtr menuRef) { int count = HIToolbox.CountMenuItems(menuRef); var existingitems = new uint[count]; for (int i = 0; i < count; i++) { existingitems[i] = HIToolbox.GetMenuItemCommandID(new HIMenuItem(menuRef, (ushort)(i + 1))); } return(existingitems); }
//updates commands and populates arrays and dynamic menus static CarbonEventHandlerStatus HandleMenuOpening(IntPtr callRef, IntPtr eventRef, IntPtr user_data) { DestroyOldMenuObjects(); menuOpenDepth++; try { IntPtr menuRef = Carbon.GetEventParameter(eventRef, CarbonEventParameterName.DirectObject, CarbonEventParameterType.MenuRef); //don't update dynamic menus recursively if (!mainMenus.Contains(menuRef) && menuRef != appMenu) { return(CarbonEventHandlerStatus.NotHandled); } // uint cmd = HIToolbox.GetMenuItemCommandID (new HIMenuItem (menuRef, 0)); CommandTargetRoute route = new CommandTargetRoute(); ushort count = HIToolbox.CountMenuItems(menuRef); for (ushort i = 1; i <= count; i++) { HIMenuItem mi = new HIMenuItem(menuRef, i); uint macCmdID = HIToolbox.GetMenuItemCommandID(mi); object cmdID; //link items if (macCmdID == linkCommandId) { if (IsGloballyDisabled) { HIToolbox.DisableMenuItem(mi); } else { HIToolbox.EnableMenuItem(mi); } continue; } //items that map to command objects if (!commands.TryGetValue(macCmdID, out cmdID) || cmdID == null) { continue; } CommandInfo cinfo = manager.GetCommandInfo(cmdID, route); menuIdMap[cmdID] = HIToolbox.GetMenuID(menuRef); UpdateMenuItem(menuRef, menuRef, ref i, ref count, macCmdID, cinfo); } } catch (Exception ex) { System.Console.WriteLine(ex); } return(CarbonEventHandlerStatus.NotHandled); }
static void UpdateAutoHide(HIMenuItem item) { IntPtr submenu; if (HIToolbox.GetMenuItemHierarchicalMenu(item.MenuRef, item.Index, out submenu) != CarbonMenuStatus.Ok) { return; } if (HasVisibleItems(submenu)) { HIToolbox.ChangeMenuItemAttributes(item, 0, MenuItemAttributes.Hidden); } else { HIToolbox.ChangeMenuItemAttributes(item, MenuItemAttributes.Hidden, 0); } }
static void OnCommandActivating(object sender, CommandActivationEventArgs args) { uint menuId; if (args.Source == CommandSource.Keybinding && menuIdMap.TryGetValue(args.CommandId, out menuId)) { //FIXME: for some reason we have to flash again after a delay to toggle the previous flash off? //some flashes can be unreliable, e.g. minimize, and modal dialogs don't seem to run timeouts, so the flash comes late GLib.Timeout.Add(50, delegate { HIToolbox.FlashMenuBar(menuId); return(false); }); GLib.Timeout.Add(250, delegate { HIToolbox.FlashMenuBar(menuId); return(false); }); } }
static void UpdateMenuItem(IntPtr rootMenu, IntPtr menuRef, ref ushort index, ref ushort count, uint macCmdID, CommandInfo cinfo) { if (cinfo.ArrayInfo != null) { //remove the existing items, except one, which we hide, so it gets updated next time even if the list is empty HIToolbox.ChangeMenuItemAttributes(new HIMenuItem(menuRef, index), MenuItemAttributes.Hidden, 0); index++; while (index <= count && HIToolbox.GetMenuItemCommandID(new HIMenuItem(menuRef, index)) == macCmdID) { HIToolbox.DeleteMenuItem(menuRef, index); count--; } index--; //add the new items foreach (CommandInfo ci in cinfo.ArrayInfo) { count++; HIToolbox.InsertMenuItem(menuRef, ci.Text, index++, 0, macCmdID); //associate a reference constant with the menu, used to index the DataItem //it's one-based, so that 0 can be used as a flag that there's no associated object objectsToDestroyOnMenuClose.Add(ci.DataItem); uint refcon = (uint)objectsToDestroyOnMenuClose.Count; SetMenuItemAttributes(new HIMenuItem(menuRef, index), ci, refcon); if (ci is CommandInfoSet) { BuildDynamicSubMenu(rootMenu, menuRef, index, macCmdID, (CommandInfoSet)ci); } } } else { SetMenuItemAttributes(new HIMenuItem(menuRef, index), cinfo, 0); if (cinfo is CommandInfoSet) { BuildDynamicSubMenu(rootMenu, menuRef, index, macCmdID, (CommandInfoSet)cinfo); } } }
static bool HasVisibleItems(IntPtr submenu) { var route = new CommandTargetRoute(); ushort count = HIToolbox.CountMenuItems(submenu); for (ushort i = 1; i <= count; i++) { HIMenuItem mi = new HIMenuItem(submenu, i); uint macCmdID = HIToolbox.GetMenuItemCommandID(mi); object cmdID; if (macCmdID == linkCommandId) { return(true); } if (!commands.TryGetValue(macCmdID, out cmdID) || cmdID == null) { continue; } CommandInfo cinfo = manager.GetCommandInfo(cmdID, route); if (cinfo.ArrayInfo != null) { foreach (CommandInfo ci in cinfo.ArrayInfo) { if (ci.Visible) { return(true); } } } else if (cinfo.Visible) { return(true); } } return(false); }
static void SetMenuAccelerator(HIMenuItem item, string accelKey) { MenuAccelModifier mod; ushort glyphCode, charCode, hardwareCode; if (GetAcceleratorKeys(accelKey, out glyphCode, out charCode, out hardwareCode, out mod)) { if (glyphCode != 0) { HIToolbox.SetMenuItemKeyGlyph(item.MenuRef, item.Index, (short)glyphCode); } else if (hardwareCode != 0) { HIToolbox.SetMenuItemCommandKey(item.MenuRef, item.Index, true, hardwareCode); } else { HIToolbox.SetMenuItemCommandKey(item.MenuRef, item.Index, false, charCode); } HIToolbox.SetMenuItemModifiers(item.MenuRef, item.Index, mod); } }
static void CreateChildren(IntPtr parentMenu, CommandEntrySet entrySet, HashSet <object> ignoreCommands) { var menuId = HIToolbox.GetMenuID(parentMenu); foreach (CommandEntry entry in entrySet) { CommandEntrySet ces = entry as CommandEntrySet; if (ces == null) { ushort pos; if (ignoreCommands.Contains(entry.CommandId)) { continue; } if (entry.CommandId == Command.Separator) { HIToolbox.AppendMenuSeparator(parentMenu); continue; } if (entry is LinkCommandEntry) { LinkCommandEntry lce = (LinkCommandEntry)entry; pos = HIToolbox.AppendMenuItem(parentMenu, (lce.Text ?? "").Replace("_", ""), 0, linkCommandId); HIToolbox.SetMenuItemReferenceConstant(new HIMenuItem(parentMenu, pos), (uint)linkCommands.Count); linkCommands.Add(lce.Url); continue; } Command cmd = manager.GetCommand(entry.CommandId); if (cmd == null) { MonoDevelop.Core.LoggingService.LogError( "Mac main menu '{0}' child '{1}' maps to null command", entrySet.Name, entry.CommandId); continue; } if (cmd is CustomCommand) { MonoDevelop.Core.LoggingService.LogWarning( "Mac main menu does not support custom command widgets for command '{0}'", entry.CommandId); continue; } menuIdMap[entry.CommandId] = menuId; ActionCommand acmd = cmd as ActionCommand; if (acmd == null) { MonoDevelop.Core.LoggingService.LogWarning( "Mac main menu does not support command type '{0}' for command '{1}'", cmd.GetType(), entry.CommandId); continue; } uint macCmdId = GetNewMenuItemId(cmd); pos = HIToolbox.AppendMenuItem(parentMenu, (cmd.Text ?? "").Replace("_", ""), 0, macCmdId); } else { var macCmdId = (ces.AutoHide) ? autohideSubmenuCommandId : submenuCommandId; IntPtr menuRef = HIToolbox.CreateMenu(idSeq++, GetName(ces), MenuAttributes.CondenseSeparators); mainMenus.Add(menuRef); CreateChildren(menuRef, ces, ignoreCommands); ushort pos = HIToolbox.AppendMenuItem(parentMenu, GetName(ces), 0, macCmdId); HIToolbox.CheckResult(HIToolbox.SetMenuItemHierarchicalMenu(parentMenu, pos, menuRef)); } } }
static void AddMenuItems(HIMenuItem afterItem, IEnumerable <CommandEntry> entries) { IntPtr parentMenu = afterItem.MenuRef; var menuId = HIToolbox.GetMenuID(parentMenu); var index = afterItem.Index; foreach (CommandEntry entry in entries) { var ces = entry as CommandEntrySet; if (ces != null) { var macMenuCmdId = (ces.AutoHide) ? autohideSubmenuCommandId : submenuCommandId; IntPtr menuRef = HIToolbox.CreateMenu(idSeq++, GetName(ces), MenuAttributes.CondenseSeparators); mainMenus.Add(menuRef); AddMenuItems(new HIMenuItem(menuRef, 0), ces); index = HIToolbox.InsertMenuItem(parentMenu, GetName(ces), index, 0, macMenuCmdId); HIToolbox.CheckResult(HIToolbox.SetMenuItemHierarchicalMenu(parentMenu, index, menuRef)); continue; } if (entry.CommandId == Command.Separator) { index = HIToolbox.InsertMenuSeparator(parentMenu, index); continue; } if (entry is LinkCommandEntry) { var lce = (LinkCommandEntry)entry; index = HIToolbox.InsertMenuItem(parentMenu, (lce.Text ?? "").Replace("_", ""), index, 0, linkCommandId); HIToolbox.SetMenuItemReferenceConstant(new HIMenuItem(parentMenu, index), (uint)linkCommands.Count); linkCommands.Add(lce.Url); continue; } Command cmd = manager.GetCommand(entry.CommandId); if (cmd == null) { MonoDevelop.Core.LoggingService.LogError( "Mac main menu item '{0}' maps to null command", entry.CommandId); continue; } if (cmd is CustomCommand) { MonoDevelop.Core.LoggingService.LogWarning( "Mac main menu does not support custom command widget for command '{0}'", entry.CommandId); continue; } menuIdMap[entry.CommandId] = menuId; var acmd = cmd as ActionCommand; if (acmd == null) { MonoDevelop.Core.LoggingService.LogWarning( "Mac main menu does not support command type '{0}' for command '{1}'", cmd.GetType(), entry.CommandId); continue; } uint macCmdId = GetMacID(entry.CommandId); index = HIToolbox.InsertMenuItem(parentMenu, (cmd.Text ?? "").Replace("_", ""), index, 0, macCmdId); } }
public static void SetAppMenuItems(CommandManager manager, CommandEntrySet entrySet) { HIMenuItem mnu = HIToolbox.GetMenuItem((uint)CarbonCommandID.Hide); bool firstTime = appMenu == IntPtr.Zero; appMenu = mnu.MenuRef; var existingitems = GetMenuCommandIDs(appMenu).ToList(); for (int i = existingitems.Count - 1; i >= 0; i--) { var item = new HIMenuItem(appMenu, (ushort)(i + 1)); //if first time, keep all the existing items except QuitAndCloseAllWindows if (firstTime && existingitems [i] != (uint)CarbonCommandID.QuitAndCloseAllWindows) { //mark them so we can recognise and keep them in future //HIToolbox.SetMenuItemReferenceConstant (item, uint.MaxValue); continue; } // if it's not the first time, keep the original items we marked if (!firstTime && HIToolbox.GetMenuItemReferenceConstant(item) == UInt32.MaxValue) { continue; } //remove everything else HIToolbox.DeleteMenuItem(item); existingitems.RemoveAt(i); } // Iterate backwards over the entries. For each one, try to find a matching built-in item. If successful, // get all the items from that point to the last item that was not yet inserted, and insert them // at that point. var entries = entrySet.ToList(); int uninsertedCount = entries.Count; for (int i = entries.Count - 1; i >= 0; i--) { var entry = entries [i]; if (entry.CommandId == null || entry.CommandId == Command.Separator) { continue; } var id = GetMacID(entry.CommandId); //find an existing item int match = existingitems.IndexOf(id); if (match >= 0) { //if there are uninserted items *after* this one, insert them at this point if ((uninsertedCount - i) > 0) { var items = entries.Skip(i + 1).Take(uninsertedCount - i - 2); var insertionPoint = new HIMenuItem(appMenu, (ushort)(match + 1)); AddMenuItems(insertionPoint, items); } uninsertedCount = i; } } // Finally, insert any remaining items at the beginning/top of the menu. if (uninsertedCount > 0) { AddMenuItems(new HIMenuItem(appMenu, 0), entries.Take(uninsertedCount)); } }
static void SetMenuItemAttributes(HIMenuItem item, CommandInfo ci, uint refcon) { MenuItemData data = new MenuItemData(); IntPtr text = IntPtr.Zero; try { if (ci.IsArraySeparator) { data.Attributes |= MenuItemAttributes.Separator; } else if (!ci.Visible) { data.Attributes |= MenuItemAttributes.Hidden; } else { data.Attributes &= ~MenuItemAttributes.Hidden; data.CFText = CoreFoundation.CreateString(GetCleanCommandText(ci)); //disable also when MD main window doesn't have toplevel focus, or commands will be //accessible when modal dialogs are active bool disabled = !ci.Enabled || IsGloballyDisabled; data.Enabled = !disabled; if (disabled) { data.Attributes |= MenuItemAttributes.Disabled; } ushort glyphCode, charCode, hardwareCode; MenuAccelModifier mod; if (GetAcceleratorKeys(ci.AccelKey, out glyphCode, out charCode, out hardwareCode, out mod)) { data.CommandKeyModifiers = mod; if (glyphCode != 0) { data.CommandKeyGlyph = glyphCode; data.Attributes ^= MenuItemAttributes.UseVirtualKey; } else if (hardwareCode != 0) { data.CommandVirtualKey = (char)hardwareCode; data.Attributes |= MenuItemAttributes.UseVirtualKey; } else { data.CommandKey = (char)charCode; data.Attributes ^= MenuItemAttributes.UseVirtualKey; } } //else{ //FIXME: remove existing commands if necessary data.Mark = ci.Checked ? ci.CheckedInconsistent ? '-' //FIXME: is this a good symbol for CheckedInconsistent? : (char)MenuGlyphs.Checkmark : '\0'; data.ReferenceConstant = refcon; } HIToolbox.SetMenuItemData(item.MenuRef, item.Index, false, ref data); } finally { if (text != IntPtr.Zero) { CoreFoundation.Release(text); } } }