diff -u -u -r1.22 Makefile.am
--- data/glade/Makefile.am	12 Mar 2006 00:45:32 -0000	1.22
+++ data/glade/Makefile.am	17 Apr 2006 15:15:49 -0000
@@ -17,7 +17,9 @@
 		daap-prefs.glade		\
 		uri.glade			\
 		plugins.glade			\
-		audioscrobbler-prefs.glade
+		audioscrobbler-prefs.glade	\
+		playlist-save.glade		\
+		$(NULL)
 
 gladedir = $(pkgdatadir)/glade
 
diff -u -u -r1.94 rb-playlist-manager.c
--- shell/rb-playlist-manager.c	3 Mar 2006 00:33:37 -0000	1.94
+++ shell/rb-playlist-manager.c	17 Apr 2006 15:15:52 -0000
@@ -49,6 +49,7 @@
 #include "rhythmdb.h"
 #include "rb-stock-icons.h"
 #include "eel-gconf-extensions.h"
+#include "rb-glade-helpers.h"
 
 #define RB_PLAYLIST_MGR_VERSION (xmlChar *) "1.0"
 #define RB_PLAYLIST_MGR_PL (xmlChar *) "rhythmdb-playlists"
@@ -1279,44 +1280,183 @@
 				 G_CALLBACK (load_playlist_response_cb), mgr, 0);
 }
 
+
+
+typedef struct {
+  const gchar *description;
+  /* NULL terminated array of extensions for this file format.  The first
+   * one is the prefered extension for files of this type. */
+  const gchar **extensions;
+  const RBPlaylistExportType type;
+} RBPlaylistExportFilter;
+
+static const char *m3u_extensions [] = {"m3u", NULL};
+static const char *pls_extensions [] = {"pls", NULL};
+
+static RBPlaylistExportFilter playlist_export_formats[] = {
+	{N_("MPEG Version 3.0 URL"), m3u_extensions, RB_PLAYLIST_EXPORT_TYPE_M3U},
+	{N_("Shoutcast playlist"), pls_extensions, RB_PLAYLIST_EXPORT_TYPE_PLS},
+};
+
 static void
 save_playlist_response_cb (GtkDialog *dialog,
 			   int response_id,
 			   RBPlaylistManager *mgr)
 {
 	char *file = NULL;
+	GtkWidget *menu;
+	gint index;
+	RBPlaylistExportType export_type = RB_PLAYLIST_EXPORT_TYPE_UNKNOWN;
 
-	if (response_id != GTK_RESPONSE_ACCEPT) {
+	if (response_id != GTK_RESPONSE_OK) {
 		gtk_widget_destroy (GTK_WIDGET (dialog));
 		return;
 	}
 
 	file = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+	if (file == NULL || file[0] == '\0')
+		return;
 
-	gtk_widget_destroy (GTK_WIDGET (dialog));
+	menu = g_object_get_data (G_OBJECT(dialog), "export-menu");
+	index = gtk_combo_box_get_active (GTK_COMBO_BOX (menu));
 
-	if (file == NULL)
-		return;
+	/* by extension selected */
+	if (index <= 0) {
+		int i;
+
+		for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
+			int j;
+
+			/* determine the playlist type from the extension */
+			for (j = 0; playlist_export_formats[i].extensions[j] != NULL; j++) {
+				if (g_str_has_prefix (file, playlist_export_formats[i].extensions[j])) {
+					export_type = playlist_export_formats[i].type;
+					break;
+				}
+			}
+		}
+	} else {
+		export_type = playlist_export_formats[index-1].type;
+	}
+
+	if (export_type == RB_PLAYLIST_EXPORT_TYPE_UNKNOWN) {
+			rb_error_dialog (NULL, _("Couldn't save playlist"), _("Unsupported file extension given."));
+	} else {
+		rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source), 
+						  file, (export_type == RB_PLAYLIST_EXPORT_TYPE_M3U));
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+	}
 
-	rb_playlist_source_save_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source), 
-					  file, FALSE);
 	g_free (file);
 }
 
 static void
