? shell/rb-playlist-manager-glue.h
Index: data/glade/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/data/glade/Makefile.am,v
retrieving revision 1.21
diff -u -r1.21 Makefile.am
--- data/glade/Makefile.am	9 Dec 2005 16:41:08 -0000	1.21
+++ data/glade/Makefile.am	19 Feb 2006 13:26:00 -0000
@@ -14,6 +14,7 @@
 		create-playlist.glade		\
 		druid.glade			\
 		recorder.glade			\
+		exporter.glade			\
 		daap-prefs.glade		\
 		uri.glade			\
 		audioscrobbler-prefs.glade
Index: data/glade/exporter.glade
===================================================================
RCS file: data/glade/exporter.glade
diff -N data/glade/exporter.glade
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ data/glade/exporter.glade	19 Feb 2006 13:26:00 -0000
@@ -0,0 +1,296 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkWindow" id="window1">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">window1</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="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_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="recorder_vbox">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">12</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox2">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">12</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="message_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Create a music collection from playlist?</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">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">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">6</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkFrame" id="progress_frame">
+	  <property name="visible">True</property>
+	  <property name="sensitive">False</property>
+	  <property name="label_xalign">0</property>
+	  <property name="label_yalign">0.5</property>
+	  <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox2">
+	      <property name="border_width">6</property>
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkProgressBar" id="progress">
+		  <property name="visible">True</property>
+		  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+		  <property name="fraction">0</property>
+		  <property name="pulse_step">0.10000000149</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="progress_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes"></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</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>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="progress_frame_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Progress</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="type">label_item</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox3">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="location_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">_Location</property>
+	      <property name="use_underline">True</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="GtkFileChooserButton" id="location_filechooserbutton">
+	      <property name="visible">True</property>
+	      <property name="title" translatable="yes"></property>
+	      <property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
+	      <property name="local_only">True</property>
+	      <property name="show_hidden">False</property>
+	      <property name="do_overwrite_confirmation">False</property>
+	      <property name="width_chars">-1</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkExpander" id="expander1">
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="expanded">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox4">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkRadioButton" id="copy_radiobutton">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label" translatable="yes">_Copy songs</property>
+		  <property name="use_underline">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		  <property name="active">False</property>
+		  <property name="inconsistent">False</property>
+		  <property name="draw_indicator">True</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkRadioButton" id="symlink_radiobutton">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="label" translatable="yes">_Make symlinks to songs</property>
+		  <property name="use_underline">True</property>
+		  <property name="relief">GTK_RELIEF_NORMAL</property>
+		  <property name="focus_on_click">True</property>
+		  <property name="active">False</property>
+		  <property name="inconsistent">False</property>
+		  <property name="draw_indicator">True</property>
+		  <property name="group">copy_radiobutton</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="options_expander_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Options</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="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>
Index: shell/rb-playlist-manager.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-playlist-manager.c,v
retrieving revision 1.89
diff -u -r1.89 rb-playlist-manager.c
--- shell/rb-playlist-manager.c	17 Feb 2006 15:05:47 -0000	1.89
+++ shell/rb-playlist-manager.c	19 Feb 2006 13:26:02 -0000
@@ -70,6 +70,8 @@
 						   RBPlaylistManager *mgr);
 static void rb_playlist_manager_cmd_burn_playlist (GtkAction *action,
 						   RBPlaylistManager *mgr);
+static void rb_playlist_manager_cmd_export_playlist (GtkAction *action,
+						   RBPlaylistManager *mgr);
 static void rb_playlist_manager_cmd_new_playlist (GtkAction *action,
 						  RBPlaylistManager *mgr);
 static void rb_playlist_manager_cmd_new_automatic_playlist (GtkAction *action,
@@ -162,6 +164,9 @@
 	{ "MusicPlaylistBurnPlaylist", GTK_STOCK_CDROM, N_("_Create Audio CD..."), NULL,
 	  N_("Create an audio CD from playlist"),
 	  G_CALLBACK (rb_playlist_manager_cmd_burn_playlist) },
+	{ "MusicPlaylistExportPlaylist", GTK_STOCK_CDROM, N_("_Export playlist..."), NULL,
+	  N_("Create a music collection from playlist"),
+	  G_CALLBACK (rb_playlist_manager_cmd_export_playlist) },
 	{ "MusicPlaylistRenamePlaylist", NULL, N_("_Rename"), NULL,
 	  N_("Rename playlist"),
 	  G_CALLBACK (rb_playlist_manager_cmd_rename_playlist) },
@@ -1199,6 +1204,13 @@
 	g_signal_connect_object (G_OBJECT (dialog), "response",
 				 G_CALLBACK (save_playlist_response_cb),
 				 mgr, 0);
+}
+
+static void
+rb_playlist_manager_cmd_export_playlist (GtkAction *action,
+				       RBPlaylistManager *mgr)
+{
+	rb_playlist_source_export_playlist (RB_PLAYLIST_SOURCE (mgr->priv->selected_source));
 }
 
 static void
Index: sources/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/Makefile.am,v
retrieving revision 1.32
diff -u -r1.32 Makefile.am
--- sources/Makefile.am	21 Jan 2006 23:06:10 -0000	1.32
+++ sources/Makefile.am	19 Feb 2006 13:26:02 -0000
@@ -20,6 +20,8 @@
 	rb-audiocd-source.h		\
 	rb-playlist-source-recorder.c   \
 	rb-playlist-source-recorder.h	\
