~kaozilon/totem/test

« back to all changes in this revision

Viewing changes to src/totem-playlist.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons, Josselin Mouette, Sjoerd Simons, Emilio Pozuelo Monfort
  • Date: 2009-04-19 17:28:51 UTC
  • mfrom: (1.2.52 upstream) (5.1.1 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090419172851-epoqimnq62akn294
Tags: 2.26.1-1
[ Josselin Mouette ]
* totem-plugins depends on python-gdbm. Closes: #523582.

[ Sjoerd Simons ]
* New upstream release (2.26.1)
* debian/patches/02_flv.patch: Dropped, fixed upstream
* debian/patches/04_tracker_build.patch: Dropped, fixed upstream
* debian/patches/01_fake_keypresses.patch: Updated and simplified
* debian/patches/70_bbc_plugin.patch: Updated
* debian/patches/90_autotools.patch: Updated

[ Emilio Pozuelo Monfort ]
* Recommend gnome-codec-install rather than gnome-app-install.
  Closes: #523052.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
#include <gio/gio.h>
33
33
#include <string.h>
34
34
 
 
35
#include "eggfileformatchooser.h"
 
36
#include "totem-dnd-menu.h"
35
37
#include "totem-uri.h"
36
38
#include "totem-interface.h"
37
39
#include "video-utils.h"
42
44
static void ensure_shuffled (TotemPlaylist *playlist, gboolean shuffle);
43
45
static gboolean totem_playlist_add_one_mrl (TotemPlaylist *playlist, const char *mrl, const char *display_name);
44
46
 
45
 
typedef gboolean (*PlaylistCallback) (TotemPlaylist *playlist, const char *mrl,
46
 
                gpointer data);
47
47
typedef gboolean (*ClearComparisonFunc) (TotemPlaylist *playlist, GtkTreeIter *iter, gconstpointer data);
48
48
 
49
49
static void totem_playlist_clear_with_compare (TotemPlaylist *playlist,
67
67
        gpointer user_data;
68
68
} PlaylistForeachContext;
69
69
 
70
 
typedef struct {
71
 
        char *mimetype;
72
 
        PlaylistCallback func;
73
 
} PlaylistTypes;
74
 
 
75
 
typedef struct {
76
 
        const char *name;
77
 
        const char *suffix;
78
 
        TotemPlParserType type;
79
 
} PlaylistSaveType;
80
 
 
81
70
struct TotemPlaylistPrivate
82
71
{
83
72
        GtkBuilder *xml;
91
80
        GtkActionGroup *action_group;
92
81
        GtkUIManager *ui_manager;
93
82
 
94
 
        /* This is a scratch list for when we're removing files */
95
 
        GList *list;
96
 
 
97
83
        /* These is the current paths for the file selectors */
98
84
        char *path;
99
85
        char *save_path;
 
86
        guint save_format;
 
87
        GtkWidget *file_chooser;
100
88
 
101
89
        /* Shuffle mode */
102
90
        int *shuffled;
108
96
        GtkTreePath *tree_path;
109
97
        GtkTreeViewDropPosition drop_pos;
110
98
 
 
99
        /* This is a scratch list for when we're removing files */
 
100
        GList *list;
 
101
        guint current_to_be_removed : 1;
 
102
 
111
103
        guint disable_save_to_disk : 1;
112
104
 
113
105
        /* Repeat mode */
147
139
        NUM_COLS
148
140
};
149
141
 
 
142
typedef struct {
 
143
        const char *name;
 
144
        const char *suffix;
 
145
        TotemPlParserType type;
 
146
} PlaylistSaveType;
 
147
 
150
148
static PlaylistSaveType save_types [] = {
151
 
        {".PLS", ".pls", TOTEM_PL_PARSER_PLS},
152
 
        {".M3U", ".m3u", TOTEM_PL_PARSER_M3U},
153
 
        {".M3U (DOS)", ".m3u", TOTEM_PL_PARSER_M3U_DOS},
154
 
        {".XSPF", ".xspf", TOTEM_PL_PARSER_XSPF}
 
149
        { NULL, NULL, -1 }, /* By extension entry */
 
150
        { N_("MP3 ShoutCast playlist"), "pls", TOTEM_PL_PARSER_PLS },
 
151
        { N_("MP3 audio (streamed)"), "m3u", TOTEM_PL_PARSER_M3U },
 
152
        { N_("MP3 audio (streamed, DOS format)"), "m3u", TOTEM_PL_PARSER_M3U_DOS },
 
153
        { N_("XML Shareable Playlist"), "xspf", TOTEM_PL_PARSER_XSPF }
155
154
};
156
155
 
157
156
static int totem_playlist_table_signals[LAST_SIGNAL];
161
160
        { "_NETSCAPE_URL", 0, 1 }
162
161
};
163
162
 
164
 
static void totem_playlist_class_init (TotemPlaylistClass *class);
165
 
static void totem_playlist_init       (TotemPlaylist      *playlist);
166
 
 
167
163
static void init_treeview (GtkWidget *treeview, TotemPlaylist *playlist);
168
164
 
169
165
#define totem_playlist_unset_playing(x) totem_playlist_set_playing(x, TOTEM_PLAYLIST_STATUS_NONE)
246
242
                                GTK_DIALOG_MODAL,
247
243
                                GTK_MESSAGE_ERROR,
248
244
                                GTK_BUTTONS_OK,
249
 
                                title);
 
245
                                "%s", title);
250
246
        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog),
251
 
                                                  reason);
 
247
                                                  "%s", reason);
252
248
 
253
249
        gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5);
