[banshee] [Audiobook] Much improved playback; add Resume action



commit 1fccec8a49bdf1a284b7679c898b142088204b13
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Mon May 17 15:23:02 2010 -0700

    [Audiobook] Much improved playback; add Resume action
    
    The resume action is available if there's a last-played bookmark.
    Playback can now happen from a given book (via a smart playlist limited
    to that book) even while you continue to browse the rest of the
    Audiobook library.  This is similar to what we do in the Music library
    but there we achieve it by having child playlists that can be the
    playback source - here it's an implicit source based on the playing
    book.

 .../Banshee.Audiobook/Banshee.Audiobook.csproj     |    2 +
 .../Banshee.Audiobook/Banshee.Audiobook/Actions.cs |   43 +++++-
 .../Banshee.Audiobook/AudiobookLibrarySource.cs    |  144 +++++++++++++++++---
 .../Banshee.Audiobook/AudiobookModel.cs            |    7 +-
 .../Banshee.Audiobook/BookPlaylist.cs              |   70 ++++++++++
 .../Banshee.Audiobook/BookView.cs                  |   44 +++---
 src/Extensions/Banshee.Audiobook/Makefile.am       |    1 +
 .../Banshee.Audiobook/Resources/ActiveSourceUI.xml |    1 +
 .../Banshee.Audiobook/Resources/GlobalUI.xml       |    2 +
 9 files changed, 264 insertions(+), 50 deletions(-)
---
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook.csproj b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook.csproj
index ec5a7ec..27302fa 100644
--- a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook.csproj
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook.csproj
@@ -66,6 +66,7 @@
       <HintPath>..\..\..\bin\taglib-sharp.dll</HintPath>
     </Reference>
     <Reference Include="Mono.Cairo" />
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Banshee.Audiobook.addin.xml">
@@ -87,6 +88,7 @@
     <Compile Include="Banshee.Audiobook\AudiobookModel.cs" />
     <Compile Include="Banshee.Audiobook\BookView.cs" />
     <Compile Include="Banshee.Audiobook\BookCover.cs" />
+    <Compile Include="Banshee.Audiobook\BookPlaylist.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/Actions.cs b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/Actions.cs
index 0a8ee0d..73b0dc1 100644
--- a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/Actions.cs
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/Actions.cs
@@ -55,22 +55,24 @@ namespace Banshee.Audiobook
                 new ActionEntry ("AudiobookOpen", null, Catalog.GetString ("Open Book"), null, null, OnOpen),
                 new ActionEntry ("AudiobookMerge", null, Catalog.GetString ("Merge Discs..."), null, null, OnMerge),
                 new ActionEntry ("AudiobookEdit", Stock.Edit,
-                    Catalog.GetString ("_Edit Track Information"), "E", null, OnEdit)
+                    Catalog.GetString ("_Edit Track Information"), "E", null, OnEdit),
+                new ActionEntry ("AudiobookResumeSelected", Stock.MediaPlay,
+                    Catalog.GetString ("Resume"), null, Catalog.GetString ("Resume playback of this audiobook"), OnResume)
             );
 
+            AddImportant (new ActionEntry ("AudiobookResume", Stock.MediaPlay,
+                Catalog.GetString ("Resume"), null, Catalog.GetString ("Resume playback of this audiobook"), OnResume));
+
             AddUiFromFile ("GlobalUI.xml");
 
             Register ();
 
-            library.BooksModel.Selection.Changed += HandleSelectionChanged;
-        }
-
-        private void HandleSelectionChanged (object sender, EventArgs args)
-        {
-            ThreadAssist.ProxyToMain (UpdateActions);
+            UpdateActions ();
+            library.BooksModel.Selection.Changed += (o, a) => UpdateActions ();
+            library.BooksModel.Selection.FocusChanged += (o, a) => UpdateActions ();
         }
 