+	rb-playlist-source-exporter.c   \
+	rb-playlist-source-exporter.h	\
 	rb-playlist-source.c            \
 	rb-playlist-source.h		\
 	rb-playlist-xml.h		\
Index: sources/rb-playlist-source-exporter.c
===================================================================
RCS file: sources/rb-playlist-source-exporter.c
diff -N sources/rb-playlist-source-exporter.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sources/rb-playlist-source-exporter.c	19 Feb 2006 13:26:02 -0000
@@ -0,0 +1,852 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ *  arch-tag: Implementation of playlist source exporter object
+ *
+ *  Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003 Colin Walters <walters@gnome.org>
+ *  Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs-uri.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <glade/glade.h>
+
+#include "rb-file-helpers.h"
+#include "rb-glade-helpers.h"
+#include "rb-preferences.h"
+#include "rb-dialog.h"
+#include "rb-util.h"
+#include "rb-playlist-source.h"
+#include "rb-playlist-source-exporter.h"
+#include "rb-debug.h"
+#include "eel-gconf-extensions.h"
+
+static void rb_playlist_source_exporter_class_init (RBPlaylistSourceExporterClass *klass);
+static void rb_playlist_source_exporter_init       (RBPlaylistSourceExporter *source);
+static void rb_playlist_source_exporter_finalize   (GObject *object);
+
+typedef struct
+{
+        char   *artist;
+        char   *title;
+        char   *uri;
+        guint64 file_size;
+} RBExporterSong;
+
+typedef struct
+{
+        char *filename;
+        int   count;
+} UsedFilename;
+
+struct RBPlaylistSourceExporterPrivate
+{
+        GtkWidget   *parent;
+
+        char        *name;
+
+        GSList      *songs;
+        GSList      *current;
+
+        GtkWidget   *cd_icon;
+        GtkWidget   *vbox;
+        GtkWidget   *multiple_copies_checkbutton;
+        GtkWidget   *cancel_button;
+        GtkWidget   *burn_button;
+        GtkWidget   *message_label;
+        GtkWidget   *progress_label;
+        GtkWidget   *progress;
+        GtkWidget   *options_box;
+        GtkWidget   *progress_frame;
+        GtkWidget   *dir_selector;
+        GtkWidget   *copy_rbutton;
+        GtkWidget   *symlink_rbutton;
+
+        gboolean     burning;
+        gboolean     already_converted;
+        gboolean     handling_error;
+        gboolean     confirmed_exit;
+
+        char        *destination_dir;
+        GSList      *used_filename;
+        gboolean     is_copying;
+        GTimer      *timer;
+        gint64       file_size_total;
+        gint64       file_size_copied;
+};
+
+static GObjectClass *parent_class = NULL;
+
+typedef enum {
+        NAME_CHANGED,
+        FILE_ADDED,
+        ERROR,
+        LAST_SIGNAL
+} RBPlaylistSourceExporterSignalType;
+
+static guint rb_playlist_source_exporter_signals [LAST_SIGNAL] = { 0 };
+
+#define RB_PLAYLIST_SOURCE_EXPORTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYLIST_SOURCE_EXPORTER, RBPlaylistSourceExporterPrivate))
+
+G_DEFINE_TYPE(RBPlaylistSourceExporter, rb_playlist_source_exporter, GTK_TYPE_DIALOG)
+
+#define RB_EXPORTER_ERROR rb_playlist_source_exporter_error_quark ()
+
+static GQuark
+rb_playlist_source_exporter_error_quark (void)
+{
+        static GQuark quark = 0;
+        if (!quark)
+                quark = g_quark_from_static_string ("rb_exporter_error");
+
+        return quark;
+}
+
+static void
+rb_playlist_source_exporter_style_set (GtkWidget *widget,
+                                       GtkStyle  *previous_style)
+{
+        GtkDialog *dialog;
+
+        if (GTK_WIDGET_CLASS (parent_class)->style_set)
+                GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
+
+        dialog = GTK_DIALOG (widget);
+
+        gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
+        gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
+
+        gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
+        gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
+}
+
+
+static void
+rb_playlist_source_exporter_class_init (RBPlaylistSourceExporterClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+        parent_class = g_type_class_peek_parent (klass);
+
+        widget_class->style_set = rb_playlist_source_exporter_style_set;
+
+        object_class->finalize = rb_playlist_source_exporter_finalize;
+
+        rb_playlist_source_exporter_signals [NAME_CHANGED] =
+                g_signal_new ("name_changed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
+        
+        rb_playlist_source_exporter_signals [FILE_ADDED] =
+                g_signal_new ("file_added",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
+
+        g_type_class_add_private (klass, sizeof (RBPlaylistSourceExporterPrivate));
+}
+
+static void
+set_message_text (RBPlaylistSourceExporter *source,
+                  const char               *message,
+                  ...)
+{
+        char   *markup;
+        char   *text = "";
+        va_list args;
+
+        va_start (args, message);
+        g_vasprintf (&text, message, args);
+        va_end (args);
+
+        markup = g_strdup_printf ("%s", text);
+
+        gtk_label_set_text (GTK_LABEL (source->priv->message_label), markup);
+        g_free (markup);
+        g_free (text);
+}
+
+static gboolean
+response_idle_cb (RBPlaylistSourceExporter *source)
+{
+
+        gtk_dialog_response (GTK_DIALOG (source),
+                             GTK_RESPONSE_CANCEL);
+
+        return FALSE;
+}
+
+static gboolean
+error_dialog_response_cb (GtkWidget *dialog,
+                          gint       response_id,
+                          gpointer   data)
+{
+        RBPlaylistSourceExporter *source = RB_PLAYLIST_SOURCE_EXPORTER (data);
+
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+
+        g_idle_add ((GSourceFunc)response_idle_cb, source);
+
+        return TRUE;
+}
+
+static void
+error_dialog (RBPlaylistSourceExporter *source,
+              const char               *primary,
+              const char               *secondary,
+              ...)
+{
+        char      *text = "";
+        va_list    args;
+        GtkWidget *dialog;
+
+        va_start (args, secondary);
+        g_vasprintf (&text, secondary, args);
+        va_end (args);
+
+        dialog = gtk_message_dialog_new (GTK_WINDOW (source),
+                                         GTK_DIALOG_DESTROY_WITH_PARENT,
+                                         GTK_MESSAGE_ERROR,
+                                         GTK_BUTTONS_CLOSE,
+                                         primary);
+
+        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                  text);
+
+        gtk_window_set_title (GTK_WINDOW (dialog), "");
+
+        gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+
+        g_signal_connect (dialog,
+                          "response",
+                          G_CALLBACK (error_dialog_response_cb),
+                          source);
+
+        gtk_widget_show (dialog);
+
+        g_free (text);
+}
+
+static char *
+get_song_description (RBExporterSong *song)
+{
+        char *desc = NULL;
+
+        if (song->artist && song->title)
+                desc = g_strdup_printf ("%s - %s", song->artist, song->title);
+        else if (song->title)
+                desc = g_strdup (song->title);
+        else if (song->artist)
+                desc = g_strdup (song->artist);
+
+        return desc;
+}
+
+/* Adapted from totem_time_to_string_text */
+static char *
+time_to_string_text (long time)
+{
+	char *secs, *mins, *hours, *string;
+	int sec, min, hour;
+
+	sec = time % 60;
+	time = time - sec;
+	min = (time % (60 * 60)) / 60;
+	time = time - (min * 60);
+	hour = time / (60 * 60);
+
+	hours = g_strdup_printf (ngettext ("%d hour", "%d hours", hour), hour);
+
+	mins = g_strdup_printf (ngettext ("%d minute",
+					  "%d minutes", min), min);
+
+	secs = g_strdup_printf (ngettext ("%d second",
+					  "%d seconds", sec), sec);
+
+	if (hour > 0) {
+		/* hour:minutes:seconds */
+		string = g_strdup_printf (_("%s %s %s"), hours, mins, secs);
+	} else if (min > 0) {
+		/* minutes:seconds */
+		string = g_strdup_printf (_("%s %s"), mins, secs);
+	} else if (sec > 0) {
+		/* seconds */
+		string = g_strdup_printf (_("%s"), secs);
+	} else {
+		/* 0 seconds */
+		string = g_strdup (_("0 seconds"));
+	}
+
+	g_free (hours);
+	g_free (mins);
+	g_free (secs);
+
+	return string;
+}
+
+static void
+progress_set_time (GtkWidget *progress,
+                   long       seconds)
+{
+        char *text;
+
+	if (seconds >= 0) {
+		char *remaining;
+		remaining = time_to_string_text (seconds);
+		text = g_strdup_printf (_("About %s left"), remaining);
+		g_free (remaining);
+	} else {
+		text = g_strdup (" ");
+	}
+
+        gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), text);
+        g_free (text);
+}
+
+static void
+progress_set_fraction (GtkWidget *progress,
+                       gdouble    fraction)
+{
+        gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), fraction);
+}
+
+static gboolean
+write_file (RBPlaylistSourceExporter *source)
+{
+        RBExporterSong *song   = source->priv->current->data;
+        char           *cdtext = NULL;
+        char           *markup;
+	char           *target;
+	char           *ext;
+	GnomeVFSURI    *uri, *uri_source;
+	GnomeVFSResult  result;
+	GSList         *used_filename_iter;
+	UsedFilename   *used_filename;
+	gdouble         elapsed;
+
+        gtk_widget_set_sensitive (source->priv->progress_frame, TRUE);
+
+        cdtext = get_song_description (song);
+
+        markup = g_markup_printf_escaped ("<i>'%s'</i>", cdtext);
+        gtk_label_set_markup (GTK_LABEL (source->priv->progress_label), markup);
+        g_free (markup);
+
+	progress_set_fraction(source->priv->progress,
+			      (gdouble)source->priv->file_size_copied/source->priv->file_size_total);
+	elapsed = g_timer_elapsed(source->priv->timer,NULL);
+	progress_set_time(source->priv->progress,
+			  elapsed*source->priv->file_size_total/source->priv->file_size_copied-elapsed);
+	
+	uri_source = gnome_vfs_uri_new (song->uri);
+	ext = strrchr(uri_source->text,'.');
+	if (ext)
+		target = g_strdup_printf("%s/%s%s",source->priv->destination_dir,cdtext,ext);
+	else
+		target = g_strdup_printf("%s/%s",source->priv->destination_dir,cdtext);
+	used_filename_iter = source->priv->used_filename;
+	while (used_filename_iter) {
+		used_filename = (UsedFilename *)used_filename_iter->data;
+		if (!strcmp(target,used_filename->filename)) {
+			used_filename->count ++;
+			g_free (target);
+			if (ext)
+				target = g_strdup_printf("%s/%s(%d)%s",source->priv->destination_dir,cdtext,used_filename->count,ext);
+			else
+				target = g_strdup_printf("%s/%s(%d)",source->priv->destination_dir,cdtext,used_filename->count);
+			break;
+		}
+		used_filename_iter = used_filename_iter->next;
+	}
+	/* Not found */
+	if (!used_filename_iter) {
+		used_filename = g_new0(UsedFilename,1);
+		used_filename->filename = target;
+		source->priv->used_filename = g_slist_append (source->priv->used_filename, used_filename);
+	}
+	uri = gnome_vfs_uri_new (target);
+        g_free (cdtext);
+
+	if (source->priv->is_copying)
+		result = gnome_vfs_xfer_uri (uri_source, uri, GNOME_VFS_XFER_DEFAULT, GNOME_VFS_XFER_ERROR_MODE_ABORT, GNOME_VFS_XFER_OVERWRITE_MODE_ABORT, NULL, NULL);
+	else
+		result = gnome_vfs_create_symbolic_link (uri, song->uri);
+	source->priv->file_size_copied += song->file_size;
+	gnome_vfs_uri_unref (uri_source);
+	gnome_vfs_uri_unref (uri);
+
+	if (result != GNOME_VFS_OK) {
+		error_dialog (source,
+			      _("Export error"),
+			      gnome_vfs_result_to_string (result));
+		return FALSE;
+	}
+
+	if (source->priv->current->next) {
+                source->priv->current = source->priv->current->next;
+		return TRUE;
+        } else {
+		set_message_text (source, _("Finished creating audio CD."));
+		gtk_widget_set_sensitive (GTK_WIDGET (source), FALSE);
+		g_idle_add ((GSourceFunc)response_idle_cb, source);
+	}
+	return FALSE;
+}
+
+static void
+interrupt_burn_dialog_response_cb (GtkDialog *dialog,
+                                   gint       response_id,
+                                   gpointer   data)
+{
+        RBPlaylistSourceExporter *source = RB_PLAYLIST_SOURCE_EXPORTER (data);
+
+        if (response_id == GTK_RESPONSE_ACCEPT) {
+                if (source->priv->burning) {
+                        /* FIXME */
+                } else {
+                        source->priv->confirmed_exit = TRUE;
+                        gtk_dialog_response (GTK_DIALOG (source),
+                                             GTK_RESPONSE_CANCEL);
+                }
+        } else {
+                source->priv->confirmed_exit = FALSE;
+        }
+
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+response_cb (GtkDialog *dialog,
+             gint       response_id)
+{
+        RBPlaylistSourceExporter *source = RB_PLAYLIST_SOURCE_EXPORTER (dialog);
+
+        /* Act only on response IDs we recognize */
+        if (!(response_id == GTK_RESPONSE_ACCEPT
+              || response_id == GTK_RESPONSE_CANCEL)) {
+                g_signal_stop_emission_by_name (dialog, "response");
+                return;
+        }
+
+        if (response_id == GTK_RESPONSE_CANCEL) {
+                if (source->priv->burning
+                    && !source->priv->confirmed_exit) {
+                        GtkWidget *interrupt_dialog;
+
+                        source->priv->confirmed_exit = FALSE;
+
+                        interrupt_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
+                                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                                   GTK_MESSAGE_QUESTION,
+                                                                   GTK_BUTTONS_NONE,
+                                                                   _("Do you wish to interrupt writing this disc?"));
+
+                        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (interrupt_dialog),
+                                                                  _("This may result in an unusable disc."));
+
+                        gtk_window_set_title (GTK_WINDOW (interrupt_dialog), "");
+
+                        gtk_container_set_border_width (GTK_CONTAINER (interrupt_dialog), 6);
+
+                        gtk_dialog_add_buttons (GTK_DIALOG (interrupt_dialog),
+                                                _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                                _("_Interrupt"), GTK_RESPONSE_ACCEPT,
+                                                NULL);
+
+                        gtk_dialog_set_default_response (GTK_DIALOG (interrupt_dialog),
+                                                         GTK_RESPONSE_CANCEL);
+
+                        g_signal_connect (interrupt_dialog,
+                                          "response",
+                                          G_CALLBACK (interrupt_burn_dialog_response_cb),
+                                          source);
+
+                        gtk_widget_show (interrupt_dialog);
+
+                        g_signal_stop_emission_by_name (dialog, "response");
+                }
+                return;
+        }
+
+        if (response_id == GTK_RESPONSE_ACCEPT) {
+                rb_playlist_source_exporter_start (source);
+                g_signal_stop_emission_by_name (dialog, "response");
+        }
+}
+
+static int
+delete_event_handler (GtkWidget   *widget,
+                      GdkEventAny *event,
+                      gpointer     user_data)
+{
+        /* emit response signal */
+        gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
+
+        /* Do the destroy by default */
+        return TRUE;
+}
+
+static void
+rb_playlist_source_exporter_init (RBPlaylistSourceExporter *source)
+{
+        GladeXML       *xml;
+        GtkWidget      *widget;
+        GtkWidget      *hbox;
+        int             font_size;
+        PangoAttrList  *pattrlist;
+        PangoAttribute *attr;
+
+        g_signal_connect (GTK_DIALOG (source),
+                          "delete_event",
+                          G_CALLBACK (delete_event_handler),
+                          NULL);
+
+        source->priv = RB_PLAYLIST_SOURCE_EXPORTER_GET_PRIVATE (source);
+
+	source->priv->timer = g_timer_new ();
+	
+        gtk_window_set_resizable (GTK_WINDOW (source), FALSE);
+        gtk_dialog_set_has_separator (GTK_DIALOG (source), FALSE);
+        source->priv->cancel_button =  gtk_dialog_add_button (GTK_DIALOG (source),
+                                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+        source->priv->burn_button = gtk_button_new ();
+        GTK_WIDGET_SET_FLAGS (source->priv->burn_button, GTK_CAN_DEFAULT);
+
+        widget = gtk_alignment_new (0.5, 0.5, 0, 0);
+        gtk_container_add (GTK_CONTAINER (source->priv->burn_button), widget);
+        gtk_widget_show (widget);
+        hbox = gtk_hbox_new (FALSE, 6);
+        gtk_container_add (GTK_CONTAINER (widget), hbox);
+        gtk_widget_show (hbox);
+        widget = gtk_image_new_from_stock (GTK_STOCK_CDROM, GTK_ICON_SIZE_BUTTON);
+        source->priv->cd_icon = widget;
+        g_object_ref (source->priv->cd_icon);
+        gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+        gtk_widget_show (widget);
+        widget = gtk_label_new_with_mnemonic (_("C_reate"));
+        gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
+        gtk_widget_show (widget);
+        gtk_dialog_add_action_widget (GTK_DIALOG (source),
+                                      source->priv->burn_button,
+                                      GTK_RESPONSE_ACCEPT);
+        gtk_widget_show (source->priv->burn_button);
+
+        gtk_dialog_set_default_response (GTK_DIALOG (source), GTK_RESPONSE_ACCEPT);
+
+
+        xml = rb_glade_xml_new ("exporter.glade",
+                                "recorder_vbox",
+                                source);
+
+        source->priv->vbox = glade_xml_get_widget (xml, "recorder_vbox");
+
+        source->priv->message_label  = glade_xml_get_widget (xml, "message_label");
+        source->priv->progress_label  = glade_xml_get_widget (xml, "progress_label");
+
+        source->priv->progress = glade_xml_get_widget (xml, "progress");
+        gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (source->priv->progress), PANGO_ELLIPSIZE_END);
+        gtk_progress_bar_set_text (GTK_PROGRESS_BAR (source->priv->progress), " ");
+        gtk_widget_set_size_request (source->priv->progress, 400, -1);
+
+        source->priv->progress_frame = glade_xml_get_widget (xml, "progress_frame");
+
+        source->priv->options_box    = glade_xml_get_widget (xml, "options_box");
+        source->priv->dir_selector = glade_xml_get_widget (xml, "location_filechooserbutton");
+
+        source->priv->copy_rbutton = glade_xml_get_widget (xml, "copy_radiobutton");
+
+        source->priv->symlink_rbutton = glade_xml_get_widget (xml, "symlink_radiobutton");
+
+	rb_glade_boldify_label (xml, "progress_frame_label");
+	rb_glade_boldify_label (xml, "options_expander_label");
+	rb_glade_boldify_label (xml, "location_label");
+
+        pattrlist = pango_attr_list_new ();
+        attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+        attr->start_index = 0;
+        attr->end_index = G_MAXINT;
+        pango_attr_list_insert (pattrlist, attr);
+
+        font_size = pango_font_description_get_size (GTK_WIDGET (source->priv->message_label)->style->font_desc);
+        attr = pango_attr_size_new (font_size * 1.2);
+        attr->start_index = 0;
+        attr->end_index = G_MAXINT;
+        pango_attr_list_insert (pattrlist, attr);
+
+        gtk_label_set_attributes (GTK_LABEL (source->priv->message_label), pattrlist);
+
+        pango_attr_list_unref (pattrlist);
+
+        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (source)->vbox),
+                            source->priv->vbox,
+                            TRUE, TRUE, 0);
+        gtk_widget_show_all (source->priv->vbox);
+
+        g_signal_connect (GTK_DIALOG (source), "response",
+                          G_CALLBACK (response_cb), NULL);
+}
+
+static RBExporterSong *
+exporter_song_new ()
+{
+        RBExporterSong *song = g_new0 (RBExporterSong, 1);
+        return song;
+}
+
+static void
+exporter_song_free (RBExporterSong *song)
+{
+        g_return_if_fail (song != NULL);
+
+        g_free (song->title);
+        g_free (song->uri);
+        g_free (song);
+}
+
+static void
+free_song_list (GSList *songs)
+{
+        GSList *l;
+
+        for (l = songs; l; l = l->next) {
+                exporter_song_free ((RBExporterSong *)l->data);
+        }
+
+        g_slist_free (songs);
+        songs = NULL;
+}
+
+static void
+rb_playlist_source_exporter_finalize (GObject *object)
+{
+        RBPlaylistSourceExporter *source;
+	GSList                   *used_filename_iter;
+	UsedFilename             *used_filename;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (RB_IS_PLAYLIST_SOURCE_EXPORTER (object));
+
+        source = RB_PLAYLIST_SOURCE_EXPORTER (object);
+
+        g_return_if_fail (source->priv != NULL);
+
+        rb_debug ("Finalize source exporter");
+
+        g_object_unref (source->priv->cd_icon);
+
+        g_free (source->priv->name);
+	
+        source->priv->name = NULL;
+
+	if (source->priv->destination_dir) {
+		g_free (source->priv->destination_dir);
+		source->priv->destination_dir = NULL;
+	}
+
+	used_filename_iter = source->priv->used_filename;
+	while (used_filename_iter) {
+		used_filename = (UsedFilename *)used_filename_iter->data;
+		g_free (used_filename->filename);
+		g_free (used_filename);
+		used_filename_iter = used_filename_iter->next;
+	}
+
+	g_slist_free (source->priv->used_filename);
+
+        free_song_list (source->priv->songs);
+
+	g_timer_destroy (source->priv->timer);
+
+        G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GtkWidget *
+rb_playlist_source_exporter_new (GtkWidget  *parent,
+                                 RBShell    *shell,
+                                 const char *name)
+{
+        GtkWidget *result;
+        RBPlaylistSourceExporter *source;
+        
+        result = g_object_new (RB_TYPE_PLAYLIST_SOURCE_EXPORTER,
+                               "title", _("Create Audio CD"),
+                               NULL);
+        
+        source = RB_PLAYLIST_SOURCE_EXPORTER (result);
+        if (parent) {
+                source->priv->parent = gtk_widget_get_toplevel (parent);
+
+                gtk_window_set_transient_for (GTK_WINDOW (source),
+                                              GTK_WINDOW (source->priv->parent));
+                gtk_window_set_destroy_with_parent (GTK_WINDOW (source), TRUE);
+        }
+
+        if (name) {
+                source->priv->name = g_strdup (name);
+
+                set_message_text (source, _("Create audio CD from '%s' playlist?"), name);
+        }
+
+        return result;
+}
+
+void
+rb_playlist_source_exporter_set_name (RBPlaylistSourceExporter *source,
+                                      const char               *name,
+                                      GError                  **error)
+{
+        g_return_if_fail (source != NULL);
+        g_return_if_fail (RB_IS_PLAYLIST_SOURCE_EXPORTER (source));
+
+        g_return_if_fail (name != NULL);
+
+        g_free (source->priv->name);
+        source->priv->name = g_strdup (name);
+
+        g_signal_emit (G_OBJECT (source),
+                       rb_playlist_source_exporter_signals [NAME_CHANGED],
+                       0,
+                       name);
+}
+
+gboolean
+rb_playlist_source_exporter_add_from_model (RBPlaylistSourceExporter *source,
+                                            GtkTreeModel             *model,
+                                            RBPlaylistSourceExporterIterFunc  func,
+                                            GError                  **error)
+{
+        GtkTreeIter iter;
+        gboolean    failed;
+        GSList     *songs  = NULL;
+        GSList     *l;
+
+        g_return_val_if_fail (source != NULL, FALSE);
+        g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE_EXPORTER (source), FALSE);
+
+        g_return_val_if_fail (model != NULL, FALSE);
+
+        if (! gtk_tree_model_get_iter_first (model, &iter)) {
+                g_set_error (error,
+                             RB_EXPORTER_ERROR,
+                             0,
+                             _("Unable to build an audio track list."));
+                return FALSE;
+        }
+
+        /* Make sure we can use all of the songs before we
+           modify the song list */
+        failed = FALSE;
+        do {
+                RBExporterSong *song = exporter_song_new ();
+                gboolean        res;
+
+                res = func (model, &iter, &song->uri, &song->artist, &song->title, &song->file_size);
+                if (! res) {
+                        failed = TRUE;
+                        g_set_error (error,
+                                     RB_EXPORTER_ERROR,
+                                     0,
+                                     _("Unable to build an audio track list."));
+                        break;
+                }
+
+                songs = g_slist_append (songs, song);
+        } while (gtk_tree_model_iter_next (model, &iter));
+
+        if (failed) {
+                free_song_list (songs);
+
+                return FALSE;
+        }
+
+        /* now that we've checked all the songs, add them to the song list */
+        for (l = songs; l; l = l->next) {
+                RBExporterSong *song = l->data;
+
+                source->priv->songs = g_slist_append (source->priv->songs, song);
+
+                g_signal_emit (G_OBJECT (source),
+                               rb_playlist_source_exporter_signals [FILE_ADDED],
+                               0,
+                               song->uri);
+        }
+
+        return TRUE;
+}
+
+void
+rb_playlist_source_exporter_start (RBPlaylistSourceExporter *source)
+{
+	gchar          *uri;
+	GnomeVFSResult  result;
+	GSList         *list_iter;
+
+        g_return_if_fail (source != NULL);
+        g_return_if_fail (RB_IS_PLAYLIST_SOURCE_EXPORTER (source));
+
+	source->priv->file_size_total = 0;
+	source->priv->file_size_copied = 0;
+	for (list_iter = source->priv->songs;list_iter;list_iter = list_iter->next)
+		source->priv->file_size_total += ((RBExporterSong*)list_iter->data)->file_size;
+        source->priv->current = source->priv->songs;
+	source->priv->is_copying = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source->priv->copy_rbutton));
+
+        gtk_widget_set_sensitive (source->priv->burn_button, FALSE);
+        gtk_widget_set_sensitive (source->priv->options_box, FALSE);
+
+	uri = gtk_file_chooser_get_current_folder ( GTK_FILE_CHOOSER (source->priv->dir_selector));
+	source->priv->destination_dir = g_strdup_printf("%s/%s",uri,source->priv->name);
+	result = gnome_vfs_make_directory (source->priv->destination_dir,GNOME_VFS_PERM_USER_ALL);
+	g_free (uri);
+
+	if (result != GNOME_VFS_OK) {
+		error_dialog (source,
+			      _("Export error"),
+			      gnome_vfs_result_to_string (result));
+		return;
+	}
+
+        progress_set_fraction (source->priv->progress, 0);
+        progress_set_time (source->priv->progress, -1);
+
+	g_timer_start (source->priv->timer);
+	g_idle_add((GSourceFunc)write_file, source);
+}
Index: sources/rb-playlist-source-exporter.h
===================================================================
RCS file: sources/rb-playlist-source-exporter.h
diff -N sources/rb-playlist-source-exporter.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sources/rb-playlist-source-exporter.h	19 Feb 2006 13:26:02 -0000
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ *  arch-tag: Header for playlist music collection creation object
+ *
+ *  Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
+ *  Copyright (C) 2003,2004 Colin Walters <walters@redhat.com>
+ *  Copyright (C) 2004 William Jon McCann <mccann@jhu.edu>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_PLAYLIST_SOURCE_EXPORTER_H
+#define __RB_PLAYLIST_SOURCE_EXPORTER_H
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_PLAYLIST_SOURCE_EXPORTER         (rb_playlist_source_exporter_get_type ())
+#define RB_PLAYLIST_SOURCE_EXPORTER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_PLAYLIST_SOURCE_EXPORTER, RBPlaylistSourceExporter))
+#define RB_PLAYLIST_SOURCE_EXPORTER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_PLAYLIST_SOURCE_EXPORTER, RBPlaylistSourceExporterClass))
+#define RB_IS_PLAYLIST_SOURCE_EXPORTER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_PLAYLIST_SOURCE_EXPORTER))
+#define RB_IS_PLAYLIST_SOURCE_EXPORTER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_PLAYLIST_SOURCE_EXPORTER))
+#define RB_PLAYLIST_SOURCE_EXPORTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_PLAYLIST_SOURCE_EXPORTER, RBPlaylistSourceExporterClass))
+
+typedef struct RBPlaylistSourceExporterPrivate RBPlaylistSourceExporterPrivate;
+
+typedef struct
+{
+        GtkDialog                        parent;
+        RBPlaylistSourceExporterPrivate *priv;
+} RBPlaylistSourceExporter;
+
+typedef struct
+{
+        GtkDialogClass parent_class;
+} RBPlaylistSourceExporterClass;
+
+typedef gboolean (*RBPlaylistSourceExporterIterFunc) (GtkTreeModel *model,
+                                                      GtkTreeIter  *iter,
+                                                      char        **uri,
+                                                      char        **artist,
+                                                      char        **title,
+                                                      guint64      *duration);
+
+GType       rb_playlist_source_exporter_get_type (void);
+
+GtkWidget * rb_playlist_source_exporter_new            (GtkWidget                *parent,
+                                                        RBShell                  *shell,
+                                                        const char               *name);
+
+void        rb_playlist_source_exporter_set_name       (RBPlaylistSourceExporter *exporter,
+                                                        const char               *name,
+                                                        GError                  **error);
+gboolean    rb_playlist_source_exporter_add_from_model (RBPlaylistSourceExporter *exporter,
+                                                        GtkTreeModel             *model,
+                                                        RBPlaylistSourceExporterIterFunc  func,
+                                                        GError                  **error);
+void        rb_playlist_source_exporter_add_uri        (RBPlaylistSourceExporter *exporter,
+                                                        const char               *uri,
+                                                        GError                  **error);
+void        rb_playlist_source_exporter_start          (RBPlaylistSourceExporter *exporter);
+void        rb_playlist_source_exporter_stop           (RBPlaylistSourceExporter *exporter,
+                                                        GError                  **error);
+
+G_END_DECLS
+
+#endif /* __RB_PLAYLIST_SOURCE_EXPORTER_H */
Index: sources/rb-playlist-source-recorder.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-playlist-source-recorder.c,v
retrieving revision 1.23
diff -u -r1.23 rb-playlist-source-recorder.c
--- sources/rb-playlist-source-recorder.c	17 Jan 2006 15:14:01 -0000	1.23
+++ sources/rb-playlist-source-recorder.c	19 Feb 2006 13:26:03 -0000
@@ -1407,7 +1407,7 @@
 gboolean
 rb_playlist_source_recorder_add_from_model (RBPlaylistSourceRecorder *source,
                                             GtkTreeModel             *model,
-                                            RBPlaylistSourceIterFunc  func,
+                                            RBPlaylistSourceRecorderIterFunc  func,
                                             GError                  **error)
 {
         GtkTreeIter iter;
Index: sources/rb-playlist-source-recorder.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-playlist-source-recorder.h,v
retrieving revision 1.3
diff -u -r1.3 rb-playlist-source-recorder.h
--- sources/rb-playlist-source-recorder.h	29 Nov 2005 01:40:49 -0000	1.3
+++ sources/rb-playlist-source-recorder.h	19 Feb 2006 13:26:03 -0000
@@ -47,12 +47,12 @@
         GtkDialogClass parent_class;
 } RBPlaylistSourceRecorderClass;
 