254
250
        gtk_dialog_set_default_response (GTK_DIALOG (error_dialog),
262
258
        gtk_widget_show (error_dialog);
263
259
}
264
260
 
 
261
void
 
262
totem_playlist_select_subtitle_dialog(TotemPlaylist *playlist, TotemPlaylistSelectDialog mode)
 
263
{
 
264
        char *subtitle, *current, *path;
 
265
        GFile *file, *dir;
 
266
        TotemPlaylistStatus playing;
 
267
        GtkTreeIter iter;
 
268
 
 
269
        if (mode == TOTEM_PLAYLIST_DIALOG_PLAYING) {
 
270
                /* Set subtitle file for the currently playing movie */
 
271
                gtk_tree_model_get_iter (playlist->priv->model, &iter, playlist->priv->current);
 
272
        } else if (mode == TOTEM_PLAYLIST_DIALOG_SELECTED) {
 
273
                /* Set subtitle file in for the first selected playlist item */
 
274
                GList *l;
 
275
 
 
276
                l = gtk_tree_selection_get_selected_rows (playlist->priv->selection, NULL);
 
277
                gtk_tree_model_get_iter (playlist->priv->model, &iter, l->data);
 
278
                g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
 
279
                g_list_free (l);
 
280
        } else {
 
281
                g_assert_not_reached ();
 
282
        }
 
283
 
 
284
        /* Look for the directory of the current movie */
 
285
        gtk_tree_model_get (playlist->priv->model, &iter,
 
286
                            FILENAME_COL, &current,
 
287
                            -1);
 
288
 
 
289
        if (current == NULL)
 
290
                return;
 
291
 
 
292
        path = NULL;
 
293
        file = g_file_new_for_commandline_arg (current);
 
294
        dir = g_file_get_parent (file);
 
295
        g_object_unref (file);
 
296
        if (dir != NULL) {
 
297
                path = g_file_get_path (dir);
 
298
                g_object_unref (dir);
 
299
        }
 
300
 
 
301
        subtitle = totem_add_subtitle (totem_playlist_get_toplevel (playlist), path);
 
302
        g_free (path);
 
303
 
 
304
        if (subtitle == NULL)
 
305
                return;
 
306
 
 
307
        gtk_tree_model_get (playlist->priv->model, &iter,
 
308
                            PLAYING_COL, &playing,
 
309
                            -1);
 
310
 
 
311
        gtk_list_store_set (GTK_LIST_STORE(playlist->priv->model), &iter,
 
312
                            SUBTITLE_URI_COL, subtitle,
 
313
                            -1);
 
314
 
 
315
        if (playing != TOTEM_PLAYLIST_STATUS_NONE) {
 
316
                g_signal_emit (G_OBJECT (playlist),
 
317
                               totem_playlist_table_signals[SUBTITLE_CHANGED], 0,
 
318
                               NULL);
 
319
        }
 
320
 
 
321
        g_free(subtitle);
 
322
}
 
323
 
 
324
void
 
325
totem_playlist_set_current_subtitle (TotemPlaylist *playlist, const char *subtitle_uri)
 
326
{
 
327
        GtkTreeIter iter;
 
328
 
 
329
        if (playlist->priv->current == NULL)
 
330
                return;
 
331
 
 
332
        gtk_tree_model_get_iter (playlist->priv->model, &iter, playlist->priv->current);
 
333
 
 
334
        gtk_list_store_set (GTK_LIST_STORE(playlist->priv->model), &iter,
 
335
                            SUBTITLE_URI_COL, subtitle_uri,
 
336
                            -1);
 
337
 
 
338
        g_signal_emit (G_OBJECT (playlist),
 
339
                       totem_playlist_table_signals[SUBTITLE_CHANGED], 0,
 
340
                       NULL);
 
341
}
 
342
 
265
343
/* This one returns a new string, in UTF8 even if the MRL is encoded
266
344
 * in the locale's encoding
267
345
 */
391
469
        GList *p, *file_list;
392
470
        guint i;
393
471
 
 
472
        if (context->suggested_action == GDK_ACTION_ASK) {
 
473
                context->action = totem_drag_ask (PL_LEN != 0);
 
474
                if (context->action == GDK_ACTION_DEFAULT) {
 
475
                        gtk_drag_finish (context, FALSE, FALSE, time);
 
476
                        return;
 
477
                }
 
478
        }
 
479
 
 
480
        if (context->action == GDK_ACTION_MOVE)
 
481
                totem_playlist_clear (playlist);
 
482
 
394
483
        list = g_uri_list_extract_uris ((char *)data->data);
395
484
        file_list = NULL;
396
485
 
