[gnac/file-chooser] Improved file filters



commit aa0cf78c43d4a01470953bb6aa3a78d33a635643
Author: Benoît Dupasquier <bdupasqu src gnome org>
Date:   Tue Jun 8 21:23:10 2010 +0100

    Improved file filters

 data/ui/gnac.xml    |  105 +++++++++++++
 src/gnac-main.c     |  318 ++-------------------------------------
 src/gnac-main.h     |    5 -
 src/gnac-playlist.c |   98 +++++++++----
 src/gnac-playlist.h |    7 +
 src/gnac-ui.c       |  411 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/gnac-ui.h       |   30 ++++
 7 files changed, 635 insertions(+), 339 deletions(-)
---
diff --git a/data/ui/gnac.xml b/data/ui/gnac.xml
index 1141a46..39a5a35 100644
--- a/data/ui/gnac.xml
+++ b/data/ui/gnac.xml
@@ -387,4 +387,109 @@ Alexandre Roux &lt;alexroux src gnome org&gt;</property>
       </object>
     </child>
   </object>
+  <object class="GtkFileChooserDialog" id="gnac_file_chooser">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Open a File...</property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <property name="select_multiple">True</property>
+    <property name="use_preview_label">False</property>
+    <property name="preview_widget_active">False</property>
+    <signal name="delete-event" handler="gtk_widget_hide_on_delete"/>
+    <signal name="key-press-event" handler="gnac_ui_file_chooser_key_press_event_cb"/>
+    <signal name="file-activated" handler="gnac_ui_file_chooser_file_activated_cb"/>
+    <signal name="response" handler="gnac_ui_file_chooser_response_cb"/>
+    <signal name="destroy" handler="gnac_ui_file_chooser_response_cb"/>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="bottom_padding">12</property>
+            <property name="left_padding">5</property>
+            <property name="right_padding">5</property>
+            <child>
+              <object class="GtkHBox" id="hbox2">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkCheckButton" id="close_on_add_button">
+                    <property name="label" translatable="yes">Close dialog on add</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBox" id="filters_combo">
+                    <property name="visible">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="add_button">
+                <property name="label" translatable="yes">gtk-add</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label" translatable="yes">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-1">add_button</action-widget>
+      <action-widget response="-7">close_button</action-widget>
+    </action-widgets>
+  </object>
 </interface>
diff --git a/src/gnac-main.c b/src/gnac-main.c
index 074265f..3c481ea 100644
--- a/src/gnac-main.c
+++ b/src/gnac-main.c
@@ -27,7 +27,6 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#include <gdk/gdkkeysyms.h>
 #include <gio/gio.h>
 #include <glib/gi18n.h>
 #include <gst/gst.h>
@@ -54,10 +53,6 @@
 #include "profiles/gnac-profiles-utils.h"
 
 
-static GtkFileFilter    *current_file_filter;
-static GtkFileFilter    *default_file_filter;
-static GSList           *filters;
-static gchar            *current_directory;
 static LibgnacConverter *converter;
 static gboolean          conversion_errors;
 static guint64           prev_time_left;
@@ -72,9 +67,9 @@ static gboolean          remember_overwrite = FALSE;
 gint                     nb_files_total;
 guint                    nb_files_added;
 
-GnacState                state;
-GnacState                prev_state;
-LibgnacMetadata         *metadata;
+GnacState        state;
+GnacState        prev_state;
+LibgnacMetadata *metadata;
 
 static const gchar *states[] = {
   "GNAC_AUDIO_EMPTY_STATE",
@@ -336,179 +331,6 @@ gnac_add_files(GSList *files)
 }
 
 
