[Banshee-List] [PATCH] Fix repeat/shuffle buttons & logic



Hi all,

Posting this patch here for some review/discussion.

There are really two major changes that this patch makes, the first being
changing the API of ICanonicalPlaybackController and
IBasicPlaybackController. Basically, all I'm doing here is changing Next ()
to Next (bool restartAtEnd). If restartAtEnd is true, the implementing
class shouldn't stop playback merely because there are no more tracks to
play linearly, it should queue up the first track if there are none left.
Anyway, simple stuff.

The other issue that'll probably get a bit more discussion is the way we
handle changing the Sensitive state on the shuffle/repeat toggle button
widgets. At the moment, it should be possible to do:
(action_service.UIManager.GetWidget ("/FooterToolbar/ShuffleToggleButton")
as Widget).Sensitive = false;, but this doesn't quite work for some reason.
Instead, we end up getting a GtkMenuSeperator widget (at least, that's what
I think it was called - this is off the top of my head). If we loop through
the child elements of FooterToolbar, we end up with something like this:

ShuffleToggleButton | Separator | RepeatToggleButton | Separator |
StatusBar | Separator | TrackMetadataAction

So, from the looks of things, it's getting the widget /adjacent/ to the
widget we really want. So, at the moment, to work around this, we loop
through the children of FooterToolbar and find items that are of type
MultiStateToggleButton, and set their Sensitive value to false. Not sure if
this is a problem with the way I'm approaching the issue, or a bug in Gtk#.

Anyway, the attached patch works as advertised - it adds support for the
shuffle and repeat footer togglebuttons, and implements the necessary logic
to drive them all. It also disables shuffle and repeat while we're looking
at the Last.fm radio source.

Comments, suggestions on the implementation?

Cheers,
Alex Hixon
Index: src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
===================================================================
--- src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	(revision 3250)
+++ src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	(working copy)
@@ -47,6 +47,9 @@
 using Banshee.MediaEngine;
 using Banshee.Collection;
 using Banshee.ServiceStack;
+
+using Banshee.Gui;
+using Banshee.Gui.Widgets;
  
 namespace Banshee.Lastfm
 {
@@ -184,20 +187,57 @@
             OnUpdated ();
         }
         
+        private void SetStatusIconSensitive (bool val) {
+            Toolbar footer = (action_service.UIManager.GetWidget ("/FooterToolbar") as Toolbar);
+            
+            foreach (Widget child in footer.Children) {
+                try {
+                    GenericToolItem<Gtk.Widget> w = (GenericToolItem<Gtk.Widget>) child;
+                    
+                    if (w.Widget is MultiStateToggleButton) {
+                        w.Widget.Sensitive = val;
+                    }
+                } catch {
+                }
+            }
+        }
+        
         private bool shuffle;
