banshee r3709 - in trunk/banshee: . src/Clients/Nereid/Nereid src/Core/Banshee.Services/Banshee.PlaybackController src/Core/Banshee.ThickClient src/Core/Banshee.ThickClient/Banshee.Gui src/Core/Banshee.ThickClient/Banshee.Gui.Widgets src/Core/Banshee.ThickClient/Resources src/Libraries/Hyena.Gui src/Libraries/Hyena.Gui/Hyena.Gui src/Libraries/Hyena.Gui/Hyena.Widgets



Author: scottp
Date: Mon Apr  7 22:38:23 2008
New Revision: 3709
URL: http://svn.gnome.org/viewvc/banshee?rev=3709&view=rev

Log:
* src/Clients/Nereid/Nereid/PlayerInterface.cs: Pack in the
NextArrowButton.

* src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs:
Adds all of the shuffle action definitions.

* src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs: Use new
shuffle actions.

* src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs:
Replaced if/elses with a cast of the action value to the enum type.

* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/NextArrowButton.cs:
The next button now has a drop-down menu to select the shuffle
setting.

* src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs:
Moved all of the logic into Hyena.Widgets.ActionButton.

* src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml:
Added new shuffle actions to the menu, replace the old next button
with a placeholder for the fancy new one.

* src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackShuffleMode.cs:
Added values for shuffling by song, artist, album.

* src/Libraries/Hyena.Gui/Hyena.Gui/IRadioActionGroup.cs: An interface
we seem to need until someone comes up with a more creative
solution.

* src/Libraries/Hyena.Gui/Hyena.Widgets/ArrowButton.cs: This widget
visually works just the Firefox 2 forward/back buttons. There is a
main button which is tied to the active action and an arrow to the
right side. Clicking the arrow brings up a menu of the actions in
the IRadioActionGroup.

* src/Libraries/Hyena.Gui/Hyena.Widgets/ActionButton.cs: An
ActionButton is a button which pops up a menu when you click on it.
It is tied to an IRadioActionGroup.

Added:
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/NextArrowButton.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/IRadioActionGroup.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ActionButton.cs
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ArrowButton.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackShuffleMode.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp
   trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
   trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
   trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp
   trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am

Modified: trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs
==============================================================================
--- trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs	(original)
+++ trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs	Mon Apr  7 22:38:23 2008
@@ -147,6 +147,10 @@
             
             primary_vbox.PackStart (toolbar_alignment, false, false, 0);
             