461
550
void
462
551
playlist_select_subtitle_action_callback (GtkAction *action, TotemPlaylist *playlist)
463
552
{
464
 
        char *subtitle, *current, *path;
465
 
        GList *l;
466
 
        GFile *file, *dir;
467
 
        TotemPlaylistStatus playing;
468
 
        GtkTreeIter iter;
469
 
 
470
 
        l = gtk_tree_selection_get_selected_rows (playlist->priv->selection, NULL);
471
 
        gtk_tree_model_get_iter (playlist->priv->model, &iter, l->data);
472
 
        g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
473
 
        g_list_free (l);
474
 
 
475
 
        /* Look for the directory of the current movie */
476
 
        gtk_tree_model_get (playlist->priv->model, &iter,
477
 
                            FILENAME_COL, &current,
478
 
                            -1);
479
 
 
480
 
        if (current == NULL)
481
 
                return;
482
 
 
483
 
        path = NULL;
484
 
        file = g_file_new_for_commandline_arg (current);
485
 
        dir = g_file_get_parent (file);
486
 
        g_object_unref (file);
487
 
        if (dir != NULL) {
488
 
                path = g_file_get_path (dir);
489
 
                g_object_unref (dir);
490
 
        }
491
 
 
492
 
        subtitle = totem_add_subtitle (totem_playlist_get_toplevel (playlist), path);
493
 
        g_free (path);
494
 
 
495
 
        if (subtitle == NULL)
496
 
                return;
497
 
 
498
 
        gtk_tree_model_get (playlist->priv->model, &iter,
499
 
                            PLAYING_COL, &playing,
500
 
                            -1);
501
 
 
502
 
        gtk_list_store_set (GTK_LIST_STORE(playlist->priv->model), &iter, 
503
 
                            SUBTITLE_URI_COL, subtitle,
504
 
                            -1);
505
 
 
506
 
        if (playing != TOTEM_PLAYLIST_STATUS_NONE) {
507
 
                g_signal_emit (G_OBJECT (playlist),
508
 
                               totem_playlist_table_signals[SUBTITLE_CHANGED], 0,
509
 
                               NULL);
510
 
        }
511
 
 
512
 
        g_free(subtitle);
 
553
        totem_playlist_select_subtitle_dialog (playlist, TOTEM_PLAYLIST_DIALOG_SELECTED);
513
554
}
514
555
 