+        private int repeat;
+        private InterfaceActionService action_service;
         public override void Activate ()
         {
+            if (action_service == null) {
+                action_service = ServiceManager.Get<InterfaceActionService> ("InterfaceActionService");
+            }
+        
             base.Activate ();
-            //shuffle = (action_service.GlobalActions ["ShuffleAction"] as ToggleAction).Active;
-            //(action_service.GlobalActions ["ShuffleAction"] as ToggleAction).Active = false;
-            //Globals.ActionManager["ShuffleAction"].Sensitive = false;
+            
+            SetStatusIconSensitive (false);
+            shuffle = (action_service.PlaybackActions ["ShuffleAction"] as ToggleAction).Active;
+            (action_service.PlaybackActions ["ShuffleAction"] as ToggleAction).Active = false;
+            action_service.PlaybackActions ["ShuffleAction"].Sensitive = false;
+            
+            if ((action_service.PlaybackActions ["RepeatNoneAction"] as ToggleAction).Active) {
+                repeat = 0;
+            } else if ((action_service.PlaybackActions ["RepeatAllAction"] as ToggleAction).Active) {
+                repeat = 1;
+            } else {
+                repeat = 2;
+            }
+            
+            (action_service.PlaybackActions ["RepeatNoneAction"] as ToggleAction).Active = true;
+            
+            action_service.PlaybackActions ["RepeatNoneAction"].Sensitive = false;
+            action_service.PlaybackActions ["RepeatAllAction"].Sensitive = false;
+            action_service.PlaybackActions ["RepeatSingleAction"].Sensitive = false;
  
             /*if (show_status)
                 status_bar.Show ();
             else
                 status_bar.Hide ();*/
 
-            //action_service.GlobalActions ["PreviousAction"].Sensitive = false;
+            action_service.PlaybackActions ["PreviousAction"].Sensitive = false;
             //InterfaceElements.MainContainer.PackEnd (status_bar, false, false, 0);
 
             // We lazy load the Last.fm connection, so if we're not already connected, do it
@@ -222,11 +262,23 @@
 
         public override void Deactivate ()
         {
-            //(Globals.ActionManager["ShuffleAction"] as ToggleAction).Active = shuffle;
-            //Globals.ActionManager["ShuffleAction"].Sensitive = true;
+            (action_service.PlaybackActions ["ShuffleAction"] as ToggleAction).Active = shuffle;
+            action_service.PlaybackActions ["ShuffleAction"].Sensitive = true;
 
-            //Globals.ActionManager["PreviousAction"].Sensitive = true;
+            action_service.PlaybackActions ["PreviousAction"].Sensitive = true;
             //InterfaceElements.MainContainer.Remove (status_bar);
+            
+            SetStatusIconSensitive (true);
+            
+            action_service.PlaybackActions ["RepeatNoneAction"].Sensitive = true;
+            action_service.PlaybackActions ["RepeatAllAction"].Sensitive = true;
+            action_service.PlaybackActions ["RepeatSingleAction"].Sensitive = true;
+            
+            if (repeat == 1) {
+                (action_service.PlaybackActions ["RepeatAllAction"] as ToggleAction).Active = true;
+            } else if (repeat == 2) {
+                (action_service.PlaybackActions ["RepeatSingleAction"] as ToggleAction).Active = true;
+            }
         }
 
         // Last.fm requires you to 'tune' to a station before requesting a track list/playing it
Index: src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
===================================================================
--- src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	(revision 3250)
+++ src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	(working copy)
@@ -187,10 +187,10 @@
         
         void IBasicPlaybackController.First ()
         {
-            ((IBasicPlaybackController)this).Next ();
+            ((IBasicPlaybackController)this).Next (false);
         }
         
-        void IBasicPlaybackController.Next ()
+        void IBasicPlaybackController.Next (bool restartAtEnd)
         {
             RemoveFirstTrack ();
             
Index: src/Clients/Nereid/Nereid/PlayerInterface.cs
===================================================================
--- src/Clients/Nereid/Nereid/PlayerInterface.cs	(revision 3250)
+++ src/Clients/Nereid/Nereid/PlayerInterface.cs	(working copy)
@@ -56,8 +56,8 @@
         // Major Layout Components
         private VBox primary_vbox;
         private Toolbar header_toolbar;
+        private Toolbar footer_toolbar;
         private HPaned views_pane;
-        private HBox footer_box;
         private ViewContainer view_container;
         
         // Major Interaction Components
@@ -192,29 +192,34 @@
         }
 
         private void BuildFooter ()
-        {
-            footer_box = new HBox ();
-            footer_box.Spacing = 2;
+        {   
+            footer_toolbar = (Toolbar)action_service.UIManager.GetWidget ("/FooterToolbar");
+            footer_toolbar.ShowArrow = false;
+            footer_toolbar.ToolbarStyle = ToolbarStyle.BothHoriz;
+            footer_toolbar.IconSize = IconSize.Menu;
 
             status_label = new Label ();
             status_label.ModifyFg (StateType.Normal, Hyena.Gui.GtkUtilities.ColorBlend (
                 status_label.Style.Foreground (StateType.Normal), status_label.Style.Background (StateType.Normal)));
-            
-            ActionButton song_properties_button = new ActionButton
+                
+            action_service.PopulateToolbarPlaceholder (footer_toolbar, "/FooterToolbar/Statusbar", status_label, true);
+
+            /*ActionButton song_properties_button = new ActionButton
                 (action_service.TrackActions["TrackPropertiesAction"]);
             song_properties_button.IconSize = IconSize.Menu;
             song_properties_button.Padding = 0;
-            song_properties_button.LabelVisible = false;
+            song_properties_button.LabelVisible = false;*/
             
-            //footer_box.PackStart (shuffle_toggle_button, false, false, 0);
-            //footer_box.PackStart (repeat_toggle_button, false, false, 0);
-            footer_box.PackStart (status_label, true, true, 0);
-            footer_box.PackStart (song_properties_button, false, false, 0);
+            MultiStateToggleButton shuffle_toggle_button = new ShuffleToggleButton (action_service);
+            action_service.PopulateToolbarPlaceholder (footer_toolbar, "/FooterToolbar/ShuffleToggleAction", shuffle_toggle_button);
+            
+            MultiStateToggleButton repeat_toggle_button = new RepeatToggleButton (action_service);
+            action_service.PopulateToolbarPlaceholder (footer_toolbar, "/FooterToolbar/RepeatToggleAction", repeat_toggle_button);
 
             Alignment align = new Alignment (0.5f, 0.5f, 1.0f, 1.0f);
-            align.TopPadding = 2;
+            align.TopPadding = 0;
             align.BottomPadding = 0;
-            align.Add (footer_box);
+            align.Add (footer_toolbar);
             align.ShowAll ();
 
             primary_vbox.PackStart (align, false, true, 0);
@@ -282,6 +287,7 @@
             };
             
             header_toolbar.ExposeEvent += OnHeaderToolbarExposeEvent;
+            footer_toolbar.ExposeEvent += OnFooterToolbarExposeEvent;
         }
         
 #endregion
@@ -379,6 +385,19 @@
             }
         }
         
