banshee r3343 - in trunk/banshee: . src/Clients/Nereid src/Clients/Nereid/Nereid 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/Hyena tests tests/Hyena



Author: abock
Date: Wed Feb 27 23:53:11 2008
New Revision: 3343
URL: http://svn.gnome.org/viewvc/banshee?rev=3343&view=rev

Log:
2008-02-27  Aaron Bockover  <abock gnome org>

    * src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs:
    Created UI actions for repeat mode functionality; this is where the
    repeat mode can be accessed and is stored/loaded from configuration;
    the UI actions are set up here

    * src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs:
    A special button that syncs its state with the PlaybackRepeatActions and
    presents the actions as a popup menu

    * src/Clients/Nereid/Nereid/PlayerInterface.cs: Make the footer actually
    a Gtk.Toolbar, pack the new RepeatActionButton, and do some allocation
    magic to ensure the status bar is always centered on the toolbar; moved
    the only two schema items that are still at the client level into here

    * src/Core/Banshee.ThickClient/Banshee.Gui/BansheeIconFactory.cs: Added
    the media-repeat icons into the stock

    * src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs: Load the
    PlaybackRepeatActions as a child set of actions

    * src/Clients/Nereid/Nereid/PlayerWindowSchema.cs: Removed

    * src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml:
    Added a ToolbarFooter object

    * src/Libraries/Hyena/Hyena/StringUtil.cs: Added UnderCaseToCamelCase
    method that does the opposite of what CamelCaseToUnderCaseDoes; use
    a StringBuilder for the latter

    * tests/Hyena/StringUtilTests.cs: Added some tests for the above two methods



Added:
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs
   trunk/banshee/tests/Hyena/StringUtilTests.cs
Removed:
   trunk/banshee/src/Clients/Nereid/Nereid/PlayerWindowSchema.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Clients/Nereid/Makefile.am
   trunk/banshee/src/Clients/Nereid/Nereid.mdp
   trunk/banshee/src/Clients/Nereid/Nereid/PlayerInterface.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeIconFactory.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.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/Hyena/StringUtil.cs
   trunk/banshee/tests/Makefile.am

Modified: trunk/banshee/src/Clients/Nereid/Makefile.am
==============================================================================
--- trunk/banshee/src/Clients/Nereid/Makefile.am	(original)
+++ trunk/banshee/src/Clients/Nereid/Makefile.am	Wed Feb 27 23:53:11 2008
@@ -4,7 +4,6 @@
 SOURCES =  \
 	Nereid/Client.cs \
 	Nereid/PlayerInterface.cs \
-	Nereid/PlayerWindowSchema.cs \
 	Nereid/ViewContainer.cs
 
 bin_SCRIPTS = banshee2

Modified: trunk/banshee/src/Clients/Nereid/Nereid.mdp
==============================================================================
--- trunk/banshee/src/Clients/Nereid/Nereid.mdp	(original)
+++ trunk/banshee/src/Clients/Nereid/Nereid.mdp	Wed Feb 27 23:53:11 2008
@@ -10,7 +10,6 @@
   <Contents>
     <File name="Nereid/PlayerInterface.cs" subtype="Code" buildaction="Compile" />
     <File name="Nereid/ViewContainer.cs" subtype="Code" buildaction="Compile" />
-    <File name="Nereid/PlayerWindowSchema.cs" subtype="Code" buildaction="Compile" />
     <File name="Nereid/Client.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>

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	Wed Feb 27 23:53:11 2008
@@ -42,6 +42,7 @@
 using Banshee.Collection;
 using Banshee.Collection.Database;
 using Banshee.MediaEngine;
+using Banshee.Configuration;
 
 using Banshee.Gui;
 using Banshee.Gui.Widgets;
@@ -57,8 +58,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
@@ -195,31 +196,28 @@
 
         private void BuildFooter ()
         {
-            footer_box = new HBox ();
-            footer_box.Spacing = 2;
+            footer_toolbar = (Toolbar)ActionService.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
-                (ActionService.TrackActions["TrackPropertiesAction"]);
-            song_properties_button.IconSize = IconSize.Menu;
-            song_properties_button.Padding = 0;
-            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);
-
-            Alignment align = new Alignment (0.5f, 0.5f, 1.0f, 1.0f);
-            align.TopPadding = 2;
-            align.BottomPadding = 0;
-            align.Add (footer_box);
-            align.ShowAll ();
 
