[banshee] Play queue enhancements and fixes (bgo#565767, bgo#553399, bgo#553684)
- From: Alexander Kojevnikov <alexk src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [banshee] Play queue enhancements and fixes (bgo#565767, bgo#553399, bgo#553684)
- Date: Thu, 10 Sep 2009 02:12:03 +0000 (UTC)
commit e5fe5800dbf5ae7eda3ba4e2616d92b8387aae19
Author: Alexander Kojevnikov <alexander kojevnikov com>
Date: Thu Sep 10 11:40:12 2009 +1000
Play queue enhancements and fixes (bgo#565767, bgo#553399, bgo#553684)
New features:
* Songs are never removed from the play queue.
* Option to control the number of played songs to show.
* Option to randomly populate the play queue from a playlist
using any shuffle mode.
* Option to control the number of upcoming songs.
* Played tracks are rendered with a different opacity.
* Manually added songs are always inserted before the randomly added ones.
src/Clients/Nereid/Nereid/PlayerInterface.cs | 10 +
src/Clients/Nereid/Nereid/ViewContainer.cs | 23 +-
.../Banshee.Core/Banshee.Collection/TrackInfo.cs | 6 +
.../Banshee.Configuration/SchemaEntry.cs | 20 +-
.../DatabaseTrackListModel.cs | 2 +-
.../Banshee.Database/BansheeDbFormatMigrator.cs | 18 +-
.../Banshee.Playlist/PlaylistSource.cs | 2 +-
.../Banshee.Preferences/SchemaPreference.cs | 12 +-
.../Banshee.Collection.Gui/BaseTrackListView.cs | 2 +-
.../ColumnCellCreativeCommons.cs | 4 +-
.../ColumnCellStatusIndicator.cs | 4 +-
.../Banshee.Collection.Gui/ColumnCellTrack.cs | 2 +-
.../Banshee.Preferences.Gui/WidgetFactory.cs | 32 +
.../Banshee.Sources.Gui/SourceRowRenderer.cs | 12 +
.../Banshee.PlayQueue/Banshee.PlayQueue.addin.xml | 2 +-
.../Banshee.PlayQueue/Banshee.PlayQueue.csproj | 3 +
.../Banshee.PlayQueue/HeaderWidget.cs | 128 ++++
.../Banshee.PlayQueue/PlayQueueActions.cs | 16 +-
.../Banshee.PlayQueue/PlayQueueSource.cs | 633 +++++++++++++++++---
.../Banshee.PlayQueue/PlayQueueTrackListModel.cs | 67 ++
.../Banshee.PlayQueue/QueueableSourceComboBox.cs | 106 ++++
src/Extensions/Banshee.PlayQueue/Makefile.am | 5 +-
.../Banshee.PlayQueue/Resources/ActiveSourceUI.xml | 1 +
.../Banshee.PlayQueue/Resources/GlobalUI.xml | 2 +
.../Banshee.Podcasting.Data/PodcastSource.cs | 10 +-
.../Banshee.Podcasting.Data/PodcastTrackInfo.cs | 1 +
.../ColumnCellPodcastStatusIndicator.cs | 2 +-
.../Banshee.Podcasting.Gui/PodcastItemView.cs | 21 -
.../PodcastSourceContents.cs | 4 +-
.../Hyena.Gui/Hyena.Data.Gui/CellContext.cs | 10 +-
.../Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs | 8 +-
.../Hyena.Data.Gui/ListView/ListView_Model.cs | 59 +-
.../Hyena.Data.Gui/ListView/ListView_Rendering.cs | 12 +-
src/Libraries/Hyena/Hyena.Collections/Selection.cs | 13 +
34 files changed, 1075 insertions(+), 177 deletions(-)
---
diff --git a/src/Clients/Nereid/Nereid/PlayerInterface.cs b/src/Clients/Nereid/Nereid/PlayerInterface.cs
index 1b95574..73b8216 100644
--- a/src/Clients/Nereid/Nereid/PlayerInterface.cs
+++ b/src/Clients/Nereid/Nereid/PlayerInterface.cs
@@ -346,6 +346,7 @@ namespace Nereid
ISourceContents contents = source.GetProperty<ISourceContents> ("Nereid.SourceContents",
source.GetInheritedProperty<bool> ("Nereid.SourceContentsPropagate"));
+ view_container.ClearHeaderWidget ();
view_container.ClearFooter ();
if (contents != null) {
@@ -379,6 +380,15 @@ namespace Nereid
view_container.Header.Visible = source.Properties.Contains ("Nereid.SourceContents.HeaderVisible") ?
source.Properties.Get<bool> ("Nereid.SourceContents.HeaderVisible") : true;
+ Widget header_widget = null;
+ if (source.Properties.Contains ("Nereid.SourceContents.HeaderWidget")) {
+ header_widget = source.Properties.Get<Widget> ("Nereid.SourceContents.HeaderWidget");
+ }
+
+ if (header_widget != null) {
+ view_container.SetHeaderWidget (header_widget);
+ }
+
Widget footer_widget = null;
if (source.Properties.Contains ("Nereid.SourceContents.FooterWidget")) {
footer_widget = source.Properties.Get<Widget> ("Nereid.SourceContents.FooterWidget");
diff --git a/src/Clients/Nereid/Nereid/ViewContainer.cs b/src/Clients/Nereid/Nereid/ViewContainer.cs
index ef9c479..515e96a 100644
--- a/src/Clients/Nereid/Nereid/ViewContainer.cs
+++ b/src/Clients/Nereid/Nereid/ViewContainer.cs
@@ -46,6 +46,7 @@ namespace Nereid
{
private SearchEntry search_entry;
private HBox header;
+ private EventBox header_box;
private Label title_label;
private Label search_label;
private Banshee.ContextPane.ContextPane context_pane;
@@ -83,6 +84,8 @@ namespace Nereid
ServiceManager.Get<InterfaceActionService> ().SourceActions ["SourceContextMenuAction"].Activate ();
}
};
+
+ header_box = new EventBox ();
BuildSearchEntry ();
@@ -90,6 +93,7 @@ namespace Nereid
search_label.MnemonicWidget = search_entry.InnerEntry;
header.PackStart (title_box, true, true, 0);
+ header.PackStart (header_box, false, false, 0);
header.PackStart (search_label, false, false, 5);
header.PackStart (search_entry, false, false, 0);
@@ -185,7 +189,24 @@ namespace Nereid
editable.Position = search_entry.Query.Length;
}
}
-
+
+ public void SetHeaderWidget (Widget widget)
+ {
+ if (widget != null) {
+ header_box.Add (widget);
+ widget.Show ();
+ header_box.Show ();
+ }
+ }
+
+ public void ClearHeaderWidget ()
+ {
+ header_box.Hide ();
+ if (header_box.Child != null) {
+ header_box.Remove (header_box.Child);
+ }
+ }
+
public void SetFooter (Widget contents)
{
if (contents != null) {
diff --git a/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs b/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
index 6a4c340..f225a59 100644
--- a/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
+++ b/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
@@ -477,6 +477,12 @@ namespace Banshee.Collection
get { return can_play; }
set { can_play = value; }
}
+
+ private bool enabled = true;
+ public bool Enabled {
+ get { return enabled && can_play; }
+ set { enabled = value; }
+ }
public virtual string MetadataHash {
get {
diff --git a/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs b/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
index 41fad67..704dfff 100644
--- a/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
+++ b/src/Core/Banshee.Core/Banshee.Configuration/SchemaEntry.cs
@@ -35,11 +35,19 @@ namespace Banshee.Configuration
public static SchemaEntry<T> Zero;
public SchemaEntry (string @namespace, string key, T defaultValue,
+ string shortDescription, string longDescription) :
+ this (@namespace, key, defaultValue, default(T), default(T), shortDescription, longDescription)
+ {
+ }
+
+ public SchemaEntry (string @namespace, string key, T defaultValue, T minValue, T maxValue,
string shortDescription, string longDescription)
{
Namespace = @namespace;
Key = key;
DefaultValue = defaultValue;
+ MinValue = minValue;
+ MaxValue = maxValue;
ShortDescription = shortDescription;
LongDescription = longDescription;
}
@@ -54,14 +62,24 @@ namespace Banshee.Configuration
return ConfigurationClient.Get<T> (this, fallback);
}
- public void Set (T value)
+ public bool Set (T value)
{
+ if (!Object.Equals (MinValue, default (T)) || !Object.Equals(MaxValue, default (T))) {
+ if (((IComparable<T>)MinValue).CompareTo (value) > 0 ||
+ ((IComparable<T>)MaxValue).CompareTo (value) < 0) {
+ return false;
+ }
+ }
+
ConfigurationClient.Set<T> (this, value);
+ return true;
}
public readonly string Namespace;
public readonly string Key;
public readonly T DefaultValue;
+ public readonly T MinValue;
+ public readonly T MaxValue;
public readonly string ShortDescription;
public readonly string LongDescription;
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
index 54b239e..384edaf 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
@@ -187,7 +187,7 @@ namespace Banshee.Collection.Database
}
private string unfiltered_query;
- public string UnfilteredQuery {
+ public virtual string UnfilteredQuery {
get {
return unfiltered_query ?? (unfiltered_query = String.Format (
"FROM {0} WHERE {1} {2}",
diff --git a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
index 51970ea..780f4b6 100644
--- a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
+++ b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
@@ -56,7 +56,7 @@ namespace Banshee.Database
// NOTE: Whenever there is a change in ANY of the database schema,
// this version MUST be incremented and a migration method
// MUST be supplied to match the new version number
- protected const int CURRENT_VERSION = 34;
+ protected const int CURRENT_VERSION = 35;
protected const int CURRENT_METADATA_VERSION = 6;
#region Migration Driver
@@ -789,6 +789,19 @@ namespace Banshee.Database
#endregion
+#region Version 35
+
+ [DatabaseVersion (35)]
+ private bool Migrate_35 ()
+ {
+ if (!connection.ColumnExists ("CorePlaylistEntries", "Generated")) {
+ Execute ("ALTER TABLE CorePlaylistEntries ADD COLUMN Generated INTEGER NOT NULL DEFAULT 0");
+ }
+ return true;
+ }
+
+#endregion
+
#pragma warning restore 0169
#region Fresh database setup
@@ -950,7 +963,8 @@ namespace Banshee.Database
EntryID INTEGER PRIMARY KEY,
PlaylistID INTEGER NOT NULL,
TrackID INTEGER NOT NULL,
- ViewOrder INTEGER NOT NULL DEFAULT 0
+ ViewOrder INTEGER NOT NULL DEFAULT 0,
+ Generated INTEGER NOT NULL DEFAULT 0
)
");
Execute("CREATE INDEX CorePlaylistEntriesIndex ON CorePlaylistEntries(PlaylistID, TrackID)");
diff --git a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
index 97b3902..ee97408 100644
--- a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
+++ b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
@@ -258,7 +258,7 @@ namespace Banshee.Playlist
return false;
}
- public void ReorderSelectedTracks (int drop_row)
+ public virtual void ReorderSelectedTracks (int drop_row)
{
if (TrackModel.Selection.Count == 0 || TrackModel.Selection.AllSelected) {
return;
diff --git a/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs b/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
index defff60..416510f 100644
--- a/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
+++ b/src/Core/Banshee.Services/Banshee.Preferences/SchemaPreference.cs
@@ -58,12 +58,22 @@ namespace Banshee.Preferences
public override T Value {
get { return schema.Get (); }
set {
- schema.Set (value);
+ if (!schema.Set (value)) {
+ return;
+ }
if (handler != null) {
handler ();
}
OnValueChanged ();
}
}
+
+ public T MinValue {
+ get { return schema.MinValue; }
+ }
+
+ public T MaxValue {
+ get { return schema.MaxValue; }
+ }
}
}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
index 0d21bc4..fb52f7e 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/BaseTrackListView.cs
@@ -49,7 +49,7 @@ namespace Banshee.Collection.Gui
public BaseTrackListView () : base ()
{
RulesHint = true;
- RowSensitivePropertyName = "CanPlay";
+ RowOpaquePropertyName = "Enabled";
RowBoldPropertyName = "IsPlaying";
ServiceManager.PlayerEngine.ConnectEvent (
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
index 6912b74..170e473 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellCreativeCommons.cs
@@ -86,14 +86,14 @@ namespace Banshee.Collection.Gui
Cairo.Rectangle pixbuf_area = new Cairo.Rectangle (draw_x,
(cellHeight - render_pixbuf.Height) / 2, render_pixbuf.Width, render_pixbuf.Height);
- if (!context.Sensitive) {
+ if (!context.Opaque) {
context.Context.Save ();
}
Gdk.CairoHelper.SetSourcePixbuf (context.Context, render_pixbuf, pixbuf_area.X, pixbuf_area.Y);
context.Context.Rectangle (pixbuf_area);
- if (!context.Sensitive) {
+ if (!context.Opaque) {
context.Context.Clip ();
context.Context.PaintWithAlpha (0.5);
context.Context.Restore ();
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
index 06d82d9..134c1f1 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellStatusIndicator.cs
@@ -142,14 +142,14 @@ namespace Banshee.Collection.Gui
Cairo.Rectangle pixbuf_area = new Cairo.Rectangle ((cellWidth - render_pixbuf.Width) / 2,
(cellHeight - render_pixbuf.Height) / 2, render_pixbuf.Width, render_pixbuf.Height);
- if (!context.Sensitive) {
+ if (!context.Opaque) {
context.Context.Save ();
}
Gdk.CairoHelper.SetSourcePixbuf (context.Context, render_pixbuf, pixbuf_area.X, pixbuf_area.Y);
context.Context.Rectangle (pixbuf_area);
- if (!context.Sensitive) {
+ if (!context.Opaque) {
context.Context.Clip ();
context.Context.PaintWithAlpha (0.5);
context.Context.Restore ();
diff --git a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
index 226f55f..31783cf 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Collection.Gui/ColumnCellTrack.cs
@@ -81,7 +81,7 @@ namespace Banshee.Collection.Gui
context.Context.MoveTo (4, ((int)cellHeight - text_height) / 2);
Cairo.Color color = context.Theme.Colors.GetWidgetColor (
context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
- color.A = (!context.Sensitive) ? 0.3 : 1.0;
+ color.A = (!context.Opaque) ? 0.3 : 1.0;
context.Context.Color = color;
PangoCairoHelper.ShowLayout (context.Context, context.Layout);
diff --git a/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs b/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
index 969fcda..bec410d 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Preferences.Gui/WidgetFactory.cs
@@ -56,6 +56,13 @@ namespace Banshee.Preferences.Gui
pref_widget = new PreferenceCheckButton (preference);
} else if (type == typeof (string)) {
pref_widget = new PreferenceEntry (preference);
+ } else if (type == typeof (int)) {
+ var schema_preference = preference as SchemaPreference<int>;
+ if (schema_preference == null) {
+ pref_widget = new PreferenceSpinButton (preference);
+ } else {
+ pref_widget = new PreferenceSpinButton (preference, schema_preference.MinValue, schema_preference.MaxValue);
+ }
}
if (pref_widget != null) {
@@ -146,5 +153,30 @@ namespace Banshee.Preferences.Gui
}
}
}
+
+ private class PreferenceSpinButton : HBox
+ {
+ private bool sync;
+ private PreferenceBase preference;
+
+ public PreferenceSpinButton (PreferenceBase preference) : this (preference, 0, 100)
+ {
+ }
+
+ public PreferenceSpinButton (PreferenceBase preference, int min_value, int max_value)
+ {
+ var spin_button = new SpinButton (min_value, max_value, 1);
+ spin_button.Changed += delegate {
+ if (sync) {
+ this.preference.BoxedValue = (int)spin_button.Value;
+ }
+ };
+ spin_button.Show ();
+ PackEnd (spin_button, false, false, 0);
+ this.preference = preference;
+ spin_button.Value = (int)preference.BoxedValue;
+ sync = true;
+ }
+ }
}
}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
index 51e62ca..f53d07e 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Sources.Gui/SourceRowRenderer.cs
@@ -146,6 +146,18 @@ namespace Banshee.Sources.Gui
bool hide_counts = source.Count <= 0;
Pixbuf icon = SourceIconResolver.ResolveIcon (source);
+
+ if (state == StateType.Insensitive) {
+ // Code ported from gtk_cell_renderer_pixbuf_render()
+ var icon_source = new IconSource () {
+ Pixbuf = icon,
+ Size = IconSize.SmallToolbar,
+ SizeWildcarded = false
+ };
+
+ icon = widget.Style.RenderIcon (icon_source, widget.Direction, state,
+ (IconSize)(-1), widget, "SourceRowRenderer");
+ }
FontDescription fd = widget.PangoContext.FontDescription.Copy ();
fd.Weight = (ISource)ServiceManager.PlaybackController.NextSource == (ISource)source
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
index 014a6cb..05ddffb 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.addin.xml
@@ -7,7 +7,7 @@
name="Play Queue"
category="User Interface"
description="Provides a source that acts as a queue of playing tracks. When the queue is populated, all playback happens from the queue in a forced order."
- author="Aaron Bockover"
+ author="Aaron Bockover, Alexander Kojevnikov"
url="http://banshee-project.org/"
defaultEnabled="true">
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
index 5fe9ce6..d3cb3dd 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
@@ -72,6 +72,9 @@
<Compile Include="Banshee.PlayQueue\PlayQueueSource.cs" />
<Compile Include="Banshee.PlayQueue\PlayQueueActions.cs" />
<Compile Include="Banshee.PlayQueue\IPlayQueue.cs" />
+ <Compile Include="Banshee.PlayQueue\PlayQueueTrackListModel.cs" />
+ <Compile Include="Banshee.PlayQueue\QueueableSourceComboBox.cs" />
+ <Compile Include="Banshee.PlayQueue\HeaderWidget.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\ActiveSourceUI.xml">
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
new file mode 100644
index 0000000..96e6e63
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
@@ -0,0 +1,128 @@
+//
+// HeaderWidget.cs
+//
+// Author:
+// Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// 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 Gtk;
+using Mono.Unix;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Banshee.PlaybackController;
+using Banshee.Sources;
+
+namespace Banshee.PlayQueue
+{
+ public class HeaderWidget : HBox
+ {
+ public event EventHandler<ModeChangedEventArgs> ModeChanged;
+ public event EventHandler<SourceChangedEventArgs> SourceChanged;
+
+ private readonly List<Widget> sensitive_widgets = new List<Widget> ();
+ private readonly Dictionary<string, PlaybackShuffleMode> modes = new Dictionary<string, PlaybackShuffleMode> {
+ { Catalog.GetString ("manually"), PlaybackShuffleMode.Linear },
+ { Catalog.GetString ("by song"), PlaybackShuffleMode.Song },
+ { Catalog.GetString ("by album"), PlaybackShuffleMode.Album },
+ { Catalog.GetString ("by artist"), PlaybackShuffleMode.Artist },
+ { Catalog.GetString ("by rating"), PlaybackShuffleMode.Rating },
+ { Catalog.GetString ("by score"), PlaybackShuffleMode.Score },
+ };
+
+ public HeaderWidget (PlaybackShuffleMode mode, string source_name) : base ()
+ {
+ this.Spacing = 6;
+
+ var fill_label = new Label (Catalog.GetString ("_Fill"));
+ var mode_combo = new ComboBox (modes.Keys.OrderBy (s => modes[s]).ToArray ());
+ fill_label.MnemonicWidget = mode_combo;
+ mode_combo.Changed += delegate {
+ var value = modes[mode_combo.ActiveText];
+ foreach (var widget in sensitive_widgets) {
+ widget.Sensitive = value != PlaybackShuffleMode.Linear;
+ }
+ var handler = ModeChanged;
+ if (handler != null) {
+ handler (this, new ModeChangedEventArgs (value));
+ }
+ };
+
+ var from_label = new Label (Catalog.GetString ("f_rom"));
+ sensitive_widgets.Add (from_label);
+ var source_combo_box = new QueueableSourceComboBox (source_name);
+ from_label.MnemonicWidget = source_combo_box;
+ sensitive_widgets.Add (source_combo_box);
+ source_combo_box.Changed += delegate {
+ var handler = SourceChanged;
+ if (handler != null) {
+ handler (this, new SourceChangedEventArgs (source_combo_box.Source));
+ }
+ };
+
+ PackStart (fill_label, false, false, 0);
+ PackStart (mode_combo, false, false, 0);
+ PackStart (from_label, false, false, 0);
+ PackStart (source_combo_box, false, false, 0);
+
+ // Select the population mode.
+ mode_combo.Model.Foreach (delegate (TreeModel model, TreePath path, TreeIter iter) {
+ if (modes[(string)model.GetValue (iter, 0)] == mode) {
+ mode_combo.SetActiveIter (iter);
+ return true;
+ }
+ return false;
+ });
+ }
+ }
+
+ public sealed class ModeChangedEventArgs : EventArgs
+ {
+ private PlaybackShuffleMode value;
+
+ public ModeChangedEventArgs (PlaybackShuffleMode value)
+ {
+ this.value = value;
+ }
+
+ public PlaybackShuffleMode Value {
+ get { return this.value; }
+ }
+ }
+
+ public sealed class SourceChangedEventArgs : EventArgs
+ {
+ private ITrackModelSource value;
+
+ public SourceChangedEventArgs (ITrackModelSource value)
+ {
+ this.value = value;
+ }
+
+ public ITrackModelSource Value {
+ get { return this.value; }
+ }
+ }
+}
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
index 0b655c1..8c6318d 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueActions.cs
@@ -52,6 +52,13 @@ namespace Banshee.PlayQueue
});
AddImportant (
+ new ActionEntry ("RefreshPlayQueueAction", Stock.Refresh,
+ Catalog.GetString ("Refresh"), null,
+ Catalog.GetString ("Refresh random tracks in the play queue"),
+ OnRefreshPlayQueue)
+ );
+
+ AddImportant (
new ActionEntry ("ClearPlayQueueAction", Stock.Clear,
Catalog.GetString ("Clear"), null,
Catalog.GetString ("Remove all tracks from the play queue"),
@@ -94,6 +101,11 @@ namespace Banshee.PlayQueue
playqueue.Clear ();
}
+ private void OnRefreshPlayQueue (object o, EventArgs args)
+ {
+ playqueue.Refresh ();
+ }
+
private void OnClearPlayQueueOnQuit (object o, EventArgs args)
{
ToggleAction action = this["ClearPlayQueueOnQuitAction"] as Gtk.ToggleAction;
@@ -117,7 +129,9 @@ namespace Banshee.PlayQueue
Source source = ServiceManager.SourceManager.ActiveSource;
if (source != null) {
DatabaseSource db_source = source as DatabaseSource ?? source.Parent as DatabaseSource;
- UpdateAction ("ClearPlayQueueAction", true, playqueue.Count > 0);
+ UpdateAction ("RefreshPlayQueueAction", playqueue.Populate);
+ UpdateAction ("ClearPlayQueueAction", !playqueue.Populate, playqueue.Count > 0);
+ UpdateAction ("ClearPlayQueueOnQuitAction", !playqueue.Populate);
UpdateAction ("AddToPlayQueueAction", db_source != null && db_source != playqueue, true);
}
}
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
index 76957b7..217a9b0 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
@@ -1,10 +1,12 @@
//
// PlayQueueSource.cs
//
-// Author:
+// Authors:
// Aaron Bockover <abockover novell com>
+// Alexander Kojevnikov <alexander kojevnikov com>
//
// Copyright (C) 2008 Novell, Inc.
+// Copyright (C) 2009 Alexander Kojevnikov
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -27,38 +29,50 @@
//
using System;
+using System.Collections.Generic;
+using System.Linq;
using Mono.Unix;
+using Hyena.Collections;
using Hyena.Data.Sqlite;
-using Banshee.ServiceStack;
-using Banshee.Sources;
-using Banshee.Playlist;
-using Banshee.Library;
-using Banshee.Database;
using Banshee.Collection;
using Banshee.Collection.Database;
-using Banshee.PlaybackController;
-using Banshee.MediaEngine;
using Banshee.Configuration;
+using Banshee.Database;
using Banshee.Gui;
+using Banshee.Library;
+using Banshee.MediaEngine;
+using Banshee.PlaybackController;
+using Banshee.Playlist;
+using Banshee.Preferences;
+using Banshee.ServiceStack;
+using Banshee.Sources;
namespace Banshee.PlayQueue
{
public class PlayQueueSource : PlaylistSource, IBasicPlaybackController, IPlayQueue, IDBusExportable, IDisposable
{
- private static string special_playlist_name = "Play Queue";//typeof (PlayQueueSource).ToString ();
+ private static string special_playlist_name = "Play Queue";
private ITrackModelSource prior_playback_source;
- private DatabaseTrackInfo playing_track;
+ private DatabaseTrackInfo current_track;
+ private long offset;
private TrackInfo prior_playback_track;
private PlayQueueActions actions;
private bool was_playing = false;
-
- protected override bool HasArtistAlbum {
- get { return false; }
- }
+ protected DateTime source_set_at = DateTime.MinValue;
+ private HeaderWidget header_widget;
+
+ private SourcePage pref_page;
+ private Section pref_section;
+
+ private PlaybackShuffleMode populate_mode = (PlaybackShuffleMode) PopulateModeSchema.Get ();
+ private string populate_from_name = PopulateFromSchema.Get ();
+ private ITrackModelSource populate_from = null;
+ private int played_songs_number = PlayedSongsNumberSchema.Get ();
+ private int upcoming_songs_number = UpcomingSongsNumberSchema.Get ();
public PlayQueueSource () : base (Catalog.GetString ("Play Queue"), null)
{
@@ -70,12 +84,12 @@ namespace Banshee.PlayQueue
Order = 20;
Properties.SetString ("Icon.Name", "source-playlist");
Properties.SetString ("RemoveTracksActionLabel", Catalog.GetString ("Remove From Play Queue"));
-
+
DatabaseTrackModel.ForcedSortQuery = "CorePlaylistEntries.ViewOrder ASC, CorePlaylistEntries.EntryID ASC";
DatabaseTrackModel.CanReorder = true;
-
+
ServiceManager.PlayerEngine.ConnectEvent (OnPlayerEvent);
- ServiceManager.PlaybackController.Transition += OnCanonicalPlaybackControllerTransition;
+ ServiceManager.PlaybackController.TrackStarted += OnTrackStarted;
ServiceManager.SourceManager.AddSource (this);
@@ -92,18 +106,43 @@ namespace Banshee.PlayQueue
ServiceManager.SourceManager.VideoLibrary.TracksChanged += HandleTracksChanged;
ServiceManager.SourceManager.VideoLibrary.TracksDeleted += HandleTracksDeleted;
- TrackModel.Reloaded += delegate {
- if (Count == 0) {
- if (this == ServiceManager.PlaybackController.Source || this == ServiceManager.PlaybackController.NextSource) {
- ServiceManager.PlaybackController.NextSource = ServiceManager.PlaybackController.Source = PriorSource;
- }
- }
+ populate_from = ServiceManager.SourceManager.Sources.FirstOrDefault (
+ source => source.Name == populate_from_name) as ITrackModelSource;
+ if (populate_from != null) {
+ populate_from.Reload ();
+ }
+
+ TrackModel.Reloaded += HandleReloaded;
+
+ Offset = CurrentOffsetSchema.Get ();
+ }
+
+ protected override void Initialize ()
+ {
+ base.Initialize ();
+
+ InstallPreferences ();
+
+ header_widget = new HeaderWidget (populate_mode, populate_from_name);
+ header_widget.ModeChanged += delegate(object sender, ModeChangedEventArgs e) {
+ populate_mode = e.Value;
+ PopulateModeSchema.Set ((int) e.Value);
+ UpdatePlayQueue ();
+ OnUpdated ();
};
-
- Reload ();
- SetAsPlaybackSourceUnlessPlaying ();
+ header_widget.SourceChanged += delegate(object sender, SourceChangedEventArgs e) {
+ populate_from = e.Value;
+ populate_from_name = e.Value.Name;
+ PopulateFromSchema.Set (e.Value.Name);
+ source_set_at = DateTime.Now;
+ populate_from.Reload ();
+ Refresh ();
+ };
+ header_widget.ShowAll ();
+
+ Properties.Set<Gtk.Widget> ("Nereid.SourceContents.HeaderWidget", header_widget);
}
-
+
#region IPlayQueue, IDBusExportable
public void EnqueueUri (string uri)
@@ -113,33 +152,66 @@ namespace Banshee.PlayQueue
public void EnqueueUri (string uri, bool prepend)
{
- EnqueueId (DatabaseTrackInfo.GetTrackIdForUri (uri), prepend);
+ EnqueueId (DatabaseTrackInfo.GetTrackIdForUri (uri), prepend, false);
}
public void EnqueueTrack (TrackInfo track, bool prepend)
{
DatabaseTrackInfo db_track = track as DatabaseTrackInfo;
if (db_track != null) {
- EnqueueId (db_track.TrackId, prepend);
+ EnqueueId (db_track.TrackId, prepend, false);
} else {
EnqueueUri (track.Uri.AbsoluteUri, prepend);
}
}
- private void EnqueueId (int trackId, bool prepend)
+ private void EnqueueId (int trackId, bool prepend, bool generated)
{
if (trackId <= 0) {
return;
}
-
- if (prepend) {
- ServiceManager.DbConnection.Execute ("UPDATE CorePlaylistEntries SET ViewOrder = ViewOrder + 1 WHERE PlaylistID = ?", DbId);
+
+ long view_order;
+ if (prepend && current_track != null) {
+ // We are going to prepend the track to the play queue, which means
+ // adding it after the current_track. Now find the corresponding view_order.
+ view_order = ServiceManager.DbConnection.Query<long> (@"
+ SELECT ViewOrder + 1
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND EntryID = ?",
+ DbId, Convert.ToInt64 (current_track.CacheEntryId)
+ );
+ } else {
+ if (generated) {
+ // view_order will point after the last track in the queue.
+ view_order = MaxViewOrder;
+ }
+ else {
+ // view_order will point after the last non-generated track in the queue.
+ view_order = ServiceManager.DbConnection.Query<long> (@"
+ SELECT MAX(ViewOrder) + 1
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND Generated = 0",
+ DbId
+ );
+ }
}
- HyenaSqliteCommand insert_command = new HyenaSqliteCommand (String.Format (
- @"INSERT INTO CorePlaylistEntries (PlaylistID, TrackID, ViewOrder) VALUES ({0}, ?, {1})",
- DbId, prepend ? 0 : MaxViewOrder));
- ServiceManager.DbConnection.Execute (insert_command, trackId);
+ // Increment the order of all tracks after view_order
+ ServiceManager.DbConnection.Execute (@"
+ UPDATE CorePlaylistEntries
+ SET ViewOrder = ViewOrder + 1
+ WHERE PlaylistID = ? AND ViewOrder >= ?",
+ DbId, view_order
+ );
+
+ // Add the track to the queue using the view order calculated above.
+ ServiceManager.DbConnection.Execute (@"
+ INSERT INTO CorePlaylistEntries
+ (PlaylistID, TrackID, ViewOrder, Generated)
+ VALUES (?, ?, ?, ?)",
+ DbId, trackId, view_order, generated ? 1 : 0
+ );
OnTracksAdded ();
NotifyUser ();
@@ -154,10 +226,74 @@ namespace Banshee.PlayQueue
}
#endregion
-
+
+ public override bool AddSelectedTracks (Source source)
+ {
+ if ((Parent == null || source == Parent || source.Parent == Parent) && AcceptsInputFromSource (source)) {
+ DatabaseTrackListModel model = (source as ITrackModelSource).TrackModel as DatabaseTrackListModel;
+ if (model == null) {
+ return false;
+ }
+
+ // Get the ViewOrder of the current_track
+ long current_view_order = current_track == null ?
+ ServiceManager.DbConnection.Query<long> (@"
+ SELECT MAX(ViewOrder) + 1
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ?",
+ DbId
+ ) :
+ ServiceManager.DbConnection.Query<long> (@"
+ SELECT ViewOrder
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND EntryID = ?",
+ DbId, Convert.ToInt64 (current_track.CacheEntryId)
+ );
+
+ // If the current_track is not playing, insert before it.
+ int index = -1;
+ if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+ current_view_order--;
+ index = TrackModel.IndexOf (current_track);
+ }
+
+ // view_order will point to the last pending non-generated track in the queue
+ // or to the current_track if all tracks are generated. We want to insert tracks after it.
+ long view_order = Math.Max(current_view_order, ServiceManager.DbConnection.Query<long> (@"
+ SELECT MAX(ViewOrder)
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND ViewOrder > ? AND Generated = 0",
+ DbId, current_view_order
+ ));
+
+ // Add the tracks to the end of the queue.
+ WithTrackSelection (model, AddTrackRange);
+
+ // Shift generated tracks to the end of the queue.
+ ServiceManager.DbConnection.Execute (@"
+ UPDATE CorePlaylistEntries
+ SET ViewOrder = ViewOrder - ? + ?
+ WHERE PlaylistID = ? AND ViewOrder > ? AND Generated = 1",
+ view_order, MaxViewOrder, DbId, view_order
+ );
+
+ OnTracksAdded ();
+ OnUserNotifyUpdated ();
+
+ // If the current_track was not playing, and there were no non-generated tracks,
+ // mark the first added track as current.
+ if (view_order == current_view_order && current_track != null &&
+ !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+ SetCurrentTrack (TrackModel[index] as DatabaseTrackInfo);
+ }
+ return true;
+ }
+ return false;
+ }
+
private void SetAsPlaybackSourceUnlessPlaying ()
{
- if (Count > 0 && ServiceManager.PlaybackController.Source != this) {
+ if (current_track != null && ServiceManager.PlaybackController.Source != this) {
PriorSource = ServiceManager.PlaybackController.Source;
ServiceManager.PlaybackController.NextSource = this;
}
@@ -165,20 +301,42 @@ namespace Banshee.PlayQueue
public void Clear ()
{
- playing_track = null;
- RemoveTrackRange (DatabaseTrackModel, new Hyena.Collections.RangeCollection.Range (0, Count));
+ ServiceManager.DbConnection.Execute (@"
+ DELETE FROM CorePlaylistEntries
+ WHERE PlaylistID = ?", DbId
+ );
+ offset = 0;
+ SetCurrentTrack (null);
+
+ if (this == ServiceManager.PlaybackController.Source && ServiceManager.PlayerEngine.IsPlaying ()) {
+ ServiceManager.PlayerEngine.Close();
+ }
+
Reload ();
}
public void Dispose ()
{
+ int track_index = current_track == null ? Count : Math.Max (0, TrackModel.IndexOf (current_track));
+ CurrentTrackSchema.Set (track_index);
+
ServiceManager.PlayerEngine.DisconnectEvent (OnPlayerEvent);
+ ServiceManager.PlaybackController.TrackStarted -= OnTrackStarted;
if (actions != null) {
actions.Dispose ();
}
- if (ClearOnQuitSchema.Get ()) {
+ UninstallPreferences ();
+
+ Properties.Remove ("Nereid.SourceContents.HeaderWidget");
+
+ if (header_widget != null) {
+ header_widget.Destroy ();
+ header_widget = null;
+ }
+
+ if (!Populate && ClearOnQuitSchema.Get ()) {
Clear ();
}
}
@@ -201,22 +359,84 @@ namespace Banshee.PlayQueue
protected override void OnTracksAdded ()
{
+ int old_count = Count;
+
base.OnTracksAdded ();
+
+ if (current_track == null && old_count < Count) {
+ SetCurrentTrack (TrackModel[old_count] as DatabaseTrackInfo);
+ }
+
SetAsPlaybackSourceUnlessPlaying ();
}
-
- private void OnCanonicalPlaybackControllerTransition (object o, EventArgs args)
+
+ protected override void OnTracksRemoved ()
{
- if (Count > 0) {
- PriorSource = ServiceManager.PlaybackController.Source;
- ServiceManager.PlaybackController.Source = this;
+ base.OnTracksRemoved ();
+
+ if (this == ServiceManager.PlaybackController.Source &&
+ ServiceManager.PlayerEngine.IsPlaying () &&
+ TrackModel.IndexOf (ServiceManager.PlayerEngine.CurrentTrack) == -1) {
+ if (ServiceManager.PlayerEngine.CurrentState == PlayerState.Paused || current_track == null) {
+ ServiceManager.PlayerEngine.Close();
+ } else {
+ ServiceManager.PlayerEngine.OpenPlay (current_track);
+ }
}
+ UpdatePlayQueue ();
}
-
+
+ protected override void RemoveTrackRange (DatabaseTrackListModel model, RangeCollection.Range range)
+ {
+ base.RemoveTrackRange (model, range);
+
+ model.Selection.UnselectRange (range.Start, range.End);
+
+ int index = TrackModel.IndexOf (current_track);
+ if (range.Start <= index && index <= range.End) {
+ SetCurrentTrack (range.End + 1 < Count ? TrackModel[range.End + 1] as DatabaseTrackInfo : null);
+ }
+ }
+
+ private void HandleReloaded(object sender, EventArgs e)
+ {
+ int track_index = CurrentTrackSchema.Get ();
+ if (track_index < Count) {
+ SetCurrentTrack (TrackModel[track_index] as DatabaseTrackInfo);
+ }
+
+ SetAsPlaybackSourceUnlessPlaying ();
+
+ TrackModel.Reloaded -= HandleReloaded;
+ }
+
+ public override void ReorderSelectedTracks (int drop_row)
+ {
+ // If the current_track is not playing, make the first pending unselected track the current one.
+ if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+ int current_index = TrackModel.IndexOf (current_track);
+ int new_index = -1;
+ for (int index = current_index; index < TrackModel.Count; index++) {
+ if (!TrackModel.Selection.Contains (index)) {
+ new_index = index;
+ break;
+ }
+ }
+ if (new_index != current_index) {
+ SetCurrentTrack (new_index == -1 ? null : TrackModel[new_index] as DatabaseTrackInfo);
+ }
+ }
+
+ base.ReorderSelectedTracks (drop_row);
+ }
+
private void OnPlayerEvent (PlayerEventArgs args)
{
if (args.Event == PlayerEvent.EndOfStream) {
- if (RemovePlayingTrack () && Count == 0) {
+ if (this == ServiceManager.PlaybackController.Source &&
+ TrackModel.IndexOf (current_track) == Count - 1) {
+ SetCurrentTrack (null);
+ UpdatePlayQueue ();
if (was_playing) {
ServiceManager.PlaybackController.PriorTrack = prior_playback_track;
} else {
@@ -224,15 +444,34 @@ namespace Banshee.PlayQueue
}
}
} else if (args.Event == PlayerEvent.StartOfStream) {
- if (this == ServiceManager.PlaybackController.Source) {
- playing_track = ServiceManager.PlayerEngine.CurrentTrack as DatabaseTrackInfo;
+ if (TrackModel.IndexOf (ServiceManager.PlayerEngine.CurrentTrack) != -1) {
+ SetCurrentTrack (ServiceManager.PlayerEngine.CurrentTrack as DatabaseTrackInfo);
+ SetAsPlaybackSourceUnlessPlaying ();
+ UpdatePlayQueue ();
} else {
- playing_track = null;
prior_playback_track = ServiceManager.PlayerEngine.CurrentTrack;
}
}
}
+ public override void Reload ()
+ {
+ enabled_cache.Clear ();
+ base.Reload ();
+
+ if (current_track == null) {
+ if (this == ServiceManager.PlaybackController.Source ||
+ this == ServiceManager.PlaybackController.NextSource) {
+ ServiceManager.PlaybackController.NextSource = PriorSource;
+ }
+ }
+ }
+
+ protected override DatabaseTrackListModel CreateTrackModelFor (DatabaseSource src)
+ {
+ return new PlayQueueTrackListModel (ServiceManager.DbConnection, DatabaseTrackInfo.Provider, (PlayQueueSource) src);
+ }
+
bool IBasicPlaybackController.First ()
{
return ((IBasicPlaybackController)this).Next (false);
@@ -240,9 +479,12 @@ namespace Banshee.PlayQueue
bool IBasicPlaybackController.Next (bool restart)
{
- RemovePlayingTrack ();
-
- if (Count == 0) {
+ if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {
+ int index = TrackModel.IndexOf (current_track) + 1;
+ SetCurrentTrack (index < Count ? TrackModel[index] as DatabaseTrackInfo : null);
+ }
+ if (current_track == null) {
+ UpdatePlayQueue ();
ServiceManager.PlaybackController.Source = PriorSource;
if (was_playing) {
ServiceManager.PlaybackController.PriorTrack = prior_playback_track;
@@ -252,26 +494,185 @@ namespace Banshee.PlayQueue
}
return true;
}
-
- ServiceManager.PlayerEngine.OpenPlay ((DatabaseTrackInfo)TrackModel[0]);
+
+ ServiceManager.PlayerEngine.OpenPlay (current_track);
return true;
}
bool IBasicPlaybackController.Previous (bool restart)
{
+ if (current_track != null && ServiceManager.PlayerEngine.CurrentTrack == current_track) {
+ int index = TrackModel.IndexOf (current_track);
+ if (index > 0) {
+ SetCurrentTrack (TrackModel[index - 1] as DatabaseTrackInfo);
+ }
+ ServiceManager.PlayerEngine.OpenPlay (current_track);
+ }
return true;
}
- private bool RemovePlayingTrack ()
+ private void UpdatePlayQueue ()
{
- if (playing_track != null) {
- RemoveTrack (playing_track);
- playing_track = null;
- return true;
+ // Find the ViewOrder of the current_track.
+ long view_order;
+ if (current_track == null) {
+ view_order = ServiceManager.DbConnection.Query<long> (@"
+ SELECT MAX(ViewOrder) + 1
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ?", DbId
+ );
+ }
+ else {
+ view_order = ServiceManager.DbConnection.Query<long> (@"
+ SELECT ViewOrder
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND EntryID = ?",
+ DbId, Convert.ToInt64 (current_track.CacheEntryId)
+ );
+ }
+
+ // Offset the model so that no more than played_songs_number tracks are shown before the current_track.
+ Offset = played_songs_number == 0 ? view_order : ServiceManager.DbConnection.Query<long> (@"
+ SELECT MIN(ViewOrder)
+ FROM (
+ SELECT ViewOrder
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND ViewOrder < ?
+ ORDER BY ViewOrder DESC
+ LIMIT ?
+ )", DbId, view_order, played_songs_number
+ );
+
+ // Check if we need to add more tracks.
+ int tracks_to_add = upcoming_songs_number -
+ (current_track == null ? 0 : Count - TrackModel.IndexOf (current_track) - 1);
+
+ // If the current track is not playing count it as well.
+ if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+ tracks_to_add--;
+ }
+
+ if (tracks_to_add > 0 && Populate && populate_from != null) {
+ // Add songs from the selected source, skip if all tracks need to be populated.
+ bool skip = tracks_to_add == upcoming_songs_number;
+ for (int i = 0; i < tracks_to_add; i++) {
+ var track = populate_from.TrackModel.GetRandom (
+ source_set_at, populate_mode, false, skip && i == 0) as DatabaseTrackInfo;
+ if (track != null) {
+ track.LastPlayed = DateTime.Now;
+ // track.Save() is quite slow, update LastPlayedStamp directly in the database.
+ ServiceManager.DbConnection.Execute (@"
+ UPDATE CoreTracks
+ SET LastPlayedStamp = ?
+ WHERE TrackID = ?",
+ Hyena.DateTimeUtil.ToTimeT (track.LastPlayed), track.TrackId
+ );
+ EnqueueId (track.TrackId, false, true);
+ }
+ }
+ OnTracksAdded ();
+ if (current_track == null && Count > 0) {
+ // If the queue was empty, make the first added track the current one.
+ SetCurrentTrack (TrackModel[0] as DatabaseTrackInfo);
+ ServiceManager.PlayerEngine.OpenPlay (current_track);
+ }
}
- return false;
}
-
+
+ private readonly Dictionary<int, bool> enabled_cache = new Dictionary<int, bool> ();
+ public bool IsTrackEnabled (int index)
+ {
+ if (!enabled_cache.ContainsKey (index)) {
+ int current_index = current_track == null ? Count : TrackModel.IndexOf (current_track);
+ enabled_cache.Add (index, index >= current_index);
+ }
+ return enabled_cache[index];
+ }
+
+ private void SetCurrentTrack (DatabaseTrackInfo track)
+ {
+ enabled_cache.Clear ();
+ current_track = track;
+ }
+
+ public long Offset {
+ get { return offset; }
+ protected set {
+ if (value != offset) {
+ offset = value;
+ CurrentOffsetSchema.Set ((int) offset);
+ Reload ();
+ }
+ }
+ }
+
+ public void Refresh ()
+ {
+ int index = current_track == null ? Count : TrackModel.IndexOf (current_track);
+
+ // If the current track is not playing refresh it too.
+ if (current_track != null && !ServiceManager.PlayerEngine.IsPlaying (current_track)) {
+ index--;
+ }
+
+ if (index + 1 < Count) {
+ // Get the ViewOrder of the current_track
+ long current_view_order = current_track == null ?
+ ServiceManager.DbConnection.Query<long> (@"
+ SELECT MAX(ViewOrder) + 1
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ?",
+ DbId
+ ) :
+ ServiceManager.DbConnection.Query<long> (@"
+ SELECT ViewOrder
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND EntryID = ?",
+ DbId, Convert.ToInt64 (current_track.CacheEntryId)
+ );
+ // Get the list of generated tracks.
+ var generated = new HashSet<long> ();
+ foreach(long trackID in ServiceManager.DbConnection.QueryEnumerable<long> ( @"
+ SELECT TrackID
+ FROM CorePlaylistEntries
+ WHERE PlaylistID = ? AND Generated = 1 AND ViewOrder >= ?",
+ DbId, current_view_order)) {
+
+ generated.Add (trackID);
+ }
+
+ // Collect the indices of all generated tracks.
+ var ranges = new RangeCollection ();
+ for (int i = index + 1; i < Count; i++) {
+ if (generated.Contains (((DatabaseTrackInfo)TrackModel[i]).TrackId)) {
+ ranges.Add (i);
+ }
+ }
+
+ bool removed = false;
+ foreach (var range in ranges.Ranges) {
+ RemoveTrackRange (DatabaseTrackModel, range);
+ removed = true;
+ }
+
+ if (removed) {
+ OnTracksRemoved ();
+ }
+ }
+ else if (Count == 0 || current_track == null) {
+ UpdatePlayQueue ();
+ }
+ }
+
+ private void OnTrackStarted(object sender, EventArgs e)
+ {
+ SetAsPlaybackSourceUnlessPlaying ();
+ }
+
+ public bool Populate {
+ get { return populate_mode != PlaybackShuffleMode.Linear; }
+ }
+
private ITrackModelSource PriorSource {
get {
if (prior_playback_source == null || prior_playback_source == this) {
@@ -287,15 +688,23 @@ namespace Banshee.PlayQueue
was_playing = ServiceManager.PlayerEngine.IsPlaying ();
}
}
-
+
public override bool CanRename {
get { return false; }
}
-
+
+ public override bool CanSearch {
+ get { return false; }
+ }
+
public override bool ShowBrowser {
get { return false; }
}
-
+
+ protected override bool HasArtistAlbum {
+ get { return false; }
+ }
+
public override bool ConfirmRemoveTracks {
get { return false; }
}
@@ -311,7 +720,38 @@ namespace Banshee.PlayQueue
public override bool CanUnmap {
get { return false; }
}
-
+
+ public override string PreferencesPageId {
+ get { return "play-queue"; }
+ }
+
+ private void InstallPreferences ()
+ {
+ pref_page = new Banshee.Preferences.SourcePage (PreferencesPageId, Name, "source-playlist", 500);
+
+ pref_section = pref_page.Add (new Section ());
+ pref_section.ShowLabel = false;
+ pref_section.Add (new SchemaPreference<int> (PlayedSongsNumberSchema,
+ Catalog.GetString ("Number of _played songs to show"), null, delegate {
+ played_songs_number = PlayedSongsNumberSchema.Get ();
+ UpdatePlayQueue ();
+ }
+ ));
+ pref_section.Add (new SchemaPreference<int> (UpcomingSongsNumberSchema,
+ Catalog.GetString ("Number of _upcoming songs to show"), null, delegate {
+ upcoming_songs_number = UpcomingSongsNumberSchema.Get ();
+ UpdatePlayQueue ();
+ }
+ ));
+ }
+
+ private void UninstallPreferences ()
+ {
+ pref_page.Dispose ();
+ pref_page = null;
+ pref_section = null;
+ }
+
public static readonly SchemaEntry<bool> ClearOnQuitSchema = new SchemaEntry<bool> (
"plugins.play_queue", "clear_on_quit",
false,
@@ -319,21 +759,46 @@ namespace Banshee.PlayQueue
"Clear the play queue when quitting"
);
- // Reserve strings in preparation for the forthcoming string freeze.
- public void Strings ()
- {
- Catalog.GetString ("manual");
- Catalog.GetString ("by song");
- Catalog.GetString ("by album");
- Catalog.GetString ("by artist");
- Catalog.GetString ("by rating");
- Catalog.GetString ("by score");
- Catalog.GetString ("_Fill");
- Catalog.GetString ("f_rom");
- Catalog.GetString ("Refresh");
- Catalog.GetString ("Refresh random tracks in the play queue");
- Catalog.GetString ("Number of _played songs to show");
- Catalog.GetString ("Number of _upcoming songs to show");
- }
+ public static readonly SchemaEntry<int> CurrentTrackSchema = new SchemaEntry<int> (
+ "plugins.play_queue", "current_track",
+ 0,
+ "Current Track",
+ "Current track in the Play Queue"
+ );
+
+ public static readonly SchemaEntry<int> CurrentOffsetSchema = new SchemaEntry<int> (
+ "plugins.play_queue", "current_offset",
+ 0,
+ "Current Offset",
+ "Current offset of the Play Queue"
+ );
+
+ public static readonly SchemaEntry<int> PopulateModeSchema = new SchemaEntry<int> (
+ "plugins.play_queue", "populate_mode",
+ (int) PlaybackShuffleMode.Linear,
+ "Play Queue population mode",
+ "How (and if) the Play Queue should be randomly populated"
+ );
+
+ public static readonly SchemaEntry<string> PopulateFromSchema = new SchemaEntry<string> (
+ "plugins.play_queue", "populate_from",
+ ServiceManager.SourceManager.MusicLibrary.Name,
+ "Source to poplulate from",
+ "Name of the source to populate the the Play Queue from"
+ );
+
+ public static readonly SchemaEntry<int> PlayedSongsNumberSchema = new SchemaEntry<int> (
+ "plugins.play_queue", "played_songs_number",
+ 10, 0, 100,
+ "Played Songs Number",
+ "Number of played songs to show in the Play Queue"
+ );
+
+ public static readonly SchemaEntry<int> UpcomingSongsNumberSchema = new SchemaEntry<int> (
+ "plugins.play_queue", "upcoming_songs_number",
+ 10, 1, 100,
+ "Upcoming Songs Number",
+ "Number of upcoming songs to show in the Play Queue"
+ );
}
}
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs
new file mode 100644
index 0000000..8bf70a0
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueTrackListModel.cs
@@ -0,0 +1,67 @@
+//
+// PlayQueueTrackListModel.cs
+//
+// Author:
+// Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Banshee.Database;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+
+namespace Banshee.PlayQueue
+{
+ public class PlayQueueTrackListModel : DatabaseTrackListModel
+ {
+ private readonly PlayQueueSource source;
+
+ public PlayQueueTrackListModel (
+ BansheeDbConnection conn, IDatabaseTrackModelProvider provider, PlayQueueSource source) :
+ base (conn, provider, source)
+ {
+ this.source = source;
+ }
+
+ public override TrackInfo this[int index] {
+ get {
+ lock (this) {
+ var track = cache.GetValue (index);
+ if (track != null) {
+ track.Enabled = source.IsTrackEnabled (index);
+ }
+ return track;
+ }
+ }
+ }
+
+ public override string UnfilteredQuery {
+ get {
+ return String.Format ("{0} AND ViewOrder >= {1}", base.UnfilteredQuery, source.Offset);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
new file mode 100644
index 0000000..83d8362
--- /dev/null
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
@@ -0,0 +1,106 @@
+// QueueableSourceComboBox.cs
+//
+// Authors:
+// Alexander Kojevnikov <alexander kojevnikov com>
+//
+// Copyright (C) 2009 Alexander Kojevnikov
+//
+// 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 Gtk;
+
+using Banshee.Library;
+using Banshee.ServiceStack;
+using Banshee.Sources;
+using Banshee.Sources.Gui;
+
+namespace Banshee.PlayQueue
+{
+ public class QueueableSourceComboBox : ComboBox
+ {
+ private readonly TreeModelFilter filter;
+
+ public QueueableSourceComboBox (string source_name)
+ {
+ SourceRowRenderer renderer = new SourceRowRenderer ();
+ renderer.ParentWidget = this;
+ PackStart (renderer, true);
+ SetCellDataFunc (renderer, new CellLayoutDataFunc (SourceRowRenderer.CellDataHandler));
+
+ var store = new SourceModel ();
+ filter = new TreeModelFilter (store, null);
+ filter.VisibleFunc = (model, iter) => IsQueueable (((SourceModel)model).GetSource (iter));
+ Model = filter;
+
+ store.Refresh ();
+
+ SetActiveSource (source_name);
+ }
+
+ private void SetActiveSource (string name)
+ {
+ TreeIter first;
+ if (filter.GetIterFirst (out first)) {
+ TreeIter iter = FindSource (name, first);
+ if (!iter.Equals (TreeIter.Zero)) {
+ SetActiveIter (iter);
+ }
+ }
+ }
+
+ private bool IsQueueable (Source source)
+ {
+ return source != null && (
+ source is MusicLibrarySource || source.Parent is MusicLibrarySource ||
+ source is VideoLibrarySource || source.Parent is VideoLibrarySource);
+ }
+
+ private TreeIter FindSource (string name, TreeIter iter)
+ {
+ do {
+ var source = filter.GetValue (iter, 0) as ISource;
+ if (source != null && source.Name == name) {
+ return iter;
+ }
+
+ TreeIter citer;
+ if (filter.IterChildren (out citer, iter)) {
+ var yiter = FindSource (name, citer);
+ if (!yiter.Equals (TreeIter.Zero)) {
+ return yiter;
+ }
+ }
+ } while (filter.IterNext (ref iter));
+
+ return TreeIter.Zero;
+ }
+
+ public ITrackModelSource Source {
+ get {
+ TreeIter iter;
+ if (GetActiveIter (out iter)) {
+ return filter.GetValue(iter, 0) as ITrackModelSource;
+ }
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/Extensions/Banshee.PlayQueue/Makefile.am b/src/Extensions/Banshee.PlayQueue/Makefile.am
index 68ca5cc..fd5b24b 100644
--- a/src/Extensions/Banshee.PlayQueue/Makefile.am
+++ b/src/Extensions/Banshee.PlayQueue/Makefile.am
@@ -4,9 +4,12 @@ LINK = $(REF_EXTENSION_PLAYQUEUE)
INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
SOURCES = \
+ Banshee.PlayQueue/HeaderWidget.cs \
Banshee.PlayQueue/IPlayQueue.cs \
Banshee.PlayQueue/PlayQueueActions.cs \
- Banshee.PlayQueue/PlayQueueSource.cs
+ Banshee.PlayQueue/PlayQueueSource.cs \
+ Banshee.PlayQueue/PlayQueueTrackListModel.cs \
+ Banshee.PlayQueue/QueueableSourceComboBox.cs
RESOURCES = \
Banshee.PlayQueue.addin.xml \
diff --git a/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml b/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
index 06c28cc..74bdd6f 100644
--- a/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
+++ b/src/Extensions/Banshee.PlayQueue/Resources/ActiveSourceUI.xml
@@ -1,6 +1,7 @@
<ui>
<toolbar name="HeaderToolbar">
<placeholder name="SourceActions">
+ <toolitem action="RefreshPlayQueueAction" />
<toolitem action="ClearPlayQueueAction" />
</placeholder>
</toolbar>
diff --git a/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml b/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
index 0459c6c..eb95674 100644
--- a/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
+++ b/src/Extensions/Banshee.PlayQueue/Resources/GlobalUI.xml
@@ -1,7 +1,9 @@
<ui>
<popup name="PlayQueueContextMenu">
+ <menuitem action="RefreshPlayQueueAction"></menuitem>
<menuitem action="ClearPlayQueueAction"></menuitem>
<menuitem action="ClearPlayQueueOnQuitAction"></menuitem>
+ <menuitem name="SourcePreferences" action="SourcePreferencesAction"/>
</popup>
<popup name="TrackContextMenu" action="TrackContextMenuAction">
<placeholder name="AboveAddToPlaylist">
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
index b1e663b..9863da6 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastSource.cs
@@ -98,6 +98,7 @@ namespace Banshee.Podcasting.Gui
MediaTypes = TrackMediaAttributes.Podcast;
NotMediaTypes = TrackMediaAttributes.AudioBook;
SyncCondition = "(substr(CoreTracks.Uri, 0, 4) != 'http' AND CoreTracks.PlayCount = 0)";
+ TrackModel.Reloaded += OnReloaded;
Properties.SetString ("Icon.Name", "podcast");
@@ -197,7 +198,14 @@ namespace Banshee.Podcasting.Gui
AfterInitialized ();
}
}
-
+
+ void OnReloaded(object sender, EventArgs e)
+ {
+ for (int i=0; i < TrackModel.Count; i++) {
+ PodcastTrackInfo.From (TrackModel[i]);
+ }
+ }
+
// Probably don't want this -- do we want to allow actually removing the item? It will be
// repopulated the next time we update the podcast feed...
/*protected override void DeleteTrack (DatabaseTrackInfo track)
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
index 410c804..ad92b35 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Data/PodcastTrackInfo.cs
@@ -64,6 +64,7 @@ namespace Banshee.Podcasting.Data
PodcastTrackInfo pi = track.ExternalObject as PodcastTrackInfo;
if (pi != null) {
track.ReleaseDate = pi.PublishedDate;
+ track.Enabled = !pi.Item.IsRead;
}
return pi;
}
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
index a724134..6e849de 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/ColumnCellPodcastStatusIndicator.cs
@@ -84,7 +84,7 @@ namespace Banshee.Podcasting.Gui
PodcastTrackInfo podcast = PodcastTrackInfo.From (BoundTrack);
if (podcast != null) {
if (podcast.Activity == PodcastItemActivity.DownloadPending) {
- context.Sensitive = false;
+ context.Opaque = false;
}
}
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
index 312d2aa..6825962 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastItemView.cs
@@ -47,27 +47,6 @@ using Banshee.Podcasting.Data;
namespace Banshee.Podcasting.Gui
{
- public class PodcastItemView : TrackListView
- {
- public PodcastItemView () : base ()
- {
- }
-
- protected override void ColumnCellDataProvider (ColumnCell cell, object boundItem)
- {
- ColumnCellText text_cell = cell as ColumnCellText;
- if (text_cell == null) {
- return;
- }
-
- PodcastTrackInfo podcast = PodcastTrackInfo.From (boundItem as TrackInfo);
- if (podcast == null) {
- return;
- }
-
- text_cell.Opacity = podcast.IsNew ? 1.0 : 0.5;
- }
- }
/*public class PodcastItemView : ListView<TrackInfo>
{
private PersistentColumnController columnController;
diff --git a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
index b1e408b..66a6a60 100644
--- a/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
+++ b/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
@@ -53,7 +53,7 @@ namespace Banshee.Podcasting.Gui
{
public class PodcastSourceContents : FilteredListSourceContents, ITrackModelSourceContents
{
- private PodcastItemView track_view;
+ private TrackListView track_view;
private PodcastFeedView feed_view;
private PodcastUnheardFilterView unheard_view;
private DownloadStatusFilterView download_view;
@@ -64,7 +64,7 @@ namespace Banshee.Podcasting.Gui
protected override void InitializeViews ()
{
- SetupMainView (track_view = new PodcastItemView ());
+ SetupMainView (track_view = new TrackListView ());
SetupFilterView (unheard_view = new PodcastUnheardFilterView ());
SetupFilterView (download_view = new DownloadStatusFilterView ());
SetupFilterView (feed_view = new PodcastFeedView ());
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
index 603ab61..bd4a625 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/CellContext.cs
@@ -42,7 +42,7 @@ namespace Hyena.Data.Gui
private Gdk.Rectangle area;
private Gdk.Rectangle clip;
private bool text_as_foreground = false;
- private bool sensitive = true;
+ private bool opaque = true;
public Cairo.Context Context {
get { return context; }
@@ -83,10 +83,10 @@ namespace Hyena.Data.Gui
get { return text_as_foreground; }
set { text_as_foreground = value; }
}
-
- public bool Sensitive {
- get { return sensitive; }
- set { sensitive = value; }
+
+ public bool Opaque {
+ get { return opaque; }
+ set { opaque = value; }
}
}
}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
index bc5b6d4..484a292 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ColumnCellText.cs
@@ -44,7 +44,6 @@ namespace Hyena.Data.Gui
private Pango.Weight font_weight = Pango.Weight.Normal;
private Pango.EllipsizeMode ellipsize_mode = Pango.EllipsizeMode.End;
private Pango.Alignment alignment = Pango.Alignment.Left;
- private double opacity = 1.0;
private int text_width;
private int text_height;
private string text_format = null;
@@ -81,7 +80,7 @@ namespace Hyena.Data.Gui
context.Context.MoveTo (Spacing, ((int)cellHeight - text_height) / 2);
Cairo.Color color = context.Theme.Colors.GetWidgetColor (
context.TextAsForeground ? GtkColorClass.Foreground : GtkColorClass.Text, state);
- color.A = (!context.Sensitive) ? 0.3 : opacity;
+ color.A = context.Opaque ? 1.0 : 0.5;
context.Context.Color = color;
PangoCairoHelper.ShowLayout (context.Context, context.Layout);
@@ -172,11 +171,6 @@ namespace Hyena.Data.Gui
set { ellipsize_mode = value; }
}
- public virtual double Opacity {
- get { return opacity; }
- set { opacity = value; }
- }
-
internal static int ComputeRowHeight (Widget widget)
{
int w_width, row_height;
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
index e9fe295..891d557 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Model.cs
@@ -130,42 +130,42 @@ namespace Hyena.Data.Gui
public virtual IListModel<T> Model {
get { return model; }
}
-
- private string row_sensitive_property_name = "Sensitive";
- private PropertyInfo row_sensitive_property_info;
- bool row_sensitive_property_invalid = false;
-
- public string RowSensitivePropertyName {
- get { return row_sensitive_property_name; }
- set {
- if (value == row_sensitive_property_name) {
+
+ private string row_opaque_property_name = "Sensitive";
+ private PropertyInfo row_opaque_property_info;
+ bool row_opaque_property_invalid = false;
+
+ public string RowOpaquePropertyName {
+ get { return row_opaque_property_name; }
+ set {
+ if (value == row_opaque_property_name) {
return;
}
-
- row_sensitive_property_name = value;
- row_sensitive_property_info = null;
- row_sensitive_property_invalid = false;
-
+
+ row_opaque_property_name = value;
+ row_opaque_property_info = null;
+ row_opaque_property_invalid = false;
+
InvalidateList ();
}
}
-
- private bool IsRowSensitive (object item)
+
+ private bool IsRowOpaque (object item)
{
- if (item == null || row_sensitive_property_invalid) {
+ if (item == null || row_opaque_property_invalid) {
return true;
}
-
- if (row_sensitive_property_info == null || row_sensitive_property_info.ReflectedType != item.GetType ()) {
- row_sensitive_property_info = item.GetType ().GetProperty (row_sensitive_property_name);
- if (row_sensitive_property_info == null || row_sensitive_property_info.PropertyType != typeof (bool)) {
- row_sensitive_property_info = null;
- row_sensitive_property_invalid = true;
+
+ if (row_opaque_property_info == null || row_opaque_property_info.ReflectedType != item.GetType ()) {
+ row_opaque_property_info = item.GetType ().GetProperty (row_opaque_property_name);
+ if (row_opaque_property_info == null || row_opaque_property_info.PropertyType != typeof (bool)) {
+ row_opaque_property_info = null;
+ row_opaque_property_invalid = true;
return true;
}
}
-
- return (bool)row_sensitive_property_info.GetValue (item, null);
+
+ return (bool)row_opaque_property_info.GetValue (item, null);
}
private string row_bold_property_name = "IsBold";
@@ -204,14 +204,5 @@ namespace Hyena.Data.Gui
return (bool)row_bold_property_info.GetValue (item, null);
}
-
- #pragma warning disable 0169
-
- private bool IsRowSensitive (int index)
- {
- return IsRowSensitive (model[index]);
- }
-
- #pragma warning restore 0169
}
}
diff --git a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
index d376eb7..d4c424e 100644
--- a/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
+++ b/src/Libraries/Hyena.Gui/Hyena.Data.Gui/ListView/ListView_Rendering.cs
@@ -131,7 +131,7 @@ namespace Hyena.Data.Gui
cell_area.Height = header_rendering_alloc.Height;
cell_context.Clip = clip;
- cell_context.Sensitive = true;
+ cell_context.Opaque = true;
cell_context.TextAsForeground = true;
bool have_drawn_separator = false;
@@ -320,7 +320,7 @@ namespace Hyena.Data.Gui
}
object item = model[row_index];
- bool sensitive = IsRowSensitive (item);
+ bool opaque = IsRowOpaque (item);
bool bold = IsRowBold (item);
Rectangle cell_area = new Rectangle ();
@@ -334,18 +334,18 @@ namespace Hyena.Data.Gui
cell_area.Width = column_cache[ci].Width;
cell_area.X = column_cache[ci].X1 + area.X;
- PaintCell (item, ci, row_index, cell_area, sensitive, bold, state, false);
+ PaintCell (item, ci, row_index, cell_area, opaque, bold, state, false);
}
if (pressed_column_is_dragging && pressed_column_index >= 0) {
cell_area.Width = column_cache[pressed_column_index].Width;
cell_area.X = pressed_column_x_drag + list_rendering_alloc.X -
list_interaction_alloc.X - HadjustmentValue;
- PaintCell (item, pressed_column_index, row_index, cell_area, sensitive, bold, state, true);
+ PaintCell (item, pressed_column_index, row_index, cell_area, opaque, bold, state, true);
}
}
- private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool sensitive, bool bold,
+ private void PaintCell (object item, int column_index, int row_index, Rectangle area, bool opaque, bool bold,
StateType state, bool dragging)
{
ColumnCell cell = column_cache[column_index].Column.GetCell (0);
@@ -368,7 +368,7 @@ namespace Hyena.Data.Gui
cairo_context.Save ();
cairo_context.Translate (area.X, area.Y);
cell_context.Area = area;
- cell_context.Sensitive = sensitive;
+ cell_context.Opaque = opaque;
cell.Render (cell_context, dragging ? StateType.Normal : state, area.Width, area.Height);
cairo_context.Restore ();
}
diff --git a/src/Libraries/Hyena/Hyena.Collections/Selection.cs b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
index 1d61e2f..75768dd 100644
--- a/src/Libraries/Hyena/Hyena.Collections/Selection.cs
+++ b/src/Libraries/Hyena/Hyena.Collections/Selection.cs
@@ -142,6 +142,19 @@ namespace Hyena.Collections
OnChanged ();
}
+ public void UnselectRange (int a, int b)
+ {
+ int start = Math.Min (a, b);
+ int end = Math.Max (a, b);
+
+ int i;
+ for (i = start; i <= end; i++) {
+ ranges.Remove (i);
+ }
+
+ OnChanged ();
+ }
+
public virtual void SelectAll ()
{
SelectRange (0, max_index);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]