+export_set_extension_cb (GtkWidget* widget, GtkDialog *dialog)
+{
+	gint index;
+	gchar *text;
+	gchar *last_dot;
+	const char *extension;
+	gchar *basename;
+	GString *basename_str;
+  
+	index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+	if (index <= 0)
+		return;
+
+	extension = playlist_export_formats[index-1].extensions[0];
+	if (extension == NULL)
+		return;
+	
+	text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+	if (text == NULL || text[0] == '\0') {
+		g_free (text);
+		return;
+	}
+
+	basename = g_path_get_basename (text);
+	basename_str = g_string_new (basename);
+	last_dot = g_utf8_strrchr (basename, -1, '.');
+	if (last_dot)
+		g_string_truncate (basename_str, (last_dot-basename));
+	g_free (basename);
+	g_free (text);
+  
+	g_string_append_printf (basename_str, ".%s", extension);
+	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename_str->str);
+	g_string_free (basename_str, TRUE);
+}
+
+static gchar *
+filter_get_export_filter_label (RBPlaylistExportFilter *efilter)
+{
+	GString *str;
+	gint ext;
+
+	str = g_string_new (_(efilter->description));
+	for (ext = 0; efilter->extensions[ext] != NULL; ext++) {
+		if (ext == 0)
+			g_string_append (str, " (*.");
+		else
+			g_string_append (str, ", *.");
+		g_string_append (str, efilter->extensions[ext]);
+	}
+    
+	if (ext > 0)
+		g_string_append (str, ")");
+    
+	return g_string_free (str, FALSE);
+}
+
+static void
+setup_format_menu (GtkWidget* menu, GtkWidget *dialog)
+{
+	GtkTreeModel *model;
+	int i;
+
+	model = gtk_combo_box_get_model (GTK_COMBO_BOX (menu));
+	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (menu), rb_combo_box_hyphen_separator_func,
+					      NULL, NULL);
+
+	for (i = 0; i < G_N_ELEMENTS (playlist_export_formats); i++) {
+		gchar *filter_label;
+		GtkTreeIter iter;
+    
+		filter_label = filter_get_export_filter_label (&playlist_export_formats[i]);
+		gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, -1,
+						   0, filter_label, -1);
+
+		g_free (filter_label);
+	}
+    
+	g_signal_connect_object (GTK_OBJECT (menu),
+				 "changed", G_CALLBACK (export_set_extension_cb),
+				 dialog, 0);
+}
+
+static void
 rb_playlist_manager_cmd_save_playlist (GtkAction *action,
 				       RBPlaylistManager *mgr)
 {
-	GtkWidget *dialog;
-    
-	dialog = rb_file_chooser_new (_("Save Playlist"),
-				      GTK_WINDOW (mgr->priv->window),
-				      GTK_FILE_CHOOSER_ACTION_SAVE,
-				      FALSE);
+	GladeXML *xml;
+	GtkWidget *dialog, *menu;
 
+	xml = rb_glade_xml_new ("playlist-save.glade",
+				"playlist_save_dialog",
+				mgr);
+	dialog = glade_xml_get_widget (xml, "playlist_save_dialog");
+
+	menu = glade_xml_get_widget (xml, "playlist_format_menu");
+	setup_format_menu (menu, dialog);
+	g_object_set_data (G_OBJECT (dialog), "export-menu", menu);
+	
+	/* FIXME: always has "by extension" as default (it should probably remember the last selection) */
+	gtk_combo_box_set_active (GTK_COMBO_BOX (menu), 0);
 	g_signal_connect_object (G_OBJECT (dialog), "response",
 				 G_CALLBACK (save_playlist_response_cb),
 				 mgr, 0);
+
+	g_object_unref (G_OBJECT (xml));
 }
 
 static void
diff -u -u -r1.22 rb-playlist-manager.h
--- shell/rb-playlist-manager.h	9 Apr 2006 07:37:29 -0000	1.22
+++ shell/rb-playlist-manager.h	17 Apr 2006 15:15:52 -0000
@@ -68,6 +68,13 @@
 	void	(*load_finish) (RBPlaylistManager *manager);
 } RBPlaylistManagerClass;
 
+typedef enum
+{
+	RB_PLAYLIST_EXPORT_TYPE_UNKNOWN,
+	RB_PLAYLIST_EXPORT_TYPE_M3U,
+	RB_PLAYLIST_EXPORT_TYPE_PLS,
+} RBPlaylistExportType;
+
 GType			rb_playlist_manager_get_type	(void);
 
 RBPlaylistManager *	rb_playlist_manager_new		(RBShell *shell,
--- /dev/null	2006-04-04 03:40:38.000000000 +1000
+++ data/glade/playlist-save.glade	2006-04-18 01:13:57.000000000 +1000
@@ -0,0 +1,176 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkFileChooserDialog" id="playlist_save_dialog">
+  <property name="visible">True</property>
+  <property name="action">GTK_FILE_CHOOSER_ACTION_SAVE</property>
+  <property name="local_only">True</property>
+  <property name="select_multiple">False</property>
+  <property name="show_hidden">False</property>
+  <property name="do_overwrite_confirmation">False</property>
+  <property name="title" translatable="yes">Save Playlist</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">gtk-save-as</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">24</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="button1">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-6</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="button2">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="has_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-open</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkFrame" id="frame1">
+	  <property name="visible">True</property>
+	  <property name="label_xalign">0</property>
+	  <property name="label_yalign">0.5</property>
+	  <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment1">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">12</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox1">
+		  <property name="border_width">5</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">12</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label2">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Select playlist format:</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkComboBox" id="playlist_format_menu">
+		      <property name="visible">True</property>
+		      <property name="items" translatable="yes">By extension</property>
+		      <property name="add_tearoffs">False</property>
+		      <property name="focus_on_click">True</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="label1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">&lt;b&gt;Playlist format&lt;/b&gt;</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="type">label_item</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
