banshee r4642 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.PlaybackController src/Core/Banshee.Services/Banshee.Playlists.Formats src/Core/Banshee.Services/Banshee.Streaming src/Core/Banshee.ThickClient/Banshee.Gui src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue src/Extensions/Banshee.Podcasting/Banshee.Podcasting src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui



Author: gburt
Date: Thu Oct  2 00:36:15 2008
New Revision: 4642
URL: http://svn.gnome.org/viewvc/banshee?rev=4642&view=rev

Log:
2008-10-01  Gabriel Burt  <gabriel burt gmail com>

	This commit fixes jump-to-playing for podcasts and radio, avoids some
	hangs when loading playlists from the command line or Media menu.

	* src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs:
	* src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs:
	* src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs:
	* src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs:
	* src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs:
	Change the Next/Previos/First methods to return a bool saying whether they
	handled the action.  So sources can implement IBasicPlaybackController and
	return false if they want the normal controller to handle that action.

	* src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue/FileSystemQueueSource.cs:
	* src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs:
	Make sure playlist parsing (which could involved network access) is done
	not on the GUI thread.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs:
	Reload the whole source when done fetching podcast artwork.

	* src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs:
	* src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSourceContents.cs:
	Implement ITrackModelSourceContents so jump-to-playing track works for
	podcasts and radio.

	* src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs:
	Implement IBasicPlaybackController, and play the next/previous stream
	within the station (if any) when Next/Previous are called.

	* src/Core/Banshee.Services/Banshee.Playlists.Formats/PlaylistParser.cs:
	Add AssertNotInMainThread to help identify places we call this from the
	GUI thread.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs:
	In IndexOf method, if the track is a RadioTrackInfo, lookup the IndexOf
	its parent track, since RadioTrackInfo is a wrapper.

	* src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs: Add
	PlayPreviousStream method, and return whether able to play next/previous
	in those methods, so we can fall back on the normal playback controller if
	not.



Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlists.Formats/PlaylistParser.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs
   trunk/banshee/src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue/FileSystemQueueSource.cs
   trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
   trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSourceContents.cs
   trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
   trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
   trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackListModel.cs	Thu Oct  2 00:36:15 2008
@@ -309,7 +309,12 @@
         public override int IndexOf (TrackInfo track)
         {
             lock (this) {
-                return (int) cache.IndexOf (track as DatabaseTrackInfo);
+                if (track is DatabaseTrackInfo) {
+                    return (int) cache.IndexOf (track as DatabaseTrackInfo);
+                } else if (track is Banshee.Streaming.RadioTrackInfo) {
+                    return (int) cache.IndexOf ((track as Banshee.Streaming.RadioTrackInfo).ParentTrack as DatabaseTrackInfo);
+                }
+                return -1;
             }
         }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/IBasicPlaybackController.cs	Thu Oct  2 00:36:15 2008