515
556
void
546
587
playlist_show_popup_menu (TotemPlaylist *playlist, GdkEventButton *event)
547
588
{
548
589
        guint button = 0;
549
 
        guint32 time;
 
590
        guint32 time;
550
591
        GtkTreePath *path;
551
592
        gint count;
552
593
        GtkWidget *menu;
644
685
 
645
686
                /* Only emit the changed signal if we changed the ->current */
646
687
                path = gtk_tree_path_new_from_indices (i, -1);
647
 
                if (gtk_tree_path_compare (path, playlist->priv->current) == 0)
648
 
                {
 
688
                if (gtk_tree_path_compare (path, playlist->priv->current) == 0) {
649
689
                        gtk_tree_path_free (path);
650
690
                } else {
651
691
                        gtk_tree_path_free (playlist->priv->current);
812
852
        ref = gtk_tree_row_reference_new (playlist->priv->model, path);
813
853
        playlist->priv->list = g_list_prepend
814
854
                (playlist->priv->list, (gpointer) ref);
 
855
        if (playlist->priv->current_to_be_removed == FALSE
 
856
            && playlist->priv->current != NULL
 
857
            && gtk_tree_path_compare (path, playlist->priv->current) == 0)
 
858
                playlist->priv->current_to_be_removed = TRUE;
815
859
}
816
860
 
817
861
static void
835
879
static void
836
880
playlist_remove_files (TotemPlaylist *playlist)
837
881
{
838
 
        GtkTreeSelection *selection;
839
 
        GtkTreeRowReference *ref;
840
 
        gboolean is_selected = FALSE;
841
 
        int next_pos;
842
 
 
843
 
        selection = gtk_tree_view_get_selection
844
 
                (GTK_TREE_VIEW (playlist->priv->treeview));
845
 
        if (selection == NULL)
846
 
                return;
847
 
 
848
 
        gtk_tree_selection_selected_foreach (selection,
849
 
                        totem_playlist_foreach_selected,
850
 
                        (gpointer) playlist);
851
 
 
852
 
        /* If the current item is to change, we need to keep an static
853
 
         * reference to it, TreeIter and TreePath don't allow that */
854
 
        if (playlist->priv->current != NULL)
855
 
        {
856
 
                int *indices;
857
 
 
858
 
                ref = gtk_tree_row_reference_new (playlist->priv->model,
859
 
                                playlist->priv->current);
860
 
                is_selected = gtk_tree_selection_path_is_selected (selection,
861
 
                                playlist->priv->current);
862
 
 
863
 
                indices = gtk_tree_path_get_indices (playlist->priv->current);
864
 
                next_pos = indices[0];
865
 
 
866
 
                gtk_tree_path_free (playlist->priv->current);
867
 
        } else {
868
 
                ref = NULL;
869
 
                next_pos = -1;
870
 
        }
871
 
 
872
 
        /* We destroy the items, one-by-one from the list built above */
873
 
        while (playlist->priv->list != NULL)
874
 
        {
875
 
                GtkTreePath *path;
876
 
                GtkTreeIter iter;
877
 
 
878
 
                path = gtk_tree_row_reference_get_path
879
 
                        ((GtkTreeRowReference *)(playlist->priv->list->data));
880
 
                gtk_tree_model_get_iter (playlist->priv->model, &iter, path);
881
 
                gtk_tree_path_free (path);
882
 
 
883
 
                totem_playlist_emit_item_removed (playlist, &iter);
884
 
                gtk_list_store_remove (GTK_LIST_STORE (playlist->priv->model), &iter);
885
 
 
886
 
                gtk_tree_row_reference_free
887
 
                        ((GtkTreeRowReference *)(playlist->priv->list->data));
888
 
                playlist->priv->list = g_list_remove (playlist->priv->list,
889
 
                                playlist->priv->list->data);
890
 
        }
891
 
        g_list_free (playlist->priv->list);
892
 
        playlist->priv->list = NULL;
893
 
 
894
 
        if (is_selected != FALSE)
895
 
        {
896
 
                /* The current item was removed from the playlist */
897
 
                if (next_pos != -1)
898
 
                {
899
 
                        char *str;
900
 
                        GtkTreeIter iter;
901
 
                        GtkTreePath *cur;
902
 
 
903
 
                        str = g_strdup_printf ("%d", next_pos);
904
 
                        cur = gtk_tree_path_new_from_string (str);
905
 
 
906
 
                        if (gtk_tree_model_get_iter (playlist->priv->model,
907
 
                                                &iter, cur) == FALSE)
908
 
                        {
909
 
                                playlist->priv->current = NULL;
910
 
                                gtk_tree_path_free (cur);
911
 
                        } else {
912
 
                                playlist->priv->current = cur;
913
 
                        }
914
 
                        g_free (str);
915
 
                } else {
916
 
                        playlist->priv->current = NULL;
917
 
                }
918
 
 
919
 
                playlist->priv->current_shuffled = -1;
920
 
                ensure_shuffled (playlist, playlist->priv->shuffle);
921
 
 
922
 
                g_signal_emit (G_OBJECT (playlist),
923
 
                                totem_playlist_table_signals[CURRENT_REMOVED],
924
 
                                0, NULL);
925
 
        } else {
926
 
                if (ref != NULL)
927
 
                {
928
 
                        /* The path to the current item changed */
929
 
                        playlist->priv->current =
930
 
                                gtk_tree_row_reference_get_path (ref);
931
 
                        gtk_tree_row_reference_free (ref);
932
 
                }
933
 
 
934
 
                ensure_shuffled (playlist, playlist->priv->shuffle);
935
 
 
936
 
                g_signal_emit (G_OBJECT (playlist),
937
 
                                totem_playlist_table_signals[CHANGED], 0,
938
 
                                NULL);
939
 
        }
940
 
        totem_playlist_update_save_button (playlist);
941
 
        gtk_tree_view_columns_autosize (GTK_TREE_VIEW (playlist->priv->treeview));
 
882
        totem_playlist_clear_with_compare (playlist, NULL, NULL);
942
883
}
943
884
 
944
885
void
956
897
static void
957
898
totem_playlist_save_playlist (TotemPlaylist *playlist, char *filename, gint active_format)
958
899
{
959
 
        PlaylistSaveType *cur = NULL;
960
 
        guint i;
961
 
 
962
 
        if (active_format > 0)
963
 
                totem_playlist_save_current_playlist_ext (playlist, filename,
964
 
                                                        save_types[active_format - 1].type);
965
 
        else {
966
 
                for (i = 0; i < G_N_ELEMENTS(save_types); i++) {
967
 
                        if (g_str_has_suffix (filename, save_types[i].suffix)) {
968
 
                                cur = &save_types[i];
969
 
                                break;
 
900
        if (active_format == 0)
 
901
                active_format = 1;
 
902
 
 
903
        totem_playlist_save_current_playlist_ext (playlist, filename,
 
904
                                                  save_types[active_format].type);
 
905
}
 
906
 
 
907
static char *
 
908
suffix_match_replace (const char *fname, guint old_format, guint new_format)
 
909
{
 
910
        char *ext;
 
911
 
 
912
        ext = g_strdup_printf (".%s", save_types[old_format].suffix);
 
913
        if (g_str_has_suffix (fname, ext) != FALSE) {
 
914
                char *no_suffix, *new_fname;
 
915
 
 
916
                no_suffix = g_strndup (fname, strlen (fname) - strlen (ext));
 
917
                new_fname = g_strconcat (no_suffix, ".", save_types[new_format].suffix, NULL);
 
918
                g_free (no_suffix);
 
919
                g_free (ext);
 
920
 
 
921
                return new_fname;
 
922
        }
 
923
        g_free (ext);
 
924
 
 
925
        return NULL;
 
926
}
 
927
 
 
928
static void
 
929
format_selection_changed (EggFileFormatChooser *chooser, TotemPlaylist *playlist)
 
930
{
 
931
        guint format;
 
932
 
 
933
        format = egg_file_format_chooser_get_format (chooser, NULL);
 
934
 
 
935
        if (format != playlist->priv->save_format) {
 
936
                char *fname, *new_fname;
 
937
 
 
938
                new_fname = NULL;
 
939
                fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (playlist->priv->file_chooser));
 
940
 
 
941
                if (format == 0) {
 
942
                        /* The new format is "By extension" don't touch anything */
 
943
                } else if (playlist->priv->save_format == 0) {
 
944
                        guint i;
 
945
 
 
946
                        for (i = 1; i < G_N_ELEMENTS (save_types); i++) {
 
947
                                new_fname = suffix_match_replace (fname, i, format);
 
948
                                if (new_fname != NULL)
 
949
                                        break;
970
950
                        }
971
 
                }
972
 
                if (cur == NULL)
973
 
                        totem_playlist_error (_("Could not save the playlist"), _("Unknown file extension."), playlist);
974
 
                else
975
 
                        totem_playlist_save_current_playlist_ext (playlist, filename, cur->type);
 
951
                } else {
 
952
                        new_fname = suffix_match_replace (fname, playlist->priv->save_format, format);
 
953
                }
 
954
                if (new_fname != NULL) {
 
955
                        char *basename;
 
956
 
 
957
                        basename = g_path_get_basename (new_fname);
 
958
                        g_free (new_fname);
 
959
                        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (playlist->priv->file_chooser), basename);
 
960
                        g_free (basename);
 
961
                }
 
962
                playlist->priv->save_format = format;
976
963
        }
977
964
}
978
965
 
979
966
static GtkWidget *
980
 
totem_playlist_save_add_format_combo_box (GtkFileChooser *fc)
 
967
totem_playlist_save_add_format_chooser (GtkFileChooser *fc, TotemPlaylist *playlist)
981
968
{
982
 
        GtkWidget *hbox, *label, *combo_box;
 
969
        GtkWidget *format_chooser;
983
970
        guint i;
984
971
 
985
 
        hbox = gtk_hbox_new (FALSE, 4);
986
 
        label = gtk_label_new (_("Select playlist format:"));
987
 
        gtk_widget_show (label);
988
 
 
989
 
        combo_box = gtk_combo_box_new_text ();
990
 
        gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box),
991
 
                        _("By extension"));
992
 
        gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
993
 
        for (i = 0; i < G_N_ELEMENTS(save_types); i++) {
994
 
                gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box),
995
 
                                save_types[i].name);
 