+        private void OnFooterToolbarExposeEvent (object o, ExposeEventArgs args)
+        {
+            // This forces the toolbar to look like it's just a regular plain container
+            // since the stock toolbar look makes Banshee look ugly.
+            footer_toolbar.GdkWindow.DrawRectangle (Style.BackgroundGC (footer_toolbar.State), 
+                true, footer_toolbar.Allocation);
+            
+            // Manually expose all the toolbar's children
+            foreach (Widget child in footer_toolbar.Children) {
+                footer_toolbar.PropagateExpose (child, args.Event);
+            }
+        }
+        
 #endregion
 
 #region Implement Interfaces
Index: src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp
===================================================================
--- src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	(revision 3250)
+++ src/Core/Banshee.ThickClient/Banshee.ThickClient.mdp	(working copy)
@@ -88,6 +88,8 @@
     <File name="Banshee.Collection.Gui/PersistentColumnController.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Gui.Widgets/PlaylistMenuItem.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Query.Gui/SmartPlaylistQueryValueEntry.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.Gui.Widgets/PlaybackToggleButtons.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.Gui/ToggleStates.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Project" localcopy="False" refto="Hyena.Gui" />
Index: src/Core/Banshee.ThickClient/Banshee.Gui/ToggleStates.cs
===================================================================
--- src/Core/Banshee.ThickClient/Banshee.Gui/ToggleStates.cs	(revision 0)
+++ src/Core/Banshee.ThickClient/Banshee.Gui/ToggleStates.cs	(revision 0)
@@ -0,0 +1,83 @@
+// 
+// ToggleStates.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell com>
+//
+// Copyright (C) 2005-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 Mono.Unix;
+using Banshee.Widgets;
+
+namespace Banshee.Gui
+{
+    public class RepeatNoneToggleState : ToggleState
+    {
+        public RepeatNoneToggleState()
+        {
+            Icon = Gdk.Pixbuf.LoadFromResource("media-repeat-none.png");
+            Label = Catalog.GetString("Repeat None");
+        }
+    }
+
+    public class RepeatSingleToggleState : ToggleState
+    {
+        public RepeatSingleToggleState()
+        {
+            Icon = Gdk.Pixbuf.LoadFromResource("media-repeat-single.png");
+            Label = Catalog.GetString("Repeat Single");
+        }
+    }
+
+    public class RepeatAllToggleState : ToggleState
+    {
+        public RepeatAllToggleState()
+        {
+            Icon = Gdk.Pixbuf.LoadFromResource("media-repeat-all.png");
+            Label = Catalog.GetString("Repeat All");
+        }
+    }
+    
+    public class ShuffleEnabledToggleState : ToggleState
+    {
+        public ShuffleEnabledToggleState()
+        {
+            Icon = Gdk.Pixbuf.LoadFromResource("media-playlist-shuffle.png");
+            Label = Catalog.GetString("Shuffle");
+            MatchActive = true;
+            MatchValue = true;
+        }
+    }
+    
+    public class ShuffleDisabledToggleState : ToggleState
+    {
+        public ShuffleDisabledToggleState()
+        {
+            Icon = Gdk.Pixbuf.LoadFromResource("media-playlist-continuous.png");
+            Label = Catalog.GetString("Continuous");
+            MatchActive = true;
+            MatchValue = false;
+        }
+    }
+}
\ No newline at end of file
Index: src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
===================================================================
--- src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	(revision 3250)
+++ src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs	(working copy)
@@ -92,7 +92,7 @@
                 new RadioActionEntry ("RepeatSingleAction", null,
                     Catalog.GetString ("Repeat Si_ngle"), null,
                     Catalog.GetString ("Repeat the current playing song"), 2)