-static void
-gnac_file_chooser_foreach(gpointer data, 
-                          gpointer user_data)
-{
-  GFile *file;
-  GSList **list;
-  
-  list = (GSList**) user_data;
-  file = g_file_new_for_uri((gchar*)data);
-  *list = g_slist_append(*list, file);
-  g_free(data);
-}
-
-
-static void
-gnac_init_filters(void)
-{
-  filters = (GSList*) NULL;
-  
-  /* To translators: translation of filters' name can be
-   * found in /usr/share/mime */
-  const gchar *lossy_mime[][2] = {
-    { "audio/mpeg"            , _("MP3 audio (*.mp3)")                  },
-    { "audio/mp4"             , _("MPEG-4 audio (*.aac, *.m4a, *.mp4)") },
-    { "audio/x-musepack"      , _("Musepack audio (*.mpc)")             },
-    { "audio/ogg"             , _("Ogg Audio (*.oga)")                  },
-    { "audio/vnd.rn-realaudio", _("RealAudio document (*.ra)")          },
-    { "audio/x-speex"         , _("Speex audio (*.spx)")                },
-    { "audio/x-ms-wma"        , _("Windows Media audio (*.wma)")        },
-    { NULL, NULL }
-  };
-
-  const gchar *lossless_mime[][2] = {
-    /* XXX the Monkey's Audio plug-in has not yet been ported 
-     * to gstreamer-0.10 */
-    /*{ "audio/x-ape" , _("Monkey's audio (*.ape)")   },*/
-    { "audio/x-flac"   , _("Flac audio (*.flac)")  },
-    { "audio/x-wav"    , _("WAV audio (*.wav)")    },
-    { "audio/x-wavpack", _("WavPack audio (*.wv)") },
-    { NULL, NULL }
-  };
-
-  const gchar *supported_mime_playlists[][2] = {
-    { "audio/x-mpegurl"     , _("MP3 audio (streamed) (*.m3u)")   },
-    { "audio/x-scpls"       , _("MP3 ShoutCast playlist (*.pls)") },
-    { "application/xspf+xml", _("XSPF playlist (*.xspf)")         },
-    { NULL, NULL }
-  };
-
-  GtkFileFilter *file_filter_all = gtk_file_filter_new();
-  gtk_file_filter_add_pattern(file_filter_all, "*");
-  gtk_file_filter_set_name(file_filter_all, _("All files (*.*)"));
-
-  GtkFileFilter* file_filter_supported = gtk_file_filter_new();
-  gtk_file_filter_set_name(file_filter_supported, _("Supported files"));
-
-  /* all supported types */
-
-  guint i = 0;
-  while (lossless_mime[i][0] != NULL) {
-    gtk_file_filter_add_mime_type(file_filter_supported, lossless_mime[i][0]);
-    i++;
-  }
-
-  i = 0;
-  while (lossy_mime[i][0] != NULL) {
-    gtk_file_filter_add_mime_type(file_filter_supported, lossy_mime[i][0]);
-    i++;
-  }
-
-  i = 0;
-  GtkFileFilter *file_filter_playlists = gtk_file_filter_new();
-  gtk_file_filter_set_name(file_filter_playlists, _("Playlists files"));
-  while (supported_mime_playlists[i][0] != NULL) {
-    gtk_file_filter_add_mime_type(file_filter_supported, supported_mime_playlists[i][0]);
-    gtk_file_filter_add_mime_type(file_filter_playlists, supported_mime_playlists[i][0]);
-    i++;
-  }
-
-  filters = g_slist_append(filters, (gpointer) file_filter_all);
-  filters = g_slist_append(filters, (gpointer) file_filter_supported);
-  filters = g_slist_append(filters, (gpointer) file_filter_playlists);
-
-  /* Lossless types */
-
-  i = 0;
-  GtkFileFilter *file_filter_lossless = gtk_file_filter_new();
-  gtk_file_filter_set_name(file_filter_lossless, _("Lossless codecs"));
-  while (lossless_mime[i][0] != NULL) {
-    gtk_file_filter_add_mime_type(file_filter_lossless, lossless_mime[i][0]);
-    i++;
-  }
-  filters = g_slist_append(filters, (gpointer) file_filter_lossless);
-
-  /* Lossy types */
-
-  i = 0;
-  GtkFileFilter *file_filter_lossy = gtk_file_filter_new();
-  gtk_file_filter_set_name(file_filter_lossy, _("Lossy codecs"));
-  while (lossy_mime[i][0] != NULL) {
-    gtk_file_filter_add_mime_type(file_filter_lossy, lossy_mime[i][0]);
-    i++;
-  }
-  filters = g_slist_append(filters, (gpointer) file_filter_lossy);
-
-  /* Create filters for each kind of audio files */
-
-  i = 0;
-  GtkFileFilter *file_filter_tmp;
-  while (lossless_mime[i][0] != NULL) {
-    file_filter_tmp = gtk_file_filter_new();
-    gtk_file_filter_add_mime_type(file_filter_tmp, lossless_mime[i][0]);
-    gtk_file_filter_set_name(file_filter_tmp, lossless_mime[i][1]);
-    filters = g_slist_append(filters, (gpointer) file_filter_tmp);
-    i++;
-  }
-
-  i = 0;
-  while (lossy_mime[i][0] != NULL) {
-    file_filter_tmp = gtk_file_filter_new();
-    gtk_file_filter_add_mime_type(file_filter_tmp, lossy_mime[i][0]);
-    gtk_file_filter_set_name(file_filter_tmp, lossy_mime[i][1]);
-    filters = g_slist_append(filters, (gpointer) file_filter_tmp);
-    i++;
-  }
-
-  /* Create filters for playlists */
-
-  i = 0;
-  while (supported_mime_playlists[i][0] != NULL) {
-    file_filter_tmp = gtk_file_filter_new();
-    gtk_file_filter_add_mime_type(file_filter_tmp, supported_mime_playlists[i][0]);
-    gtk_file_filter_set_name(file_filter_tmp, supported_mime_playlists[i][1]);
-    filters = g_slist_append(filters, (gpointer) file_filter_tmp);
-    i++;
-  }
-
-  GSList *next;
-  for (next = filters; next; next = next->next) {
-    g_object_ref(G_OBJECT(next->data)); 
-  }
-  current_file_filter = file_filter_supported;
-  default_file_filter = file_filter_supported;
-}
-
-
-static void
-gnac_add_filters(GtkFileChooser *file_chooser)
-{
-  GSList *next;
-
-  for (next = filters; next; next = next->next) {
-    gtk_file_chooser_add_filter (file_chooser, GTK_FILE_FILTER(next->data));
-  }
-
-  /* we want to keep track of the chosen file filter 
-   * (Supported files at the beginning) */
-  gtk_file_chooser_set_filter(file_chooser, current_file_filter);
-}
-
-
-static void
-gnac_unref_filters(void)
-{
-  GSList *next;
-  for (next = filters; next; next = next->next) {
-     g_object_ref_sink(G_OBJECT(next->data)); 
-     g_object_unref(next->data);
-  }
-  g_slist_free(filters);
-}
-
-
 void
 gnac_add_file(GFile *file)
 {
@@ -530,18 +352,13 @@ gnac_add_file(GFile *file)
   filter_info.mime_type = g_file_info_get_content_type(info);
 
   if (filter_info.mime_type != NULL) {
-    /* Playlists */
-    if (g_ascii_strcasecmp(filter_info.mime_type, "audio/x-mpegurl") == 0) {
-      gnac_playlist_parse_m3u(file);
-    } else if (g_ascii_strcasecmp(filter_info.mime_type, "audio/x-scpls") == 0) {
-      gnac_playlist_parse_pls(file);
-    } else if (g_ascii_strcasecmp(filter_info.mime_type, "application/xspf+xml") == 0) {
-      gnac_playlist_parse_xspf(file); 
-
-    /* Filter the files */
-    } else if (gtk_file_filter_filter(current_file_filter, &filter_info)) {
-
-      /* No duplicate files */
+    /* Check whether we have a playlist */
+    if (gnac_playlist_is_mime_playlist(filter_info.mime_type)) {
+      gnac_playlist_parse(file, filter_info.mime_type);
+    /* Check whether the file format is supported */
+    } else if (gtk_file_filter_filter(gnac_ui_get_current_filter(),
+        &filter_info))
+    {
       libgnac_info("Add file %s", g_file_get_uri(file));
       libgnac_converter_add(converter, file, &error);
     }
@@ -601,13 +418,9 @@ gnac_on_ui_destroy_cb(GtkWidget *widget,
     if (thread != NULL) g_thread_join(thread);
   }
 
-  g_free(current_directory);
-
   g_object_unref(converter);
   g_object_unref(metadata);
 
-  gnac_unref_filters();
-
   gnac_properties_destroy();
   gnac_prefs_destroy();
   gnac_profiles_destroy();
@@ -619,74 +432,6 @@ gnac_on_ui_destroy_cb(GtkWidget *widget,
 }
 
 
-static void
-gnac_file_chooser_file_activated_cb(GtkFileChooser *chooser,
-                                    gpointer        user_data)
-{
-  gnac_file_chooser_response_cb(GTK_DIALOG(chooser), GTK_RESPONSE_NONE, NULL);
-}
-
-
-static gboolean
-gnac_file_chooser_key_press_event_cb(GtkWidget   *widget,
-                                     GdkEventKey *event,
-                                     gpointer     user_data)
-{
-  if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Return)
-  {
-    gnac_file_chooser_response_cb(GTK_DIALOG(widget), GTK_RESPONSE_NONE, NULL);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-
-void
-gnac_file_chooser_response_cb(GtkDialog *dialog,
-                              gint       response,
-                              gpointer   user_data)
-{
-  GSList *list_path;
-  GSList *list_files = NULL;
-
-  GtkWidget *close_on_add_button;
-  
-  switch (response) {
-    /* Add button */
-    case GTK_RESPONSE_NONE:
-      
-      /* Set the current file filter */
-      current_file_filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
-      list_path = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog));
-
-      /* Add the differents files */
-      g_slist_foreach(list_path,(GFunc) gnac_file_chooser_foreach, (gpointer) &list_files);
-
-      /* free the list */
-      g_slist_free(list_path);
-      
-      /* add files */
-      gnac_add_files(list_files);
-
-      /* Do we have to close de file chooser? */
-      close_on_add_button = gtk_file_chooser_get_extra_widget(GTK_FILE_CHOOSER(dialog));
-
-      /* Get the current uri */
-      g_free(current_directory);
-      current_directory = gtk_file_chooser_get_current_folder_uri(GTK_FILE_CHOOSER(dialog));
-
-      if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(close_on_add_button))) return;
-
-      break;
-     
-    case GTK_RESPONSE_CLOSE:
-    default: 
-      break;
-  }
-  gtk_widget_destroy(GTK_WIDGET(dialog));
-}
-
-
 static gboolean
 gnac_on_converter_overwrite_cb(LibgnacConverter *converter, 
                                gpointer          file,
@@ -1057,37 +802,7 @@ void
 gnac_on_ui_add_cb(GtkAction *action, 
                   gpointer   data)
 {
-  GtkWidget *close_on_add_button;
-  GtkWidget *file_chooser;
-  
-  file_chooser = gtk_file_chooser_dialog_new(_("Open a File..."),
-      GTK_WINDOW(data),
-      GTK_FILE_CHOOSER_ACTION_OPEN,
-      GTK_STOCK_ADD, GTK_RESPONSE_NONE,
-      GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-      NULL);
-
-  gtk_window_set_type_hint(GTK_WINDOW(file_chooser), GDK_WINDOW_TYPE_HINT_DIALOG);
-
-  gtk_file_chooser_set_current_folder_uri(GTK_FILE_CHOOSER(file_chooser), 
-                  current_directory);
-  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_chooser), TRUE);
-
-  close_on_add_button = gtk_check_button_new_with_label(_("Close dialog on add"));
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(close_on_add_button),TRUE);
-  gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(file_chooser),
-                  close_on_add_button);
-
-  gnac_add_filters(GTK_FILE_CHOOSER(file_chooser));
-
-  g_signal_connect(file_chooser, "response", 
-                  G_CALLBACK(gnac_file_chooser_response_cb), data);
-  g_signal_connect(file_chooser, "key-press-event", 
-                  G_CALLBACK(gnac_file_chooser_key_press_event_cb), data);
-  g_signal_connect(file_chooser, "file-activated", 
-                  G_CALLBACK(gnac_file_chooser_file_activated_cb), data);
-
-  gtk_dialog_run(GTK_DIALOG(file_chooser));
+  gtk_widget_show_all(gnac_ui_get_file_chooser());
 }
 
 