972
        format_chooser = egg_file_format_chooser_new ();
 
973
 
 
974
        playlist->priv->save_format = 0;
 
975
 
 
976
        for (i = 1; i < G_N_ELEMENTS (save_types) ; i++) {
 
977
                egg_file_format_chooser_add_format (
 
978
                    EGG_FILE_FORMAT_CHOOSER (format_chooser), 0, _(save_types[i].name),
 
979
                    "gnome-mime-audio", save_types[i].suffix, NULL);
996
980
        }
997
 
        gtk_widget_show (combo_box);
998
 
 
999
 
        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1000
 
        gtk_box_pack_start (GTK_BOX (hbox), combo_box, TRUE, TRUE, 0);
1001
 
        gtk_widget_show (hbox);
1002
 
        gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fc), hbox);
1003
 
 
1004
 
        atk_object_add_relationship (gtk_widget_get_accessible (label),
1005
 
                        ATK_RELATION_LABEL_FOR,
1006
 
                        gtk_widget_get_accessible (combo_box));
1007
 
        atk_object_add_relationship (gtk_widget_get_accessible (combo_box),
1008
 
                        ATK_RELATION_LABELLED_BY,
1009
 
                        gtk_widget_get_accessible (label));
1010
 
 
1011
 
        return combo_box;
 
981
 
 
982
        g_signal_connect (format_chooser, "selection-changed",
 
983
                          G_CALLBACK (format_selection_changed), playlist);
 
984
 
 
985
        gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fc),
 
986
                                           format_chooser);
 
987
 
 
988
        return format_chooser;
1012
989
}
1013
990
 
1014
991
void
1015
992
totem_playlist_save_files (GtkWidget *widget, TotemPlaylist *playlist)
1016
993
{
1017
 
        GtkWidget *fs, *combo_box;
 
994
        GtkWidget *fs, *format_chooser;
1018
995
        char *filename;
1019
996
        int response;
1020
997
 
 
998
        g_assert (playlist->priv->file_chooser == NULL);
 
999
 
1021
1000
        fs = gtk_file_chooser_dialog_new (_("Save Playlist"),
1022
 
                        totem_playlist_get_toplevel (playlist),
1023
 
                        GTK_FILE_CHOOSER_ACTION_SAVE,
1024
 
                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1025
 
                        GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1026
 
                        NULL);
 
1001
                                          totem_playlist_get_toplevel (playlist),
 
1002
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
 
1003
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
1004
                                          GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
 
1005
                                          NULL);
1027
1006
        gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
1028
1007
        gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
1029
1008
        gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fs), TRUE);
 
1009
 
1030
1010
        /* translators: Playlist is the default saved playlist filename,
1031
1011
         * without the suffix */
1032
 
        filename = g_strconcat (_("Playlist"), save_types[0].suffix, NULL);
 
1012
        filename = g_strconcat (_("Playlist"), ".", save_types[1].suffix, NULL);
1033
1013
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fs), filename);
1034
1014
        g_free (filename);
1035
 
        combo_box = totem_playlist_save_add_format_combo_box (GTK_FILE_CHOOSER (fs));
 
1015
        format_chooser = totem_playlist_save_add_format_chooser (GTK_FILE_CHOOSER (fs), playlist);
1036
1016
 
1037
 
        if (playlist->priv->save_path != NULL)
1038
 
        {
 
1017
        if (playlist->priv->save_path != NULL) {
1039
1018
                gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fs),
1040
1019
                                playlist->priv->save_path);
1041
1020
        }
1042
1021
 
 
1022
        playlist->priv->file_chooser = fs;
 
1023
 
1043
1024
        response = gtk_dialog_run (GTK_DIALOG (fs));
1044
1025
        gtk_widget_hide (fs);
1045
1026
        while (gtk_events_pending())
1046
1027
                gtk_main_iteration();
1047
1028
 
1048
 
        if (response == GTK_RESPONSE_ACCEPT)
1049
 
        {
 
1029
        if (response == GTK_RESPONSE_ACCEPT) {
1050
1030
                char *fname;
1051
 
                gint active_format;
 
1031
                guint active_format;
1052
1032
 
1053
1033
                fname = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fs));
1054
 
                active_format = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box));
 
1034
                active_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (format_chooser),
 
1035
                                                                    fname);
1055
1036
 
 
1037
                playlist->priv->file_chooser = NULL;
1056
1038
                gtk_widget_destroy (fs);
1057
1039
 
1058
1040
                if (fname == NULL)
1064
1046
                totem_playlist_save_playlist (playlist, fname, active_format);
1065
1047
                g_free (fname);
1066
1048
        } else {
 
1049
                playlist->priv->file_chooser = NULL;
1067
1050
                gtk_widget_destroy (fs);
1068
1051
        }
1069
1052
}
1090
1073
        pos = -2;
1091
1074
        refs = NULL;
1092
1075
 
1093
 
        if (playlist->priv->current != NULL)