-            }, 0, null);
+            }, 0, OnRepeatAction);
 
             actionService.GlobalActions.Add (new ActionEntry [] {
                 new ActionEntry ("PlaybackMenuAction", null,
@@ -206,5 +206,16 @@
                 ? PlaybackShuffleMode.Shuffle
                 : PlaybackShuffleMode.Linear;
         }
+        
+        private void OnRepeatAction (object o, ChangedArgs args)
+        {
+            if (args.Current.Value == 0) {
+                ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.None;
+            } else if (args.Current.Value == 1) {
+                ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatAll;
+            } else if (args.Current.Value == 2) {
+                ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatSingle;
+            }
+        }
     }
 }
Index: src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/PlaybackToggleButtons.cs
===================================================================
--- src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/PlaybackToggleButtons.cs	(revision 0)
+++ src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/PlaybackToggleButtons.cs	(revision 0)
@@ -0,0 +1,90 @@
+//
+// PlaybackToggleButtons.cs
+//
+// Author:
+//   Alexander Hixon <hixon alexander mediati org>
+//
+// 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 Gtk;
+
+using Banshee.Gui;
+using Banshee.Widgets;
+using Banshee.ServiceStack;
+
+namespace Banshee.Gui.Widgets
+{
+    public interface InterfaceToggleButton
+    {
+        string ToggleButtonType {
+            get;
+        }
+    }
+    
+    public class ShuffleToggleButton : MultiStateToggleButton, InterfaceToggleButton
+    {
+        private InterfaceActionService action_service;
+    
+        public ShuffleToggleButton (InterfaceActionService actionService) : base ()
+        {
+            action_service = actionService;
+            
+            this.AddState(typeof(ShuffleDisabledToggleState),
+                    action_service["Playback.ShuffleAction"] as ToggleAction);
+            this.AddState(typeof(ShuffleEnabledToggleState),
+                    action_service["Playback.ShuffleAction"] as ToggleAction);
+            this.Relief = ReliefStyle.None;
+            this.ShowLabel = false;
+        }
+        
+        public string ToggleButtonType
+        {
+            get { return "Shuffle"; }
+        }
+    }
+    
+    public class RepeatToggleButton : MultiStateToggleButton, InterfaceToggleButton
+    {
+        private InterfaceActionService action_service;
+    
+        public RepeatToggleButton (InterfaceActionService actionService) : base ()
+        {
+            action_service = actionService;
+            
+            this.AddState(typeof(RepeatNoneToggleState),
+                action_service["Playback.RepeatNoneAction"] as ToggleAction);
+            this.AddState(typeof(RepeatAllToggleState),
+                action_service["Playback.RepeatAllAction"] as ToggleAction);
+            this.AddState(typeof(RepeatSingleToggleState),
+                action_service["Playback.RepeatSingleAction"] as ToggleAction);
+            this.Relief = ReliefStyle.None;
+            this.ShowLabel = false;
+        }
+        
+        public string ToggleButtonType
+        {
+            get { return "Repeat"; }
+        }
+    }
+}
\ No newline at end of file
Index: src/Core/Banshee.ThickClient/Makefile.am
===================================================================
--- src/Core/Banshee.ThickClient/Makefile.am	(revision 3250)
+++ src/Core/Banshee.ThickClient/Makefile.am	(working copy)
@@ -43,6 +43,7 @@
 	Banshee.Gui.Widgets/ArtworkPopup.cs \
 	Banshee.Gui.Widgets/ConnectedSeekSlider.cs \
 	Banshee.Gui.Widgets/ConnectedVolumeButton.cs \
