Good evening Banshee lovers.
attached is the patch for browser appliable against 0_10_2.
with all its good and evil...
do not hesitate to report problems. (i wont break your legs if it is a false alarm ;) )
Have Fun!
Ulaas.
P.S.
Index: src/PlayerInterface.cs =================================================================== RCS file: /cvs/gnome/banshee/src/PlayerInterface.cs,v retrieving revision 1.123 diff -u -r1.123 PlayerInterface.cs --- src/PlayerInterface.cs 22 Dec 2005 03:06:16 -0000 1.123 +++ src/PlayerInterface.cs 25 Dec 2005 00:58:29 -0000 @@ -58,6 +58,8 @@ [Widget] private Gtk.Label LabelInfo; [Widget] private HPaned SourceSplitter; [Widget] private Button HeaderCycleButton; + [Widget] private Expander CustomExpander; + [Widget] private Gtk.VBox DisclosureBox; private PlaylistModel playlistModel; @@ -76,6 +78,7 @@ private Tooltips toolTips; private Hashtable playlistMenuMap; private Viewport sourceViewLoadingVP; + private BrowserParent browser; private MultiStateToggleButton repeat_toggle_button; private MultiStateToggleButton shuffle_toggle_button; @@ -458,6 +461,14 @@ gxml["SearchLabel"].Sensitive = false; searchEntry.Sensitive = false; + + // Custom Expander and browser related stuff. + CustomExpander = ((Expander)gxml["CustomExpander"]); + DisclosureBox = ((VBox)gxml["DisclosureBox"]); + CustomExpander.Activated += OnExpandAreaClicked; + browser = new BrowserParent(); + browser.BrowserSearchActivated += OnBrowserSearchActivated; + DisclosureBox.Add(browser); // Repeat/Shuffle buttons @@ -675,6 +686,11 @@ searchEntry.Sensitive = true; } + private void OnBrowserSearchActivated(object o, BrowserSearchEventArgs args) + { + playlistModel.LoadFromBrowserSearch(args.artist, args.album); + } + private void OnLibraryTransactionStatusStopped(object o, EventArgs args) { headerNotebook.RemovePage(libraryTransactionStatus); @@ -873,6 +889,17 @@ handled = true; } break; + case Gdk.Key.F5: + if((args.Event.State & Gdk.ModifierType.ControlMask) != 0) { + if (CustomExpander.Expanded) { + CustomExpander.Expanded = false; + } else { + CustomExpander.Expanded = true; + } + OnExpandAreaClicked(this, new EventArgs()); + handled = true; + } + break; } args.RetVal = handled; @@ -1245,26 +1272,36 @@ audiocd_statusbar.Visible = false; if(source is LibrarySource) { - playlistModel.LoadFromLibrary(); + if(!browser.BrowserHasFilter()) { + playlistModel.LoadFromLibrary(); + } playlistModel.Source = source; + browser.Active(true); + Application.Invoke(delegate { + browser.BrowserFilter(); + }); } else if(source is LocalQueueSource) { playlistModel.LoadFromLocalQueue(); playlistModel.Source = source; + browser.Active(false); } else if(source is DapSource) { playlistModel.Clear(); playlistModel.Source = source; DapSource dap_source = source as DapSource; playlistModel.LoadFromDapSource(dap_source); UpdateDapDiskUsageBar(dap_source); + browser.Active(false); } else if(source is AudioCdSource) { playlistModel.Clear(); playlistModel.Source = source; AudioCdSource cdSource = source as AudioCdSource; playlistModel.LoadFromAudioCdSource(cdSource); UpdateAudioCdStatus(cdSource.Disk); + browser.Active(false); } else { playlistModel.LoadFromPlaylist(source.Name); playlistModel.Source = source; + browser.Active(false); } (gxml["ViewNameLabel"] as Label).Markup = "<b>" + GLib.Markup.EscapeText(source.Name) + "</b>"; @@ -1608,6 +1645,7 @@ { Source source = sourceView.SelectedSource; playlistModel.Clear(); + browser.BrowserReset(); if(!searchEntry.IsQueryAvailable) { if(source.Type == SourceType.Dap) { @@ -2064,6 +2102,7 @@ } playlistModel.AddTrack(ti); + browser.BrowserUpdate(); }); } } catch(Entagged.Audioformats.Exceptions.CannotReadException) { @@ -2183,6 +2222,9 @@ { playlistView.QueueDraw(); sourceView.QueueDraw(); + Application.Invoke(delegate { + browser.BrowserUpdate(); + }); } [GLib.ConnectBefore] @@ -2506,6 +2548,20 @@ ToggleAction action = o as ToggleAction; playlistModel.Shuffle = action.Active; Globals.Configuration.Set(GConfKeys.PlaylistShuffle, action.Active); + } + + // --- Expander --- + + private void OnExpandAreaClicked (object sender, EventArgs args) + { + if (CustomExpander.Expanded) + { + DisclosureBox.Visible = true; + } + else + { + DisclosureBox.Visible = false; + } } // --- View Menu --- Index: src/PlaylistModel.cs =================================================================== RCS file: /cvs/gnome/banshee/src/PlaylistModel.cs,v retrieving revision 1.28 diff -u -r1.28 PlaylistModel.cs --- src/PlaylistModel.cs 22 Dec 2005 02:25:53 -0000 1.28 +++ src/PlaylistModel.cs 25 Dec 2005 00:58:30 -0000 @@ -174,6 +174,51 @@ RaiseUpdated(this, new EventArgs()); } + public void LoadFromBrowserSearch(BrowserEventInfo beiArtist, BrowserEventInfo beiAlbum) + { + ClearModel(); + ICollection collection = null; + collection = Core.Library.Tracks.Values; + + foreach(TrackInfo ti in collection) { + try { + if(DoesTrackMatchSearch(ti, beiArtist, beiAlbum)) { + AddTrack(ti); + } + } catch(Exception) { + continue; + } + } + } + + private bool DoesTrackMatchSearch(TrackInfo ti, BrowserEventInfo Artist, BrowserEventInfo Album) + { + string artist = Artist.StrData.ToLower(); + string album = Album.StrData.ToLower(); + string ti_artist = ti.Artist.ToString().ToLower(); + string ti_album = ti.Album.ToString().ToLower(); + int artist_index = Artist.IntIndex; + int album_index = Album.IntIndex; + + if (artist_index == 0 && album_index == 0) { + return true; + } else if (artist_index == 0) { + if (album.Equals(ti_album)) { + return true; + } + } else if (album_index == 0) { + if (artist.Equals(ti_artist)) { + return true; + } + } else if (album_index != 0 && artist_index != 0) { + if (artist.Equals(ti_artist) && album.Equals(ti_album)) { + return true; + } + } + + return false; + } + public void LoadFromDapSource(DapSource dapSource) { ClearModel(); Index: src/Makefile.am =================================================================== RCS file: /cvs/gnome/banshee/src/Makefile.am,v retrieving revision 1.81 diff -u -r1.81 Makefile.am --- src/Makefile.am 22 Dec 2005 05:29:50 -0000 1.81 +++ src/Makefile.am 25 Dec 2005 00:58:32 -0000 @@ -73,6 +73,8 @@ FileSystemMonitor/FileSystemWatcherWatch.cs \ ToggleStates.cs \ DapPropertiesDialog.cs \ + ArtistAlbumBrowser.cs \ + BrowserParent.cs \ FileTrackInfo.cs banshee_resources = \ Index: data/banshee.glade =================================================================== RCS file: /cvs/gnome/banshee/data/banshee.glade,v retrieving revision 1.3 diff -u -r1.3 banshee.glade --- data/banshee.glade 19 Dec 2005 22:12:03 -0000 1.3 +++ data/banshee.glade 25 Dec 2005 00:58:48 -0000 @@ -275,22 +275,38 @@ <property name="spacing">5</property> <child> - <widget class="GtkLabel" id="ViewNameLabel"> + <widget class="GtkExpander" id="CustomExpander"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>Loading...</b></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">4</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="can_focus">True</property> + <property name="expanded">False</property> + <property name="spacing">0</property> + + <child> + <placeholder/> + </child> + + <child> + <widget class="GtkLabel" id="ViewNameLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Loading...</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">4</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -399,30 +415,57 @@ </child> <child> - <widget class="GtkAlignment" id="LibraryAlignment"> + <widget class="GtkVPaned" id="VPane"> <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">0</property> - <property name="right_padding">0</property> + <property name="can_focus">True</property> + <property name="position">200</property> <child> - <widget class="GtkScrolledWindow" id="LibraryContainer"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> - <property name="shadow_type">GTK_SHADOW_IN</property> - <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + <widget class="GtkVBox" id="DisclosureBox"> + <property name="homogeneous">False</property> + <property name="spacing">0</property> <child> <placeholder/> </child> </widget> + <packing> + <property name="shrink">False</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="LibraryAlignment"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkScrolledWindow" id="LibraryContainer"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> </child> </widget> <packing> --- src/ArtistAlbumBrowser.cs 2005-12-25 03:13:11.000000000 +0200 +++ src/ArtistAlbumBrowser.cs 2005-12-25 02:34:44.000000000 +0200 @@ -0,0 +1,340 @@ +// created on 11/19/2005 at 1:26 AM by Aydemir Ulas Sahin + +using System; +using System.Data; +using System.IO; +using System.Collections; +using GLib; +using Gtk; +using Mono.Unix; +using Sql; + +namespace Banshee +{ + public delegate void BrowserSearchEventHandler(object o, BrowserSearchEventArgs args); + + public class BrowserEventInfo + { + private string strdata; + private int index; + + public BrowserEventInfo(string str_data, int int_index) + { + this.StrData = str_data; + this.IntIndex = int_index; + } + + + public string StrData + { + get { + return this.strdata; + } + set { + this.strdata = value; + } + } + + public int IntIndex + { + get { + return this.index; + } + set { + this.index = value; + } + } + } + + + public class BrowserSearchEventArgs : EventArgs + { + public BrowserEventInfo artist; + public BrowserEventInfo album; + + public BrowserSearchEventArgs(BrowserEventInfo beiArtist, BrowserEventInfo beiAlbum) + { + this.artist = beiArtist; + this.album = beiAlbum; + } + } + + public class ArtistAlbumBrowser : VBox + { + private Gtk.HBox main_box = new Gtk.HBox(); + private Gtk.ScrolledWindow artist_scroller = new Gtk.ScrolledWindow(); + private Gtk.ScrolledWindow album_scroller = new Gtk.ScrolledWindow(); + private Gtk.TreeView artist_tree = new Gtk.TreeView(); + private Gtk.TreeView album_tree = new Gtk.TreeView(); + + private ListStore artist_store; + private ListStore album_store; + + private string artist_selected = "none"; + private string album_selected = "none"; + + private int artist_selection_index = 0; + private int album_selection_index = 0; + + private Statement query; + + private int [] ResetSelectPath = {0}; + + public event BrowserSearchEventHandler BrowserClicked; + + public ArtistAlbumBrowser() : base() + { + BuildUI(); + Init(); + } + + private void BuildUI() + { + artist_scroller.HscrollbarPolicy = PolicyType.Automatic; + artist_scroller.VscrollbarPolicy = PolicyType.Always; + artist_scroller.ShadowType = ShadowType.EtchedIn; + artist_scroller.BorderWidth = 3; + + album_scroller.HscrollbarPolicy = PolicyType.Automatic; + album_scroller.VscrollbarPolicy = PolicyType.Always; + album_scroller.ShadowType = ShadowType.EtchedIn; + album_scroller.BorderWidth = 3; + + artist_tree.HeadersVisible = true; + artist_tree.AppendColumn(Catalog.GetString("Artist"), new CellRendererText(), "text", 0); + artist_store = CreateStore(); + artist_tree.Model = artist_store; + artist_tree.CursorChanged += OnArtistCursorChanged; + artist_scroller.Add(artist_tree); + + album_tree.HeadersVisible = true; + album_tree.AppendColumn(Catalog.GetString("Album"), new CellRendererText(), "text", 0); + album_store = CreateStore(); + album_tree.Model = album_store; + album_tree.CursorChanged += OnAlbumCursorChanged; + album_scroller.Add(album_tree); + + main_box.PackStart(artist_scroller, true, true, 0); + main_box.PackStart(album_scroller, true, true, 0); + + PackStart(main_box, true, true, 0); + Spacing = 5; + + } + + private void Init() + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc); + PopulateArtistStore(false); + PopulateAlbumStore(false); + + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + + this.ShowAll(); + } + + public void Reset() + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc) ; + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + artist_tree.ScrollToPoint(0,0); + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + album_tree.ScrollToPoint(0,0); + } + + public void Update(bool isFiltered) + { + query = new Select("Tracks", new Statement("DISTINCT Artist")) + new OrderBy("Artist", OrderDirection.Asc); + PopulateArtistStore(true); + PopulateAlbumStore(true); + if(isFiltered) { + RunSearch(); + } + } + + public bool FilterStatus() + { + if(artist_selection_index == 0 && album_selection_index == 0) { + return false; + } else { + return true; + } + } + + private ListStore CreateStore() + { + ListStore store = new ListStore(typeof(string)); + + //We dont need a sort for this view but in case anyone asks... + /* + store.SetSortFunc(1, delegate(TreeModel model, TreeIter a, TreeIter b) { + try { + String a_string = (String)store.GetValue(a, 1); + String b_string = (String)store.GetValue(b, 1); + return String.Compare(a_string, b_string); + } catch(Exception) { + return 0; + } + }); + store.SetSortColumnId(1, SortType.Descending); + */ + return store; + } + + private void PopulateArtistStore(bool isUpdate) + { + artist_store.Clear(); + ArrayList artistlist = new ArrayList(); + try + { + IDataReader reader = Core.Library.Db.Query(query); + while(reader.Read()) + { + artistlist.Add((reader[0] as string)); + } + } catch(Exception) { + + } + + AddEntry("All", true, artist_store); + foreach(string entry in artistlist) { + AddEntry(entry, false, artist_store); + } + if (isUpdate) { + UpdateArtistList(); + } + else { + ResetArtistList(); + } + + } + + private void PopulateAlbumStore(bool isUpdate) + { + album_store.Clear(); + ArrayList albumlist = new ArrayList(); + if (artist_selection_index == 0) + { + query = new Select("Tracks", new Statement("DISTINCT AlbumTitle")) + + new OrderBy("AlbumTitle", OrderDirection.Asc); + } + else + { + query = new Select("Tracks", new Statement("DISTINCT AlbumTitle")) + + new Where(new Compare("Artist", Op.EqualTo, artist_selected)) + + new OrderBy("AlbumTitle", OrderDirection.Asc); + } + + try + { + IDataReader reader = Core.Library.Db.Query(query); + while(reader.Read()) + { + albumlist.Add((reader[0] as string)); + } + + } catch(Exception) { + + } + + AddEntry("All", true, album_store); + foreach(string entry in albumlist) { + AddEntry(entry, false, album_store); + } + if (isUpdate) { + UpdateAlbumList(); + } + else { + ResetAlbumList(); + } + + } + + private void UpdateAlbumList() + { + int [] UpdateSelectPath = {album_selection_index }; + album_tree.Selection.SelectPath(new TreePath(UpdateSelectPath)); + } + + private void UpdateArtistList() + { + int [] UpdateSelectPath = {artist_selection_index }; + artist_tree.Selection.SelectPath(new TreePath(UpdateSelectPath)); + } + + private void ResetAlbumList() + { + album_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + TreePath[] treeIndex = album_tree.Selection.GetSelectedRows(); + album_selection_index = treeIndex[0].Indices[0]; + } + + private void ResetArtistList() + { + artist_tree.Selection.SelectPath(new TreePath(ResetSelectPath)); + TreePath[] treeIndex = artist_tree.Selection.GetSelectedRows(); + artist_selection_index = treeIndex[0].Indices[0]; + } + + private void AddEntry(string entry, bool prepend,ListStore store) + { + TreeIter iter = prepend ? store.Insert(0) : store.Append(); + store.SetValue(iter, 0, entry); + } + + private void OnArtistCursorChanged(object o, EventArgs args) + { + TreeIter iter; + if(!artist_tree.Selection.GetSelected(out iter)) { + return; + } + + object artist_name = artist_store.GetValue(iter, 0); + artist_selected = (artist_name as string); + + TreePath[] treeIndex = artist_tree.Selection.GetSelectedRows(); + artist_selection_index = treeIndex[0].Indices[0]; + + if(artist_name == null) { + return; + } + + PopulateAlbumStore(false); + RunSearch(); + } + + private void OnAlbumCursorChanged(object o, EventArgs args) + { + TreeIter iter; + if(!album_tree.Selection.GetSelected(out iter)) { + return; + } + + object album_name = album_store.GetValue(iter, 0); + album_selected = (album_name as string); + + TreePath[] treeIndex = album_tree.Selection.GetSelectedRows(); + album_selection_index = treeIndex[0].Indices[0]; + + if(album_name == null) { + return; + } + + RunSearch(); + } + + private void RunSearch() + { + BrowserSearchEventHandler handler = BrowserClicked; + BrowserEventInfo bei_artist = new BrowserEventInfo(artist_selected, artist_selection_index); + BrowserEventInfo bei_album = new BrowserEventInfo(album_selected, album_selection_index); + BrowserSearchEventArgs browserArgs = new BrowserSearchEventArgs(bei_artist, bei_album); + if(handler != null) + { + handler(this, browserArgs); + } + } + + } +} --- src/BrowserParent.cs 2005-12-25 03:13:13.000000000 +0200 +++ src/BrowserParent.cs 2005-12-25 02:34:44.000000000 +0200 @@ -0,0 +1,66 @@ +// created on 11/19/2005 at 1:26 AM by Aydemir Ulas Sahin + +using System; +using GLib; +using Gtk; +using Mono.Unix; +using Sql; + +namespace Banshee +{ + public class BrowserParent : VBox + { + private ArtistAlbumBrowser browser; + + public event BrowserSearchEventHandler BrowserSearchActivated; + + public BrowserParent() : base() + { + BuildUI(); + } + + private void BuildUI() + { + browser = new ArtistAlbumBrowser(); + browser.HeightRequest = 150; + browser.BrowserClicked += OnBrowserClicked; + + PackStart(browser,true,true,0); + + ShowAll(); + } + + private void OnBrowserClicked(object o, BrowserSearchEventArgs args) + { + BrowserSearchEventHandler handler = BrowserSearchActivated; + //BrowserSearchEventArgs browser_args = new BrowserSearchEventArgs(); + if(handler != null) + handler(this, args); + } + + public void BrowserReset() + { + browser.Reset(); + } + + public void BrowserUpdate() + { + browser.Update(false); + } + + public void BrowserFilter() + { + browser.Update(true); + } + + public bool BrowserHasFilter() + { + return browser.FilterStatus(); + } + + public void Active(bool status) + { + this.Sensitive = status; + } + } +}