-        private void UpdateActions ()
+        internal void UpdateActions ()
         {
             var selection = library.BooksModel.Selection;
             bool has_selection = selection.Count > 0;
@@ -78,6 +80,31 @@ namespace Banshee.Audiobook
 
             UpdateAction ("AudiobookMerge", !has_single_selection, true);
             UpdateAction ("AudiobookEdit", true, has_selection);
+
+            bool can_resume = false;
+            if (has_single_selection) {
+                var book = library.ActiveBook;
+                if (book != null && library.GetLastPlayedBookmark (book.DbId) != null) {
+                    var playback_book = library.PlaybackSource.Book;
+                    if (playback_book == null || book.DbId != playback_book.DbId) {
+                        can_resume = true;
+                    }
+                }
+            }
+            UpdateAction ("AudiobookResume", library.CurrentViewBook != null && can_resume, true);
+            UpdateAction ("AudiobookResumeSelected", can_resume, true);
+        }
+
+        private void OnResume (object to, EventArgs a)
+        {
+            var book = library.ActiveBook;
+            var bookmark = library.GetLastPlayedBookmark (book.DbId);
+            if (bookmark != null) {
+                Log.DebugFormat ("Audiobook Library jumpting to last-played position in active book: {0}", bookmark.Name);
+                library.PlaybackSource.Book = book;
+                ServiceManager.PlaybackController.Source = library;
+                bookmark.JumpTo ();
+            }
         }
 
         private void OnOpen (object o, EventArgs a)
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookLibrarySource.cs b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookLibrarySource.cs
index 266f808..862ae2d 100644
--- a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookLibrarySource.cs
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookLibrarySource.cs
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Linq;
 using System.Collections.Generic;
 
 using Mono.Unix;
@@ -43,10 +44,12 @@ using Banshee.Database;
 using Banshee.ServiceStack;
 
 using Banshee.Sources.Gui;
+using Banshee.PlaybackController;
+using Banshee.Query;
 
 namespace Banshee.Audiobook
 {
-    public class AudiobookLibrarySource : LibrarySource
+    public class AudiobookLibrarySource : LibrarySource, IBasicPlaybackController
     {
         internal const string LAST_PLAYED_BOOKMARK = "audiobook-lastplayed";
 
@@ -55,6 +58,8 @@ namespace Banshee.Audiobook
         LazyLoadSourceContents<AudiobookContent> grid_view;
         LazyLoadSourceContents<BookView> book_view;
 
+        public BookPlaylist PlaybackSource { get; private set; }
+
         Gtk.HBox title_switcher;
         Gtk.Label book_label;
 
@@ -114,31 +119,91 @@ namespace Banshee.Audiobook
             TracksAdded += (o, a) => {
                 if (!IsAdding) {
                     MergeBooksAddedSince (DateTime.Now - TimeSpan.FromHours (2));
+
+                    ServiceManager.DbConnection.Execute (
+                        "UPDATE CoreTracks SET Attributes = Attributes | ? WHERE PrimarySourceID = ?",
+                        TrackMediaAttributes.AudioBook, this.DbId);
                 }
             };
 
+            TrackIsPlayingHandler = ServiceManager.PlayerEngine.IsPlaying;
+
+            PlaybackSource = new BookPlaylist ("audiobook-playback-source", this);
+            PlaybackSource.DatabaseTrackModel.ForcedSortQuery = BansheeQuery.GetSort ("track", true);
+
+            ServiceManager.PlaybackController.SourceChanged += OnPlaybackSourceChanged;
+
             // Listen for playback changes and auto-set the last-played bookmark
             ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent,
                 PlayerEvent.StartOfStream | PlayerEvent.EndOfStream | PlayerEvent.Seek,
                 true);
         }
 
