[banshee] OSX: Move to cocoa API (bgo#677866)



commit 26a176399705748c01ddd7e1a4be7ac50908328a
Author: Timo DÃrr <timo latecrew de>
Date:   Mon Jun 11 16:12:14 2012 +0200

    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 <bertrand lorentz gmail com>

 src/Backends/Banshee.Osx/Banshee.Osx.csproj        |   25 +-
 .../Banshee.Osx/Banshee.OsxBackend/OsxService.cs   |  149 ++---
 src/Backends/Banshee.Osx/Makefile.am               |   15 +-
 .../OsxIntegration.Framework/AppleEvent.cs         |   41 --
 .../OsxIntegration.Framework/ApplicationEvents.cs  |  219 ------
 .../Banshee.Osx/OsxIntegration.Framework/Carbon.cs |  692 -------------------
 .../OsxIntegration.Framework/CoreFoundation.cs     |   95 ---
 .../OsxIntegration.Framework/HIToolbox.cs          |  713 --------------------
 .../OsxIntegration.Framework/NavDialog.cs          |  513 --------------
 .../GtkOsxApplication.cs                           |  103 +++
 .../Banshee.Osx/OsxIntegration.Ige/IgeMacMenu.cs   |   74 --
 .../OsxIntegration.Ige/IgeMacMenuGroup.cs          |   54 --
 src/Backends/Banshee.Osx/Resources/osx_accel_map   |   81 +++
 .../Resources/core-ui-actions-layout.xml           |    2 +-
 14 files changed, 261 insertions(+), 2515 deletions(-)
---
diff --git a/src/Backends/Banshee.Osx/Banshee.Osx.csproj b/src/Backends/Banshee.Osx/Banshee.Osx.csproj
index 5ef2d12..4e58bf9 100644
--- a/src/Backends/Banshee.Osx/Banshee.Osx.csproj
+++ b/src/Backends/Banshee.Osx/Banshee.Osx.csproj
@@ -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>
diff --git a/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs b/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
index 432e208..57ae1cd 100644
--- a/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
+++ b/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
@@ -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 {
diff --git a/src/Backends/Banshee.Osx/Makefile.am b/src/Backends/Banshee.Osx/Makefile.am
index 9a5fb23..7ed14a1 100644
--- a/src/Backends/Banshee.Osx/Makefile.am
+++ b/src/Backends/Banshee.Osx/Makefile.am
@@ -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)
 
diff --git a/src/Backends/Banshee.Osx/OsxIntegration.GtkOsxApplication/GtkOsxApplication.cs b/src/Backends/Banshee.Osx/OsxIntegration.GtkOsxApplication/GtkOsxApplication.cs
new file mode 100644
index 0000000..60455fd
--- /dev/null
+++ b/src/Backends/Banshee.Osx/OsxIntegration.GtkOsxApplication/GtkOsxApplication.cs
@@ -0,0 +1,103 @@
+// 
+// GtkOsxApplication.cs
+// 
+// Author:
+//   Timo DÃrr <timo latecrew de>
+// 
+// Copyright 2012 Timo DÃrr
+// 
+// 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;
+using Gtk;
+
+namespace OsxIntegration.GtkOsxApplication
+{
+    /// <summary>
+    /// Wraps the native GtkOsxApplicationBinding function to C# style API
+    /// </summary>
+    public class GtkOsxApplication
+    {
+        // Main application handle
+        private IntPtr theApp;
+
+        public GtkOsxApplication ()
+        {
+            IntPtr osx_app = gtk_osxapplication_get_type ();
+            theApp = new GLib.GType (osx_app).Val;
+
+        }
+
+        // Takes the Gtk.MenuShell and sets it as OS X application menu
+        public void SetMenu (MenuShell shell)
+        {
+            gtk_osxapplication_set_menu_bar (theApp, shell.Handle);
+        }
+
+        // Places MenuItems into the OS X specific "Application" menu
+        // (the menu right next to the Apple-Menu)
+        // It's convention on OS X to put the about and the preferences
+        // dialog in there
+        public void InsertIntoAppMenu (MenuItem item, int index)
+        {
+            gtk_osxapplication_insert_app_menu_item (theApp, item.Handle, index);
+            gtk_osxapplication_sync_menubar (theApp);
+        }
+
+        public void SetWindowMenu (MenuItem item)
+        {
+            gtk_osxapplication_set_window_menu (theApp, item.Handle);
+            gtk_osxapplication_sync_menubar (theApp);
+        }
+        public void Ready ()
+        {
+            gtk_osxapplication_ready (theApp);
+        }
+
+        // Bindings against native gtk-mac-integration/GtkOSXApplication
+        // which uses cocoa instead of deprecated carbon
+        // for documentation of these functions, see:
+        // http://gtk-osx.sourceforge.net/ige-mac-integration/GtkOSXApplication.html
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static IntPtr gtk_osxapplication_get_type ();
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_ready (IntPtr app);
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_set_menu_bar (IntPtr app, IntPtr menu_shell);
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_insert_app_menu_item (IntPtr app, IntPtr menu_item, int index);
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_sync_menubar (IntPtr app);
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_set_dock_menu  (IntPtr app, IntPtr menu_shell);
+
+        [DllImport ("libgtkmacintegration.dylib")]
+        protected extern static void gtk_osxapplication_set_window_menu (IntPtr app, IntPtr menu_item);
+
+        // TODO add more functions from GtkOsxApplication
+
+    }
+}
\ No newline at end of file
diff --git a/src/Backends/Banshee.Osx/Resources/osx_accel_map b/src/Backends/Banshee.Osx/Resources/osx_accel_map
new file mode 100644
index 0000000..e881a8b
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Resources/osx_accel_map
@@ -0,0 +1,81 @@
+; Nereid GtkAccelMap rc-file         -*- scheme -*-
+; OS X accelerator mappings
+
+;;; OS X Application menu
+(gtk_accel_path "<Actions>/Global/PreferencesAction" "<Primary>comma")
+(gtk_accel_path "<Actions>/Global/QuitAction" "<Primary>q")
+
+;;; MEDIA MENU
+
+; use CMD-O as in iTunes to import media
+(gtk_accel_path "<Actions>/Global/ImportAction" "<Primary>o")
+
+; open location (not present in itunes)
+(gtk_accel_path "<Actions>/Global/OpenLocationAction" "<Primary><Alt>o")
+
+; Close window (mac only)
+(gtk_accel_path "<Actions>/Global/CloseAction" "<Primary>w")
+
+; New Playlist
+(gtk_accel_path "<Actions>/Source/NewPlaylistAction" "<Primary>n")
+
+; New smart playlist
+(gtk_accel_path "<Actions>/Source/NewSmartPlaylistAction" "<Primary><Alt>n")
+
+; (gtk_accel_path "<Actions>/Track/TrackEditorAction" "e")
+; (gtk_accel_path "<Actions>/Track/RemoveTracksFromLibraryAction" "")
+
+
+;;; EDIT MENU
+
+; remove from library (don't delete from disk)
+(gtk_accel_path "<Actions>/Track/RemoveTracksAction" "BackSpace")
+
+; remove from disk drive (delete)
+(gtk_accel_path "<Actions>/Track/DeleteTracksFromDriveAction" "<Primary>BackSpace")
+
+; source properties (like show id3 info etc.)
+(gtk_accel_path "<Actions>/Track/TrackPropertiesAction" "<Primary>i")
+
+; select all
+(gtk_accel_path "<Actions>/Track/SelectAllAction" "<Primary>a")
+
+; select none 
+(gtk_accel_path "<Actions>/Track/SelectNoneAction" "<Shift><Primary>a")
+
+; show/hide the browser
+(gtk_accel_path "<Actions>/BrowserView/BrowserVisibleAction" "<Primary>b")
+
+;;; VIEW MENU
+
+; this is alt-cmd-2 in iTunes but its window related
+(gtk_accel_path "<Actions>/View/ShowEqualizerAction" "<Primary>e")
+
+; fullscreen 
+(gtk_accel_path "<Actions>/View/FullScreenAction" "<Control><Primary>f")
+
+
+;;; HELP MENU
+
+(gtk_accel_path "<Actions>/Global/UserHelp" "<Primary>question")
+
+
+;;; TRACK NAVIGATION
+
+; next track 
+(gtk_accel_path "<Actions>/Playback/NextAction" "<Primary>Right")
+
+; previous track
+(gtk_accel_path "<Actions>/Playback/PreviousAction" "<Primary>Left")
+
+; show current track in file browser 
+(gtk_accel_path "<Actions>/Track/OpenContainingFolderAction" "<Primary><Shift>r")
+
+; jump to current track
+(gtk_accel_path "<Actions>/Playback/JumpToPlayingTrackAction" "<Primary>l")
+
+; Play/Pause
+(gtk_accel_path "<Actions>/Playback/PlayPauseAction" "space")
+
+; stop when finished playing this track
+(gtk_accel_path "<Actions>/Playback/StopWhenFinishedAction" "<Shift>space")
diff --git a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
index 7908e13..290d330 100644
--- a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
+++ b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
@@ -76,7 +76,7 @@
       <menuitem name="SourceProperties" action="SourcePropertiesAction"/>
       <menuitem name="UnmapSource" action="UnmapSourceAction"/>
       <menuitem name="TrackProperties" action="TrackPropertiesAction"/>
-      <separator/>
+      <separator name="PreferencesSeparator" />
       <menuitem name="Preferences" action="PreferencesAction"/>
     </menu>
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]