-            primary_vbox.PackStart (align, false, true, 0);
+            Alignment status_align = new Alignment (0.5f, 0.5f, 1.0f, 1.0f);
+            status_align.Add (status_label);
+
+            RepeatActionButton repeat_button = new RepeatActionButton ();
+            repeat_button.SizeAllocated += delegate (object o, Gtk.SizeAllocatedArgs args) {
+                status_align.LeftPadding = (uint)args.Allocation.Width;
+            };
+
+            ActionService.PopulateToolbarPlaceholder (footer_toolbar, "/FooterToolbar/StatusBar", status_align, true);
+            ActionService.PopulateToolbarPlaceholder (footer_toolbar, "/FooterToolbar/RepeatButton", repeat_button);
+
+            footer_toolbar.ShowAll ();
+            primary_vbox.PackStart (footer_toolbar, false, true, 0);
         }
 
 #endregion
@@ -228,7 +226,7 @@
         
         private void LoadSettings ()
         {
-            views_pane.Position = PlayerWindowSchema.SourceViewWidth.Get ();
+            views_pane.Position = SourceViewWidth.Get ();
         }
         
 #endregion
@@ -247,7 +245,7 @@
             // UI events
             view_container.SearchEntry.Changed += OnSearchEntryChanged;
             views_pane.SizeRequested += delegate {
-                PlayerWindowSchema.SourceViewWidth.Set (views_pane.Position);
+                SourceViewWidth.Set (views_pane.Position);
             };
             
             composite_view.TrackView.RowActivated += delegate (object o, RowActivatedArgs<TrackInfo> args) {
@@ -262,7 +260,8 @@
                 }
             };
             
-            header_toolbar.ExposeEvent += OnHeaderToolbarExposeEvent;
+            header_toolbar.ExposeEvent += OnToolbarExposeEvent;
+            footer_toolbar.ExposeEvent += OnToolbarExposeEvent;
         }
         
 #endregion
@@ -349,17 +348,19 @@
             source.FilterQuery = view_container.SearchEntry.Query;
         }
         
-        private void OnHeaderToolbarExposeEvent (object o, ExposeEventArgs args)
+        private void OnToolbarExposeEvent (object o, ExposeEventArgs args)
         {
+            Toolbar toolbar = (Toolbar)o;
+
             // This forces the toolbar to look like it's just a regular part
             // of the window since the stock toolbar look makes Banshee look ugly.
-            Style.ApplyDefaultBackground (header_toolbar.GdkWindow, true, State, 
-                args.Event.Area, header_toolbar.Allocation.X, header_toolbar.Allocation.Y, 
-                header_toolbar.Allocation.Width, header_toolbar.Allocation.Height);
+            Style.ApplyDefaultBackground (toolbar.GdkWindow, true, State, 
+                args.Event.Area, toolbar.Allocation.X, toolbar.Allocation.Y, 
+                toolbar.Allocation.Width, toolbar.Allocation.Height);
 
             // Manually expose all the toolbar's children
-            foreach (Widget child in header_toolbar.Children) {
-                header_toolbar.PropagateExpose (child, args.Event);
+            foreach (Widget child in toolbar.Children) {
+                toolbar.PropagateExpose (child, args.Event);
             }
         }
         
@@ -472,9 +473,26 @@
 
 #endregion
 
+#region Configuration Schemas
+
+        public static readonly SchemaEntry<int> SourceViewWidth = new SchemaEntry<int> (
+            "player_window", "source_view_width",
+            175,
+            "Source View Width",
+            "Width of Source View Column."
+        );
+
+        public static readonly SchemaEntry<bool> ShowCoverArt = new SchemaEntry<bool> (
+            "player_window", "show_cover_art",
+            true,
+            "Show cover art",
+            "Show cover art below source view if available"
+        );
+
+#endregion
+
         string IService.ServiceName {
             get { return "NereidPlayerInterface"; }
         }
     }
 }
