Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
folder-add.c
Go to the documentation of this file.
00001 /*
00002  * folder-add.c
00003  * Copyright 2009-2011 John Lindgren
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 2 or version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 #include <dirent.h>
00023 #include <stdio.h>
00024 #include <string.h>
00025 #include <sys/stat.h>
00026 
00027 #include <gtk/gtk.h>
00028 
00029 #include <libaudcore/audstrings.h>
00030 
00031 #include "audconfig.h"
00032 #include "config.h"
00033 #include "glib-compat.h"
00034 #include "i18n.h"
00035 #include "misc.h"
00036 #include "playback.h"
00037 #include "playlist.h"
00038 
00039 typedef struct {
00040     gchar * filename;
00041     PluginHandle * decoder;
00042 } AddFile;
00043 
00044 typedef struct {
00045     gint playlist_id, at;
00046     GQueue folders; // (gchar *)
00047     gboolean play;
00048 } AddGroup;
00049 
00050 static GQueue add_queue = G_QUEUE_INIT; // (AddGroup *)
00051 static gint add_source = 0;
00052 static GtkWidget * add_window = NULL, * add_progress_path, * add_progress_count;
00053 
00054 static void show_progress (const gchar * path, gint count)
00055 {
00056     if (add_window == NULL)
00057     {
00058         GtkWidget * vbox;
00059 
00060         add_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
00061         gtk_window_set_type_hint (GTK_WINDOW(add_window),
00062          GDK_WINDOW_TYPE_HINT_DIALOG);
00063         gtk_window_set_title (GTK_WINDOW(add_window), _("Searching ..."));
00064         gtk_window_set_resizable (GTK_WINDOW(add_window), FALSE);
00065         gtk_container_set_border_width (GTK_CONTAINER(add_window), 6);
00066 
00067         vbox = gtk_vbox_new (FALSE, 6);
00068         gtk_container_add (GTK_CONTAINER(add_window), vbox);
00069 
00070         add_progress_path = gtk_label_new ("");
00071         gtk_widget_set_size_request (add_progress_path, 320, -1);
00072         gtk_label_set_ellipsize (GTK_LABEL(add_progress_path),
00073          PANGO_ELLIPSIZE_MIDDLE);
00074         gtk_box_pack_start (GTK_BOX(vbox), add_progress_path, FALSE, FALSE, 0);
00075 
00076         add_progress_count = gtk_label_new ("");
00077         gtk_widget_set_size_request (add_progress_count, 320, -1);
00078         gtk_box_pack_start (GTK_BOX(vbox), add_progress_count, FALSE, FALSE, 0);
00079 
00080         gtk_widget_show_all (add_window);
00081 
00082         g_signal_connect (G_OBJECT(add_window), "destroy",
00083          G_CALLBACK(gtk_widget_destroyed), & add_window);
00084     }
00085 
00086     gtk_label_set_text (GTK_LABEL(add_progress_path), path);
00087 
00088     gchar scratch[128];
00089     snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found",
00090      "%d files found", count), count);
00091     gtk_label_set_text (GTK_LABEL(add_progress_count), scratch);
00092 }
00093 
00094 static void show_done (void)
00095 {
00096     gtk_widget_destroy (add_window);
00097 }
00098 
00099 static gint sort_cb (const AddFile * a, const AddFile * b)
00100 {
00101     return string_compare_encoded (a->filename, b->filename);
00102 }
00103 
00104 static gboolean add_cb (void * unused)
00105 {
00106     static GList * stack = NULL; // (gchar *) and (DIR *) alternately
00107     static GList * big_list = NULL, * little_list = NULL; // (AddFile *)
00108 
00109     if (! stack)
00110     {
00111         AddGroup * group = g_queue_peek_head (& add_queue);
00112         g_return_val_if_fail (group, FALSE);
00113         gchar * folder = g_queue_pop_head (& group->folders);
00114         g_return_val_if_fail (folder, FALSE);
00115         stack = g_list_prepend (NULL, folder);
00116     }
00117 
00118     show_progress (stack->data, g_list_length (big_list) + g_list_length
00119      (little_list));
00120 
00121     for (gint count = 0; count < 30; count ++)
00122     {
00123         struct stat info;
00124 
00125         /* top of stack is (gchar *) */
00126 
00127         if (! stat (stack->data, & info))
00128         {
00129             if (S_ISREG (info.st_mode))
00130             {
00131                 gchar * filename = filename_to_uri (stack->data);
00132                 PluginHandle * decoder = (filename == NULL) ? NULL :
00133                  file_find_decoder (filename, TRUE);
00134 
00135                 if (decoder)
00136                 {
00137                     AddFile * file = g_slice_new (AddFile);
00138                     file->filename = filename;
00139                     file->decoder = decoder;
00140                     little_list = g_list_prepend (little_list, file);
00141                 }
00142                 else
00143                     g_free (filename);
00144             }
00145             else if (S_ISDIR (info.st_mode))
00146             {
00147                 DIR * folder = opendir (stack->data);
00148 
00149                 if (folder)
00150                 {
00151                     stack = g_list_prepend (stack, folder);
00152                     goto READ;
00153                 }
00154             }
00155         }
00156 
00157         g_free (stack->data);
00158         stack = g_list_delete_link (stack, stack);
00159 
00160     READ:
00161         if (! stack)
00162             break;
00163 
00164         /* top of stack is (DIR *) */
00165 
00166         struct dirent * entry = readdir (stack->data);
00167 
00168         if (entry)
00169         {
00170             if (entry->d_name[0] == '.')
00171                 goto READ;
00172 
00173             stack = g_list_prepend (stack, g_strdup_printf ("%s"
00174              G_DIR_SEPARATOR_S "%s", (gchar *) stack->next->data, entry->d_name));
00175         }
00176         else
00177         {
00178             closedir (stack->data);
00179             stack = g_list_delete_link (stack, stack);
00180             g_free (stack->data);
00181             stack = g_list_delete_link (stack, stack);
00182             goto READ;
00183         }
00184     }
00185 
00186     if (stack)
00187         return TRUE; /* not done yet */
00188 
00189     AddGroup * group = g_queue_peek_head (& add_queue);
00190     g_return_val_if_fail (group, FALSE);
00191 
00192     little_list = g_list_sort (little_list, (GCompareFunc) sort_cb);
00193     big_list = g_list_concat (big_list, little_list);
00194     little_list = NULL;
00195 
00196     if (! g_queue_is_empty (& group->folders))
00197         return TRUE; /* not done yet */
00198 
00199     gint playlist = playlist_by_unique_id (group->playlist_id);
00200 
00201     if (playlist < 0) /* ouch! playlist deleted */
00202     {
00203         for (GList * node = big_list; node; node = node->next)
00204         {
00205             AddFile * file = node->data;
00206             g_free (file->filename);
00207             g_slice_free (AddFile, file);
00208         }
00209 
00210         g_list_free (big_list);
00211         big_list = NULL;
00212         goto ADDED;
00213     }
00214 
00215     struct index * filenames = index_new ();
00216     struct index * decoders = index_new ();
00217 
00218     for (GList * node = big_list; node; node = node->next)
00219     {
00220         AddFile * file = node->data;
00221         index_append (filenames, file->filename);
00222         index_append (decoders, file->decoder);
00223         g_slice_free (AddFile, file);
00224     }
00225 
00226     g_list_free (big_list);
00227     big_list = NULL;
00228 
00229     gint count = playlist_entry_count (playlist);
00230     if (group->at < 0 || group->at > count)
00231         group->at = count;
00232 
00233     playlist_entry_insert_batch_with_decoders (playlist, group->at,
00234      filenames, decoders, NULL);
00235 
00236     if (group->play && playlist_entry_count (playlist) > count)
00237     {
00238         playlist_set_playing (playlist);
00239         if (! cfg.shuffle)
00240             playlist_set_position (playlist, group->at);
00241         playback_play (0, FALSE);
00242     }
00243 
00244 ADDED:
00245     g_slice_free (AddGroup, group);
00246     g_queue_pop_head (& add_queue);
00247 
00248     if (! g_queue_is_empty (& add_queue))
00249         return TRUE; /* not done yet */
00250 
00251     show_done ();
00252     add_source = 0;
00253     return FALSE;
00254 }
00255 
00256 void playlist_insert_folder (gint playlist, gint at, const gchar * folder,
00257  gboolean play)
00258 {
00259     gint playlist_id = playlist_get_unique_id (playlist);
00260     g_return_if_fail (playlist_id >= 0);
00261 
00262     gchar * unix_name = uri_to_filename (folder);
00263     g_return_if_fail (unix_name);
00264 
00265     if (unix_name[strlen (unix_name) - 1] == '/')
00266         unix_name[strlen (unix_name) - 1] = 0;
00267 
00268     AddGroup * group = NULL;
00269 
00270     for (GList * node = g_queue_peek_head_link (& add_queue); node; node =
00271      node->next)
00272     {
00273         AddGroup * test = node->data;
00274         if (test->playlist_id == playlist_id && test->at == at)
00275         {
00276             group = test;
00277             break;
00278         }
00279     }
00280 
00281     if (! group)
00282     {
00283         group = g_slice_new (AddGroup);
00284         group->playlist_id = playlist_id;
00285         group->at = at;
00286         g_queue_init (& group->folders);
00287         group->play = FALSE;
00288         g_queue_push_tail (& add_queue, group);
00289     }
00290 
00291     g_queue_push_tail (& group->folders, unix_name);
00292     group->play |= play;
00293 
00294     if (! add_source)
00295         add_source = g_idle_add (add_cb, NULL);
00296 }