+	Banshee.Gui.Widgets/PlaybackToggleButtons.cs \
 	Banshee.Gui.Widgets/PlaylistMenuItem.cs \
 	Banshee.Gui.Widgets/TrackInfoDisplay.cs \
 	Banshee.Gui.Widgets/UserJobTile.cs \
@@ -58,6 +59,7 @@
 	Banshee.Gui/InterfaceActionService.cs \
 	Banshee.Gui/PlaybackActions.cs \
 	Banshee.Gui/SourceActions.cs \
+	Banshee.Gui/ToggleStates.cs \
 	Banshee.Gui/TrackActions.cs \
 	Banshee.Gui/ViewActions.cs \
 	Banshee.Library.Gui/FileImportSource.cs \
@@ -85,7 +87,12 @@
 	Resources/source-playlist-16.png \
 	Resources/source-playlist-22.png \
 	Resources/source-smart-playlist-16.png \
-	Resources/source-smart-playlist-22.png
+	Resources/source-smart-playlist-22.png \
+	Resources/media-playlist-continuous.png \
+	Resources/media-playlist-shuffle.png \
+	Resources/media-repeat-all.png \
+	Resources/media-repeat-none.png \
+	Resources/media-repeat-single.png
 
 include $(top_srcdir)/build/build.mk
 
Index: src/Core/Banshee.ThickClient/Resources/media-repeat-all.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Core/Banshee.ThickClient/Resources/media-repeat-all.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: src/Core/Banshee.ThickClient/Resources/media-playlist-shuffle.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Core/Banshee.ThickClient/Resources/media-playlist-shuffle.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: src/Core/Banshee.ThickClient/Resources/media-repeat-single.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Core/Banshee.ThickClient/Resources/media-repeat-single.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: src/Core/Banshee.ThickClient/Resources/media-playlist-continuous.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Core/Banshee.ThickClient/Resources/media-playlist-continuous.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
===================================================================
--- src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	(revision 3250)
+++ src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	(working copy)
@@ -69,12 +69,15 @@
       <menuitem name="RestartSong" action="RestartSongAction"/>
       <separator/>
       <placeholder name="PlaybackMenuAdditions"/>