@@ -30,8 +30,8 @@
 {
     public interface IBasicPlaybackController
     {
-        void First ();
-        void Next (bool restart);
-        void Previous (bool restart);
+        bool First ();
+        bool Next (bool restart);
+        bool Previous (bool restart);
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/ICanonicalPlaybackController.cs	Thu Oct  2 00:36:15 2008
@@ -30,8 +30,8 @@
 {
     public interface ICanonicalPlaybackController : IPlaybackController
     {
-        new void First ();
-        new void Next (bool restart);
-        new void Previous (bool restart);
+        /*new bool First ();
+        new bool Next (bool restart);
+        new bool Previous (bool restart);*/
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.PlaybackController/PlaybackControllerService.cs	Thu Oct  2 00:36:15 2008
@@ -139,7 +139,7 @@
                     
                     CancelErrorTransition ();
                     // TODO why is this so long? any reason not to be instantaneous?
-                    Application.RunTimeout (500, EosTransition);
+                    Application.RunTimeout (250, EosTransition);
                     break;
                 case PlayerEvent.StateChange:
                     if (((PlayerEventStateChangeArgs)args).Current != PlayerState.Loading) {
@@ -196,10 +196,9 @@
             // This and OnTransition() below commented out b/c of BGO #524556
             //raise_started_after_transition = true;
             
-            if (Source is IBasicPlaybackController) {
-                ((IBasicPlaybackController)Source).First ();
+            if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).First ()) {
             } else {
-                ((ICanonicalPlaybackController)this).First ();
+                ((IBasicPlaybackController)this).First ();
             }
             
             //OnTransition ();
@@ -219,10 +218,9 @@
 
             player_engine.IncrementLastPlayed ();
             
-            if (Source is IBasicPlaybackController) {
-                ((IBasicPlaybackController)Source).Next (restart);
+            if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Next (restart)) {
             } else {
-                ((ICanonicalPlaybackController)this).Next (restart);
+                ((IBasicPlaybackController)this).Next (restart);
             }
             
             OnTransition ();
@@ -242,23 +240,23 @@
 
             player_engine.IncrementLastPlayed ();
             
-            if (Source is IBasicPlaybackController) {
-                ((IBasicPlaybackController)Source).Previous (restart);
+            if (Source is IBasicPlaybackController && ((IBasicPlaybackController)Source).Previous (restart)) {
             } else {
-                ((ICanonicalPlaybackController)this).Previous (restart);
+                ((IBasicPlaybackController)this).Previous (restart);
             }
             
             OnTransition ();
         }
         
-        void ICanonicalPlaybackController.First ()
+        bool IBasicPlaybackController.First ()
         {
             if (Source.Count > 0) {
                 player_engine.OpenPlay (Source.TrackModel[0]);
             }
+            return true;
         }
         
-        void ICanonicalPlaybackController.Next (bool restart)
+        bool IBasicPlaybackController.Next (bool restart)
         {
             TrackInfo tmp_track = CurrentTrack;
 
@@ -274,16 +272,17 @@
                         previous_stack.Push (tmp_track);
                     }
                 } else {
-                    return;
+                    return true;
                 }
                 
                 CurrentTrack = next_track;
             }
             
             QueuePlayTrack ();
+            return true;
         }
         
-        void ICanonicalPlaybackController.Previous (bool restart)
+        bool IBasicPlaybackController.Previous (bool restart)
         {
             if (CurrentTrack != null && previous_stack.Count > 0) {
                 next_stack.Push (current_track);
@@ -296,11 +295,12 @@
                 if (track != null) {
                     CurrentTrack = track;
                 } else {
-                    return;
+                    return true;
                 }
             }
             
             QueuePlayTrack ();
+            return true;
         }
         
         private TrackInfo QueryTrack (Direction direction, bool restart)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlists.Formats/PlaylistParser.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlists.Formats/PlaylistParser.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlists.Formats/PlaylistParser.cs	Thu Oct  2 00:36:15 2008
@@ -53,6 +53,7 @@
         
         public bool Parse (SafeUri uri)
         {
+            ThreadAssist.AssertNotInMainThread ();
             lock (this) {
                 elements = null;
                 Stream stream = null;

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Streaming/RadioTrackInfo.cs	Thu Oct  2 00:36:15 2008
@@ -66,6 +66,7 @@
         {
             TrackTitle = track.Title;
             ArtistName = track.Creator;
+            
             this.track = track;
         }
         
@@ -109,12 +110,24 @@
             }
         }
         
-        public void PlayNextStream()
+        public bool PlayNextStream()
         {
             if(stream_index < stream_uris.Count - 1) {
                 stream_index++;
                 Play();
+                return true;
+            }
+            return false;
+        }
+
+        public bool PlayPreviousStream()
+        {
+            if (stream_index > 0) {
+                stream_index--;
+                Play();
+                return true;
             }
+            return false;
         }
         
         private void LoadStreamUris()

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs	Thu Oct  2 00:36:15 2008
@@ -205,10 +205,12 @@
             if (uris == null || uris.Length == 0) {
                 return;
             }
-            
-            foreach (string uri in uris) {
-                PlaylistFileUtil.ImportPlaylistToLibrary (uri);
-            }
+
+            Banshee.Kernel.Scheduler.Schedule (new Banshee.Kernel.DelegateJob (delegate {
+                foreach (string uri in uris) {
+                    PlaylistFileUtil.ImportPlaylistToLibrary (uri);
+                }
+            }));
         }
         
         private void OnQuit (object o, EventArgs args)