-typedef gboolean (*RBPlaylistSourceIterFunc) (GtkTreeModel *model,
-                                              GtkTreeIter  *iter,
-                                              char        **uri,
-                                              char        **artist,
-                                              char        **title,
-                                              gulong       *duration);
+typedef gboolean (*RBPlaylistSourceRecorderIterFunc) (GtkTreeModel *model,
+                                                      GtkTreeIter  *iter,
+                                                      char        **uri,
+                                                      char        **artist,
+                                                      char        **title,
+                                                      gulong       *duration);
 
 GType       rb_playlist_source_recorder_get_type (void);
 
@@ -65,7 +65,7 @@
                                                         GError                  **error);
 gboolean    rb_playlist_source_recorder_add_from_model (RBPlaylistSourceRecorder *recorder,
                                                         GtkTreeModel             *model,
-                                                        RBPlaylistSourceIterFunc  func,
+                                                        RBPlaylistSourceRecorderIterFunc  func,
                                                         GError                  **error);
 void        rb_playlist_source_recorder_add_uri        (RBPlaylistSourceRecorder *recorder,
                                                         const char               *uri,
Index: sources/rb-playlist-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-playlist-source.c,v
retrieving revision 1.110
diff -u -r1.110 rb-playlist-source.c
--- sources/rb-playlist-source.c	18 Feb 2006 06:56:18 -0000	1.110
+++ sources/rb-playlist-source.c	19 Feb 2006 13:26:03 -0000
@@ -41,6 +41,7 @@
 #include "rb-util.h"
 #include "rb-playlist-source.h"
 #include "rb-playlist-source-recorder.h"
+#include "rb-playlist-source-exporter.h"
 #include "rb-debug.h"
 #include "eel-gconf-extensions.h"
 #include "rb-song-info.h"
@@ -507,6 +508,21 @@
 }
 
 static gboolean