+            NextArrowButton next_button = new NextArrowButton ();
+            next_button.Show ();
+            ActionService.PopulateToolbarPlaceholder (header_toolbar, "/HeaderToolbar/NextArrowButton", next_button);
+            
             ConnectedSeekSlider seek_slider = new ConnectedSeekSlider ();
             seek_slider.Show ();
             ActionService.PopulateToolbarPlaceholder (header_toolbar, "/HeaderToolbar/SeekSlider", seek_slider);

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackShuffleMode.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackShuffleMode.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackShuffleMode.cs	Mon Apr  7 22:38:23 2008
@@ -33,6 +33,8 @@
     public enum PlaybackShuffleMode
     {
         Linear,
-        Shuffle
+        Song,
+        Artist,
+        Album
     }
 }

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/NextArrowButton.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/NextArrowButton.cs	Mon Apr  7 22:38:23 2008
@@ -0,0 +1,61 @@
+//
+// NextArrowButton.cs
+//
+// Author:
+//   Scott Peterson <lunchtimemama gmail com>
+//
+// Copyright (c) 2008 Scott Peterson
+//
+// 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;
+using Gtk;
+using Hyena.Gui;
+using Hyena.Widgets;
+
+using Banshee.ServiceStack;
+
+namespace Banshee.Gui.Widgets
+{
+    public class NextArrowButton : ArrowButton
+    {
+        private InterfaceActionService service = ServiceManager.Get<InterfaceActionService> ();
+        
+        public NextArrowButton() : base (ServiceManager.Get<InterfaceActionService> ().PlaybackActions["NextAction"])
+        {
+        }
+        
+        protected override IEnumerable<Widget> MenuItems {
+            get {
+                PlaybackShuffleActions action = service.PlaybackActions.ShuffleActions;
+                yield return action["ShuffleOffAction"].CreateMenuItem ();
+                yield return new SeparatorMenuItem ();
+                yield return action["ShuffleSongAction"].CreateMenuItem ();
+                yield return action["ShuffleArtistAction"].CreateMenuItem ();
+                yield return action["ShuffleAlbumAction"].CreateMenuItem ();
+            }
+        }
+        
+        protected override IRadioActionGroup ActionGroup {
+            get { return service.PlaybackActions.ShuffleActions; }
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs	Mon Apr  7 22:38:23 2008
@@ -29,102 +29,19 @@
 using System;
 using Gtk;
 
-using Banshee.Gui;
+using Hyena.Gui;
+using Hyena.Widgets;
+
 using Banshee.ServiceStack;
 
 namespace Banshee.Gui.Widgets
 {
-    public class RepeatActionButton : Button
+    public class RepeatActionButton : ActionButton
     {
-        private HBox box = new HBox ();
-        private Image image = new Image ();
-        private Label label = new Label ();
-
-        public RepeatActionButton () : base ()
-        {
-            InterfaceActionService service = ServiceManager.Get<InterfaceActionService> ();
-            service.PlaybackActions.RepeatActions.Changed += OnActionChanged;
-
-            Relief = ReliefStyle.None;
-            
-            label.UseUnderline = true;
-            image.IconSize = (int)IconSize.Menu;
-
-            box.Spacing = 4;
-            box.PackStart (image, false, false, 0);
-            box.PackStart (label, true, true, 0);
-            box.ShowAll ();
-            Add (box);
-            
-            SetActiveItem (service.PlaybackActions.RepeatActions.Active);
-        }
-
-        private void OnActionChanged (object o, ChangedArgs args)
-        {
-            SetActiveItem ((RadioAction)args.Current);
-        }
-
-        private void SetActiveItem (RadioAction action)
-        {
-            if (action == null) {
-                return;
-            }
-
-            image.IconName = action.IconName;
-            label.TextWithMnemonic = action.Label;
-            box.Sensitive = action.Sensitive && action.Visible;
-        }
-
-        protected override void OnActivated ()
-        {
-            BuildMenu ().Popup (null, null, PositionMenu, 1, Gtk.Global.CurrentEventTime);
-        }
-
-        protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
-        {
-            if (evnt.Button == 1 || evnt.Button == 3) {
-                BuildMenu ().Popup (null, null, PositionMenu, 1, evnt.Time);
-            }
-
-            return true;
-        }
-
-        private Menu BuildMenu ()
-        {
-            Menu menu = new Menu ();
-            InterfaceActionService service = ServiceManager.Get<InterfaceActionService> ();
-            foreach (RadioAction action in service.PlaybackActions.RepeatActions) {
-                menu.Append (action.CreateMenuItem ());
-            }
-
-            menu.ShowAll ();
-            return menu;
-        }
-
-        private void PositionMenu (Menu menu, out int x, out int y, out bool push_in) 
-        {
-            Gtk.Requisition menu_req = menu.SizeRequest ();
-            int monitor_num = Screen.GetMonitorAtWindow (GdkWindow);
-            Gdk.Rectangle monitor = Screen.GetMonitorGeometry (monitor_num < 0 ? 0 : monitor_num);
-
-            GdkWindow.GetOrigin (out x, out y);
-            
-            y += Allocation.Y;
-            x += Allocation.X + (Direction == TextDirection.Ltr
-                ? Math.Max (Allocation.Width - menu_req.Width, 0)
-                : - (menu_req.Width - Allocation.Width));
-            
-            if (y + Allocation.Height + menu_req.Height <= monitor.Y + monitor.Height) {
-                y += Allocation.Height;
-            } else if (y - menu_req.Height >= monitor.Y) {
-                y -= menu_req.Height;
-            } else if (monitor.Y + monitor.Height - (y + Allocation.Height) > y) {
-                y += Allocation.Height;
-            } else {
-                y -= menu_req.Height;
-            }
-
-            push_in = false;
+        private InterfaceActionService service = ServiceManager.Get<InterfaceActionService> ();
+        
+        protected override IRadioActionGroup ActionGroup {
+            get { return service.PlaybackActions.RepeatActions; }
         }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	Mon Apr  7 22:38:23 2008
@@ -48,14 +48,20 @@
         private InterfaceActionService action_service;
         private Gtk.Action play_pause_action;
         private PlaybackRepeatActions repeat_actions;
+        private PlaybackShuffleActions shuffle_actions;
 
         public PlaybackRepeatActions RepeatActions {
             get { return repeat_actions; }
         }
         
+        public PlaybackShuffleActions ShuffleActions {
+            get { return shuffle_actions; }
+        }
+        
         public PlaybackActions (InterfaceActionService actionService) : base ("Playback")
         {
             repeat_actions = new PlaybackRepeatActions (actionService);
+            shuffle_actions = new PlaybackShuffleActions (actionService);
 
             Add (new ActionEntry [] {
                 new ActionEntry ("PlayPauseAction", null,
@@ -84,11 +90,6 @@
             });
             
             Add (new ToggleActionEntry [] {
-                new ToggleActionEntry ("ShuffleAction", null,
-                    Catalog.GetString ("Shu_ffle"), null,
-                    Catalog.GetString ("Toggle between shuffle or continuous playback modes"), 
-                    OnShuffleAction, ShuffleEnabled.Get ()),
-                    
                 new ToggleActionEntry ("StopWhenFinishedAction", null,
                     Catalog.GetString ("_Stop When Finished"), "<Shift>space",
                     Catalog.GetString ("Stop playback after the current item finishes playing"), 
@@ -99,10 +100,6 @@
                 new ActionEntry ("PlaybackMenuAction", null,
                     Catalog.GetString ("_Playback"), null, null, null),
             });
-            
-            ServiceManager.PlaybackController.ShuffleMode = ShuffleEnabled.Get () 
-                ? PlaybackShuffleMode.Shuffle
-                : PlaybackShuffleMode.Linear;
 
             this["JumpToPlayingTrackAction"].Sensitive = false;
             this["RestartSongAction"].Sensitive = false;
@@ -111,7 +108,6 @@
             this["PlayPauseAction"].IconName = "media-playback-start";
             this["NextAction"].IconName = "media-skip-forward";
             this["PreviousAction"].IconName = "media-skip-backward";
-            this["ShuffleAction"].IconName = "media-playlist-shuffle";
             
             action_service = actionService;
             ServiceManager.PlayerEngine.StateChanged += OnPlayerEngineStateChanged;
@@ -251,21 +247,5 @@
                 }
             }
         }
-        
-        private void OnShuffleAction (object o, EventArgs args)
-        {
-            ServiceManager.PlaybackController.ShuffleMode = ((ToggleAction)o).Active 
-                ? PlaybackShuffleMode.Shuffle
-                : PlaybackShuffleMode.Linear;
-            
-            ShuffleEnabled.Set ((o as ToggleAction).Active);
-        }
-        
-        public static readonly SchemaEntry<bool> ShuffleEnabled = new SchemaEntry<bool> (
-            "playback", "shuffle",
-            false,
-            "Shuffle playback",
-            "Enable shuffle mode"
-        );
     }
 }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs	Mon Apr  7 22:38:23 2008
@@ -33,13 +33,14 @@
 using Gtk;
 
 using Hyena;
+using Hyena.Gui;
 using Banshee.Configuration;
 using Banshee.ServiceStack;
 using Banshee.PlaybackController;
 
 namespace Banshee.Gui
 {
-    public class PlaybackRepeatActions : BansheeActionGroup, IEnumerable<RadioAction>
+    public class PlaybackRepeatActions : BansheeActionGroup, IRadioActionGroup
     {
         private RadioAction active_action;
 
@@ -48,13 +49,7 @@
             set {
                 active_action = value;
                 RepeatMode.Set (active_action == null ? String.Empty : ActionNameToConfigId (active_action.Name));
-                if (active_action.Value == 0) {
-                    ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.None;
-                } else if (active_action.Value == 1) {
-                    ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatAll;
-                } else {
-                    ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatSingle;
-                }
+                ServiceManager.PlaybackController.RepeatMode = (PlaybackRepeatMode)active_action.Value;
             }
         }
 
@@ -67,15 +62,18 @@
             Add (new RadioActionEntry [] {
                 new RadioActionEntry ("RepeatNoneAction", null, 
                     Catalog.GetString ("Repeat N_one"), null,
-                    Catalog.GetString ("Do not repeat playlist"), 0),
+                    Catalog.GetString ("Do not repeat playlist"),
+                    (int)PlaybackRepeatMode.None),
                     
                 new RadioActionEntry ("RepeatAllAction", null,
                     Catalog.GetString ("Repeat _All"), null,
-                    Catalog.GetString ("Play all songs before repeating playlist"), 1),
+                    Catalog.GetString ("Play all songs before repeating playlist"),
+                    (int)PlaybackRepeatMode.RepeatAll),
                     
                 new RadioActionEntry ("RepeatSingleAction", null,
                     Catalog.GetString ("Repeat Singl_e"), null,
-                    Catalog.GetString ("Repeat the current playing song"), 2)
+                    Catalog.GetString ("Repeat the current playing song"),
+                    (int)PlaybackRepeatMode.RepeatSingle)
             }, 0, OnChanged);
 
             this["RepeatNoneAction"].IconName = "media-repeat-none";

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs	Mon Apr  7 22:38:23 2008
@@ -0,0 +1,142 @@
+//
+// PlaybackShuffleActions.cs
+//
+// Author:
+//   Scott Peterson <lunchtimemama gmail com>
+//
+// Copyright (C) 2008 Scott Peterson
+//
+// 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;
+using System.Collections.Generic;
+using Mono.Unix;
+using Gtk;
+
+using Hyena;
+using Hyena.Gui;
+using Banshee.Configuration;
+using Banshee.ServiceStack;
+using Banshee.PlaybackController;
+
+namespace Banshee.Gui
+{
+    public class PlaybackShuffleActions : BansheeActionGroup, IRadioActionGroup
+    {
+        private RadioAction active_action;
+
+        public RadioAction Active {
+            get { return active_action; }
+            set {
+                active_action = value;
+                ShuffleMode.Set (active_action == null ? String.Empty : ActionNameToConfigId (active_action.Name));
+                ServiceManager.PlaybackController.ShuffleMode = (PlaybackShuffleMode)active_action.Value;
+            }
+        }
+
+        public event ChangedHandler Changed;
+        
+        public PlaybackShuffleActions (InterfaceActionService actionService) : base ("PlaybackShuffle")
+        {
+            actionService.AddActionGroup (this);
+
+            Add (new RadioActionEntry [] {
+                new RadioActionEntry ("ShuffleOffAction", null, 
+                    Catalog.GetString ("Shuffle _Off"), null,
+                    Catalog.GetString ("Do not shuffle playlist"),
+                    (int)PlaybackShuffleMode.Linear),
+                    
+                new RadioActionEntry ("ShuffleSongAction", null,
+                    Catalog.GetString ("Shuffle by _Song"), null,
+                    Catalog.GetString ("Play songs randomly from the playlist"),
+                    (int)PlaybackShuffleMode.Song),
+                    
+                new RadioActionEntry ("ShuffleArtistAction", null,
+                    Catalog.GetString ("Shuffle by A_rtist"), null,
+                    Catalog.GetString ("Play all songs by an artist, then randomly choose another artist"),
+                    (int)PlaybackShuffleMode.Artist),
+                    
+                new RadioActionEntry ("ShuffleAlbumAction", null,
+                    Catalog.GetString ("Shuffle by A_lbum"), null,
+                    Catalog.GetString ("Play all songs from an album, then randomly choose another album"),
+                    (int)PlaybackShuffleMode.Album)
+            }, 0, OnChanged);
+                
+            this["ShuffleOffAction"].IconName = "media-skip-forward";
+            this["ShuffleSongAction"].IconName = "media-playlist-shuffle";
+            this["ShuffleArtistAction"].IconName = "media-playlist-shuffle";
+            this["ShuffleAlbumAction"].IconName = "media-playlist-shuffle";
+            this["ShuffleArtistAction"].Sensitive = false;
+            this["ShuffleAlbumAction"].Sensitive = false;
+
+            Gtk.Action action = this[ConfigIdToActionName (ShuffleMode.Get ())];
+            if (action is RadioAction) {
+                active_action = (RadioAction)action;
+            } else {
+                Active = (RadioAction)this["ShuffleOffAction"];
+            }
+            
+            Active.Activate ();
+        }
+
+        private void OnChanged (object o, ChangedArgs args)
+        {
+            Active = args.Current;
+            
+            ChangedHandler handler = Changed;
+            if (handler != null) {
+                handler (o, args);
+            }
+        }
+
+        public IEnumerator<RadioAction> GetEnumerator ()
+        {
+            yield return (RadioAction)this["ShuffleOffAction"];
+            yield return (RadioAction)this["ShuffleSongAction"];
+            yield return (RadioAction)this["ShuffleArtistAction"];
+            yield return (RadioAction)this["ShuffleAlbumAction"];
+        }
+
+        IEnumerator IEnumerable.GetEnumerator ()
+        {
+            return GetEnumerator ();
+        }
+
+        private static string ConfigIdToActionName (string configuration)
+        {
+            return String.Format ("{0}Action", StringUtil.UnderCaseToCamelCase (configuration));
+        }
+
+        private static string ActionNameToConfigId (string actionName)
+        {
+            return StringUtil.CamelCaseToUnderCase (actionName.Substring (0, 
+                actionName.Length - (actionName.EndsWith ("Action") ? 6 : 0)));
+        }
+
+        public static readonly SchemaEntry<string> ShuffleMode = new SchemaEntry<string> (
+            "playback", "shuffle_mode",
+            "off",
+            "Shuffle playback",
+            "Shuffle mode (shuffle_off, shuffle_song, shuffle_artist, shuffle_album)"
+        );
+    }
+}

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	Mon Apr  7 22:38:23 2008
@@ -93,6 +93,8 @@
     <File name="Banshee.Collection.Gui/TerseTrackListView.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Collection.Gui/ColumnCellTrack.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Sources.Gui/ITrackModelSourceContents.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.Gui.Widgets/NextArrowButton.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.Gui/PlaybackShuffleActions.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Project" localcopy="False" refto="Hyena.Gui" />

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Makefile.am	Mon Apr  7 22:38:23 2008
@@ -40,6 +40,7 @@
 	Banshee.Gui.Widgets/ConnectedMessageBar.cs \
 	Banshee.Gui.Widgets/ConnectedSeekSlider.cs \
 	Banshee.Gui.Widgets/ConnectedVolumeButton.cs \
+	Banshee.Gui.Widgets/NextArrowButton.cs \
 	Banshee.Gui.Widgets/PlaylistMenuItem.cs \
 	Banshee.Gui.Widgets/RepeatActionButton.cs \
 	Banshee.Gui.Widgets/TrackInfoDisplay.cs \
@@ -58,6 +59,7 @@
 	Banshee.Gui/InterfaceActionService.cs \
 	Banshee.Gui/PlaybackActions.cs \
 	Banshee.Gui/PlaybackRepeatActions.cs \
+	Banshee.Gui/PlaybackShuffleActions.cs \
 	Banshee.Gui/SourceActions.cs \
 	Banshee.Gui/TrackActions.cs \
 	Banshee.Gui/ViewActions.cs \

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	Mon Apr  7 22:38:23 2008
@@ -2,7 +2,7 @@
   <toolbar name="HeaderToolbar">
     <toolitem action="PreviousAction"/>
     <toolitem action="PlayPauseAction"/>
-    <toolitem action="NextAction"/>
+    <placeholder name="NextArrowButton"/>
     <placeholder name="SeekSlider"/>
     <placeholder name="TrackInfoDisplay"/>
     <placeholder name="SourceActions">
@@ -79,7 +79,10 @@
       <menuitem name="RepeatAll" action="RepeatAllAction"/>
       <menuitem name="RepeatSingle" action="RepeatSingleAction"/>
       <separator/>
-      <menuitem name="Shuffle" action="ShuffleAction"/>
+      <menuitem name="ShuffleOff" action="ShuffleOffAction"/>
+      <menuitem name="ShuffleSong" action="ShuffleSongAction"/>
+      <menuitem name="ShuffleArtist" action="ShuffleArtistAction"/>
+      <menuitem name="ShuffleAlbum" action="ShuffleAlbumAction"/>
     </menu>
     
     <menu name="ToolsMenu" action="ToolsMenuAction">

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui.mdp	Mon Apr  7 22:38:23 2008
@@ -65,6 +65,9 @@
     <File name="Hyena.Query.Gui/TimeSpanQueryValueEntry.cs" subtype="Code" buildaction="Compile" />
     <File name="Hyena.Data.Gui/IListView.cs" subtype="Code" buildaction="Compile" />
     <File name="Hyena.Widgets/SmoothScrolledWindow.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Gui/IRadioActionGroup.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Widgets/ActionButton.cs" subtype="Code" buildaction="Compile" />
+    <File name="Hyena.Widgets/ArrowButton.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/IRadioActionGroup.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Gui/IRadioActionGroup.cs	Mon Apr  7 22:38:23 2008
@@ -0,0 +1,36 @@
+//
+// IRadioActionGroup.cs
+//
+// Copyright (c) 2008 Scott Peterson <lunchtimemama gmail 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;
+using Gtk;
+
+namespace Hyena.Gui
+{
+    public interface IRadioActionGroup : IEnumerable<RadioAction>
+    {
+        RadioAction Active { get; }
+        event ChangedHandler Changed;
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ActionButton.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ActionButton.cs	Mon Apr  7 22:38:23 2008
@@ -0,0 +1,186 @@
+//
+// ActionButton.cs
+//
+// Authors:
+//   Aaron Bockover <abockover novell com>
+//   Scott Peterson <lunchtimemama gmail com>
+//
+// Copyright (C) 2008 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.Collections.Generic;
+using Gtk;
+
+using Hyena.Gui;
+
+namespace Hyena.Widgets
+{
+    public enum ActionButtonStyle
+    {
+        IconOnly,
+        TextOnly,
+        IconAndText
+    }
+    
+    public abstract class ActionButton : Button
+    {
+        private Button target_button;
+        private HBox box = new HBox ();
+        private Label label = new Label ();
+        private Image image = new Image ();
+        private ActionButtonStyle? style;
+        
+        protected ActionButton ()
+        {
+            ActionGroup.Changed += OnActionChanged;
+            
+            Relief = ReliefStyle.None;
+            box.Spacing = 4;
+            image.IconSize = (int)Gtk.IconSize.Menu;
+            label.UseUnderline = true;
+            
+            TargetButton = this;
+            ActionButtonStyle = ActionButtonStyle.IconAndText;
+            
+            SetActiveItem (ActionGroup.Active);
+        }
+        
+        protected abstract IRadioActionGroup ActionGroup { get; }
+        
+        private void OnActionChanged (object o, ChangedArgs args)
+        {
+            SetActiveItem (args.Current);
+        }
+        
+        private void SetActiveItem (RadioAction action)
+        {
+            if (action == null) {
+                return;
+            }
+
+            image.IconName = action.IconName;
+            label.TextWithMnemonic = action.Label;
+            target_button.Sensitive = action.Sensitive && action.Visible;
+        }
+        
+        protected void ShowMenu ()
+        {
+            BuildMenu ().Popup (null, null, PositionMenu, 1, Gtk.Global.CurrentEventTime);
+        }
+        
+        private Menu BuildMenu ()
+        {
+            Menu menu = new Menu ();
+            foreach (Widget widget in MenuItems) {
+                menu.Append (widget);
+            }
+
+            menu.ShowAll ();
+            return menu;
+        }
+        
+        protected virtual IEnumerable<Widget> MenuItems {
+            get {
+                foreach (RadioAction action in ActionGroup) {
+                    yield return action.CreateMenuItem ();
+                }
+            }
+        }
+
+        private void PositionMenu (Menu menu, out int x, out int y, out bool push_in) 
+        {
+            Gtk.Requisition menu_req = menu.SizeRequest ();
+            int monitor_num = Screen.GetMonitorAtWindow (target_button.GdkWindow);
+            Gdk.Rectangle monitor = Screen.GetMonitorGeometry (monitor_num < 0 ? 0 : monitor_num);
+
+            target_button.GdkWindow.GetOrigin (out x, out y);
+            
+            y += target_button.Allocation.Y;
+            x += target_button.Allocation.X + (Direction == TextDirection.Ltr
+                ? Math.Max (target_button.Allocation.Width - menu_req.Width, 0)
+                : - (menu_req.Width - target_button.Allocation.Width));
+            
+            if (y + target_button.Allocation.Height + menu_req.Height <= monitor.Y + monitor.Height) {
+                y += target_button.Allocation.Height;
+            } else if (y - menu_req.Height >= monitor.Y) {
+                y -= menu_req.Height;
+            } else if (monitor.Y + monitor.Height - (y + target_button.Allocation.Height) > y) {
+                y += target_button.Allocation.Height;
+            } else {
+                y -= menu_req.Height;
+            }
+
+            push_in = false;
+        }
+        
+        protected override void OnActivated ()
+        {
+            ShowMenu ();
+        }
+
+        protected override bool OnButtonPressEvent (Gdk.EventButton evnt)
+        {
+            ShowMenu ();
+            return true;
+        }
+        
+        public Button TargetButton {
+            get { return target_button; }
+            protected set {
+                if (target_button != null) {
+                    target_button.Remove (box);
+                }
+                target_button = value;
+                target_button.Add (box);
+            }
+        }
+        
+        protected int IconSize {
+            get { return image.IconSize; }
+            set { image.IconSize = value; }
+        }
+        
+        protected ActionButtonStyle ActionButtonStyle {
+            get { return style ?? ActionButtonStyle.IconAndText; }
+            set {
+                if (style != null) {
+                    if (style.Value != ActionButtonStyle.TextOnly) {
+                        box.Remove (image);
+                    }
+                    if (style.Value != ActionButtonStyle.IconOnly) {
+                        box.Remove (label);
+                    }
+                }
+                
+                style = value;
+                
+                if (style.Value != ActionButtonStyle.TextOnly) {
+                    box.PackStart (image, false, false, 0);
+                }
+                if (style.Value != ActionButtonStyle.IconOnly) {
+                    box.PackStart (label, true, true, 0);
+                }
+                box.ShowAll ();
+            }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ArrowButton.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Hyena.Widgets/ArrowButton.cs	Mon Apr  7 22:38:23 2008
@@ -0,0 +1,153 @@
+//
+// ArrowButton.cs
+//
+// Author:
+//   Scott Peterson <lunchtimemama gmail com>
+//
+// Copyright (C) 2008 Scott Peterson
+//
+// 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 Gtk;
+using Gdk;
+
+using Hyena.Gui;
+using Hyena.Gui.Theming;
+
+namespace Hyena.Widgets
+{
+    public abstract class ArrowButton : ActionButton
+    {
+        private Gtk.Action action;
+        private Button button;
+        private ReliefStyle relief;
+        private Rectangle arrow_alloc;
+        
+        protected ArrowButton () : this (null)
+        {
+        }
+        
+        protected ArrowButton (Gtk.Action action)
+        {
+            this.action = action;
+            button = new Button ();
+            button.FocusOnClick = false;
+            button.Entered += delegate { base.Relief = ReliefStyle.Normal; };
+            button.Left += delegate { base.Relief = relief; };
+            button.StateChanged += delegate { State = button.State; };
+            button.Clicked += delegate { OnActivate (); };
+            arrow_alloc.Width = 5;
+            arrow_alloc.Height = 3;
+            TargetButton = button;
+            ActionButtonStyle = ActionButtonStyle.IconOnly;
+            IconSize = (int)Gtk.IconSize.LargeToolbar;
+            Relief = base.Relief;
+        }
+        
+        protected new virtual Gtk.Action Action {
+            get { return action ?? ActionGroup.Active; }
+        }
+        
+        public new ReliefStyle Relief {
+            get { return relief; }
+            set {
+                relief = value;
+                base.Relief = value;
+                button.Relief = value;
+            }
+        }
+        
+        protected override void OnRealized ()
+        {
+            base.OnRealized ();
+            button.Parent = this.Parent;
+            button.Realize ();
+        }
+        
+        protected override void OnUnrealized ()
+        {
+            button.Unrealize ();
+            base.OnUnrealized ();
+        }
+        
+        protected override void OnMapped ()
+        {
+            base.OnMapped ();
+            button.ShowAll ();
+        }
+
+        protected override void OnUnmapped ()
+        {
+            button.Hide ();
+            base.OnUnmapped ();
+        }
+        
+        protected override void OnSizeRequested (ref Requisition requisition)
+        {
+            requisition = button.SizeRequest ();
+            requisition.Width += 13;
+        }
+        
+        protected override void OnSizeAllocated (Rectangle allocation)
+        {
+            base.OnSizeAllocated (allocation);
+            allocation.Width -= 13;
+            arrow_alloc.X = allocation.Right + 3;
+            arrow_alloc.Y = allocation.Top + allocation.Height / 2;
+            button.SizeAllocate (allocation);
+        }
+
+        protected override bool OnExposeEvent (EventExpose evnt)
+        {
+            base.OnExposeEvent (evnt);
+            button.SendExpose (evnt);
+            
+            Gtk.Style.PaintArrow (Style, GdkWindow, State, ShadowType.None, arrow_alloc, this, null,
+                ArrowType.Down, true, arrow_alloc.X, arrow_alloc.Y, arrow_alloc.Width, arrow_alloc.Height);
+            
+            return true;
+        }
+        
+        protected override bool OnEnterNotifyEvent (EventCrossing evnt)
+        {
+            button.Relief = ReliefStyle.None;
+            return base.OnEnterNotifyEvent (evnt);
+        }
+        
+        protected override bool OnLeaveNotifyEvent (EventCrossing evnt)
+        {
+            button.Relief = relief;
+            return base.OnLeaveNotifyEvent (evnt);
+        }
+        
+        protected override bool OnButtonPressEvent (EventButton evnt)
+        {
+            ShowMenu ();
+            return true;
+        }
+        
+        protected override void OnActivate ()
+        {
+            Action.Activate ();
+        }
+
+    }
+}

Modified: trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	(original)
+++ trunk/banshee/src/Libraries/Hyena.Gui/Makefile.am	Mon Apr  7 22:38:23 2008
@@ -36,6 +36,7 @@
 	Hyena.Gui/EntryInsertAction.cs \
 	Hyena.Gui/EntryUndoAdapter.cs \
 	Hyena.Gui/GtkUtilities.cs \
+	Hyena.Gui/IRadioActionGroup.cs \
 	Hyena.Gui/PangoCairoHelper.cs \
 	Hyena.Gui/ShadingTestWindow.cs \
 	Hyena.Query.Gui/DateQueryValueEntry.cs \
@@ -50,11 +51,13 @@
 	Hyena.Query.Gui/RelativeTimeSpanQueryValueEntry.cs \
 	Hyena.Query.Gui/StringQueryValueEntry.cs \
 	Hyena.Query.Gui/TimeSpanQueryValueEntry.cs \
+	Hyena.Widgets/ActionButton.cs \
 	Hyena.Widgets/AnimatedBox.cs \
 	Hyena.Widgets/AnimatedHBox.cs \
 	Hyena.Widgets/AnimatedImage.cs \
 	Hyena.Widgets/AnimatedVBox.cs \
 	Hyena.Widgets/AnimatedWidget.cs \
+	Hyena.Widgets/ArrowButton.cs \
 	Hyena.Widgets/MessageBar.cs \
 	Hyena.Widgets/RoundedFrame.cs \
 	Hyena.Widgets/ScrolledWindow.cs \



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