+        private void OnPlaybackSourceChanged (object o, EventArgs args)
+        {
+            if (ServiceManager.PlaybackController.Source == this) {
+                PlaybackSource.Book = PlaybackSource.Book ?? ActiveBook;
+            }
+
+            Actions.UpdateActions ();
+        }
+
+        public override bool CanSearch {
+            get { return false; }
+        }
+
         private void SwitchToGrid ()
         {
-            Properties.Set<ISourceContents> ("Nereid.SourceContents", grid_view);
-            book_label.Visible = false;
+            var last_book = CurrentViewBook;
+            if (last_book != null) {
+                CurrentViewBook = null;
+                book_label.Visible = false;
+                Properties.Set<ISourceContents> ("Nereid.SourceContents", grid_view);
+                Actions.UpdateActions ();
+            }
         }
 
-        public DatabaseAlbumInfo CurrentBook { get; private set; }
         public void SwitchToBookView (DatabaseAlbumInfo book)
         {
             if (!book_label.Visible) {
-                CurrentBook = book;
+                CurrentViewBook = book;
                 book_label.Text = String.Format (" »  {0}", book.DisplayTitle);
                 book_label.Visible = true;
                 book_view.SetSource (this);
                 book_view.Contents.SetBook (book);
                 Properties.Set<ISourceContents> ("Nereid.SourceContents", book_view);
+
+                if (BooksModel.Selection.Count != 1) {
+                    var index = BooksModel.Selection.FocusedIndex;
+                    BooksModel.Selection.Clear (false);
+                    BooksModel.Selection.Select (index);
+                }
+
+                Actions.UpdateActions ();
+            }
+        }
+
+        public DatabaseAlbumInfo CurrentViewBook { get; private set; }
+
+        public DatabaseAlbumInfo ActiveBook {
+            get {
+                if (CurrentViewBook != null) {
+                    return CurrentViewBook;
+                }
+
+                if (BooksModel.Selection.FocusedIndex != -1) {
+                    return BooksModel [BooksModel.Selection.FocusedIndex] as DatabaseAlbumInfo;
+                }
+
+                if (BooksModel.Selection.Count > 0) {
+                    return BooksModel.SelectedItems.First () as DatabaseAlbumInfo;
+                }
+
+                if (BooksModel.Count > 0) {
+                    return BooksModel[0] as DatabaseAlbumInfo;
+                }
+
+                return null;
             }
         }
 
@@ -169,6 +234,19 @@ namespace Banshee.Audiobook
                 case PlayerEvent.StartOfStream:
                     if (book_track != null) {
                         StartTimeout ();
+
+                        if (PlaybackSource.Book == null || PlaybackSource.Book.DbId != book_track.AlbumId) {
+                            PlaybackSource.Book = DatabaseAlbumInfo.Provider.FetchSingle (book_track.AlbumId);
+                        }
+
+                        if (book_track.CacheModelId != PlaybackSource.DatabaseTrackModel.CacheId) {
+                            var index = PlaybackSource.DatabaseTrackModel.IndexOfFirst (book_track);
+                            if (index >= 0) {
+                                ServiceManager.PlaybackController.PriorTrack = PlaybackSource.TrackModel [index];
+                            } else {
+                                Log.Error ("Audiobook track started, but couldn't find in the Audiobook.PlaybackSource");
+                            }
+                        }
                     }
                     break;
                 case PlayerEvent.EndOfStream:
@@ -200,23 +278,24 @@ namespace Banshee.Audiobook
         private void UpdateLastPlayed ()
         {
             if (book_track != null) {
-                // Find and remove the last bookmark for this book
-                if (bookmark == null) {
-                    bookmark = Bookmark.Provider.FetchFirstMatching (
-                        "Type = ? AND TrackID IN (SELECT TrackID FROM CoreTracks WHERE PrimarySourceID = ? AND AlbumID = ?)",
-                        LAST_PLAYED_BOOKMARK, this.DbId, book_track.AlbumId
-                    );
-                }
+                bookmark = GetLastPlayedBookmark (book_track.AlbumId) ?? new Bookmark ();
 
-                if (bookmark != null) {
-                    bookmark.Remove ();
-                }
-
-                // Insert the new one
-                bookmark = new Bookmark (book_track, (int)ServiceManager.PlayerEngine.Position, LAST_PLAYED_BOOKMARK);
+                bookmark.Type = LAST_PLAYED_BOOKMARK;
+                bookmark.CreatedAt = DateTime.Now;
+                bookmark.Track = book_track;
+                bookmark.Position = TimeSpan.FromMilliseconds ((int)ServiceManager.PlayerEngine.Position);
+                bookmark.Save ();
             }
         }
 