+export_playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **artist, char **title, guint64 *file_size)
+{
+	RhythmDBEntry *entry;
+
+	gtk_tree_model_get (model, iter, 0, &entry, -1);
+
+	*uri = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_LOCATION);
+	*title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
+	*artist = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
+	*file_size = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+
+	return TRUE;
+}
+
+static gboolean
 burn_playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **artist, char **title, gulong *duration)
 {
 	RhythmDBEntry *entry;
@@ -570,6 +586,55 @@
 	gtk_widget_show (recorder);
 }
 
+void
+rb_playlist_source_export_playlist (RBPlaylistSource *source)
+{
+	GtkWidget *exporter;
+	GtkWidget *parent;
+	char *name;
+	RBShell *shell;
+	gboolean res;
+	GError *error;
+
+	/* don't burn if the playlist is empty */
+	if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (source->priv->model), NULL) == 0)
+		return;
+
+	rb_debug ("burning playlist");
+
+	g_object_get (source, "name", &name, "shell", &shell, NULL);
+
+	parent = gtk_widget_get_toplevel (GTK_WIDGET (source));
+	exporter = rb_playlist_source_exporter_new (parent,
+						    shell,
+						    name);
+	g_object_unref (shell);
+	g_free (name);
+
+	error = NULL;
+	res = rb_playlist_source_exporter_add_from_model (RB_PLAYLIST_SOURCE_EXPORTER (exporter),
+							  GTK_TREE_MODEL (source->priv->model),
+							  export_playlist_iter_func,
+							  &error);
+	if (! res) {
+		rb_error_dialog (GTK_WINDOW (parent),
+				 _("Unable to create audio CD"),
+				 "%s", error->message);
+		g_error_free (error);
+
+		gtk_widget_destroy (exporter);
+
+		return;
+	}
+
+        g_signal_connect (exporter,
+			  "response",
+			  G_CALLBACK (gtk_widget_destroy),
+			  NULL);
+
+	gtk_widget_show (exporter);
+}
+
 /* Adapted from yelp-toc-pager.c */
 static xmlChar *
 xml_get_and_trim_names (xmlNodePtr node)
@@ -837,6 +902,7 @@
 	GList *actions = NULL;
 
 	actions = g_list_prepend (actions, "MusicPlaylistBurnPlaylist");
+	actions = g_list_prepend (actions, "MusicPlaylistExportPlaylist");
 
 	return actions;
 }
Index: sources/rb-playlist-source.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-playlist-source.h,v
retrieving revision 1.22
diff -u -r1.22 rb-playlist-source.h
--- sources/rb-playlist-source.h	6 Feb 2006 10:38:44 -0000	1.22
+++ sources/rb-playlist-source.h	19 Feb 2006 13:26:03 -0000
@@ -71,6 +71,7 @@
 						 xmlNodePtr node);
 
 void		rb_playlist_source_burn_playlist(RBPlaylistSource *source);
+void		rb_playlist_source_export_playlist(RBPlaylistSource *source);
 
 /* methods for subclasses to call */
 