-

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/RepeatActionButton.cs	Wed Feb 27 23:53:11 2008
@@ -0,0 +1,130 @@
+//
+// ConnectedRepeatComboBox.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell 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 Gtk;
+
+using Banshee.Gui;
+using Banshee.ServiceStack;
+
+namespace Banshee.Gui.Widgets
+{
+    public class RepeatActionButton : Button
+    {
+        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.Stock = action.StockId;
+            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;
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeIconFactory.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeIconFactory.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/BansheeIconFactory.cs	Wed Feb 27 23:53:11 2008
@@ -44,6 +44,9 @@
             "media-playback-pause",
             "media-playback-stop",
             "media-eject",
+            "media-repeat-all",
+            "media-repeat-none",
+            "media-repeat-single",
             
             /* Volume Button Icons */
             "audio-volume-high", 

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	Wed Feb 27 23:53:11 2008
@@ -43,9 +43,16 @@
     {
         private InterfaceActionService action_service;
         private Gtk.Action play_pause_action;
+        private PlaybackRepeatActions repeat_actions;
+
+        public PlaybackRepeatActions RepeatActions {
+            get { return repeat_actions; }
+        }
         
         public PlaybackActions (InterfaceActionService actionService) : base ("Playback")
         {
+            repeat_actions = new PlaybackRepeatActions (actionService);
+
             Add (new ActionEntry [] {
                 new ActionEntry ("PlayPauseAction", "media-playback-start",
                     Catalog.GetString ("_Play"), "space",
@@ -79,20 +86,6 @@
                     Catalog.GetString ("Stop playback after the current song finishes playing"), 
                     OnStopWhenFinishedAction, false)
             });
-            
-            Add (new RadioActionEntry [] {
-                new RadioActionEntry ("RepeatNoneAction", null, 
-                    Catalog.GetString ("Repeat N_one"), null,
-                    Catalog.GetString ("Do not repeat playlist"), 0),
-                    
-                new RadioActionEntry ("RepeatAllAction", null,
-                    Catalog.GetString ("Repeat _All"), null,
-                    Catalog.GetString ("Play all songs before repeating playlist"), 1),
-                    
-                new RadioActionEntry ("RepeatSingleAction", null,
-                    Catalog.GetString ("Repeat Si_ngle"), null,
-                    Catalog.GetString ("Repeat the current playing song"), 2)
-            }, 0, null);
 
             actionService.GlobalActions.Add (new ActionEntry [] {
                 new ActionEntry ("PlaybackMenuAction", null,

Added: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackRepeatActions.cs	Wed Feb 27 23:53:11 2008
@@ -0,0 +1,123 @@
+//
+// PlaybackRepeatActions.cs
+//
+// Author:
+//   Aaron Bockover <abockover novell 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;
+using System.Collections.Generic;
+using Mono.Unix;
+using Gtk;
+
+using Hyena;
+using Banshee.Configuration;
+
+namespace Banshee.Gui
+{
+    public class PlaybackRepeatActions : BansheeActionGroup, IEnumerable<RadioAction>
+    {
+        private InterfaceActionService action_service;
+        private RadioAction active_action;
+
+        public RadioAction Active {
+            get { return active_action; }
+            set {
+                active_action = value;
+                RepeatMode.Set (active_action == null ? String.Empty : ActionNameToConfigId (active_action.Name));
+            }
+        }
+
+        public event ChangedHandler Changed;
+        
+        public PlaybackRepeatActions (InterfaceActionService actionService) : base ("PlaybackRepeat")
+        {
+            actionService.AddActionGroup (this);
+
+            Add (new RadioActionEntry [] {
+                new RadioActionEntry ("RepeatNoneAction", "media-repeat-none", 
+                    Catalog.GetString ("Repeat N_one"), null,
+                    Catalog.GetString ("Do not repeat playlist"), 0),
+                    
+                new RadioActionEntry ("RepeatAllAction", "media-repeat-all",
+                    Catalog.GetString ("Repeat _All"), null,
+                    Catalog.GetString ("Play all songs before repeating playlist"), 1),
+                    
+                new RadioActionEntry ("RepeatSingleAction", "media-repeat-single",
+                    Catalog.GetString ("Repeat Si_ngle"), null,
+                    Catalog.GetString ("Repeat the current playing song"), 2)
+            }, 0, OnChanged);
+
+            action_service = actionService;
+
+            Gtk.Action action = this[ConfigIdToActionName (RepeatMode.Get ())];
+            if (action is RadioAction) {
+                active_action = (RadioAction)action;
+            } else {
+                Active = (RadioAction)this["RepeatNoneAction"];
+            }
+        }
+
+        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["RepeatNoneAction"];
+            yield return (RadioAction)this["RepeatAllAction"];
+            yield return (RadioAction)this["RepeatSingleAction"];
+        }
+
+        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> RepeatMode = new SchemaEntry<string> (
+            "playback", "repeat_mode",
+            "none",
+            "Repeat playback",
+            "Repeat mode (repeat_none, repeat_all, repeat_single)"
+        );
+    }
+}

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	Wed Feb 27 23:53:11 2008
@@ -94,6 +94,11 @@
     <File name="Banshee.Sources.Gui/ISourceContents.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Sources.Gui/ObjectListSourceContents.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.Playlist.Gui/PlaylistExportDialog.cs" subtype="Code" buildaction="Compile" />
+    <File name="Resources/media-repeat-all.png" subtype="Code" buildaction="EmbedAsResource" />
+    <File name="Resources/media-repeat-none.png" subtype="Code" buildaction="EmbedAsResource" />
+    <File name="Resources/media-repeat-single.png" subtype="Code" buildaction="EmbedAsResource" />
+    <File name="Banshee.Gui.Widgets/RepeatActionButton.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.Gui/PlaybackRepeatActions.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	Wed Feb 27 23:53:11 2008
@@ -44,6 +44,7 @@
 	Banshee.Gui.Widgets/ConnectedSeekSlider.cs \
 	Banshee.Gui.Widgets/ConnectedVolumeButton.cs \
 	Banshee.Gui.Widgets/PlaylistMenuItem.cs \
+	Banshee.Gui.Widgets/RepeatActionButton.cs \
 	Banshee.Gui.Widgets/TrackInfoDisplay.cs \
 	Banshee.Gui.Widgets/UserJobTile.cs \
 	Banshee.Gui.Widgets/UserJobTileHost.cs \
@@ -58,6 +59,7 @@
 	Banshee.Gui/IHasSourceView.cs \
 	Banshee.Gui/InterfaceActionService.cs \
 	Banshee.Gui/PlaybackActions.cs \
+	Banshee.Gui/PlaybackRepeatActions.cs \
 	Banshee.Gui/SourceActions.cs \
 	Banshee.Gui/TrackActions.cs \
 	Banshee.Gui/ViewActions.cs \
@@ -85,6 +87,9 @@
 	Resources/banshee-logo.png \
 	Resources/browser-album-cover.png \
 	Resources/core-ui-actions-layout.xml \
+	Resources/media-repeat-all.png \
+	Resources/media-repeat-none.png \
+	Resources/media-repeat-single.png \
 	Resources/source-cd-audio.png \
 	Resources/source-library.png \
 	Resources/source-localqueue.png \

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	Wed Feb 27 23:53:11 2008
@@ -10,6 +10,10 @@
     </placeholder>
     <placeholder name="VolumeButton"/>
   </toolbar>
+  <toolbar name="FooterToolbar">
+    <placeholder name="StatusBar"/>
+    <placeholder name="RepeatButton"/>
+  </toolbar>
 
   <menubar name="MainMenu">
     <menu name="MusicMenu" action="MusicMenuAction">
@@ -70,9 +74,9 @@
       <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>

Modified: trunk/banshee/src/Libraries/Hyena/Hyena/StringUtil.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena/StringUtil.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena/StringUtil.cs	Wed Feb 27 23:53:11 2008
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Text;
 using System.Globalization;
 using System.Text.RegularExpressions;
 
@@ -66,7 +67,7 @@
                 return null;
             }
         
-            string undercase = String.Empty;
+            StringBuilder undercase = new StringBuilder ();
             string [] tokens = Regex.Split (s, "([A-Z]{1}[a-z]+)");
             
             for (int i = 0; i < tokens.Length; i++) {
@@ -74,13 +75,42 @@
                     continue;
                 }
 
-                undercase += tokens[i].ToLower ();
+                undercase.Append (tokens[i].ToLower ());
                 if (i < tokens.Length - 2) {
-                    undercase += "_";
+                    undercase.Append ('_');
                 }
             }
             
-            return undercase;
+            return undercase.ToString ();
+        }
+
+        public static string UnderCaseToCamelCase (string s)
+        {
+            if (String.IsNullOrEmpty (s)) {
+                return null;
+            }
+
+            StringBuilder builder = new StringBuilder ();
+
+            for (int i = 0, n = s.Length, b = -1; i < n; i++) {
+                if (b < 0 && s[i] != '_') {
+                    builder.Append (Char.ToUpper (s[i]));
+                    b = i;
+                } else if (s[i] == '_' && i + 1 < n && s[i + 1] != '_') {
+                    if (builder.Length > 0 && Char.IsUpper (builder[builder.Length - 1])) {
+                        builder.Append (Char.ToLower (s[i + 1]));
+                    } else {
+                        builder.Append (Char.ToUpper (s[i + 1]));
+                    }
+                    i++;
+                    b = i;
+                } else if (s[i] != '_') {
+                    builder.Append (Char.ToLower (s[i]));
+                    b = i;
+                }
+            }
+
+            return builder.ToString ();
         }
     }
 }

Added: trunk/banshee/tests/Hyena/StringUtilTests.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/tests/Hyena/StringUtilTests.cs	Wed Feb 27 23:53:11 2008
@@ -0,0 +1,60 @@
+using System;
+using NUnit.Framework;
+using Hyena;
+
+[TestFixture]
+public class StringUtilTests
+{
+    private class Map
+    {
+        public Map (string camel, string under)
+        {
+            Camel = camel;
+            Under = under;
+        }
+
+        public string Camel;
+        public string Under;
+    }
+
+    private Map [] u_to_c_maps = new Map [] {
+        new Map ("Hello", "hello"),
+        new Map ("HelloWorld", "hello_world"),
+        new Map ("HelloWorld", "hello__world"),
+        new Map ("HelloWorld", "hello___world"),
+        new Map ("HelloWorld", "hello____world"),
+        new Map ("HelloWorld", "_hello_world"),
+        new Map ("HelloWorld", "__hello__world"),
+        new Map ("HelloWorld", "___hello_world_"),
+        new Map ("HelloWorldHowAreYou", "_hello_World_HOW_ARE__YOU__"),
+        new Map (null, ""),
+        new Map ("H", "h")
+    };
+
+    [Test]
+    public void TestUnderCaseToCamelCase ()
+    {
+        foreach (Map map in u_to_c_maps) {
+            Assert.AreEqual (map.Camel, StringUtil.UnderCaseToCamelCase (map.Under));
+        }
+    }
+
+    private Map [] c_to_u_maps = new Map [] {
+        new Map ("Hello", "hello"),
+        new Map ("HelloWorld", "hello_world"),
+        new Map ("HiWorldHowAreYouDoingToday", "hi_world_how_are_you_doing_today"),
+        new Map ("SRSLYHowAreYou", "srsly_how_are_you"),
+        new Map ("OMGThisShitIsBananas", "omg_this_shit_is_bananas"),
+        new Map ("KTHXBAI", "kthxbai"),
+        new Map ("", null),
+        new Map ("H", "h")
+    };
+
+    [Test]
+    public void TestCamelCaseToUnderCase ()
+    {
+        foreach (Map map in c_to_u_maps) {
+            Assert.AreEqual (map.Under, StringUtil.CamelCaseToUnderCase (map.Camel));
+        }
+    }
+}

Modified: trunk/banshee/tests/Makefile.am
==============================================================================
--- trunk/banshee/tests/Makefile.am	(original)
+++ trunk/banshee/tests/Makefile.am	Wed Feb 27 23:53:11 2008
@@ -13,7 +13,7 @@
 	Hyena/CryptoUtilTests.cs \
 	Hyena/RangeCollectionTests.cs
 
-CFS=Hyena/RangeCollectionTests.cs Hyena/CryptoUtilTests.cs
+CFS=Hyena/RangeCollectionTests.cs Hyena/CryptoUtilTests.cs Hyena/StringUtilTests.cs
 
 NUNIT_TESTER_NAME = ConsoleUi
 NUNIT_TESTER = $(NUNIT_TESTER_NAME).exe



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