+        public Bookmark GetLastPlayedBookmark (int book_id)
+        {
+            return Bookmark.Provider.FetchFirstMatching (
+                "Type = ? AND TrackID IN (SELECT TrackID FROM CoreTracks WHERE PrimarySourceID = ? AND AlbumID = ?)",
+                LAST_PLAYED_BOOKMARK, this.DbId, book_id
+            );
+        }
+
         public override void Dispose ()
         {
             if (Actions != null) {
@@ -239,11 +318,40 @@ namespace Banshee.Audiobook
             yield return books_model;
         }
 
+        #region IBasicPlaybackController implementation
+
+        public bool First ()
+        {
+            return DoPlaybackAction ();
+        }
+
+
+        public bool Next (bool restart, bool changeImmediately)
+        {
+            return DoPlaybackAction ();
+        }
+
+        public bool Previous (bool restart)
+        {
+            return DoPlaybackAction ();
+        }
+
+        #endregion
+
+        private bool DoPlaybackAction ()
+        {
+            PlaybackSource.Book = PlaybackSource.Book ?? ActiveBook;
+            ServiceManager.PlaybackController.Source = PlaybackSource;
+            ServiceManager.PlaybackController.NextSource = this;
+            return false;
+        }
+
         public DatabaseAlbumListModel BooksModel {
             get { return books_model; }
         }
 
         public override string DefaultBaseDirectory {
+            // FIXME should probably translate this fallback directory
             get { return XdgBaseDirectorySpec.GetXdgDirectoryUnderHome ("XDG_AUDIOBOOKS_DIR", "Audiobooks"); }
         }
 
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookModel.cs b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookModel.cs
index 6a6fdf7..5316670 100644
--- a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookModel.cs
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/AudiobookModel.cs
@@ -45,10 +45,15 @@ namespace Banshee.Audiobook
 {
     public class AudiobookModel : DatabaseAlbumListModel
     {
-        public AudiobookModel (DatabaseSource source, DatabaseTrackListModel trackModel, BansheeDbConnection connection, string uuid) : base (source, trackModel, connection, uuid)
+        public AudiobookModel (AudiobookLibrarySource source, DatabaseTrackListModel trackModel, BansheeDbConnection connection, string uuid) : base (source, trackModel, connection, uuid)
         {
             Selection = new Hyena.Collections.Selection ();
             HasSelectAllItem = false;
+
+            ReloadFragmentFormat = String.Format (@"
+                FROM CoreAlbums WHERE CoreAlbums.AlbumID IN (SELECT AlbumID FROM CoreTracks WHERE PrimarySourceID = {0})
+                ORDER BY CoreAlbums.TitleSortKey, CoreAlbums.ArtistNameSortKey",
+                source.DbId);
         }
     }
 }
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookPlaylist.cs b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookPlaylist.cs
new file mode 100644
index 0000000..836f011
--- /dev/null
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookPlaylist.cs
@@ -0,0 +1,70 @@
+//
+// BookPlaylist.cs
+//
+// Author:
+//   Gabriel Burt <gabriel burt gmail com>
+//
+// Copyright (c) 2010 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using Banshee.Sources;
+using Banshee.SmartPlaylist;
+using Banshee.Query;
+using Banshee.Collection.Database;
+
+namespace Banshee.Audiobook
+{
+    public class BookPlaylist : SmartPlaylistSource
+    {
+        AudiobookLibrarySource library;
+
+        public BookPlaylist (string name, AudiobookLibrarySource parent) : base (name, parent)
+        {
+            library = parent;
+            IsTemporary = true;
+            Save ();
+        }
+
+        DatabaseAlbumInfo book;
+        public DatabaseAlbumInfo Book {
+            get { return book; }
+            set {
+                if (value == null)
+                    return;
+
+                if (book != null && value.DbId == book.DbId) {
+                    Reload ();
+                } else {
+                    book = value;
+                    ConditionSql = String.Format ("CoreTracks.AlbumID = {0}", value.DbId);
+                    RefreshAndReload ();
+                }
+
+                library.Actions.UpdateActions ();
+            }
+        }
+
+        public override bool CanShuffle {
+            get { return false; }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookView.cs b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookView.cs
index aa2300a..9d0d895 100644
--- a/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookView.cs
+++ b/src/Extensions/Banshee.Audiobook/Banshee.Audiobook/BookView.cs
@@ -57,6 +57,7 @@ using Banshee.Preferences;
 using Banshee.ServiceStack;
 using Banshee.Sources;
 using Hyena.Gui.Theming;
+using Banshee.Query;
 
 namespace Banshee.Audiobook
 {
@@ -66,7 +67,6 @@ namespace Banshee.Audiobook
 
         Alignment align;
         Label title_label;
-        Label bookmarks_label;
         BookCover cover;
         RatingEntry rating_entry;
         BaseTrackListView track_list;
@@ -93,15 +93,10 @@ namespace Banshee.Audiobook
 
             UpdateCover ();
 
-            var bookmarks = Bookmark.Provider.FetchAllMatching (
+            /*var bookmarks = Bookmark.Provider.FetchAllMatching (
                 "TrackID IN (SELECT TrackID FROM CoreTracks WHERE PrimarySourceID = ? AND AlbumID = ?)",
                 library.DbId, book.DbId
-            );
-
-            bookmarks_label.Text = "";
-            foreach (var bookmark in bookmarks) {
-                bookmarks_label.Text += String.Format ("{0}{1}\n", bookmark.Type == AudiobookLibrarySource.LAST_PLAYED_BOOKMARK ? "* " : "", bookmark.Name);
-            }
+            );*/
 
             rating_entry.Value = (int) Math.Round (ServiceManager.DbConnection.Query<double> (
                 "SELECT AVG(RATING) FROM CoreTracks WHERE PrimarySourceID = ? AND AlbumID = ?", library.DbId, book.DbId
@@ -177,6 +172,11 @@ namespace Banshee.Audiobook
                 Yalign = 0
             };
 
+            var continue_button = new ImageButton ("<b>Continue Playback</b>\n<small>Disc 3 - Track 3b (2:53)</small>", null, IconSize.Button);
+            continue_button.ImageWidget.Stock = Stock.MediaPlay;
+            continue_button.LabelWidget.Xalign = 0;
+            continue_button.Spacing = 6;
+
             // FIXME the left padding on this is not right
             rating_entry = new RatingEntry () {
                 AlwaysShowEmptyStars = true,
@@ -188,20 +188,15 @@ namespace Banshee.Audiobook
             // Packing
             left_box.PackStart (editable_cover, false, false,  0);
             left_box.PackStart (title_label, false, false,  0);
+            //left_box.PackStart (continue_button, false, false,  0);
             //left_box.PackStart (rating, false, false,  0);
 
             hbox.PackStart (left_box, false, false, 0);
 
-            // Right box - bookmarks, track list
+            // Right box - track list
             var right_box = new VBox () { Spacing = 12 };
 
-            var notebook = new Notebook () { WidthRequest = 450, HeightRequest = 600 };
-            notebook.ShowBorder = false;
-            bookmarks_label = new Label ();
-            notebook.AppendPage (bookmarks_label, new Label (Catalog.GetString ("Bookmarks")));
-
             // Tracks
-
             track_list = new BaseTrackListView () {
                 HeaderVisible = true,
                 IsEverReorderable = false
@@ -209,16 +204,23 @@ namespace Banshee.Audiobook
 
             var columns = new DefaultColumnController ();
             var file_columns = new ColumnController ();
+            var disc_column = DefaultColumnController.Create (
+                    BansheeQuery.DiscNumberField, 0.02, false, new ColumnCellPositiveInt (null, false, 2, 2));
+
             file_columns.AddRange (
                 columns.IndicatorColumn,
-                columns.DiscNumberAndCountColumn,
+                disc_column,
                 columns.TitleColumn,
                 columns.DurationColumn
             );
             file_columns.SortColumn = columns.DiscNumberAndCountColumn;
 
-            var track_sw = new Gtk.ScrolledWindow ();
-            track_sw.Child = track_list;
+            var track_sw = new Gtk.ScrolledWindow () {
+                Child = track_list,
+                ShadowType = ShadowType.None,
+                WidthRequest = 450,
+                HeightRequest = 600
+            };
 
             foreach (var col in file_columns) {
                 col.Visible = true;
@@ -226,11 +228,7 @@ namespace Banshee.Audiobook
 
             track_list.ColumnController = file_columns;
 
-            notebook.AppendPage (track_sw, new Label (Catalog.GetString ("Tracks")));
-
-
-            right_box.PackStart (notebook, false, false, 0);
-
+            right_box.PackStart (track_sw, false, false, 0);
             hbox.PackStart (right_box, false, false, 0);
 
             align.Child = hbox;
diff --git a/src/Extensions/Banshee.Audiobook/Makefile.am b/src/Extensions/Banshee.Audiobook/Makefile.am
index b8ec015..8dcf188 100644
--- a/src/Extensions/Banshee.Audiobook/Makefile.am
+++ b/src/Extensions/Banshee.Audiobook/Makefile.am
@@ -11,6 +11,7 @@ SOURCES =  \
 	Banshee.Audiobook/AudiobookLibrarySource.cs \
 	Banshee.Audiobook/AudiobookModel.cs \
 	Banshee.Audiobook/BookCover.cs \
+	Banshee.Audiobook/BookPlaylist.cs \
 	Banshee.Audiobook/BookView.cs
 
 RESOURCES =  \
diff --git a/src/Extensions/Banshee.Audiobook/Resources/ActiveSourceUI.xml b/src/Extensions/Banshee.Audiobook/Resources/ActiveSourceUI.xml
index 43ed389..90b72e9 100644
--- a/src/Extensions/Banshee.Audiobook/Resources/ActiveSourceUI.xml
+++ b/src/Extensions/Banshee.Audiobook/Resources/ActiveSourceUI.xml
@@ -1,6 +1,7 @@
 <ui>
     <toolbar name="HeaderToolbar">
         <placeholder name="SourceActions">
+            <toolitem action="AudiobookResume"/>
         </placeholder>
     </toolbar>
 </ui>
diff --git a/src/Extensions/Banshee.Audiobook/Resources/GlobalUI.xml b/src/Extensions/Banshee.Audiobook/Resources/GlobalUI.xml
index 8b6e126..bc818f3 100644
--- a/src/Extensions/Banshee.Audiobook/Resources/GlobalUI.xml
+++ b/src/Extensions/Banshee.Audiobook/Resources/GlobalUI.xml
@@ -2,6 +2,8 @@
     <popup name="AudiobookBookPopup">
         <menuitem action="AudiobookOpen"/>
         <separator/>
+        <menuitem action="AudiobookResumeSelected"/>
+        <separator/>
         <menuitem action="AudiobookMerge"/>
         <menuitem action="AudiobookEdit"/>
     </popup>



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