Modified: trunk/banshee/src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue/FileSystemQueueSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue/FileSystemQueueSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.FileSystemQueue/Banshee.FileSystemQueue/FileSystemQueueSource.cs	Thu Oct  2 00:36:15 2008
@@ -140,7 +140,9 @@
                 }
             
                 if (PlaylistFileUtil.PathHasPlaylistExtension (path)) {
-                    PlaylistFileUtil.ImportPlaylistToLibrary (path, this, importer);
+                    Banshee.Kernel.Scheduler.Schedule (new DelegateJob (delegate {
+                        PlaylistFileUtil.ImportPlaylistToLibrary (path, this, importer);
+                    }));
                 } else {
                     importer.Enqueue (path);
                 }

Modified: trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSource.cs	Thu Oct  2 00:36:15 2008
@@ -39,13 +39,14 @@
 using Banshee.Collection;
 using Banshee.Collection.Database;
 using Banshee.Configuration;
+using Banshee.PlaybackController;
 
 using Banshee.Gui;
 using Banshee.Sources.Gui;
 
 namespace Banshee.InternetRadio
 {
-    public class InternetRadioSource : PrimarySource, IDisposable
+    public class InternetRadioSource : PrimarySource, IDisposable, IBasicPlaybackController
     {
         private InternetRadioSourceContents source_contents;
         private uint ui_id;
@@ -134,6 +135,7 @@
             ));
             
             ServiceManager.PlayerEngine.TrackIntercept += OnPlayerEngineTrackIntercept;
+            //ServiceManager.PlayerEngine.ConnectEvent (OnTrackInfoUpdated, Banshee.MediaEngine.PlayerEvent.TrackInfoUpdated);
             
             TrackEqualHandler = delegate (DatabaseTrackInfo a, TrackInfo b) {
                 RadioTrackInfo radio_track = b as RadioTrackInfo;
@@ -161,6 +163,8 @@
         public override void Dispose ()
         {
             base.Dispose ();
+
+            //ServiceManager.PlayerEngine.DisconnectEvent (OnTrackInfoUpdated);
             
             InterfaceActionService uia_service = ServiceManager.Get<InterfaceActionService> ();
             if (uia_service == null) {
@@ -175,6 +179,18 @@
             
             ServiceManager.PlayerEngine.TrackIntercept -= OnPlayerEngineTrackIntercept;
         }
+
+        // TODO the idea with this is to grab and display cover art when we get updated info
+        // for a radio station (eg it changes song and lets us know).  The problem is I'm not sure
+        // if that info ever/usually includes the album name, and also we would probably want to mark
+        // such downloaded/cached cover art as temporary.
+        /*private void OnTrackInfoUpdated (Banshee.MediaEngine.PlayerEventArgs args)
+        {
+            RadioTrackInfo radio_track = ServiceManager.PlaybackController.CurrentTrack as RadioTrackInfo;
+            if (radio_track != null) {
+                Banshee.Metadata.MetadataService.Instance.Lookup (radio_track);
+            }
+        }*/
         
         private bool OnPlayerEngineTrackIntercept (TrackInfo track)
         {
@@ -252,6 +268,36 @@
                 }
             }
         }
