[banshee] Add ShuffleModes extension point
- From: Gabriel Burt <gburt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [banshee] Add ShuffleModes extension point
- Date: Tue, 16 Feb 2010 03:48:20 +0000 (UTC)
commit 206e2e307f741979422c37c9c81c3a7f113dedf5
Author: Gabriel Burt <gabriel burt gmail com>
Date: Mon Feb 15 19:36:57 2010 -0800
Add ShuffleModes extension point
Refactor Shuffler, PlaybackShuffleActions, PlayQueue, etc to reflect
this, dynamically adding/removing shuffle modes.
po/POTFILES.in | 6 +
.../DatabaseTrackListModel.cs | 6 +-
.../Banshee.Collection.Database/RandomBy.cs | 27 +++--
.../Banshee.Collection.Database/RandomByAlbum.cs | 7 +-
.../Banshee.Collection.Database/RandomByArtist.cs | 7 +-
.../Banshee.Collection.Database/RandomByOff.cs | 60 +++++++++
.../Banshee.Collection.Database/RandomByRating.cs | 7 +-
.../Banshee.Collection.Database/RandomByScore.cs | 7 +-
.../Banshee.Collection.Database/RandomBySlot.cs | 2 +-
.../Banshee.Collection.Database/RandomByTrack.cs | 7 +-
.../Banshee.Collection.Database/Shuffler.cs | 68 +++++++++--
.../Banshee.Metrics/BansheeMetrics.cs | 2 +-
.../IPlaybackController.cs | 2 +-
.../IPlaybackControllerService.cs | 2 +-
.../PlaybackControllerService.cs | 16 ++--
.../PlaybackShuffleMode.cs | 42 ------
.../Banshee.Services/Banshee.Services.addin.xml | 12 ++
src/Core/Banshee.Services/Banshee.Services.csproj | 2 +-
src/Core/Banshee.Services/Makefile.am | 2 +-
.../Banshee.Gui/PlaybackShuffleActions.cs | 131 +++++++++++---------
.../Banshee.PlayQueue/Banshee.PlayQueue.csproj | 4 +
.../Banshee.PlayQueue/HeaderWidget.cs | 46 ++++----
.../Banshee.PlayQueue/PlayQueueSource.cs | 23 ++--
23 files changed, 311 insertions(+), 177 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 57fa364..632e063 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -41,6 +41,12 @@ src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseAlbumListModel.cs
src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistInfo.cs
src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseArtistListModel.cs
src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseQueryFilterModel.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByAlbum.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByArtist.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByOff.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByRating.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByScore.cs
+src/Core/Banshee.Services/Banshee.Collection.Database/RandomByTrack.cs
src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs
src/Core/Banshee.Services/Banshee.Collection/RescanPipeline.cs
src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
index 5e4c47c..e2c948b 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
@@ -368,14 +368,14 @@ namespace Banshee.Collection.Database
public override TrackInfo GetRandom (DateTime notPlayedSince)
{
- return GetRandom (notPlayedSince, PlaybackShuffleMode.Song, true, false, Shuffler.Playback);
+ return GetRandom (notPlayedSince, "song", true, false, Shuffler.Playback);
}
- public TrackInfo GetRandom (DateTime notPlayedSince, PlaybackShuffleMode mode, bool repeat, bool resetSinceTime, Shuffler shuffler)
+ public TrackInfo GetRandom (DateTime notPlayedSince, string shuffle_mode, bool repeat, bool resetSinceTime, Shuffler shuffler)
{
lock (this) {
shuffler.SetModelAndCache (this, cache);
- return shuffler.GetRandom (notPlayedSince, mode, repeat, resetSinceTime);
+ return shuffler.GetRandom (notPlayedSince, shuffle_mode, repeat, resetSinceTime);
}
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBy.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBy.cs
index ae01174..58058b0 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBy.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBy.cs
@@ -48,16 +48,23 @@ namespace Banshee.Collection.Database
protected Shuffler Shuffler { get; private set; }
+ public string Id { get; private set; }
+ public string Label { get; protected set; }
+ public string Adverb { get; protected set; }
+ public string Description { get; protected set; }
+ public string IconName { get; protected set; }
+
public virtual bool IsReady { get { return true; } }
- public PlaybackShuffleMode Mode { get; private set; }
+ protected string Select { get; set; }
+ protected string From { get; set; }
protected string Condition { get; set; }
protected string OrderBy { get; set; }
- public RandomBy (PlaybackShuffleMode mode, Shuffler shuffler)
+ public RandomBy (string id, Shuffler shuffler)
{
+ Id = id;
Shuffler = shuffler;
- Mode = mode;
insert_shuffle = new HyenaSqliteCommand ("INSERT OR REPLACE INTO CoreShuffles (ShufflerID, TrackID, LastShuffledAt) VALUES (?, ?, ?)");
}
@@ -67,13 +74,13 @@ namespace Banshee.Collection.Database
if (shuffler_query == null) {
var provider = DatabaseTrackInfo.Provider;
shuffler_query = new HyenaSqliteCommand (String.Format (@"
- SELECT {0}
- FROM {1} LEFT OUTER JOIN CoreShuffles ON (CoreShuffles.ShufflerId = {2} AND CoreShuffles.TrackID = CoreTracks.TrackID)
- WHERE {3} {4} AND {5} AND
+ SELECT {0} {1}
+ FROM {2} {3} LEFT OUTER JOIN CoreShuffles ON (CoreShuffles.ShufflerId = {4} AND CoreShuffles.TrackID = CoreTracks.TrackID)
+ WHERE {5} {6} AND {7} AND
LastStreamError = 0 AND (CoreShuffles.LastShuffledAt < ? OR CoreShuffles.LastShuffledAt IS NULL)
- ORDER BY {6}",
- provider.Select,
- Model.FromFragment, Shuffler.DbId,
+ ORDER BY {8}",
+ provider.Select, Select,
+ Model.FromFragment, From, Shuffler.DbId,
String.IsNullOrEmpty (provider.Where) ? "1=1" : provider.Where, Model.ConditionFragment ?? "1=1", Condition,
OrderBy
));
@@ -134,4 +141,4 @@ namespace Banshee.Collection.Database
return null;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByAlbum.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByAlbum.cs
index 185fde8..2cd2077 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByAlbum.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByAlbum.cs
@@ -34,6 +34,7 @@ using Hyena.Data.Sqlite;
using Banshee.ServiceStack;
using Banshee.PlaybackController;
+using Mono.Unix;
namespace Banshee.Collection.Database
{
@@ -44,8 +45,12 @@ namespace Banshee.Collection.Database
private HyenaSqliteCommand album_query;
private int? album_id;
- public RandomByAlbum (Shuffler shuffler) : base (PlaybackShuffleMode.Album, shuffler)
+ public RandomByAlbum (Shuffler shuffler) : base ("album", shuffler)
{
+ Label = Catalog.GetString ("Shuffle by A_lbum");
+ Adverb = Catalog.GetString ("by album");
+ Description = Catalog.GetString ("Play all songs from an album, then randomly choose another album");
+
Condition = "CoreTracks.AlbumID = ?";
OrderBy = "Disc ASC, TrackNumber ASC";
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByArtist.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByArtist.cs
index ed20f36..e2bae5e 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByArtist.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByArtist.cs
@@ -34,6 +34,7 @@ using Hyena.Data.Sqlite;
using Banshee.ServiceStack;
using Banshee.PlaybackController;
+using Mono.Unix;
namespace Banshee.Collection.Database
{
@@ -43,8 +44,12 @@ namespace Banshee.Collection.Database
private HyenaSqliteCommand query;
private int? id;
- public RandomByArtist (Shuffler shuffler) : base (PlaybackShuffleMode.Artist, shuffler)
+ public RandomByArtist (Shuffler shuffler) : base ("artist", shuffler)
{
+ Label = Catalog.GetString ("Shuffle by A_rtist");
+ Adverb = Catalog.GetString ("by artist");
+ Description = Catalog.GetString ("Play all songs by an artist, then randomly choose another artist");
+
Condition = "CoreAlbums.ArtistID = ?";
OrderBy = "CoreTracks.Year, CoreTracks.AlbumID ASC, Disc ASC, TrackNumber ASC";
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByOff.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByOff.cs
new file mode 100644
index 0000000..fffff9d
--- /dev/null
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByOff.cs
@@ -0,0 +1,60 @@
+//
+// RandomByOff.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 Mono.Unix;
+
+namespace Banshee.Collection.Database
+{
+ public class RandomByOff : RandomBy
+ {
+ public RandomByOff (Shuffler shuffler) : base ("off", shuffler)
+ {
+ Label = Catalog.GetString ("Shuffle _Off");
+ Adverb = Catalog.GetString ("manually");
+ Description = Catalog.GetString ("Do not shuffle playlist");
+ }
+
+ #region Empty implementation of RandomBy
+
+ public override bool Next (DateTime after)
+ {
+ return false;
+ }
+
+ public override TrackInfo GetPlaybackTrack (DateTime after)
+ {
+ return null;
+ }
+
+ public override DatabaseTrackInfo GetShufflerTrack (DateTime after)
+ {
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByRating.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByRating.cs
index 2f71260..64f51ce 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByRating.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByRating.cs
@@ -33,6 +33,7 @@
using System;
using Banshee.PlaybackController;
+using Mono.Unix;
namespace Banshee.Collection.Database
{
@@ -40,8 +41,12 @@ namespace Banshee.Collection.Database
{
private static string track_condition = String.Format ("AND (CoreTracks.Rating = ? OR (? = 3 AND CoreTracks.Rating = 0)) {0} ORDER BY RANDOM()", RANDOM_CONDITION);
- public RandomByRating (Shuffler shuffler) : base (PlaybackShuffleMode.Rating, shuffler)
+ public RandomByRating (Shuffler shuffler) : base ("rating", shuffler)
{
+ Label = Catalog.GetString ("Shuffle by _Rating");
+ Adverb = Catalog.GetString ("by rating");
+ Description = Catalog.GetString ("Play songs randomly, prefer higher rated songs");
+
Condition = "(CoreTracks.Rating = ? OR (? = 3 AND CoreTracks.Rating = 0))";
OrderBy = "RANDOM()";
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByScore.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByScore.cs
index c997773..051979f 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByScore.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByScore.cs
@@ -29,6 +29,7 @@
using System;
using Banshee.PlaybackController;
+using Mono.Unix;
namespace Banshee.Collection.Database
{
@@ -36,8 +37,12 @@ namespace Banshee.Collection.Database
{
private static string track_condition = String.Format ("AND (CoreTracks.Score BETWEEN ? AND ? OR (? = 50 AND CoreTracks.Score = 0)) {0} ORDER BY RANDOM()", RANDOM_CONDITION);
- public RandomByScore (Shuffler shuffler) : base (PlaybackShuffleMode.Score, shuffler)
+ public RandomByScore (Shuffler shuffler) : base ("score", shuffler)
{
+ Label = Catalog.GetString ("Shuffle by S_core");
+ Adverb = Catalog.GetString ("by score");
+ Description = Catalog.GetString ("Play songs randomly, prefer higher scored songs");
+
Condition = "(CoreTracks.Score BETWEEN ? AND ? OR (? = 50 AND CoreTracks.Score = 0))";
OrderBy = "RANDOM()";
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBySlot.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBySlot.cs
index 2a988de..4827151 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBySlot.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomBySlot.cs
@@ -49,7 +49,7 @@ namespace Banshee.Collection.Database
private HyenaSqliteCommand query;
protected int slot;
- public RandomBySlot (PlaybackShuffleMode mode, Shuffler shuffler) : base (mode, shuffler)
+ public RandomBySlot (string id, Shuffler shuffler) : base (id, shuffler)
{
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByTrack.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByTrack.cs
index 18c57dc..f7b6037 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByTrack.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/RandomByTrack.cs
@@ -34,6 +34,7 @@ using Hyena.Data.Sqlite;
using Banshee.ServiceStack;
using Banshee.PlaybackController;
+using Mono.Unix;
namespace Banshee.Collection.Database
{
@@ -41,8 +42,12 @@ namespace Banshee.Collection.Database
{
private static string track_condition = String.Format ("{0} ORDER BY RANDOM()", RANDOM_CONDITION);
- public RandomByTrack (Shuffler shuffler) : base (PlaybackShuffleMode.Song, shuffler)
+ public RandomByTrack (Shuffler shuffler) : base ("song", shuffler)
{
+ Label = Catalog.GetString ("Shuffle by _Song");
+ Adverb = Catalog.GetString ("by song");
+ Description = Catalog.GetString ("Play songs randomly from the playlist");
+
Condition = "1=1";
OrderBy = "RANDOM()";
}
diff --git a/src/Core/Banshee.Services/Banshee.Collection.Database/Shuffler.cs b/src/Core/Banshee.Services/Banshee.Collection.Database/Shuffler.cs
index b9bfab6..6ee4ead 100644
--- a/src/Core/Banshee.Services/Banshee.Collection.Database/Shuffler.cs
+++ b/src/Core/Banshee.Services/Banshee.Collection.Database/Shuffler.cs
@@ -29,12 +29,15 @@
using System;
using System.Linq;
+using Mono.Addins;
+
using Hyena;
using Hyena.Data;
using Hyena.Data.Sqlite;
using Banshee.ServiceStack;
using Banshee.PlaybackController;
+using System.Collections.Generic;
namespace Banshee.Collection.Database
{
@@ -44,17 +47,21 @@ namespace Banshee.Collection.Database
private DateTime random_began_at = DateTime.MinValue;
private DateTime last_random = DateTime.MinValue;
- private RandomBy [] randoms;
+ private List<RandomBy> random_modes;
private DatabaseTrackListModel model;
public string Id { get; private set; }
public int DbId { get; private set; }
+ public Action<RandomBy> RandomModeAdded;
+ public Action<RandomBy> RandomModeRemoved;
+
+ public IList<RandomBy> RandomModes { get { return random_modes; } }
+
private Shuffler ()
{
- randoms = new RandomBy [] {
- new RandomByTrack (this), new RandomByArtist (this), new RandomByAlbum (this), new RandomByRating (this), new RandomByScore (this)
- };
+ random_modes = new List<RandomBy> ();
+ AddinManager.AddExtensionNodeHandler ("/Banshee/PlaybackController/ShuffleModes", OnExtensionChanged);
}
public Shuffler (string id) : this ()
@@ -63,16 +70,55 @@ namespace Banshee.Collection.Database
LoadOrCreate ();
}
+ private void OnExtensionChanged (object o, ExtensionNodeEventArgs args)
+ {
+ var tnode = (TypeExtensionNode)args.ExtensionNode;
+ RandomBy random_by = null;
+
+ if (args.Change == ExtensionChange.Add) {
+ lock (random_modes) {
+ try {
+ random_by = (RandomBy) Activator.CreateInstance (tnode.Type, this);
+ } catch (Exception e) {
+ Log.Exception (String.Format ("Failed to load RandomBy extension: {0}", args.Path), e);
+ }
+ random_modes.Add (random_by);
+ }
+
+ if (random_by != null) {
+ if (!tnode.Type.AssemblyQualifiedName.Contains ("Banshee.Service")) {
+ Log.DebugFormat ("Loaded RandomBy: {0}", random_by.Id);
+ }
+ var handler = RandomModeAdded;
+ if (handler != null) {
+ handler (random_by);
+ }
+ }
+ } else {
+ lock (random_modes) {
+ random_by = random_modes.First (r => r.GetType () == tnode.Type);
+ }
+
+ if (random_by != null) {
+ Log.DebugFormat ("Removed RandomBy: {0}", random_by.Id);
+ var handler = RandomModeRemoved;
+ if (handler != null) {
+ handler (random_by);
+ }
+ }
+ }
+ }
+
public void SetModelAndCache (DatabaseTrackListModel model, IDatabaseTrackModelCache cache)
{
this.model = model;
- foreach (var random in randoms) {
+ foreach (var random in random_modes) {
random.SetModelAndCache (model, cache);
}
}
- public TrackInfo GetRandom (DateTime notPlayedSince, PlaybackShuffleMode mode, bool repeat, bool resetSinceTime)
+ public TrackInfo GetRandom (DateTime notPlayedSince, string mode, bool repeat, bool resetSinceTime)
{
lock (this) {
if (this == Playback) {
@@ -90,7 +136,7 @@ namespace Banshee.Collection.Database
}
TrackInfo track = GetRandomTrack (mode, repeat, resetSinceTime);
- if (track == null && (repeat || mode != PlaybackShuffleMode.Linear)) {
+ if (track == null && (repeat || mode != "off")) {
random_began_at = (random_began_at == last_random) ? DateTime.Now : last_random;
track = GetRandomTrack (mode, repeat, true);
}
@@ -100,15 +146,15 @@ namespace Banshee.Collection.Database
}
}
- private TrackInfo GetRandomTrack (PlaybackShuffleMode mode, bool repeat, bool resetSinceTime)
+ private TrackInfo GetRandomTrack (string mode, bool repeat, bool resetSinceTime)
{
- foreach (var r in randoms) {
- if (resetSinceTime || r.Mode != mode) {
+ foreach (var r in random_modes) {
+ if (resetSinceTime || r.Id != mode) {
r.Reset ();
}
}
- var random = randoms.First (r => r.Mode == mode);
+ var random = random_modes.First (r => r.Id == mode);
if (random != null) {
if (!random.IsReady) {
if (!random.Next (random_began_at) && repeat) {
diff --git a/src/Core/Banshee.Services/Banshee.Metrics/BansheeMetrics.cs b/src/Core/Banshee.Services/Banshee.Metrics/BansheeMetrics.cs
index 6ca09db..a73c437 100644
--- a/src/Core/Banshee.Services/Banshee.Metrics/BansheeMetrics.cs
+++ b/src/Core/Banshee.Services/Banshee.Metrics/BansheeMetrics.cs
@@ -267,7 +267,7 @@ namespace Banshee.Metrics
playback_source_changed.PushSample (GetSourceString (ServiceManager.PlaybackController.Source as Source));
}
- private void OnShuffleModeChanged (object o, EventArgs<PlaybackShuffleMode> args)
+ private void OnShuffleModeChanged (object o, EventArgs<string> args)
{
shuffle_changed.PushSample (args.Value);
}
diff --git a/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackController.cs b/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackController.cs
index 57d4e10..1c7106c 100644
--- a/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackController.cs
+++ b/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackController.cs
@@ -30,7 +30,7 @@ namespace Banshee.PlaybackController
{
public interface IPlaybackController : IBasicPlaybackController
{
- PlaybackShuffleMode ShuffleMode { get; set; }
+ string ShuffleMode { get; set; }
PlaybackRepeatMode RepeatMode { get; set; }
bool StopWhenFinished { get; set; }
}
diff --git a/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs b/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs
index 8eed1e4..47e86e2 100644
--- a/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs
+++ b/src/Core/Banshee.Services/Banshee.PlaybackController/IPlaybackControllerService.cs
@@ -50,7 +50,7 @@ namespace Banshee.PlaybackController
void Previous (bool restart);
void RestartOrPrevious (bool restart);
- PlaybackShuffleMode ShuffleMode { get; set; }
+ string ShuffleMode { get; set; }
PlaybackRepeatMode RepeatMode { get; set; }
bool StopWhenFinished { get; set; }
}
diff --git a/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs b/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
index 2322609..02961bf 100644
--- a/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
+++ b/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
@@ -61,7 +61,7 @@ namespace Banshee.PlaybackController
private uint error_transition_id;
private DateTime source_auto_set_at = DateTime.MinValue;
- private PlaybackShuffleMode shuffle_mode;
+ private string shuffle_mode;
private PlaybackRepeatMode repeat_mode;
private bool stop_when_finished = false;
@@ -80,7 +80,7 @@ namespace Banshee.PlaybackController
public event EventHandler NextSourceChanged;
public event EventHandler TrackStarted;
public event EventHandler Transition;
- public event EventHandler<EventArgs<PlaybackShuffleMode>> ShuffleModeChanged;
+ public event EventHandler<EventArgs<string>> ShuffleModeChanged;
public event EventHandler<EventArgs<PlaybackRepeatMode>> RepeatModeChanged;
public PlaybackControllerService ()
@@ -330,7 +330,7 @@ namespace Banshee.PlaybackController
private TrackInfo QueryTrack (Direction direction, bool restart)
{
Log.DebugFormat ("Querying model for track to play in {0}:{1} mode", ShuffleMode, direction);
- return ShuffleMode == PlaybackShuffleMode.Linear
+ return ShuffleMode == "off"
? QueryTrackLinear (direction, restart)
: QueryTrackRandom (ShuffleMode, restart);
}
@@ -361,12 +361,12 @@ namespace Banshee.PlaybackController
}
}
- private TrackInfo QueryTrackRandom (PlaybackShuffleMode mode, bool restart)
+ private TrackInfo QueryTrackRandom (string shuffle_mode, bool restart)
{
var track_shuffler = Source.TrackModel as Banshee.Collection.Database.DatabaseTrackListModel;
TrackInfo track = track_shuffler == null
? Source.TrackModel.GetRandom (source_set_at)
- : track_shuffler.GetRandom (source_set_at, mode, restart, last_was_skipped, Banshee.Collection.Database.Shuffler.Playback);
+ : track_shuffler.GetRandom (source_set_at, shuffle_mode, restart, last_was_skipped, Banshee.Collection.Database.Shuffler.Playback);
// Reset to default of true, only ever set to false by EosTransition
last_was_skipped = true;
return track;
@@ -475,13 +475,13 @@ namespace Banshee.PlaybackController
}
}
- public PlaybackShuffleMode ShuffleMode {
+ public string ShuffleMode {
get { return shuffle_mode; }
set {
shuffle_mode = value;
- EventHandler<EventArgs<PlaybackShuffleMode>> handler = ShuffleModeChanged;
+ var handler = ShuffleModeChanged;
if (handler != null) {
- handler (this, new EventArgs<PlaybackShuffleMode> (shuffle_mode));
+ handler (this, new EventArgs<string> (shuffle_mode));
}
}
}
diff --git a/src/Core/Banshee.Services/Banshee.Services.addin.xml b/src/Core/Banshee.Services/Banshee.Services.addin.xml
index 5dbf22f..1f02fc8 100644
--- a/src/Core/Banshee.Services/Banshee.Services.addin.xml
+++ b/src/Core/Banshee.Services/Banshee.Services.addin.xml
@@ -21,6 +21,10 @@
<ExtensionNode name="ImportSource"/>
</ExtensionPoint>
+ <ExtensionPoint path="/Banshee/PlaybackController/ShuffleModes">
+ <ExtensionNode name="ShuffleMode"/>
+ </ExtensionPoint>
+
<ExtensionPoint path="/Banshee/MediaEngine/PlayerEngine">
<ExtensionNode name="PlayerEngine"/>
</ExtensionPoint>
@@ -57,4 +61,12 @@
<ExtensionNode name="ScreensaverManager"/>
</ExtensionPoint>
+ <Extension path="/Banshee/PlaybackController/ShuffleModes">
+ <ShuffleMode class="Banshee.Collection.Database.RandomByOff"/>
+ <ShuffleMode class="Banshee.Collection.Database.RandomByTrack"/>
+ <ShuffleMode class="Banshee.Collection.Database.RandomByArtist"/>
+ <ShuffleMode class="Banshee.Collection.Database.RandomByAlbum"/>
+ <ShuffleMode class="Banshee.Collection.Database.RandomByRating"/>
+ <ShuffleMode class="Banshee.Collection.Database.RandomByScore"/>
+ </Extension>
</Addin>
diff --git a/src/Core/Banshee.Services/Banshee.Services.csproj b/src/Core/Banshee.Services/Banshee.Services.csproj
index 6d5746d..12b33ea 100644
--- a/src/Core/Banshee.Services/Banshee.Services.csproj
+++ b/src/Core/Banshee.Services/Banshee.Services.csproj
@@ -179,7 +179,6 @@
<Compile Include="Banshee.Database\BansheeModelProvider.cs" />
<Compile Include="Banshee.Database\BansheeModelCache.cs" />
<Compile Include="Banshee.PlaybackController\IPlaybackController.cs" />
- <Compile Include="Banshee.PlaybackController\PlaybackShuffleMode.cs" />
<Compile Include="Banshee.PlaybackController\PlaybackRepeatMode.cs" />
<Compile Include="Banshee.PlaybackController\PlaybackControllerDatabaseStack.cs" />
<Compile Include="Banshee.PlaybackController\PlaybackControllerService.cs" />
@@ -308,6 +307,7 @@
<Compile Include="Banshee.Library\MusicFileNamePattern.cs" />
<Compile Include="Banshee.Metrics\BansheeMetrics.cs" />
<Compile Include="Banshee.Metrics\Configuration.cs" />
+ <Compile Include="Banshee.Collection.Database\RandomByOff.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Banshee.Services.addin.xml">
diff --git a/src/Core/Banshee.Services/Makefile.am b/src/Core/Banshee.Services/Makefile.am
index e04afc8..d6f1a8f 100644
--- a/src/Core/Banshee.Services/Makefile.am
+++ b/src/Core/Banshee.Services/Makefile.am
@@ -25,6 +25,7 @@ SOURCES = \
Banshee.Collection.Database/RandomBy.cs \
Banshee.Collection.Database/RandomByAlbum.cs \
Banshee.Collection.Database/RandomByArtist.cs \
+ Banshee.Collection.Database/RandomByOff.cs \
Banshee.Collection.Database/RandomByRating.cs \
Banshee.Collection.Database/RandomByScore.cs \
Banshee.Collection.Database/RandomBySlot.cs \
@@ -138,7 +139,6 @@ SOURCES = \
Banshee.PlaybackController/PlaybackControllerDatabaseStack.cs \
Banshee.PlaybackController/PlaybackControllerService.cs \
Banshee.PlaybackController/PlaybackRepeatMode.cs \
- Banshee.PlaybackController/PlaybackShuffleMode.cs \
Banshee.Playlist/AbstractPlaylistSource.cs \
Banshee.Playlist/PlaylistFileUtil.cs \
Banshee.Playlist/PlaylistSource.cs \
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs
index 7d0ddd4..08d960b 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackShuffleActions.cs
@@ -37,6 +37,7 @@ using Hyena.Gui;
using Banshee.Configuration;
using Banshee.ServiceStack;
using Banshee.PlaybackController;
+using Banshee.Collection.Database;
namespace Banshee.Gui
{
@@ -45,12 +46,15 @@ namespace Banshee.Gui
private RadioAction active_action;
private RadioAction saved_action;
private PlaybackActions playback_actions;
+ private const string shuffle_off_action = "Shuffle_off";
+
+ private Dictionary<int, string> shuffle_modes = new Dictionary<int, string> ();
public RadioAction Active {
get { return active_action; }
set {
active_action = value;
- ServiceManager.PlaybackController.ShuffleMode = (PlaybackShuffleMode)active_action.Value;
+ ServiceManager.PlaybackController.ShuffleMode = shuffle_modes[active_action.Value];
}
}
@@ -76,65 +80,84 @@ namespace Banshee.Gui
Catalog.GetString ("Shuffle"), null)
});
- Add (new RadioActionEntry [] {
- new RadioActionEntry ("ShuffleOffAction", null,
- Catalog.GetString ("Shuffle _Off"), null,
- Catalog.GetString ("Do not shuffle playlist"),
- (int)PlaybackShuffleMode.Linear),
-
- new RadioActionEntry ("ShuffleSongAction", null,
- Catalog.GetString ("Shuffle by _Song"), null,
- Catalog.GetString ("Play songs randomly from the playlist"),
- (int)PlaybackShuffleMode.Song),
-
- new RadioActionEntry ("ShuffleArtistAction", null,
- Catalog.GetString ("Shuffle by A_rtist"), null,
- Catalog.GetString ("Play all songs by an artist, then randomly choose another artist"),
- (int)PlaybackShuffleMode.Artist),
-
- new RadioActionEntry ("ShuffleAlbumAction", null,
- Catalog.GetString ("Shuffle by A_lbum"), null,
- Catalog.GetString ("Play all songs from an album, then randomly choose another album"),
- (int)PlaybackShuffleMode.Album),
-
- new RadioActionEntry ("ShuffleRatingAction", null,
- Catalog.GetString ("Shuffle by _Rating"), null,
- Catalog.GetString ("Play songs randomly, prefer higher rated songs"),
- (int)PlaybackShuffleMode.Rating),
-
- new RadioActionEntry ("ShuffleScoreAction", null,
- Catalog.GetString ("Shuffle by S_core"), null,
- Catalog.GetString ("Play songs randomly, prefer higher scored songs"),
- (int)PlaybackShuffleMode.Score)
- }, 0, OnActionChanged);
-
- this["ShuffleOffAction"].StockId = Gtk.Stock.MediaNext;
- this["ShuffleSongAction"].IconName = "media-playlist-shuffle";
- this["ShuffleArtistAction"].IconName = "media-playlist-shuffle";
- this["ShuffleAlbumAction"].IconName = "media-playlist-shuffle";
- this["ShuffleRatingAction"].IconName = "media-playlist-shuffle";
- this["ShuffleScoreAction"].IconName = "media-playlist-shuffle";
-
ServiceManager.PlaybackController.ShuffleModeChanged += OnShuffleModeChanged;
ServiceManager.PlaybackController.SourceChanged += OnPlaybackSourceChanged;
- Gtk.Action action = this[ConfigIdToActionName (ShuffleMode.Get ())];
+ SetShuffler (Banshee.Collection.Database.Shuffler.Playback);
+ }
+
+ private Shuffler shuffler;
+ public void SetShuffler (Shuffler shuffler)
+ {
+ if (this.shuffler == shuffler) {
+ return;
+ }
+
+ if (this.shuffler != null) {
+ this.shuffler.RandomModeAdded -= OnShufflerChanged;
+ this.shuffler.RandomModeRemoved -= OnShufflerChanged;
+ }
+
+ this.shuffler = shuffler;
+ this.shuffler.RandomModeAdded += OnShufflerChanged;
+ this.shuffler.RandomModeRemoved += OnShufflerChanged;
+
+ UpdateActions ();
+ }
+
+ private void OnShufflerChanged (RandomBy random_by)
+ {
+ UpdateActions ();
+ }
+
+ private void UpdateActions ()
+ {
+ // Clear out the old options
+ foreach (string id in shuffle_modes.Values) {
+ Remove (String.Format ("Shuffle_{0}", id));
+ }
+ shuffle_modes.Clear ();
+
+ var radio_group = new RadioActionEntry [shuffler.RandomModes.Count];
+ int i = 0;
+
+ // Add all the shuffle options
+ foreach (var random_by in shuffler.RandomModes) {
+ string action_name = String.Format ("Shuffle_{0}", random_by.Id);
+ int id = shuffle_modes.Count;
+ shuffle_modes[id] = random_by.Id;
+ radio_group[i++] = new RadioActionEntry (
+ action_name, null,
+ random_by.Label, null,
+ random_by.Description,
+ id);
+ }
+
+ Add (radio_group, 0, OnActionChanged);
+
+ // Set the icons
+ foreach (var random_by in shuffler.RandomModes) {
+ this[String.Format ("Shuffle_{0}", random_by.Id)].IconName = random_by.IconName ?? "media-playlist-shuffle";
+ }
+ this[shuffle_off_action].StockId = Gtk.Stock.MediaNext;
+
+ var action = this[ConfigIdToActionName (ShuffleMode.Get ())];
if (action is RadioAction) {
active_action = (RadioAction)action;
} else {
- Active = (RadioAction)this["ShuffleOffAction"];
+ Active = (RadioAction)this[shuffle_off_action];
}
Active.Activate ();
}
- private void OnShuffleModeChanged (object o, EventArgs<PlaybackShuffleMode> args)
+ private void OnShuffleModeChanged (object o, EventArgs<string> args)
{
- if (active_action.Value != (int)args.Value) {
+ if (shuffle_modes[active_action.Value] != args.Value) {
// This happens only when changing the mode using DBus.
// In this case we need to locate the action by its value.
foreach (RadioAction action in this) {
- if (action.Value == (int)args.Value) {
+ if (shuffle_modes[action.Value] == args.Value) {
active_action = action;
break;
}
@@ -153,7 +176,7 @@ namespace Banshee.Gui
if (saved_action == null && !source.CanShuffle) {
saved_action = Active;
- Active = this["ShuffleOffAction"] as RadioAction;
+ Active = this[shuffle_off_action] as RadioAction;
Sensitive = false;
} else if (saved_action != null && source.CanShuffle) {
Active = saved_action;
@@ -207,12 +230,9 @@ namespace Banshee.Gui
public IEnumerator<RadioAction> GetEnumerator ()
{
- yield return (RadioAction)this["ShuffleOffAction"];
- yield return (RadioAction)this["ShuffleSongAction"];
- yield return (RadioAction)this["ShuffleArtistAction"];
- yield return (RadioAction)this["ShuffleAlbumAction"];
- yield return (RadioAction)this["ShuffleRatingAction"];
- yield return (RadioAction)this["ShuffleScoreAction"];
+ foreach (string id in shuffle_modes.Values) {
+ yield return (RadioAction)this[String.Format ("Shuffle_{0}", id)];
+ }
}
IEnumerator IEnumerable.GetEnumerator ()
@@ -222,13 +242,12 @@ namespace Banshee.Gui
private static string ConfigIdToActionName (string configuration)
{
- return String.Format ("{0}Action", StringUtil.UnderCaseToCamelCase (configuration));
+ return String.Format ("{0}", StringUtil.UnderCaseToCamelCase (configuration));
}
private static string ActionNameToConfigId (string actionName)
{
- return StringUtil.CamelCaseToUnderCase (actionName.Substring (0,
- actionName.Length - (actionName.EndsWith ("Action") ? 6 : 0)));
+ return StringUtil.CamelCaseToUnderCase (actionName);
}
public static readonly SchemaEntry<string> ShuffleMode = new SchemaEntry<string> (
@@ -238,4 +257,4 @@ namespace Banshee.Gui
"Shuffle mode (shuffle_off, shuffle_song, shuffle_artist, shuffle_album, shuffle_rating, shuffle_score)"
);
}
-}
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
index b48a86a..bffe948 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue.csproj
@@ -71,6 +71,10 @@
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ <ProjectReference Include="..\..\Core\Banshee.Widgets\Banshee.Widgets.csproj">
+ <Project>{A3701765-E571-413D-808C-9788A22791AF}</Project>
+ <Name>Banshee.Widgets</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
index 65db812..40bc212 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/HeaderWidget.cs
@@ -35,47 +35,48 @@ using System.Linq;
using Hyena;
using Banshee.PlaybackController;
using Banshee.Sources;
+using Banshee.Collection.Database;
+using Banshee.Widgets;
namespace Banshee.PlayQueue
{
public class HeaderWidget : HBox
{
- public event EventHandler<EventArgs<PlaybackShuffleMode>> ModeChanged;
+ public event EventHandler<EventArgs<RandomBy>> ModeChanged;
public event EventHandler<EventArgs<DatabaseSource>> 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 ()
+ public HeaderWidget (Shuffler shuffler, string shuffle_mode_id, 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 ());
+ var mode_combo = new DictionaryComboBox<RandomBy> ();
+ foreach (var random_by in shuffler.RandomModes.OrderBy (r => r.Adverb)) {
+ mode_combo.Add (random_by.Adverb, random_by);
+ }
+
fill_label.MnemonicWidget = mode_combo;
mode_combo.Changed += delegate {
- var value = modes[mode_combo.ActiveText];
+ var random_by = mode_combo.ActiveValue;
foreach (var widget in sensitive_widgets) {
- widget.Sensitive = value != PlaybackShuffleMode.Linear;
+ widget.Sensitive = random_by.Id != "off";
}
+
var handler = ModeChanged;
if (handler != null) {
- handler (this, new EventArgs<PlaybackShuffleMode> (value));
+ handler (this, new EventArgs<RandomBy> (random_by));
}
};
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);
+ sensitive_widgets.Add (from_label);
+
source_combo_box.Changed += delegate {
var handler = SourceChanged;
if (handler != null) {
@@ -88,14 +89,11 @@ namespace Banshee.PlayQueue
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;
- });
+ // Select the saved population mode.
+ var default_randomby = shuffler.RandomModes.FirstOrDefault (r => r.Id == shuffle_mode_id);
+ if (default_randomby != null) {
+ mode_combo.ActiveValue = default_randomby;
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
index d8e020f..5877a3a 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
@@ -70,7 +70,7 @@ namespace Banshee.PlayQueue
private SourcePage pref_page;
private Section pref_section;
- private PlaybackShuffleMode populate_mode = (PlaybackShuffleMode) PopulateModeSchema.Get ();
+ private string populate_shuffle_mode = PopulateModeSchema.Get ();
private string populate_from_name = PopulateFromSchema.Get ();
private DatabaseSource populate_from = null;
private int played_songs_number = PlayedSongsNumberSchema.Get ();
@@ -128,21 +128,20 @@ namespace Banshee.PlayQueue
{
base.Initialize ();
+ shuffler = new Shuffler (UniqueId);
InstallPreferences ();
header_widget = CreateHeaderWidget ();
header_widget.ShowAll ();
- shuffler = new Shuffler (UniqueId);
-
Properties.Set<Gtk.Widget> ("Nereid.SourceContents.HeaderWidget", header_widget);
}
public HeaderWidget CreateHeaderWidget ()
{
- var header_widget = new HeaderWidget (populate_mode, populate_from_name);
- header_widget.ModeChanged += delegate (object sender, EventArgs<PlaybackShuffleMode> e) {
- populate_mode = e.Value;
- PopulateModeSchema.Set ((int) e.Value);
+ var header_widget = new HeaderWidget (shuffler, populate_shuffle_mode, populate_from_name);
+ header_widget.ModeChanged += delegate (object sender, EventArgs<RandomBy> e) {
+ populate_shuffle_mode = e.Value.Id;
+ PopulateModeSchema.Set (populate_shuffle_mode);
UpdatePlayQueue ();
OnUpdated ();
};
@@ -603,7 +602,7 @@ namespace Banshee.PlayQueue
for (int i = 0; i < tracks_to_add; i++) {
var track = populate_from.DatabaseTrackModel.GetRandom (
- source_set_at, populate_mode, false, skip && i == 0, shuffler) as DatabaseTrackInfo;
+ source_set_at, populate_shuffle_mode, false, skip && i == 0, shuffler) as DatabaseTrackInfo;
if (track != null) {
EnqueueId (track.TrackId, false, true);
@@ -717,7 +716,7 @@ namespace Banshee.PlayQueue
}
public bool Populate {
- get { return populate_mode != PlaybackShuffleMode.Linear; }
+ get { return populate_shuffle_mode != "off"; }
}
private ITrackModelSource PriorSource {
@@ -820,9 +819,9 @@ namespace Banshee.PlayQueue
"Current offset of the Play Queue"
);
- public static readonly SchemaEntry<int> PopulateModeSchema = new SchemaEntry<int> (
- "plugins.play_queue", "populate_mode",
- (int) PlaybackShuffleMode.Linear,
+ public static readonly SchemaEntry<string> PopulateModeSchema = new SchemaEntry<string> (
+ "plugins.play_queue", "populate_shuffle_mode",
+ "off",
"Play Queue population mode",
"How (and if) the Play Queue should be randomly populated"
);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]