-      <!--<menuitem name="RepeatNone" action="RepeatNoneAction"/>
+      <menuitem name="RepeatNone" action="RepeatNoneAction"/>
       <menuitem name="RepeatAll" action="RepeatAllAction"/>
-      <menuitem name="RepeatSingle" action="RepeatSingleAction"/>-->
+      <menuitem name="RepeatSingle" action="RepeatSingleAction"/>
       <separator/>
       <menuitem name="Shuffle" action="ShuffleAction"/>
     </menu>
+    
+    <menu name="ToolsMenu" action="ToolsMenuAction">
+    </menu>
 
     <menu name="HelpMenu" action="HelpMenuAction">
       <menu name="WebMenu" action="WebMenuAction">
@@ -88,6 +91,14 @@
       <menuitem name="About" action="AboutAction"/>
     </menu>
   </menubar>
+  
+  <toolbar name="FooterToolbar">
+    <placeholder name="ShuffleToggleAction" />
+    <placeholder name="RepeatToggleAction" />
+    
+    <placeholder name="Statusbar" />
+    <toolitem action="TrackPropertiesAction" />
+  </toolbar>
 
   <popup name="LibraryContextMenu" action="LibraryContextMenuAction">
     <menuitem name="NewPlaylist" action="NewPlaylistAction"/>
Index: src/Core/Banshee.ThickClient/Resources/media-repeat-none.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Core/Banshee.ThickClient/Resources/media-repeat-none.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs
===================================================================
--- src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs	(revision 3250)
+++ src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs	(working copy)
@@ -31,7 +31,7 @@
     public interface ICanonicalPlaybackController : IPlaybackController
     {
         new void First ();
-        new void Next ();
+        new void Next (bool restartAtEnd);
         new void Previous ();
     }
 }
Index: src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
===================================================================
--- src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	(revision 3250)
+++ src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	(working copy)
@@ -97,7 +97,11 @@
             switch (args.Event) {
                 case PlayerEngineEvent.EndOfStream:
                     if (!StopWhenFinished) {
-                        Next ();
+                       if (RepeatMode == PlaybackRepeatMode.RepeatSingle) {
+                            QueuePlayTrack ();
+                        } else {
+                            Next ();
+                        }
                     } else {
                         OnStopped ();
                     }
@@ -125,15 +129,20 @@
                 ((ICanonicalPlaybackController)this).First ();
             }
         }
-        
-        public void Next ()
+        
+        public void Next ()
+        {
+            Next (RepeatMode == PlaybackRepeatMode.RepeatAll);
+        }
+        
+        public void Next (bool restartAtEnd)
         {
             OnTransition ();
             
             if (Source is IBasicPlaybackController) {
-                ((IBasicPlaybackController)Source).Next ();
+                ((IBasicPlaybackController)Source).Next (restartAtEnd);
             } else {
-                ((ICanonicalPlaybackController)this).Next ();
+                ((ICanonicalPlaybackController)this).Next (restartAtEnd);
             }
         }
         
@@ -155,7 +164,7 @@
             }
         }
         
-        void ICanonicalPlaybackController.Next ()
+        void ICanonicalPlaybackController.Next (bool restartAtEnd)
         {
             TrackInfo tmp_track = CurrentTrack;
 
@@ -170,6 +179,14 @@
                     if (tmp_track != null) {
                         previous_stack.Push (tmp_track);
                     }
+                } else if (restartAtEnd && Source.Count > 0) {
+                    if (tmp_track != null) {
+                        previous_stack.Push (tmp_track);
+                    }
+                    
+                    CurrentTrack = Source.TrackModel[0];
+                    QueuePlayTrack ();
+                    return;
                 } else {
                     return;
                 }
Index: src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs
===================================================================
--- src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	(revision 3250)
+++ src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	(working copy)
@@ -31,7 +31,7 @@
     public interface IBasicPlaybackController
     {
         void First ();
-        void Next ();
+        void Next (bool restartAtEnd);
         void Previous ();
     }
 }


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