+
+        #region IBasicPlaybackController implementation 
+        
+        public bool First ()
+        {
+            return false;
+        }
+        
+        public bool Next (bool restart)
+        {
+            RadioTrackInfo radio_track = ServiceManager.PlaybackController.CurrentTrack as RadioTrackInfo;
+            if (radio_track != null && radio_track.PlayNextStream ()) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        
+        public bool Previous (bool restart)
+        {
+            RadioTrackInfo radio_track = ServiceManager.PlaybackController.CurrentTrack as RadioTrackInfo;
+            if (radio_track != null && radio_track.PlayPreviousStream ()) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        
+        #endregion 
+        
         
         public override bool CanDeleteTracks {
             get { return false; }

Modified: trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSourceContents.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSourceContents.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.InternetRadio/Banshee.InternetRadio/InternetRadioSourceContents.cs	Thu Oct  2 00:36:15 2008
@@ -47,7 +47,7 @@
 
 namespace Banshee.InternetRadio
 {
-    public class InternetRadioSourceContents : FilteredListSourceContents
+    public class InternetRadioSourceContents : FilteredListSourceContents, ITrackModelSourceContents
     {
         private TrackListView track_view;
         private QueryFilterView<string> genre_view;
@@ -77,7 +77,7 @@
             get { return "left"; }
         }
 
-#region Implement ISourceContents
+        #region Implement ISourceContents
 
         public override bool SetSource (ISource source)
         {
@@ -107,6 +107,14 @@
             genre_view.SetModel (null);
         }
 
-#endregion  
+        #endregion
+
+        #region ITrackModelSourceContents implementation 
+        
+        public IListView<TrackInfo> TrackView {
+            get { return track_view; }
+        }
+        
+        #endregion
     }
 }

Modified: trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	Thu Oct  2 00:36:15 2008
@@ -289,13 +289,13 @@
         
 #region IBasicPlaybackController
 
-        void IBasicPlaybackController.First ()
+        bool IBasicPlaybackController.First ()
         {
-            ((IBasicPlaybackController)this).Next (false);
+            return ((IBasicPlaybackController)this).Next (false);
         }
         
         private bool playback_requested;    
-        void IBasicPlaybackController.Next (bool restart)
+        bool IBasicPlaybackController.Next (bool restart)
         {
             TrackInfo next = NextTrack;
             if (next != null) {
@@ -303,10 +303,12 @@
             }  else {
                 playback_requested = true;
             }
+            return true;
         }
         
-        void IBasicPlaybackController.Previous (bool restart)
+        bool IBasicPlaybackController.Previous (bool restart)
         {
+            return true;
         }
         
 #endregion

Modified: trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	Thu Oct  2 00:36:15 2008
@@ -197,26 +197,28 @@
             }
         }
 
-        void IBasicPlaybackController.First ()
+        bool IBasicPlaybackController.First ()
         {
-            ((IBasicPlaybackController)this).Next (false);
+            return ((IBasicPlaybackController)this).Next (false);
         }
         
-        void IBasicPlaybackController.Next (bool restart)
+        bool IBasicPlaybackController.Next (bool restart)
         {
             RemovePlayingTrack ();
             
             if (Count == 0) {
                 ServiceManager.PlaybackController.Source = PriorSource;
                 ServiceManager.PlaybackController.Next (restart);
-                return;
+                return true;
             }
             
             ServiceManager.PlayerEngine.OpenPlay ((DatabaseTrackInfo)TrackModel[0]);
+            return true;
         }
         
-        void IBasicPlaybackController.Previous (bool restart)
+        bool IBasicPlaybackController.Previous (bool restart)
         {
+            return true;
         }
         
         private void RemovePlayingTrack ()

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting.Gui/PodcastSourceContents.cs	Thu Oct  2 00:36:15 2008
@@ -51,7 +51,7 @@
 
 namespace Banshee.Podcasting.Gui
 {
-    public class PodcastSourceContents : FilteredListSourceContents
+    public class PodcastSourceContents : FilteredListSourceContents, ITrackModelSourceContents
     {
         private PodcastItemView track_view;
         private PodcastFeedView feed_view;
@@ -89,7 +89,7 @@
             }
         }
 
-#region Implement ISourceContents
+        #region Implement ISourceContents
 
         public override bool SetSource (ISource source)
         {
@@ -131,7 +131,15 @@
             //Console.WriteLine ("PSC.reset_source 2");
         }
 
-#endregion        
+        #endregion
+
+        #region ITrackModelSourceContents implementation 
+        
+        public IListView<TrackInfo> TrackView {
+            get { return track_view; }
+        }
+        
+        #endregion 
 
         public static readonly SchemaEntry<int> VPanedPositionSchema = new SchemaEntry<int> (
             "plugins.podcasting", "vpaned_position", 120, "VPaned Position", ""

Modified: trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Podcasting/Banshee.Podcasting/PodcastImageFetchJob.cs	Thu Oct  2 00:36:15 2008
@@ -81,7 +81,7 @@
             
             if (SaveHttpStreamCover (new Uri (feed.ImageUrl), cover_art_id, null)) {
                 if (ServiceManager.SourceManager.ActiveSource is PodcastSource) {
-                    (ServiceManager.SourceManager.ActiveSource as PodcastSource).FeedModel.Reload ();
+                    (ServiceManager.SourceManager.ActiveSource as PodcastSource).Reload ();
                 }
                 return;
             }



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