1094
 
        {
 
1076
        if (playlist->priv->current != NULL) {
1095
1077
                current = gtk_tree_row_reference_new (model,
1096
1078
                                playlist->priv->current);
1097
1079
        } else {
1100
1082
 
1101
1083
        /* Build a list of tree references */
1102
1084
        paths = gtk_tree_selection_get_selected_rows (selection, NULL);
1103
 
        for (l = paths; l != NULL; l = l->next)
1104
 
        {
 
1085
        for (l = paths; l != NULL; l = l->next) {
1105
1086
                GtkTreePath *path = l->data;
1106
1087
                int cur_pos, *indices;
1107
1088
 
1131
1112
        else
1132
1113
                pos = pos - 2;
1133
1114
 
1134
 
        for (l = refs; l != NULL; l = l->next)
1135
 
        {
 
1115
        for (l = refs; l != NULL; l = l->next) {
1136
1116
                GtkTreeIter *position, cur;
1137
1117
                GtkTreeRowReference *ref = l->data;
1138
1118
                GtkTreePath *path;
1139
1119
 
1140
 
                if (pos < 0)
1141
 
                {
 
1120
                if (pos < 0) {
1142
1121
                        position = NULL;
1143
1122
                } else {
1144
1123
                        char *str;
1171
1150
        g_list_free (refs);
1172
1151
 
1173
1152
        /* Update the current path */
1174
 
        if (current != NULL)
1175
 
        {
 
1153
        if (current != NULL) {
1176
1154
                gtk_tree_path_free (playlist->priv->current);
1177
1155
                playlist->priv->current = gtk_tree_row_reference_get_path
1178
1156
                        (current);
1405
1383
{
1406
1384
        RandomData data;
1407
1385
        GArray *array;
1408
 
        int i, current;
 
1386
        int i, current, current_new;
1409
1387
        int *indices;
1410
1388
 
1411
1389
        if (shuffle == FALSE || PL_LEN != playlist->priv->shuffle_len)
1424
1402
        } else {
1425
1403
                current = -1;
1426
1404
        }
 
1405
        
 
1406
        current_new = -1;
1427
1407
 
1428
1408
        playlist->priv->shuffled = g_new (int, PL_LEN);
1429
1409
        playlist->priv->shuffle_len = PL_LEN;
1448
1428
 
1449
1429
                if (playlist->priv->current != NULL
1450
1430
                                && playlist->priv->shuffled[i] == current)
1451
 
                        playlist->priv->current_shuffled = i;
 
1431
                        current_new = i;
 
1432
        }
 
1433
 
 
1434
        if (current_new > -1) {
 
1435
                playlist->priv->shuffled[current_new] = playlist->priv->shuffled[0];
 
1436
                playlist->priv->shuffled[0] = current;
 
1437
                playlist->priv->current_shuffled = 0;
1452
1438
        }
1453
1439
 
1454
1440
        g_array_free (array, TRUE);
1532
1518
        totem_playlist_add_one_mrl (playlist, uri, title);
1533
1519
}
1534
1520
 
 
1521
static gboolean
 
1522
totem_playlist_compare_with_monitor (TotemPlaylist *playlist, GtkTreeIter *iter, gconstpointer data)
 
1523
{
 
1524
        GFileMonitor *monitor = (GFileMonitor *) data;
 
1525
        GFileMonitor *_monitor;
 
1526
        gboolean retval = FALSE;
 
1527
 
 
1528
        gtk_tree_model_get (playlist->priv->model, iter,
 
1529
                            FILE_MONITOR_COL, &_monitor, -1);
 
1530
 
 
1531
        if (_monitor == monitor)
 
1532
                retval = TRUE;
 
1533
 
 
1534
        if (_monitor != NULL)
 
1535
                g_object_unref (_monitor);
 
1536
 
 
1537
        return retval;
 
1538
}
 
1539
 
 
1540
static void
 
1541
totem_playlist_file_changed (GFileMonitor *monitor,
 
1542
                             GFile *file,
 
1543
                             GFile *other_file,
 
1544
                             GFileMonitorEvent event_type,
 
1545
                             TotemPlaylist *playlist)
 
1546
{
 
1547
        if (event_type == G_FILE_MONITOR_EVENT_PRE_UNMOUNT ||
 
1548
            event_type == G_FILE_MONITOR_EVENT_UNMOUNTED) {
 
1549
                totem_playlist_clear_with_compare (playlist,
 
1550
                                                   (ClearComparisonFunc) totem_playlist_compare_with_monitor,
 
1551
                                                   monitor);
 
1552
        }
 
1553
}
 
1554
 
1535
1555
static void
1536
1556
totem_playlist_dispose (GObject *object)
1537
1557
{
1640
1660
        GtkTreeIter iter;
1641
1661
        char *filename_for_display, *uri;
1642
1662
        GtkTreeRowReference *ref;
 
1663
        GFileMonitor *monitor;
 
1664
        GFile *file;
1643
1665
        int pos;
1644
1666
 
1645
1667
        g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
1646
1668
        g_return_val_if_fail (mrl != NULL, FALSE);
1647
1669
 
1648
 
        if (display_name == NULL)
 
1670
        if (display_name == NULL || *display_name == '\0')
1649
1671
                filename_for_display = totem_playlist_mrl_to_title (mrl);
1650
1672
        else
1651
1673
                filename_for_display = g_strdup (display_name);
1667
1689
 
1668
1690
        store = GTK_LIST_STORE (playlist->priv->model);
1669
1691
 
 
1692
        /* Get the file monitor */
 
1693
        file = g_file_new_for_uri (uri ? uri : mrl);
 
1694
        if (g_file_is_native (file) != FALSE) {
 
1695
                monitor = g_file_monitor_file (file,
 
1696
                                               G_FILE_MONITOR_NONE,
 
1697
                                               NULL,
 
1698
                                               NULL);
 
1699
                g_signal_connect (G_OBJECT (monitor),
 
1700
                                  "changed",
 
1701
                                  G_CALLBACK (totem_playlist_file_changed),
 
1702
                                  playlist);
 
1703
        } else {
 
1704
                monitor = NULL;
 
1705
        }
 
1706
 
1670
1707
        gtk_list_store_insert_with_values (store, &iter, pos,
1671
1708
                        PLAYING_COL, TOTEM_PLAYLIST_STATUS_NONE,
1672
1709
                        FILENAME_COL, filename_for_display,
1673
1710
                        URI_COL, uri ? uri : mrl,
1674
1711
                        TITLE_CUSTOM_COL, display_name ? TRUE : FALSE,
 
1712
                        FILE_MONITOR_COL, monitor,
1675
1713
                        -1);
1676
1714
 
1677
1715
        g_signal_emit (playlist,
1781
1819
                                   ClearComparisonFunc func,
1782
1820
                                   gconstpointer data)
1783
1821
{
1784
 
        GList *list = NULL, *l;
1785
 
        guint num_items, i;
1786
 
        gboolean has_items, current_removed;
1787
 
 
1788
 
        num_items = PL_LEN;
1789
 
        if (num_items == 0)
1790
 
                return;
1791
 
 
1792
 
        current_removed = FALSE;
1793
 
 
1794
 
        for (i = 0; i < num_items; i++)
1795
 
        {
1796
 
                GtkTreeIter iter;
1797
 
                char *index;
1798
 
 
1799
 
                index = g_strdup_printf ("%d", i);
1800
 
                if (gtk_tree_model_get_iter_from_string
1801
 
                                (playlist->priv->model,
1802
 
                                 &iter, index) == FALSE)
1803
 
                {
 
1822
        GtkTreeRowReference *ref;
 
1823
        int next_pos;
 
1824
 
 
1825
        ref = NULL;
 
1826
        next_pos = -1;
 
1827
 
 
1828
        if (func == NULL) {
 
1829
                GtkTreeSelection *selection;
 
1830
 
 
1831
                selection = gtk_tree_view_get_selection
 
1832
                        (GTK_TREE_VIEW (playlist->priv->treeview));
 
1833
                if (selection == NULL)
 
1834
                        return;
 
1835
 
 
1836
                gtk_tree_selection_selected_foreach (selection,
 
1837
                                                     totem_playlist_foreach_selected,
 
1838
                                                     (gpointer) playlist);
 
1839
        } else {
 
1840
                guint num_items, i;
 
1841
 
 
1842
                num_items = PL_LEN;
 
1843
                if (num_items == 0)
 
1844
                        return;
 
1845
 
 
1846
                for (i = 0; i < num_items; i++) {
 
1847
                        GtkTreeIter iter;
 
1848
                        char *index;
 
1849
 
 
1850
                        index = g_strdup_printf ("%d", i);
 
1851
                        if (gtk_tree_model_get_iter_from_string (playlist->priv->model, &iter, index) == FALSE) {
 
1852
                                g_free (index);
 
1853
                                continue;
 
1854
                        }
1804
1855
                        g_free (index);
1805
 
                        continue;
1806
 
                }
1807
 
                g_free (index);
1808
 
 
1809
 
                if ((* func) (playlist, &iter, data) != FALSE)
1810
 
                {
1811
 
                        GtkTreePath *path;
1812
 
                        GtkTreeRowReference *ref;
1813
 
 
1814
 
                        path = gtk_tree_path_new_from_indices (i, -1);
1815
 
                        ref = gtk_tree_row_reference_new
1816
 
                                (playlist->priv->model, path);
1817
 
                        list = g_list_prepend (list, ref);
1818
 
                        gtk_tree_path_free (path);
1819
 
                }
1820
 
        }
1821
 
 
1822
 
        has_items = (list != NULL);
1823
 
 
1824
 
        for (l = list; l != NULL; l = l->next)
 
1856
 
 
1857
                        if ((* func) (playlist, &iter, data) != FALSE) {
 
1858
                                GtkTreePath *path;
 
1859
                                GtkTreeRowReference *r;
 
1860
 
 
1861
                                path = gtk_tree_path_new_from_indices (i, -1);
 
1862
                                r = gtk_tree_row_reference_new (playlist->priv->model, path);
 
1863
                                if (playlist->priv->current != NULL) {
 
1864
                                        if (gtk_tree_path_compare (path, playlist->priv->current) == 0)
 
1865
                                                playlist->priv->current_to_be_removed = TRUE;
 
1866
                                }
 
1867
                                playlist->priv->list = g_list_prepend (playlist->priv->list, r);
 
1868
                                gtk_tree_path_free (path);
 
1869
                        }
 
1870
                }
 
1871
 
 
1872
                if (playlist->priv->list == NULL)
 
1873
                        return;
 
1874
        }
 
1875
 
 
1876
        /* If the current item is to change, we need to keep an static
 
1877
         * reference to it, TreeIter and TreePath don't allow that */
 
1878
        if (playlist->priv->current != NULL) {
 
1879
                int *indices;
 
1880
 
 
1881
                ref = gtk_tree_row_reference_new (playlist->priv->model,
 
1882
                                playlist->priv->current);
 
1883
                indices = gtk_tree_path_get_indices (playlist->priv->current);
 
1884
                next_pos = indices[0];
 
1885
 
 
1886
        }
 
1887
 
 
1888
        /* We destroy the items, one-by-one from the list built above */
 
1889
        while (playlist->priv->list != NULL)
1825
1890
        {
1826
1891
                GtkTreePath *path;
1827
1892
                GtkTreeIter iter;
1828
1893
 
1829
 
                path = gtk_tree_row_reference_get_path (l->data);
 
1894
                path = gtk_tree_row_reference_get_path
 
1895
                        ((GtkTreeRowReference *)(playlist->priv->list->data));
1830
1896
                gtk_tree_model_get_iter (playlist->priv->model, &iter, path);
1831
 
                if (gtk_tree_path_compare (path, playlist->priv->current) == 0)
1832
 
                        current_removed = TRUE;
 
1897
                gtk_tree_path_free (path);
1833
1898
 
1834
1899
                totem_playlist_emit_item_removed (playlist, &iter);
1835
1900
                gtk_list_store_remove (GTK_LIST_STORE (playlist->priv->model), &iter);
1836
 
                gtk_tree_path_free (path);
1837
 
                gtk_tree_row_reference_free (l->data);
 
1901
 
 
1902
                gtk_tree_row_reference_free
 
1903
                        ((GtkTreeRowReference *)(playlist->priv->list->data));
 
1904
                playlist->priv->list = g_list_remove (playlist->priv->list,
 
1905
                                playlist->priv->list->data);
1838
1906
        }
1839
 
        g_list_free (list);
1840
 
 
1841
 
        if (has_items != FALSE && current_removed != FALSE) {
 
1907
        g_list_free (playlist->priv->list);
 
1908
        playlist->priv->list = NULL;
 
1909
 
 
1910
        if (playlist->priv->current_to_be_removed != FALSE) {
 
1911
                /* The current item was removed from the playlist */
 
1912
                if (next_pos != -1) {
 
1913
                        char *str;
 
1914
                        GtkTreeIter iter;
 
1915
                        GtkTreePath *cur;
 
1916
 
 
1917
                        str = g_strdup_printf ("%d", next_pos);
 
1918
                        cur = gtk_tree_path_new_from_string (str);
 
1919
 
 
1920
                        if (gtk_tree_model_get_iter (playlist->priv->model, &iter, cur) == FALSE) {
 
1921
                                playlist->priv->current = NULL;
 
1922
                                gtk_tree_path_free (cur);
 
1923
                        } else {
 
1924
                                playlist->priv->current = cur;
 
1925
                        }
 
1926
                        g_free (str);
 
1927
                } else {
 
1928
                        playlist->priv->current = NULL;
 
1929
                }
 
1930
 
1842
1931
                playlist->priv->current_shuffled = -1;
 
1932
                ensure_shuffled (playlist, playlist->priv->shuffle);
1843
1933
 
1844
 
                ensure_shuffled (playlist, playlist->priv->shuffle);
1845
 
                gtk_tree_path_free (playlist->priv->current);
1846
 
                playlist->priv->current = NULL;
1847
1934
                g_signal_emit (G_OBJECT (playlist),
1848
1935
                                totem_playlist_table_signals[CURRENT_REMOVED],
1849
1936
                                0, NULL);
1850
 
        } else if (has_items != FALSE) {
 
1937
        } else {
 
1938
                if (ref != NULL) {
 
1939
                        /* The path to the current item changed */
 
1940
                        playlist->priv->current =
 
1941
                                gtk_tree_row_reference_get_path (ref);
 
1942
                        gtk_tree_row_reference_free (ref);
 
1943
                }
 
1944
 
1851
1945
                ensure_shuffled (playlist, playlist->priv->shuffle);
 
1946
 
1852
1947
                g_signal_emit (G_OBJECT (playlist),
1853
 
                               totem_playlist_table_signals[CHANGED], 0,
1854
 
                               NULL);
 
1948
                                totem_playlist_table_signals[CHANGED], 0,
 
1949
                                NULL);
1855
1950
        }
 
1951
        totem_playlist_update_save_button (playlist);
 
1952
        gtk_tree_view_columns_autosize (GTK_TREE_VIEW (playlist->priv->treeview));
 
1953
 
 
1954
        playlist->priv->current_to_be_removed = FALSE;
1856
1955
}
1857
1956
 
1858
1957
static gboolean
2281
2380
        }
2282
2381
}
2283
2382
 
2284
 
guint
 
2383
int
2285
2384
totem_playlist_get_current (TotemPlaylist *playlist)
2286
2385
{
2287
2386
        char *path;
2288
2387
        double index;
2289
2388
 
2290
 
        g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), 0);
 
2389
        g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), -1);
2291
2390
 
2292
2391
        if (playlist->priv->current == NULL)
2293
 
                return 0;
 
2392
                return -1;
2294
2393
        path = gtk_tree_path_to_string (playlist->priv->current);
2295
2394
        if (path == NULL)
2296
 
                return 0;
 
2395
                return -1;
2297
2396
 
2298
2397
        index = g_ascii_strtod (path, NULL);
2299
2398
        g_free (path);
2301
2400
        return index;
2302
2401
}
2303
2402
 
2304
 
guint
 
2403
int
2305
2404
totem_playlist_get_last (TotemPlaylist *playlist)
2306
2405
{
 
2406
        guint len = PL_LEN;
 
2407
 
2307
2408
        g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), -1);
2308
2409
 
2309
 
        return PL_LEN - 1;
 
2410
        if (len == 0)
 
2411
                return -1;
 
2412
 
 
2413
        return len - 1;
2310
2414
}
2311
2415
 
2312
2416
void