Commit 26a17639 authored by Timo Dörr's avatar Timo Dörr Committed by Bertrand Lorentz

OSX: Move to cocoa API (bgo#677866)

This commit moves from the ige-/gtk-mac-integration API to the
GtkOSXApplication API (which is still provided by the same library
called 'gtk-mac-integration', not to be confused with the API name).
This means banshee no longer uses any OS X carbon functions (which are
deprecated), but cocoa only. The Banshee.Osx backend also now fully
relies on gtk-mac-integration as interface to OS X specific functions
and does no longer contain own DllImports into OS X core framework.

This drops the support for closing the banshee main window (via
Cmd+Close) since GtkOSXApplication does not handle this properly right
now. Banshee is a single-window only application at the moment, so this
shouldn't be a usecase anyway.
Signed-off-by: Bertrand Lorentz's avatarBertrand Lorentz <bertrand.lorentz@gmail.com>
parent abf82b62
......@@ -20,7 +20,6 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<OutputPath>..\..\..\bin</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
......@@ -29,7 +28,6 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<OutputPath>..\..\..\bin</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
......@@ -63,31 +61,27 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="gtk-sharp">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="gtk-sharp" />
<Reference Include="Mono.Posix">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..bin\Mono.Posix.dll</HintPath>
</Reference>
<Reference Include="MonoMac, Version=0.0.0.0, Culture=neutral" />
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Banshee.OsxBackend\HardwareManager.cs" />
<Compile Include="Banshee.OsxBackend\OsxService.cs" />
<Compile Include="OsxIntegration.Ige\IgeMacMenu.cs" />
<Compile Include="OsxIntegration.Ige\IgeMacMenuGroup.cs" />
<Compile Include="OsxIntegration.Framework\AppleEvent.cs" />
<Compile Include="OsxIntegration.Framework\ApplicationEvents.cs" />
<Compile Include="OsxIntegration.Framework\Carbon.cs" />
<Compile Include="OsxIntegration.Framework\CoreFoundation.cs" />
<Compile Include="OsxIntegration.Framework\HIToolbox.cs" />
<Compile Include="OsxIntegration.Framework\NavDialog.cs" />
<Compile Include="OsxIntegration.GtkOsxApplication\GtkOsxApplication.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Banshee.Osx.addin.xml">
<LogicalName>Banshee.Osx.addin.xml</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\osx_accel_map">
<LogicalName>osx_accel_map</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
......@@ -106,6 +100,7 @@
</MonoDevelop>
</ProjectExtensions>
<ItemGroup>
<Folder Include="OsxIntegration.Ige\" />
<Folder Include="OsxIntegration.GtkOsxApplication\" />
<Folder Include="Resources\" />
</ItemGroup>
</Project>
......@@ -30,14 +30,16 @@
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using Gtk;
using Mono.Unix;
using Banshee.ServiceStack;
using Banshee.Gui;
using OsxIntegration.Ige;
using OsxIntegration.Framework;
using OsxIntegration.GtkOsxApplication;
using Hyena;
namespace Banshee.OsxBackend
{
......@@ -45,8 +47,7 @@ namespace Banshee.OsxBackend
{
private GtkElementsService elements_service;
private InterfaceActionService interface_action_service;
private uint ui_manager_id;
private bool disposed;
private string accel_map_filename = "osx_accel_map";
void IExtensionService.Initialize ()
{
......@@ -83,110 +84,82 @@ namespace Banshee.OsxBackend
private void Initialize ()
{
elements_service.PrimaryWindow.WindowStateEvent += OnWindowStateEvent;
// add close action
interface_action_service.GlobalActions.Add (new ActionEntry [] {
new ActionEntry ("CloseAction", Stock.Close,
Catalog.GetString ("_Close"), "<Control>W",
Catalog.GetString ("Close"), CloseWindow)
});
// merge close menu item
ui_manager_id = interface_action_service.UIManager.AddUiFromString (@"
<ui>
<menubar name=""MainMenu"">
<menu name=""MediaMenu"" action=""MediaMenuAction"">
<placeholder name=""ClosePlaceholder"">
<menuitem name=""Close"" action=""CloseAction""/>
</placeholder>
</menu>
</menubar>
</ui>
");
RegisterCloseHandler ();
ConfigureOsxMainMenu ();
IgeMacMenu.GlobalKeyHandlerEnabled = false;
ApplicationEvents.Quit += (o, e) => {
Banshee.ServiceStack.Application.Shutdown ();
e.Handled = true;
};
// load OS X specific key mappings, possibly overriding default mappings
// set in GlobalActions or $HOME/.config/banshee-1/gtk_accel_map
string accel_map = Paths.Combine (Paths.ApplicationData, accel_map_filename);
if (!File.Exists (accel_map)) {
// copy our template
CopyAccelMapToDataDir ();
}
Gtk.AccelMap.Load (accel_map);
ApplicationEvents.Reopen += (o, e) => {
SetWindowVisibility (true);
e.Handled = true;
};
ConfigureOsxMainMenu ();
}
public void Dispose ()
{
if (disposed) {
return;
}
elements_service.PrimaryWindowClose = null;
interface_action_service.GlobalActions.Remove ("CloseAction");
interface_action_service.UIManager.RemoveUi (ui_manager_id);
disposed = true;
}
private void ConfigureOsxMainMenu ()
{
IgeMacMenu.MenuBar = (MenuShell)interface_action_service.UIManager.GetWidget ("/MainMenu");
var osx_app = new GtkOsxApplication ();
var ui = interface_action_service.UIManager;
IgeMacMenu.QuitMenuItem = ui.GetWidget ("/MainMenu/MediaMenu/Quit") as MenuItem;
// remove the "Quit" item as this is auto-added by gtk-mac-integration to the AppMenu
var quit_item = ((MenuItem)interface_action_service.UIManager.GetWidget ( "/MainMenu/MediaMenu/Quit"));
if(quit_item != null) {
quit_item.Hide ();
}
var group = IgeMacMenu.AddAppMenuGroup ();
group.AddMenuItem (ui.GetWidget ("/MainMenu/HelpMenu/About") as MenuItem, null);
group.AddMenuItem (ui.GetWidget ("/MainMenu/EditMenu/Preferences") as MenuItem, null);
}
MenuShell shell = (MenuShell) interface_action_service.UIManager.GetWidget ("/MainMenu");
if (shell != null) {
osx_app.SetMenu (shell);
}
private void RegisterCloseHandler ()
{
if (elements_service.PrimaryWindowClose == null) {
elements_service.PrimaryWindowClose = () => {
CloseWindow (null, null);
return true;
};
// place the "about" and "preferences" menu items into the OS X application menu
// as every OS X app uses this convention
var about_item = interface_action_service.UIManager.GetWidget ("/MainMenu/HelpMenu/About") as MenuItem;
if (about_item != null) {
osx_app.InsertIntoAppMenu (about_item, 0);
}
}
private void CloseWindow (object o, EventArgs args)
{
SetWindowVisibility (false);
}
// place a separator between the About and the Preferences dialog
var separator = new SeparatorMenuItem ();
osx_app.InsertIntoAppMenu (separator, 1);
private void SetCloseMenuItemSensitivity (bool sensitivity)
{
((MenuItem)interface_action_service.UIManager.GetWidget (
"/MainMenu/MediaMenu/ClosePlaceholder/Close")).Sensitive = sensitivity;
}
var preferences_item = interface_action_service.UIManager.GetWidget ("/MainMenu/EditMenu/Preferences") as MenuItem;
if (preferences_item != null) {
osx_app.InsertIntoAppMenu (preferences_item, 2);
}
private void SetWindowVisibility (bool visible)
{
SetCloseMenuItemSensitivity (visible);
if (elements_service.PrimaryWindow.Visible != visible) {
elements_service.PrimaryWindow.ToggleVisibility ();
// remove unnecessary separator as we have moved the preferences item
var preferences_seperator = interface_action_service.UIManager.GetWidget ("/MainMenu/EditMenu/PreferencesSeparator") as SeparatorMenuItem;
if (preferences_seperator != null) {
preferences_seperator.Destroy ();
}
// actually performs the menu binding
osx_app.Ready ();
}
private void OnWindowStateEvent (object obj, WindowStateEventArgs args)
/// <summary>
/// Copies the OSX specific accel map from embedded resource
/// to the user's data dir for future loading
/// </summary>
public void CopyAccelMapToDataDir ()
{
switch (args.Event.NewWindowState) {
case Gdk.WindowState.Iconified:
SetCloseMenuItemSensitivity (false);
break;
case (Gdk.WindowState)0:
SetCloseMenuItemSensitivity (true);
break;
}
byte[] buffer = new byte[1024];
var assembly = Assembly.GetExecutingAssembly ();
var accel_map = Paths.Combine (Paths.ApplicationData, accel_map_filename);
// perform the copy
using (Stream output = File.OpenWrite(accel_map)) {
using (Stream resource_stream = assembly.GetManifestResourceStream (accel_map_filename)) {
int bytes = -1;
while ((bytes = resource_stream.Read(buffer, 0, buffer.Length)) > 0) {
output.Write(buffer, 0, bytes);
}
}
}
}
string IService.ServiceName {
......
......@@ -7,16 +7,11 @@ INSTALL_DIR = $(BACKENDS_INSTALL_DIR)
SOURCES = \
Banshee.OsxBackend/HardwareManager.cs \
Banshee.OsxBackend/OsxService.cs \
OsxIntegration.Framework/AppleEvent.cs \
OsxIntegration.Framework/ApplicationEvents.cs \
OsxIntegration.Framework/Carbon.cs \
OsxIntegration.Framework/CoreFoundation.cs \
OsxIntegration.Framework/HIToolbox.cs \
OsxIntegration.Framework/NavDialog.cs \
OsxIntegration.Ige/IgeMacMenu.cs \
OsxIntegration.Ige/IgeMacMenuGroup.cs
RESOURCES = Banshee.Osx.addin.xml
OsxIntegration.GtkOsxApplication/GtkOsxApplication.cs
RESOURCES = \
Banshee.Osx.addin.xml \
Resources/osx_accel_map
EXTRA_BUNDLE = $(MONOMAC_ASSEMBLIES)
......
//
// AppleEvent.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (c) 2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
namespace OsxIntegration.Framework
{
public static class AppleEvent
{
const string AELib = Carbon.CarbonLib;
//[DllImport (AELib)]
//OSErr AECreateDesc (DescType typeCode, IntPtr dataPtr, Size dataSize, out AEDesc result);
}
}
//
// ApplicationEvents.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
//
// Copyright (c) 2010 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
#pragma warning disable 0169
namespace OsxIntegration.Framework
{
public static class ApplicationEvents
{
static object lockObj = new object ();
#region Quit
static EventHandler<ApplicationEventArgs> quit;
static IntPtr quitHandlerRef = IntPtr.Zero;
public static event EventHandler<ApplicationEventArgs> Quit {
add {
lock (lockObj) {
quit += value;
if (quitHandlerRef == IntPtr.Zero)
quitHandlerRef = Carbon.InstallApplicationEventHandler (HandleQuit, CarbonEventApple.QuitApplication);
}
}
remove {
lock (lockObj) {
quit -= value;
if (quit == null && quitHandlerRef != IntPtr.Zero) {
Carbon.RemoveEventHandler (quitHandlerRef);
quitHandlerRef = IntPtr.Zero;
}
}
}
}
static CarbonEventHandlerStatus HandleQuit (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
{
var args = new ApplicationEventArgs ();
quit (null, args);
return args.HandledStatus;
}
#endregion
#region Reopen
static EventHandler<ApplicationEventArgs> reopen;
static IntPtr reopenHandlerRef = IntPtr.Zero;
public static event EventHandler<ApplicationEventArgs> Reopen {
add {
lock (lockObj) {
reopen += value;
if (reopenHandlerRef == IntPtr.Zero)
reopenHandlerRef = Carbon.InstallApplicationEventHandler (HandleReopen, CarbonEventApple.ReopenApplication);
}
}
remove {
lock (lockObj) {
reopen -= value;
if (reopen == null && reopenHandlerRef != IntPtr.Zero) {
Carbon.RemoveEventHandler (reopenHandlerRef);
reopenHandlerRef = IntPtr.Zero;
}
}
}
}
static CarbonEventHandlerStatus HandleReopen (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
{
var args = new ApplicationEventArgs ();
reopen (null, args);
return args.HandledStatus;
}
#endregion
#region OpenDocuments
static EventHandler<ApplicationDocumentEventArgs> openDocuments;
static IntPtr openDocumentsHandlerRef = IntPtr.Zero;
public static event EventHandler<ApplicationDocumentEventArgs> OpenDocuments {
add {
lock (lockObj) {
openDocuments += value;
if (openDocumentsHandlerRef == IntPtr.Zero)
openDocumentsHandlerRef = Carbon.InstallApplicationEventHandler (HandleOpenDocuments, CarbonEventApple.OpenDocuments);
}
}
remove {
lock (lockObj) {
openDocuments -= value;
if (openDocuments == null && openDocumentsHandlerRef != IntPtr.Zero) {
Carbon.RemoveEventHandler (openDocumentsHandlerRef);
openDocumentsHandlerRef = IntPtr.Zero;
}
}
}
}
static CarbonEventHandlerStatus HandleOpenDocuments (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
{
try {
var docs = Carbon.GetFileListFromEventRef (eventRef);
var args = new ApplicationDocumentEventArgs (docs);
openDocuments (null, args);
return args.HandledStatus;
} catch (Exception ex) {
System.Console.WriteLine (ex);
return CarbonEventHandlerStatus.NotHandled;
}
}
#endregion
#region OpenUrls
static EventHandler<ApplicationUrlEventArgs> openUrls;
static IntPtr openUrlsHandlerRef = IntPtr.Zero;
public static event EventHandler<ApplicationUrlEventArgs> OpenUrls {
add {
lock (lockObj) {
openUrls += value;
if (openUrlsHandlerRef == IntPtr.Zero)
openUrlsHandlerRef = Carbon.InstallApplicationEventHandler (HandleOpenUrls,
new CarbonEventTypeSpec[] {
//For some reason GetUrl doesn't take CarbonEventClass.AppleEvent
//need to use GURL, GURL
new CarbonEventTypeSpec (CarbonEventClass.Internet, (int)CarbonEventApple.GetUrl)
}
);
}
}
remove {
lock (lockObj) {
openUrls -= value;
if (openUrls == null && openUrlsHandlerRef != IntPtr.Zero) {
Carbon.RemoveEventHandler (openUrlsHandlerRef);
openUrlsHandlerRef = IntPtr.Zero;
}
}
}
}
static CarbonEventHandlerStatus HandleOpenUrls (IntPtr callRef, IntPtr eventRef, IntPtr user_data)
{
try {
var urls = Carbon.GetUrlListFromEventRef (eventRef);
var args = new ApplicationUrlEventArgs (urls);
openUrls (null, args);
return args.HandledStatus;
} catch (Exception ex) {
System.Console.WriteLine (ex);
return CarbonEventHandlerStatus.NotHandled;
}
}
#endregion
}
public class ApplicationEventArgs : EventArgs
{
public bool Handled { get; set; }
internal CarbonEventHandlerStatus HandledStatus {
get {
return Handled? CarbonEventHandlerStatus.Handled : CarbonEventHandlerStatus.NotHandled;
}
}
}
public class ApplicationDocumentEventArgs : ApplicationEventArgs
{
public ApplicationDocumentEventArgs (IList<string> documents)
{
this.Documents = documents;
}
public IList<string> Documents { get; private set; }
}
public class ApplicationUrlEventArgs : ApplicationEventArgs
{
public ApplicationUrlEventArgs (IList<string> urls)
{
this.Urls = urls;
}
public IList<string> Urls { get; private set; }
}
}
//
// CoreFoundation.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
// Miguel de Icaza
//
// Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
namespace OsxIntegration.Framework
{
internal static class CoreFoundation
{
const string CFLib = "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation";
[DllImport (CFLib)]
static extern IntPtr CFStringCreateWithCString (IntPtr alloc, string str, int encoding);
public static IntPtr CreateString (string s)
{
// The magic value is "kCFStringENcodingUTF8"
return CFStringCreateWithCString (IntPtr.Zero, s, 0x08000100);
}
[DllImport (CFLib, EntryPoint="CFRelease")]
public static extern void Release (IntPtr cfRef);
struct CFRange {
public int Location, Length;
public CFRange (int l, int len)
{
Location = l;
Length = len;
}
}
[DllImport (CFLib, CharSet=CharSet.Unicode)]
extern static int CFStringGetLength (IntPtr handle);
[DllImport (CFLib, CharSet=CharSet.Unicode)]
extern static IntPtr CFStringGetCharactersPtr (IntPtr handle);
[DllImport (CFLib, CharSet=CharSet.Unicode)]
extern static IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);
public static string FetchString (IntPtr handle)
{
if (handle == IntPtr.Zero)
return null;
string str;
int l = CFStringGetLength (handle);
IntPtr u = CFStringGetCharactersPtr (handle);
IntPtr buffer = IntPtr.Zero;
if (u == IntPtr.Zero){
CFRange r = new CFRange (0, l);
buffer = Marshal.AllocCoTaskMem (l * 2);
CFStringGetCharacters (handle, r, buffer);
u = buffer;
}
unsafe {
str = new string ((char *) u, 0, l);
}
if (buffer != IntPtr.Zero)
Marshal.FreeCoTaskMem (buffer);
return str;
}
}
}
//
// HIToolbox.cs
//
// Author:
// Michael Hutchinson <mhutchinson@novell.com>
// Miguel de Icaza
//
// Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
#pragma warning disable 0169
namespace OsxIntegration.Framework
{
internal static class HIToolbox
{
const string hiToolboxLib = "/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox";
[DllImport (hiToolboxLib)]
static extern CarbonMenuStatus CreateNewMenu (ushort menuId, MenuAttributes attributes, out IntPtr menuRef);
public static IntPtr CreateMenu (ushort id, string title, MenuAttributes attributes)
{
IntPtr menuRef;
CheckResult (CreateNewMenu (id, attributes, out menuRef));
SetMenuTitle (menuRef, title);
return menuRef;
}
[DllImport (hiToolboxLib)]
internal static extern CarbonMenuStatus SetRootMenu (IntPtr menuRef);