@@ -1161,7 +876,7 @@ gnac_on_ui_drag_data_received_cb(GtkWidget        *widget,
   gint index = 0;
 
   /* Disable any filtering for DnD. */
-  current_file_filter = default_file_filter;
+  gnac_ui_reset_file_filter();
 
   uris = gtk_selection_data_get_uris(selection_data);
 
@@ -1223,12 +938,12 @@ main(gint    argc,
     textdomain(GETTEXT_PACKAGE);
   #endif /* ENABLE_NLS */
 
-  /* Parse command line arguments */
-  gnac_options_init(argc, argv);
-
   /* Set a name for the application */
   g_set_application_name(PACKAGE_NAME);
 
+  /* Parse command line arguments */
+  gnac_options_init(argc, argv);
+
   /* Initialisation of libraries */
   gdk_threads_init();
 
@@ -1240,12 +955,9 @@ main(gint    argc,
   /* Init gstreamer plugins helper*/
   gst_pb_utils_init();
 
-  gnac_init_filters();
   gnac_gconf_init();
   gnac_profiles_init();
 
-  current_directory = g_strdup(g_get_home_dir());
-
   /* Initialise converter and connect signals */
   metadata = LIBGNAC_METADATA(libgnac_metadata_new());
   converter = LIBGNAC_CONVERTER(libgnac_converter_new(metadata));
diff --git a/src/gnac-main.h b/src/gnac-main.h
index bea7156..b4e913f 100644
--- a/src/gnac-main.h
+++ b/src/gnac-main.h
@@ -104,11 +104,6 @@ gnac_on_file_monitor_changed_cb(GFileMonitor      *monitor,
                                 GFileMonitorEvent  event_type,
                                 gpointer           user_data);
 
-void
-gnac_file_chooser_response_cb(GtkDialog *dialog,
-                              gint       response,
-                              gpointer   user_data);
-
 G_END_DECLS
 
 #endif /* GNAC_MAIN_H */
diff --git a/src/gnac-playlist.c b/src/gnac-playlist.c
index 165f370..42333e4 100755
--- a/src/gnac-playlist.c
+++ b/src/gnac-playlist.c
@@ -34,8 +34,41 @@
 #include "gnac-main.h"
 #include "gnac-playlist.h"
 
-// TODO convert content to utf8!
-// work with utf8, needed for all gnome programs
+
+typedef struct {
+  const gchar  *mime;
+  void        (*handler)(GFile *file);
+} GnacPlaylistMimeHandler;
+
+
+static GnacPlaylistMimeHandler mime_playlist[] = {
+  { "audio/x-mpegurl"     , &gnac_playlist_parse_m3u  },
+  { "audio/x-scpls"       , &gnac_playlist_parse_pls  },
+  { "application/xspf+xml", &gnac_playlist_parse_xspf },
+  { NULL, NULL }
+};
+
+
+static void
+gnac_playlist_resolve_uri_and_add(GFile       *parent,
+                                  const gchar *filename)
+{
+  GFile *file;
+
+  if (!g_utf8_validate(filename, -1, NULL)) {
+    g_printerr("%s: %s\n", _("Invalid UTF-8 filename"), filename);
+    return;
+  }
+
+  if (g_str_has_prefix(filename, "file://")) {
+    file = g_file_new_for_uri(filename);
+  } else {
+    file = g_file_get_child(parent, filename);
+  }
+
+  gnac_add_file(file);
+}
+
 
 static gchar *
 gnac_playlist_read_file(GFile *file) {
@@ -55,11 +88,36 @@ gnac_playlist_read_file(GFile *file) {
 }
 
 
+gboolean
+gnac_playlist_is_mime_playlist(const gchar *mime)
+{
+  guint i;
+  for (i = 0; mime_playlist[i].mime; i++) {
+    if (g_ascii_strcasecmp(mime, mime_playlist[i].mime) == 0) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+
+void
+gnac_playlist_parse(GFile       *file,
+                    const gchar *mime)
+{
+  guint i;
+  for (i = 0; mime_playlist[i].mime; i++) {
+    if (g_ascii_strcasecmp(mime, mime_playlist[i].mime) == 0) {
+      mime_playlist[i].handler(file);
+    }
+  }
+}
+
+
 void
 gnac_playlist_parse_m3u(GFile *file)
 {
   GFile *parent;
-  GFile *child;
   gchar *contents;
 
   parent = g_file_get_parent(file);
@@ -70,18 +128,12 @@ gnac_playlist_parse_m3u(GFile *file)
     gchar **lines;
     lines = g_strsplit(contents, "\n", -1);
 
-    gint i, len;
+    gint i;
     for (i = 0; lines[i]; i++) {
       /* skip comments and empty lines */
       if (!*lines[i] || *lines[i] == '#') continue;
       g_strchomp(lines[i]);
-      len = g_utf8_strlen(lines[i], -1);
-      if (!g_utf8_validate(lines[i], -1, NULL)) {
-        g_printerr("%s: %s\n", _("Invalid UTF-8 filename"), lines[i]);
-      } else {
-        child = g_file_get_child(parent, lines[i]);
-        gnac_add_file(child);
-      }
+      gnac_playlist_resolve_uri_and_add(parent, lines[i]);
     }
 
     g_strfreev(lines);
@@ -94,8 +146,7 @@ void
 gnac_playlist_parse_pls(GFile *file)
 {
   GFile *parent;
-  GFile *child;
-  gchar  *contents;
+  gchar *contents;
 
   parent = g_file_get_parent(file);
   contents = gnac_playlist_read_file(file);
@@ -105,7 +156,7 @@ gnac_playlist_parse_pls(GFile *file)
     gchar **lines;
     lines = g_strsplit(contents, "\n", -1);
 
-    gint i, len;
+    gint i;
     for (i = 0; lines[i]; i++) {
       /* skip empty lines */
       if (!*lines[i]) continue;
@@ -115,13 +166,7 @@ gnac_playlist_parse_pls(GFile *file)
       key = g_utf8_strdown(pair[0], -1);
       if (pair[1] && g_str_has_prefix(key, "file")) {
         g_strchomp(pair[1]);
-        len = g_utf8_strlen(lines[i], -1);
-        if (!g_utf8_validate(pair[1], -1, NULL)) {
-          g_printerr("%s: %s\n", _("Invalid UTF-8 filename"), pair[1]);
-        } else {
-          child = g_file_get_child(parent, pair[1]);
-          gnac_add_file(child);
-        }
+        gnac_playlist_resolve_uri_and_add(parent, pair[1]);
       }
 
       g_free(key);
@@ -138,8 +183,7 @@ void
 gnac_playlist_parse_xspf(GFile *file)
 {
   GFile *parent;
-  GFile *child;
-  gchar  *contents;
+  gchar *contents;
 
   parent = g_file_get_parent(file);
   contents = gnac_playlist_read_file(file);
@@ -157,12 +201,8 @@ gnac_playlist_parse_xspf(GFile *file)
       if (g_str_has_prefix(line, "<location>")) {
         line = g_strsplit(line, ">", 2)[1];
         line = g_strsplit(line, "<", 2)[0];
-        if (!g_utf8_validate(line, -1, NULL)) {
-          g_printerr("%s: %s\n", _("Invalid UTF-8 filename"), line);
-        } else {
-          child = g_file_get_child(parent, line);
-          gnac_add_file(child);
-        }
+        g_strchomp(line);
+        gnac_playlist_resolve_uri_and_add(parent, line);
       }
     }
 
diff --git a/src/gnac-playlist.h b/src/gnac-playlist.h
index bf8801a..fc8ac5c 100755
--- a/src/gnac-playlist.h
+++ b/src/gnac-playlist.h
@@ -30,6 +30,13 @@
 
 G_BEGIN_DECLS
 
+gboolean
+gnac_playlist_is_mime_playlist(const gchar *mime);
+
+void
+gnac_playlist_parse(GFile       *file,
+                    const gchar *mime);
+
 void
 gnac_playlist_parse_m3u(GFile *file);
 
diff --git a/src/gnac-ui.c b/src/gnac-ui.c
index 7e17ee2..07204ae 100644
--- a/src/gnac-ui.c
+++ b/src/gnac-ui.c
@@ -27,6 +27,7 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
 #include <glib/gprintf.h>
 
@@ -39,10 +40,15 @@
 #include "gnac-gconf.h"
 #include "profiles/gnac-profiles.h"
 
+extern GnacState state;
 extern LibgnacMetadata *metadata;
 
+static GSList         *filters;
 static GtkBuilder     *gnac_main_builder = NULL;
+static GtkFileFilter  *default_file_filter;
 static GtkStatusIcon  *trayicon = NULL;
+static GtkWidget      *gnac_file_chooser;
+static gchar          *current_directory;
 static gchar          *status_msg = NULL;
 static gchar          *tooltip_path = NULL;
 static gchar          *progress_msg = NULL;
@@ -60,8 +66,6 @@ static GtkTargetEntry target_list[] =  {
 
 static guint n_targets = G_N_ELEMENTS(target_list);
 
-extern GnacState state;
-
 
 static void
 gnac_about_url_hook(GtkAboutDialog *dialog,
@@ -90,6 +94,401 @@ gnac_about_email_hook(GtkAboutDialog *dialog,
 }
 
 
+static void
+gnac_ui_file_chooser_unref_filters(void)
+{
+  GSList *f;
+  for (f = filters; f; f = f->next) {
+    g_object_unref(f->data);
+  }
+  g_slist_free(f);
+}
+
+
+static void
+gnac_ui_file_chooser_dispose(void)
+{
+  gnac_ui_file_chooser_unref_filters();
+  gtk_widget_destroy(gnac_file_chooser);
+}
+
+
+static void
+gnac_ui_file_chooser_cell_data_func(GtkCellLayout   *layout,
+                                    GtkCellRenderer *cell,
+                                    GtkTreeModel    *model,
+                                    GtkTreeIter     *iter,
+                                    gpointer         data)
+{
+  GtkFileFilter *filter;
+  gtk_tree_model_get(model, iter, 0, &filter, -1);
+  g_object_set(cell, "text", gtk_file_filter_get_name(filter), NULL);
+}
+
+
+static GtkTreeModel *
+gnac_ui_file_chooser_get_filters_model(void)
+{
+  guint          i;
+  GSList        *f;
+  GtkFileFilter *filter;
+  GtkTreeIter    iter1, iter2, iter3;
+  GtkTreeStore  *store;
+
+  /* To translators: translation of filters' name can be
+   * found in /usr/share/mime */
+  const gchar *lossy_mime[][2] = {
+    { "audio/mpeg"            , _("MP3 audio")           },
+    { "audio/mp4"             , _("MPEG-4 audio")        },
+    { "audio/x-musepack"      , _("Musepack audio")      },
+    { "audio/ogg"             , _("Ogg Audio")           },
+    { "audio/vnd.rn-realaudio", _("RealAudio document")  },
+    { "audio/x-speex"         , _("Speex audio")         },
+    { "audio/x-ms-wma"        , _("Windows Media audio") },
+    { NULL, NULL }
+  };
+
+  const gchar *lossless_mime[][2] = {
+    /* XXX the Monkey's Audio plug-in has not yet been ported
+     * to gstreamer-0.10 */
+    /*{ "audio/x-ape" , _("Monkey's audio")   },*/
+    { "audio/x-flac"   , _("Flac audio")    },
+    { "audio/x-wav"    , _("WAV audio")     },
+    { "audio/x-wavpack", _("WavPack audio") },
+    { NULL, NULL }
+  };
+
+  const gchar *playlists_mime[][2] = {
+    { "audio/x-mpegurl"     , _("MP3 audio (streamed)")   },
+    { "audio/x-scpls"       , _("MP3 ShoutCast playlist") },
+    { "application/xspf+xml", _("XSPF playlist")          },
+    { NULL, NULL }
+  };
+
+  filters = NULL;
+  store = gtk_tree_store_new(1, G_TYPE_POINTER);
+
+  /* All files */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_add_pattern(filter, "*");
+  gtk_file_filter_set_name(filter, _("All files"));
+  gtk_tree_store_append(store, &iter1, NULL);
+  gtk_tree_store_set(store, &iter1, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+
+  /* Supported files */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_set_name(filter, _("Supported files"));
+  i = 0;
+  while (lossless_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
+    i++;
+  }
+  i = 0;
+  while (lossy_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
+    i++;
+  }
+  i = 0;
+  while (playlists_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
+    i++;
+  }
+  gtk_tree_store_append(store, &iter1, NULL);
+  gtk_tree_store_set(store, &iter1, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+  default_file_filter = filter;
+
+  /* Audio files */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_set_name(filter, _("Audio files"));
+  i = 0;
+  while (lossless_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
+    i++;
+  }
+  i = 0;
+  while (lossy_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
+    i++;
+  }
+  i = 0;
+  while (playlists_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
+    i++;
+  }
+  gtk_tree_store_append(store, &iter1, NULL);
+  gtk_tree_store_set(store, &iter1, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+
+  /* Lossy files */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_set_name(filter, _("Lossy files"));
+  i = 0;
+  while (lossy_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
+    i++;
+  }
+  gtk_tree_store_append(store, &iter2, &iter1);
+  gtk_tree_store_set(store, &iter2, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+
+  /* Individual lossy files */
+  i = 0;
+  while (lossy_mime[i][0]) {
+    filter = gtk_file_filter_new();
+    gtk_file_filter_add_mime_type(filter, lossy_mime[i][0]);
+    gtk_file_filter_set_name(filter, lossy_mime[i][1]);
+    gtk_tree_store_append(store, &iter3, &iter2);
+    gtk_tree_store_set(store, &iter3, 0, filter, -1);
+    filters = g_slist_append(filters, (gpointer)filter);
+    i++;
+  }
+
+  /* Lossless files */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_set_name(filter, _("Lossless files"));
+  i = 0;
+  while (lossless_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
+    i++;
+  }
+  gtk_tree_store_append(store, &iter2, &iter1);
+  gtk_tree_store_set(store, &iter2, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+
+  /* Individual lossless files */
+  i = 0;
+  while (lossless_mime[i][0]) {
+    filter = gtk_file_filter_new();
+    gtk_file_filter_add_mime_type(filter, lossless_mime[i][0]);
+    gtk_file_filter_set_name(filter, lossless_mime[i][1]);
+    gtk_tree_store_append(store, &iter3, &iter2);
+    gtk_tree_store_set(store, &iter3, 0, filter, -1);
+    filters = g_slist_append(filters, (gpointer)filter);
+    i++;
+  }
+
+  /* Playlists */
+  filter = gtk_file_filter_new();
+  gtk_file_filter_set_name(filter, _("Playlists"));
+  i = 0;
+  while (playlists_mime[i][0]) {
+    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
+    i++;
+  }
+  gtk_tree_store_append(store, &iter1, NULL);
+  gtk_tree_store_set(store, &iter1, 0, filter, -1);
+  filters = g_slist_append(filters, (gpointer)filter);
+
+  /* Individual playlists */
+  i = 0;
+  while (playlists_mime[i][0]) {
+    filter = gtk_file_filter_new();
+    gtk_file_filter_add_mime_type(filter, playlists_mime[i][0]);
+    gtk_file_filter_set_name(filter, playlists_mime[i][1]);
+    gtk_tree_store_append(store, &iter2, &iter1);
+    gtk_tree_store_set(store, &iter2, 0, filter, -1);
+    filters = g_slist_append(filters, (gpointer)filter);
+    i++;
+  }
+
+  for (f = filters; f; f = f->next) {
+    g_object_ref(G_OBJECT(f->data));
+  }
+
+  return GTK_TREE_MODEL(store);
+}
+
+
+static void
+gnac_ui_file_chooser_foreach(gpointer data,
+                             gpointer user_data)
+{
+  GFile   *file;
+  GSList **list;
+
+  list = (GSList**) user_data;
+  file = g_file_new_for_uri((gchar*)data);
+  *list = g_slist_append(*list, file);
+  g_free(data);
+}
+
+
+static GtkWidget *
+gnac_ui_file_chooser_new(void)
+{
+  GtkCellRenderer *renderer;
+  GtkTreeModel    *model;
+  GtkWidget       *combo;
+
+  gnac_file_chooser = gnac_ui_get_widget("gnac_file_chooser");
+  model = gnac_ui_file_chooser_get_filters_model();
+  combo = gnac_ui_get_widget("filters_combo");
+  gtk_combo_box_set_model(GTK_COMBO_BOX(combo), model);
+
+  renderer = gtk_cell_renderer_text_new();
+  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
+  gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
+      (GtkCellLayoutDataFunc)gnac_ui_file_chooser_cell_data_func,
+      NULL, NULL);
+
+  g_signal_connect(G_OBJECT(combo), "changed",
+      G_CALLBACK(gnac_ui_on_filter_changed), gnac_file_chooser);
+
+  /* Use the 'Supported files' filter by default */
+  gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 1);
+  gnac_ui_on_filter_changed(GTK_COMBO_BOX(combo), gnac_file_chooser);
+
+  return gnac_file_chooser;
+}
+
+
+void
+gnac_ui_reset_file_filter(void)
+{
+  GtkComboBox *combo;
+  combo = GTK_COMBO_BOX(gnac_ui_get_widget("filters_combo"));
+  gtk_combo_box_set_active(combo, 1);
+  gnac_ui_on_filter_changed(combo, gnac_file_chooser);
+}
+
+
+GtkWidget *
+gnac_ui_get_file_chooser(void)
+{
+  if (G_UNLIKELY(!gnac_file_chooser)) {
+    gnac_file_chooser = gnac_ui_file_chooser_new();
+  }
+  return gnac_file_chooser;
+}
+
+
+GtkFileFilter *
+gnac_ui_get_current_filter(void)
+{
+  gpointer      filter;
+  GtkTreeIter   iter;
+  GtkTreeModel *model;
+  GtkComboBox  *combo;
+
+  /* make sure the filters have been created */
+  gnac_ui_get_file_chooser();
+
+  combo = GTK_COMBO_BOX(gnac_ui_get_widget("filters_combo"));
+
+  if (!gtk_combo_box_get_active_iter(combo, &iter)) {
+    return default_file_filter;
+  }
+
+  model = gtk_combo_box_get_model(combo);
+  g_return_val_if_fail(model, default_file_filter);
+
+  gtk_tree_model_get(model, &iter, 0, &filter, -1);
+  if (GTK_IS_FILE_FILTER(filter)) {
+    return GTK_FILE_FILTER(filter);
+  }
+
+  return default_file_filter;
+}
+
+
+GtkFileFilter *
+gnac_ui_get_default_filter(void)
+{
+  return default_file_filter;
+}
+
+
+void
+gnac_ui_on_filter_changed(GtkComboBox *combo,
+                          gpointer     user_data)
+{
+  gpointer      filter;
+  GtkTreeIter   iter;
+  GtkTreeModel *model;
+
+  if (!gtk_combo_box_get_active_iter(combo, &iter)) return;
+
+  model = gtk_combo_box_get_model(combo);
+  g_return_if_fail(model);
+  gtk_tree_model_get(model, &iter, 0, &filter, -1);
+  if (GTK_IS_FILE_FILTER(filter)) {
+    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(user_data),
+        GTK_FILE_FILTER(filter));
+  }
+}
+
+
+void
+gnac_ui_file_chooser_response_cb(GtkDialog *dialog,
+                                 gint       response,
+                                 gpointer   user_data)
+{
+  GSList *list_path;
+  GSList *list_files = NULL;
+
+  GtkWidget *close_on_add_button;
+
+  switch (response) {
+    /* Add button */
+    case GTK_RESPONSE_NONE:
+      list_path = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog));
+
+      /* Add the differents files */
+      g_slist_foreach(list_path,(GFunc) gnac_ui_file_chooser_foreach,
+          (gpointer)&list_files);
+
+      /* free the list */
+      g_slist_free(list_path);
+
+      /* add files */
+      gnac_add_files(list_files);
+
+      /* Do we have to close de file chooser? */
+      close_on_add_button = gnac_ui_get_widget("close_on_add_button");
+
+      /* Get the current uri */
+      g_free(current_directory);
+      current_directory = gtk_file_chooser_get_current_folder_uri(
+          GTK_FILE_CHOOSER(dialog));
+
+      if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(close_on_add_button)))
+          return;
+
+      break;
+
+    case GTK_RESPONSE_CLOSE:
+    default:
+      break;
+  }
+  gtk_widget_hide(GTK_WIDGET(dialog));
+}
+
+
+void
+gnac_ui_file_chooser_file_activated_cb(GtkFileChooser *chooser,
+                                       gpointer        user_data)
+{
+  gnac_ui_file_chooser_response_cb(GTK_DIALOG(chooser),
+      GTK_RESPONSE_NONE, NULL);
+}
+
+
+gboolean
+gnac_ui_file_chooser_key_press_event_cb(GtkWidget   *widget,
+                                        GdkEventKey *event,
+                                        gpointer     user_data)
+{
+  if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Return) {
+    gnac_ui_file_chooser_response_cb(GTK_DIALOG(widget),
+        GTK_RESPONSE_NONE, NULL);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+
 void
 gnac_ui_new(void)
 {
@@ -117,6 +516,8 @@ gnac_ui_new(void)
   gtk_about_dialog_set_url_hook(gnac_about_url_hook, NULL, NULL);
   gtk_about_dialog_set_email_hook(gnac_about_email_hook, NULL, NULL);
 
+  current_directory = g_strdup(g_get_home_dir());
+
   file_list = gnac_file_list_new();
 
   audio_profile_hbox = GTK_WIDGET(gtk_builder_get_object(gnac_main_builder, "audio_profile_hbox"));
@@ -356,6 +757,11 @@ gnac_ui_destroy(void)
     }
   }
 
+  if (current_directory) {
+    g_free(current_directory);
+    current_directory = NULL;
+  }
+
   gnac_file_list_destroy();
   
   if (gnac_main_builder) {
@@ -363,6 +769,7 @@ gnac_ui_destroy(void)
     if (main_window) gtk_widget_destroy(main_window);
     about_dialog = gnac_ui_get_widget("aboutdialog");
     if (about_dialog) gtk_widget_destroy(about_dialog);
+    if (gnac_file_chooser) gnac_ui_file_chooser_dispose();
     g_object_unref(gnac_main_builder); 
     gnac_main_builder = NULL;
   }
diff --git a/src/gnac-ui.h b/src/gnac-ui.h
index fa0831a..4a05701 100644
--- a/src/gnac-ui.h
+++ b/src/gnac-ui.h
@@ -67,6 +67,36 @@ void
 gnac_ui_show_about_dialog(void);
 
 void
+gnac_ui_reset_file_filter(void);
+
+GtkWidget *
+gnac_ui_get_file_chooser(void);
+
+GtkFileFilter *
+gnac_ui_get_current_filter(void);
+
+GtkFileFilter *
+gnac_ui_get_default_filter(void);
+
+void
+gnac_ui_on_filter_changed(GtkComboBox *combo,
+                          gpointer     user_data);
+
+void
+gnac_ui_file_chooser_response_cb(GtkDialog *dialog,
+                                 gint       response,
+                                 gpointer   user_data);
+
+void
+gnac_ui_file_chooser_file_activated_cb(GtkFileChooser *chooser,
+                                       gpointer        user_data);
+
+gboolean
+gnac_ui_file_chooser_key_press_event_cb(GtkWidget   *widget,
+                                        GdkEventKey *event,
+                                        gpointer     user_data);
+
+void
 gnac_ui_new(void);
 
 GtkWidget *



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