summaryrefslogtreecommitdiff
path: root/trunk/src/totem-playlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/totem-playlist.c')
-rw-r--r--trunk/src/totem-playlist.c2282
1 files changed, 2282 insertions, 0 deletions
diff --git a/trunk/src/totem-playlist.c b/trunk/src/totem-playlist.c
new file mode 100644
index 000000000..d7fe953ab
--- /dev/null
+++ b/trunk/src/totem-playlist.c
@@ -0,0 +1,2282 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* totem-playlist.c
+
+ Copyright (C) 2002, 2003, 2004, 2005 Bastien Nocera
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Bastien Nocera <hadess@hadess.net>
+ */
+
+#include "config.h"
+#include "totem-playlist.h"
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <string.h>
+
+#include "totem-uri.h"
+#include "totem-interface.h"
+#include "video-utils.h"
+#include "debug.h"
+
+#define PL_LEN (gtk_tree_model_iter_n_children (playlist->_priv->model, NULL))
+
+static void ensure_shuffled (TotemPlaylist *playlist, gboolean shuffle);
+static gboolean totem_playlist_add_one_mrl (TotemPlaylist *playlist, const char *mrl, const char *display_name);
+
+typedef gboolean (*PlaylistCallback) (TotemPlaylist *playlist, const char *mrl,
+ gpointer data);
+
+typedef struct {
+ char *mimetype;
+ PlaylistCallback func;
+} PlaylistTypes;
+
+typedef struct {
+ const char *name;
+ const char *suffix;
+ TotemPlParserType type;
+} PlaylistSaveType;
+
+struct TotemPlaylistPrivate
+{
+ GladeXML *xml;
+
+ GtkWidget *treeview;
+ GtkTreeModel *model;
+ GtkTreePath *current;
+ GtkTreeSelection *selection;
+ TotemPlParser *parser;
+
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+
+ /* This is a scratch list for when we're removing files */
+ GList *list;
+
+ /* These is the current paths for the file selectors */
+ char *path;
+ char *save_path;
+
+ /* Shuffle mode */
+ int *shuffled;
+ int current_shuffled, shuffle_len;
+
+ GConfClient *gc;
+
+ int x, y;
+
+ guint disable_save_to_disk : 1;
+
+ /* Repeat mode */
+ guint repeat : 1;
+
+ /* Reorder Flag */
+ guint drag_started : 1;
+
+ /* Drop disabled flag */
+ guint drop_disabled : 1;
+
+ /* Shuffle mode */
+ guint shuffle : 1;
+};
+
+/* Signals */
+enum {
+ CHANGED,
+ ITEM_ACTIVATED,
+ ACTIVE_NAME_CHANGED,
+ CURRENT_REMOVED,
+ REPEAT_TOGGLED,
+ SHUFFLE_TOGGLED,
+ LAST_SIGNAL
+};
+
+enum {
+ PLAYING_COL,
+ FILENAME_COL,
+ URI_COL,
+ TITLE_CUSTOM_COL,
+ CACHE_TITLE_COL,
+ CACHE_ARTIST_COL,
+ CACHE_ALBUM_COL,
+ NUM_COLS
+};
+
+static PlaylistSaveType save_types [] = {
+ {".PLS", ".pls", TOTEM_PL_PARSER_PLS},
+ {".M3U", ".m3u", TOTEM_PL_PARSER_M3U},
+ {".M3U (DOS)", ".m3u", TOTEM_PL_PARSER_M3U_DOS},
+ {".XSPF", ".xspf", TOTEM_PL_PARSER_XSPF},
+};
+
+static int totem_playlist_table_signals[LAST_SIGNAL] = { 0 };
+
+static const GtkTargetEntry target_table[] = {
+ { "text/uri-list", 0, 0 },
+ { "_NETSCAPE_URL", 0, 1 },
+};
+
+static void playlist_remove_action_activated (GtkAction *action, TotemPlaylist *playlist);
+static void playlist_copy_location_action_activated (GtkAction *action, TotemPlaylist *playlist);
+
+static const GtkActionEntry entries[] = {
+ { "remove", GTK_STOCK_REMOVE, N_("_Remove"), NULL, N_("Remove file from playlist"), G_CALLBACK (playlist_remove_action_activated) },
+ { "copy-location", GTK_STOCK_COPY, N_("_Copy Location"), NULL, N_("Copy the location to the clipboard"), G_CALLBACK (playlist_copy_location_action_activated) }
+};
+
+static void totem_playlist_class_init (TotemPlaylistClass *class);
+static void totem_playlist_init (TotemPlaylist *playlist);
+
+static void init_treeview (GtkWidget *treeview, TotemPlaylist *playlist);
+
+#define totem_playlist_unset_playing(x) totem_playlist_set_playing(x, FALSE)
+
+G_DEFINE_TYPE(TotemPlaylist, totem_playlist, GTK_TYPE_VBOX)
+
+/* Helper functions */
+static gboolean
+totem_playlist_gtk_tree_model_iter_previous (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *path;
+ gboolean ret;
+
+ path = gtk_tree_model_get_path (tree_model, iter);
+ ret = gtk_tree_path_prev (path);
+ if (ret != FALSE)
+ gtk_tree_model_get_iter (tree_model, iter, path);
+
+ gtk_tree_path_free (path);
+ return ret;
+}
+
+static gboolean
+totem_playlist_gtk_tree_path_equals (GtkTreePath *path1, GtkTreePath *path2)
+{
+ char *str1, *str2;
+ gboolean retval;
+
+ if (path1 == NULL && path2 == NULL)
+ return TRUE;
+ if (path1 == NULL || path2 == NULL)
+ return FALSE;
+
+ str1 = gtk_tree_path_to_string (path1);
+ str2 = gtk_tree_path_to_string (path2);
+
+ if (strcmp (str1, str2) == 0)
+ retval = TRUE;
+ else
+ retval = FALSE;
+
+ g_free (str1);
+ g_free (str2);
+
+ return retval;
+}
+
+static GtkWindow *
+totem_playlist_get_toplevel (TotemPlaylist *playlist)
+{
+ return GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (playlist)));
+}
+
+static void
+totem_playlist_error (char *title, char *reason, TotemPlaylist *playlist)
+{
+ GtkWidget *error_dialog;
+
+ error_dialog =
+ gtk_message_dialog_new (totem_playlist_get_toplevel (playlist),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ title);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog),
+ reason);
+
+ gtk_container_set_border_width (GTK_CONTAINER (error_dialog), 5);
+ gtk_dialog_set_default_response (GTK_DIALOG (error_dialog),
+ GTK_RESPONSE_OK);
+ g_signal_connect (G_OBJECT (error_dialog), "destroy", G_CALLBACK
+ (gtk_widget_destroy), error_dialog);
+ g_signal_connect (G_OBJECT (error_dialog), "response", G_CALLBACK
+ (gtk_widget_destroy), error_dialog);
+ gtk_window_set_modal (GTK_WINDOW (error_dialog), TRUE);
+
+ gtk_widget_show (error_dialog);
+}
+
+/* This one returns a new string, in UTF8 even if the mrl is encoded
+ * in the locale's encoding
+ */
+static char *
+totem_playlist_mrl_to_title (const gchar *mrl)
+{
+ char *filename_for_display, *filename, *unescaped;
+
+ filename = g_path_get_basename (mrl);
+ unescaped = gnome_vfs_unescape_string_for_display (filename);
+ g_free (filename);
+
+ filename_for_display = g_filename_to_utf8 (unescaped,
+ -1, /* length */
+ NULL, /* bytes_read */
+ NULL, /* bytes_written */
+ NULL); /* error */
+
+ if (filename_for_display == NULL)
+ {
+ filename_for_display = g_locale_to_utf8 (unescaped,
+ -1, NULL, NULL, NULL);
+ if (filename_for_display == NULL) {
+ filename_for_display = g_filename_display_name
+ (unescaped);
+ }
+ g_free (unescaped);
+ return filename_for_display;
+ }
+
+ g_free (unescaped);
+
+ return filename_for_display;
+}
+
+static gboolean
+totem_playlist_is_media (const char *mrl)
+{
+ if (mrl == NULL)
+ return FALSE;
+
+ if (g_str_has_prefix (mrl, "cdda:") != FALSE)
+ return TRUE;
+ if (g_str_has_prefix (mrl, "dvd:") != FALSE)
+ return TRUE;
+ if (g_str_has_prefix (mrl, "vcd:") != FALSE)
+ return TRUE;
+ if (g_str_has_prefix (mrl, "cd:") != FALSE)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+totem_playlist_update_save_button (TotemPlaylist *playlist)
+{
+ GtkWidget *button;
+ gboolean state;
+
+ button = glade_xml_get_widget (playlist->_priv->xml, "save_button");
+ state = (!playlist->_priv->disable_save_to_disk) && (PL_LEN != 0);
+ gtk_widget_set_sensitive (button, state);
+}
+
+static char*
+totem_playlist_create_full_path (const char *path)
+{
+ char *retval, *curdir, *curdir_withslash, *escaped;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (strstr (path, "://") != NULL)
+ return g_strdup (path);
+ if (totem_playlist_is_media (path) != FALSE)
+ return g_strdup (path);
+
+ if (path[0] == '/')
+ {
+ escaped = gnome_vfs_escape_path_string (path);
+
+ retval = g_strdup_printf ("file://%s", escaped);
+ g_free (escaped);
+ return retval;
+ }
+
+ curdir = g_get_current_dir ();
+ escaped = gnome_vfs_escape_path_string (curdir);
+ curdir_withslash = g_strdup_printf ("file://%s%s",
+ escaped, G_DIR_SEPARATOR_S);
+ g_free (escaped);
+ g_free (curdir);
+
+ escaped = gnome_vfs_escape_path_string (path);
+ retval = gnome_vfs_uri_make_full_from_relative
+ (curdir_withslash, escaped);
+ g_free (curdir_withslash);
+ g_free (escaped);
+
+ return retval;
+}
+
+static void
+totem_playlist_save_get_iter_func (GtkTreeModel *model,
+ GtkTreeIter *iter, char **uri, char **title,
+ gboolean *custom_title, gpointer user_data)
+{
+ gtk_tree_model_get (model, iter,
+ URI_COL, uri,
+ FILENAME_COL, title,
+ TITLE_CUSTOM_COL, custom_title,
+ -1);
+}
+
+void
+totem_playlist_save_current_playlist (TotemPlaylist *playlist, const char *output)
+{
+ totem_playlist_save_current_playlist_ext (playlist, output, TOTEM_PL_PARSER_PLS);
+}
+
+void
+totem_playlist_save_current_playlist_ext (TotemPlaylist *playlist, const char *output, TotemPlParserType type)
+{
+ GError *error = NULL;
+ gboolean retval;
+
+ retval = totem_pl_parser_write (playlist->_priv->parser,
+ playlist->_priv->model,
+ totem_playlist_save_get_iter_func,
+ output, type, NULL, &error);
+
+ if (retval == FALSE)
+ {
+ totem_playlist_error (_("Could not save the playlist"),
+ error->message, playlist);
+ g_error_free (error);
+ }
+}
+
+static void
+gtk_tree_selection_has_selected_foreach (GtkTreeModel *model,
+ GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
+{
+ int *retval = (gboolean *)user_data;
+ *retval = TRUE;
+}
+
+static gboolean
+gtk_tree_selection_has_selected (GtkTreeSelection *selection)
+{
+ int retval, *boolean;
+
+ retval = FALSE;
+ boolean = &retval;
+ gtk_tree_selection_selected_foreach (selection,
+ gtk_tree_selection_has_selected_foreach,
+ (gpointer) (boolean));
+
+ return retval;
+}
+
+static void
+drop_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ TotemPlaylist *playlist)
+{
+ GList *list, *p, *file_list;
+
+ list = gnome_vfs_uri_list_parse ((char *)data->data);
+
+ if (list == NULL) {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ p = list;
+ file_list = NULL;
+
+ while (p != NULL)
+ {
+ file_list = g_list_prepend (file_list,
+ gnome_vfs_uri_to_string
+ ((const GnomeVFSURI*)(p->data), 0));
+ p = p->next;
+ }
+
+ gnome_vfs_uri_list_free (list);
+ file_list = g_list_reverse (file_list);
+
+ if (file_list == NULL)
+ {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ for (p = file_list; p != NULL; p = p->next)
+ {
+ char *filename, *title;
+
+ if (p->data == NULL)
+ continue;
+
+ filename = totem_create_full_path (p->data);
+ title = NULL;
+
+ if (info == 1)
+ {
+ g_free (p->data);
+ p = p->next;
+ if (p != NULL) {
+ if (g_str_has_prefix (p->data, "file:") != FALSE)
+ title = (char *)p->data + 5;
+ else
+ title = p->data;
+ }
+ }
+
+ totem_playlist_add_mrl (playlist, p->data, title);
+
+ g_free (filename);
+ g_free (p->data);
+ }
+
+ g_list_free (file_list);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+}
+
+static void
+playlist_copy_location_action_activated (GtkAction *action, TotemPlaylist *playlist)
+{
+ GList *l;
+ GtkTreePath *path;
+ GtkClipboard *clip;
+ char *url;
+ GtkTreeIter iter;
+
+ l = gtk_tree_selection_get_selected_rows (playlist->_priv->selection,
+ NULL);
+ path = l->data;
+
+ gtk_tree_model_get_iter (playlist->_priv->model, &iter, path);
+
+ gtk_tree_model_get (playlist->_priv->model,
+ &iter,
+ URI_COL, &url,
+ -1);
+
+ /* Set both the middle-click and the super-paste buffers */
+ clip = gtk_clipboard_get_for_display
+ (gdk_display_get_default(), GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clip, url, -1);
+ clip = gtk_clipboard_get_for_display
+ (gdk_display_get_default(), GDK_SELECTION_PRIMARY);
+ gtk_clipboard_set_text (clip, url, -1);
+ g_free (url);
+
+ g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (l);
+}
+
+static gboolean
+playlist_show_popup_menu (TotemPlaylist *playlist, GdkEventButton *event)
+{
+ guint button = 0;
+ guint32 time;
+ GtkTreePath *path;
+ gint count;
+ GtkWidget *menu;
+ GtkAction *action;
+
+ if (event != NULL) {
+ button = event->button;
+ time = event->time;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (playlist->_priv->treeview),
+ event->x, event->y, &path, NULL, NULL, NULL)) {
+ if (!gtk_tree_selection_path_is_selected (playlist->_priv->selection, path)) {
+ gtk_tree_selection_unselect_all (playlist->_priv->selection);
+ gtk_tree_selection_select_path (playlist->_priv->selection, path);
+ }
+ gtk_tree_path_free (path);
+ } else {
+ gtk_tree_selection_unselect_all (playlist->_priv->selection);
+ }
+ } else {
+ time = gtk_get_current_event_time ();
+ }
+
+ count = gtk_tree_selection_count_selected_rows (playlist->_priv->selection);
+
+ if (count == 0) {
+ return FALSE;
+ }
+
+ action = gtk_action_group_get_action (playlist->_priv->action_group, "copy-location");
+ gtk_action_set_sensitive (action, count == 1);
+
+ menu = gtk_ui_manager_get_widget (playlist->_priv->ui_manager, "/totem-playlist-popup");
+
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
+ button, time);
+
+ return TRUE;
+}
+
+static gboolean
+treeview_button_pressed (GtkTreeView *treeview, GdkEventButton *event,
+ TotemPlaylist *playlist)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ return playlist_show_popup_menu (playlist, event);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+playlist_treeview_popup_menu (GtkTreeView *treeview, TotemPlaylist *playlist)
+{
+ return playlist_show_popup_menu (playlist, NULL);
+}
+
+static void
+totem_playlist_set_reorderable (TotemPlaylist *playlist, gboolean set)
+{
+ guint num_items, i;
+
+ gtk_tree_view_set_reorderable
+ (GTK_TREE_VIEW (playlist->_priv->treeview), set);
+
+ if (set != FALSE)
+ return;
+
+ num_items = PL_LEN;
+ for (i = 0; i < num_items; i++)
+ {
+ GtkTreeIter iter;
+ char *index;
+ GtkTreePath *path;
+ gboolean playing;
+
+ index = g_strdup_printf ("%d", i);
+ if (gtk_tree_model_get_iter_from_string
+ (playlist->_priv->model,
+ &iter, index) == FALSE)
+ {
+ g_free (index);
+ continue;
+ }
+ g_free (index);
+
+ gtk_tree_model_get (playlist->_priv->model, &iter, PLAYING_COL, &playing, -1);
+ if (!playing) {
+ continue;
+ }
+
+ /* Only emit the changed signal if we changed the ->current */
+ path = gtk_tree_path_new_from_indices (i, -1);
+ if (gtk_tree_path_compare (path, playlist->_priv->current) == 0)
+ {
+ gtk_tree_path_free (path);
+ } else {
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = path;
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED],
+ 0, NULL);
+ }
+
+ break;
+ }
+}
+
+static gboolean
+button_press_cb (GtkWidget *treeview, GdkEventButton *event, gpointer data)
+{
+ TotemPlaylist *playlist = (TotemPlaylist *)data;
+
+ if (playlist->_priv->drop_disabled)
+ return FALSE;
+
+ playlist->_priv->drop_disabled = TRUE;
+ gtk_drag_dest_unset (treeview);
+ g_signal_handlers_block_by_func (treeview, (GFunc) drop_cb, data);
+
+ totem_playlist_set_reorderable (playlist, TRUE);
+
+ return FALSE;
+}
+
+static gboolean
+button_release_cb (GtkWidget *treeview, GdkEventButton *event, gpointer data)
+{
+ TotemPlaylist *playlist = (TotemPlaylist *)data;
+
+ if (!playlist->_priv->drag_started && playlist->_priv->drop_disabled)
+ {
+ playlist->_priv->drop_disabled = FALSE;
+ totem_playlist_set_reorderable (playlist, FALSE);
+ gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL,
+ target_table, G_N_ELEMENTS (target_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ g_signal_handlers_unblock_by_func (treeview,
+ (GFunc) drop_cb, data);
+ }
+
+ return FALSE;
+}
+
+static void
+drag_begin_cb (GtkWidget *treeview, GdkDragContext *context, gpointer data)
+{
+ TotemPlaylist *playlist = (TotemPlaylist *)data;
+
+ playlist->_priv->drag_started = TRUE;
+
+ return;
+}
+
+static void
+drag_end_cb (GtkWidget *treeview, GdkDragContext *context, gpointer data)
+{
+ TotemPlaylist *playlist = (TotemPlaylist *)data;
+
+ playlist->_priv->drop_disabled = FALSE;
+ playlist->_priv->drag_started = FALSE;
+ totem_playlist_set_reorderable (playlist, FALSE);
+ gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL, target_table,
+ G_N_ELEMENTS (target_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ g_signal_handlers_unblock_by_func (treeview, (GFunc) drop_cb, data);
+
+ return;
+}
+
+static void
+selection_changed (GtkTreeSelection *treeselection, TotemPlaylist *playlist)
+{
+ GtkWidget *remove_button, *up_button, *down_button;
+ gboolean sensitivity;
+
+ remove_button = glade_xml_get_widget (playlist->_priv->xml,
+ "remove_button");
+ up_button = glade_xml_get_widget (playlist->_priv->xml, "up_button");
+ down_button = glade_xml_get_widget (playlist->_priv->xml,
+ "down_button");
+
+ if (gtk_tree_selection_has_selected (treeselection))
+ sensitivity = TRUE;
+ else
+ sensitivity = FALSE;
+
+ gtk_widget_set_sensitive (remove_button, sensitivity);
+ gtk_widget_set_sensitive (up_button, sensitivity);
+ gtk_widget_set_sensitive (down_button, sensitivity);
+}
+
+/* This function checks if the current item is NULL, and try to update it
+ * as the first item of the playlist if so. It returns TRUE if there is a
+ * current item */
+static gboolean
+update_current_from_playlist (TotemPlaylist *playlist)
+{
+ int indice;
+
+ if (playlist->_priv->current != NULL)
+ return TRUE;
+
+ if (PL_LEN != 0)
+ {
+ if (playlist->_priv->shuffle == FALSE)
+ {
+ indice = 0;
+ } else {
+ indice = playlist->_priv->shuffled[0];
+ playlist->_priv->current_shuffled = 0;
+ }
+
+ playlist->_priv->current = gtk_tree_path_new_from_indices
+ (indice, -1);
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+totem_playlist_add_files (GtkWidget *widget, TotemPlaylist *playlist)
+{
+ GSList *filenames, *l;
+
+ filenames = totem_add_files (totem_playlist_get_toplevel (playlist),
+ NULL);
+ if (filenames == NULL)
+ return;
+
+ for (l = filenames; l != NULL; l = l->next) {
+ char *mrl;
+
+ mrl = l->data;
+ totem_playlist_add_mrl (playlist, mrl, NULL);
+ g_free (mrl);
+ }
+
+ g_slist_free (filenames);
+}
+
+static void
+totem_playlist_foreach_selected (GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ TotemPlaylist *playlist = (TotemPlaylist *)data;
+ GtkTreeRowReference *ref;
+
+ /* We can't use gtk_list_store_remove() here
+ * So we build a list a RowReferences */
+ ref = gtk_tree_row_reference_new (playlist->_priv->model, path);
+ playlist->_priv->list = g_list_prepend
+ (playlist->_priv->list, (gpointer) ref);
+}
+
+static void
+playlist_remove_files (TotemPlaylist *playlist)
+{
+ GtkTreeSelection *selection;
+ GtkTreeRowReference *ref;
+ gboolean is_selected = FALSE;
+ int next_pos;
+
+ selection = gtk_tree_view_get_selection
+ (GTK_TREE_VIEW (playlist->_priv->treeview));
+ if (selection == NULL)
+ return;
+
+ gtk_tree_selection_selected_foreach (selection,
+ totem_playlist_foreach_selected,
+ (gpointer) playlist);
+
+ /* If the current item is to change, we need to keep an static
+ * reference to it, TreeIter and TreePath don't allow that */
+ if (playlist->_priv->current != NULL)
+ {
+ int *indices;
+
+ ref = gtk_tree_row_reference_new (playlist->_priv->model,
+ playlist->_priv->current);
+ is_selected = gtk_tree_selection_path_is_selected (selection,
+ playlist->_priv->current);
+
+ indices = gtk_tree_path_get_indices (playlist->_priv->current);
+ next_pos = indices[0];
+
+ gtk_tree_path_free (playlist->_priv->current);
+ } else {
+ ref = NULL;
+ next_pos = -1;
+ }
+
+ /* We destroy the items, one-by-one from the list built above */
+ while (playlist->_priv->list != NULL)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_row_reference_get_path
+ ((GtkTreeRowReference *)(playlist->_priv->list->data));
+ gtk_tree_model_get_iter (playlist->_priv->model, &iter, path);
+ gtk_tree_path_free (path);
+ gtk_list_store_remove (GTK_LIST_STORE (playlist->_priv->model),
+ &iter);
+
+ gtk_tree_row_reference_free
+ ((GtkTreeRowReference *)(playlist->_priv->list->data));
+ playlist->_priv->list = g_list_remove (playlist->_priv->list,
+ playlist->_priv->list->data);
+ }
+ g_list_free (playlist->_priv->list);
+ playlist->_priv->list = NULL;
+
+ if (is_selected != FALSE)
+ {
+ /* The current item was removed from the playlist */
+ if (next_pos != -1)
+ {
+ char *str;
+ GtkTreeIter iter;
+ GtkTreePath *cur;
+
+ str = g_strdup_printf ("%d", next_pos);
+ cur = gtk_tree_path_new_from_string (str);
+
+ if (gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter, cur) == FALSE)
+ {
+ playlist->_priv->current = NULL;
+ gtk_tree_path_free (cur);
+ } else {
+ playlist->_priv->current = cur;
+ }
+ g_free (str);
+ } else {
+ playlist->_priv->current = NULL;
+ }
+
+ playlist->_priv->current_shuffled = -1;
+ ensure_shuffled (playlist, playlist->_priv->shuffle);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CURRENT_REMOVED],
+ 0, NULL);
+ } else {
+ if (ref != NULL)
+ {
+ /* The path to the current item changed */
+ playlist->_priv->current =
+ gtk_tree_row_reference_get_path (ref);
+ gtk_tree_row_reference_free (ref);
+ }
+
+ ensure_shuffled (playlist, playlist->_priv->shuffle);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+ }
+ totem_playlist_update_save_button (playlist);
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (playlist->_priv->treeview));
+}
+
+static void
+playlist_remove_button_clicked (GtkWidget *button, TotemPlaylist *playlist)
+{
+ playlist_remove_files (playlist);
+}
+
+static void
+playlist_remove_action_activated (GtkAction *action, TotemPlaylist *playlist)
+{
+ playlist_remove_files (playlist);
+}
+
+static void
+totem_playlist_save_playlist (TotemPlaylist *playlist, char *filename, gint active_format)
+{
+ PlaylistSaveType *cur = NULL;
+ guint i;
+
+ if (active_format > 0)
+ totem_playlist_save_current_playlist_ext (playlist, filename,
+ save_types[active_format - 1].type);
+ else {
+ for (i = 0; i < G_N_ELEMENTS(save_types); i++) {
+ if (g_str_has_suffix (filename, save_types[i].suffix)) {
+ cur = &save_types[i];
+ break;
+ }
+ }
+ if (cur == NULL)
+ totem_playlist_error (_("Could not save the playlist"), _("Unknown file extension."), playlist);
+ else
+ totem_playlist_save_current_playlist_ext (playlist, filename, cur->type);
+ }
+}
+
+static GtkWidget *
+totem_playlist_save_add_format_combo_box (GtkFileChooser *fc)
+{
+ GtkWidget *hbox, *label, *combo_box;
+ guint i;
+
+ hbox = gtk_hbox_new (FALSE, 4);
+ label = gtk_label_new (_("Select playlist format:"));
+ gtk_widget_show (label);
+
+ combo_box = gtk_combo_box_new_text ();
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box),
+ _("By extension"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
+ for (i = 0; i < G_N_ELEMENTS(save_types); i++) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box),
+ save_types[i].name);
+ }
+ gtk_widget_show (combo_box);
+
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), combo_box, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fc), hbox);
+
+ atk_object_add_relationship (gtk_widget_get_accessible (label),
+ ATK_RELATION_LABEL_FOR,
+ gtk_widget_get_accessible (combo_box));
+ atk_object_add_relationship (gtk_widget_get_accessible (combo_box),
+ ATK_RELATION_LABELLED_BY,
+ gtk_widget_get_accessible (label));
+
+ return combo_box;
+}
+
+static void
+totem_playlist_save_files (GtkWidget *widget, TotemPlaylist *playlist)
+{
+ GtkWidget *fs, *combo_box;
+ char *filename;
+ int response;
+
+ fs = gtk_file_chooser_dialog_new (_("Save Playlist"),
+ totem_playlist_get_toplevel (playlist),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fs), TRUE);
+ /* translators: Playlist is the default saved playlist filename,
+ * without the suffix */
+ filename = g_strconcat (_("Playlist"), save_types[0].suffix, NULL);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fs), filename);
+ g_free (filename);
+ combo_box = totem_playlist_save_add_format_combo_box (GTK_FILE_CHOOSER (fs));
+
+ if (playlist->_priv->save_path != NULL)
+ {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fs),
+ playlist->_priv->save_path);
+ }
+
+ response = gtk_dialog_run (GTK_DIALOG (fs));
+ gtk_widget_hide (fs);
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ char *filename;
+ gint active_format;
+
+ filename = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fs));
+ active_format = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box));
+
+ gtk_widget_destroy (fs);
+
+ if (filename == NULL)
+ return;
+
+ g_free (playlist->_priv->save_path);
+ playlist->_priv->save_path = g_path_get_dirname (filename);
+
+ totem_playlist_save_playlist (playlist, filename, active_format);
+ g_free (filename);
+ } else {
+ gtk_widget_destroy (fs);
+ }
+}
+
+static void
+totem_playlist_move_files (TotemPlaylist *playlist, gboolean direction_up)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeRowReference *current;
+ GList *paths, *refs, *l;
+ int pos;
+
+ selection = gtk_tree_view_get_selection
+ (GTK_TREE_VIEW (playlist->_priv->treeview));
+ if (selection == NULL)
+ return;
+
+ model = gtk_tree_view_get_model
+ (GTK_TREE_VIEW (playlist->_priv->treeview));
+ store = GTK_LIST_STORE (model);
+ pos = -2;
+ refs = NULL;
+
+ if (playlist->_priv->current != NULL)
+ {
+ current = gtk_tree_row_reference_new (model,
+ playlist->_priv->current);
+ } else {
+ current = NULL;
+ }
+
+ /* Build a list of tree references */
+ paths = gtk_tree_selection_get_selected_rows (selection, NULL);
+ for (l = paths; l != NULL; l = l->next)
+ {
+ GtkTreePath *path = l->data;
+ int cur_pos, *indices;
+
+ refs = g_list_prepend (refs,
+ gtk_tree_row_reference_new (model, path));
+ indices = gtk_tree_path_get_indices (path);
+ cur_pos = indices[0];
+ if (pos == -2)
+ {
+ pos = cur_pos;
+ } else {
+ if (direction_up == FALSE)
+ pos = MAX (cur_pos, pos);
+ else
+ pos = MIN (cur_pos, pos);
+ }
+ }
+ g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (paths);
+
+ /* Otherwise we reverse the items when moving down */
+ if (direction_up != FALSE)
+ refs = g_list_reverse (refs);
+
+ if (direction_up == FALSE)
+ pos = pos + 2;
+ else
+ pos = pos - 2;
+
+ for (l = refs; l != NULL; l = l->next)
+ {
+ GtkTreeIter *position, current;
+ GtkTreeRowReference *ref = l->data;
+ GtkTreePath *path;
+
+ if (pos < 0)
+ {
+ position = NULL;
+ } else {
+ char *str;
+
+ str = g_strdup_printf ("%d", pos);
+ if (gtk_tree_model_get_iter_from_string (model,
+ &iter, str))
+ position = &iter;
+ else
+ position = NULL;
+
+ g_free (str);
+ }
+
+ path = gtk_tree_row_reference_get_path (ref);
+ gtk_tree_model_get_iter (model, &current, path);
+ gtk_tree_path_free (path);
+
+ if (direction_up == FALSE)
+ {
+ pos--;
+ gtk_list_store_move_before (store, &current, position);
+ } else {
+ gtk_list_store_move_after (store, &current, position);
+ pos++;
+ }
+ }
+
+ g_list_foreach (refs, (GFunc) gtk_tree_row_reference_free, NULL);
+ g_list_free (refs);
+
+ /* Update the current path */
+ if (current != NULL)
+ {
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = gtk_tree_row_reference_get_path
+ (current);
+ gtk_tree_row_reference_free (current);
+ }
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+}
+
+static void
+totem_playlist_up_files (GtkWidget *widget, TotemPlaylist *playlist)
+{
+ totem_playlist_move_files (playlist, TRUE);
+}
+
+static void
+totem_playlist_down_files (GtkWidget *widget, TotemPlaylist *playlist)
+{
+ totem_playlist_move_files (playlist, FALSE);
+}
+
+static int
+totem_playlist_key_press (GtkWidget *win, GdkEventKey *event, TotemPlaylist *playlist)
+{
+ /* Special case some shortcuts */
+ if (event->state != 0) {
+ if ((event->state & GDK_CONTROL_MASK)
+ && event->keyval == GDK_a) {
+ gtk_tree_selection_select_all
+ (playlist->_priv->selection);
+ return TRUE;
+ }
+ }
+
+ /* If we have modifiers, and either Ctrl, Mod1 (Alt), or any
+ * of Mod3 to Mod5 (Mod2 is num-lock...) are pressed, we
+ * let Gtk+ handle the key */
+ if (event->state != 0
+ && ((event->state & GDK_CONTROL_MASK)
+ || (event->state & GDK_MOD1_MASK)
+ || (event->state & GDK_MOD3_MASK)
+ || (event->state & GDK_MOD4_MASK)
+ || (event->state & GDK_MOD5_MASK)))
+ return FALSE;
+
+ if (event->keyval == GDK_Delete)
+ {
+ playlist_remove_files (playlist);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+set_playing_icon (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter, TotemPlaylist *playlist)
+{
+ gboolean playing;
+
+ gtk_tree_model_get (model, iter, PLAYING_COL, &playing, -1);
+
+ g_object_set (renderer, "icon-name",
+ playing ? "audio-volume-medium" : NULL, NULL);
+}
+
+static void
+init_columns (GtkTreeView *treeview, TotemPlaylist *playlist)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* Playing pix */
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ (GtkTreeCellDataFunc) set_playing_icon, playlist, NULL);
+ g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+ gtk_tree_view_append_column (treeview, column);
+
+ /* Labels */
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", FILENAME_COL, NULL);
+}
+
+static void
+treeview_row_changed (GtkTreeView *treeview, GtkTreePath *arg1,
+ GtkTreeViewColumn *arg2, TotemPlaylist *playlist)
+{
+ if (totem_playlist_gtk_tree_path_equals
+ (arg1, playlist->_priv->current) != FALSE)
+ {
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[ITEM_ACTIVATED], 0,
+ NULL);
+ return;
+ }
+
+ if (playlist->_priv->current != NULL)
+ {
+ totem_playlist_unset_playing (playlist);
+ gtk_tree_path_free (playlist->_priv->current);
+ }
+
+ playlist->_priv->current = gtk_tree_path_copy (arg1);
+
+ if (playlist->_priv->shuffle != FALSE)
+ {
+ int *indices, indice, i;
+
+ indices = gtk_tree_path_get_indices (playlist->_priv->current);
+ indice = indices[0];
+
+ for (i = 0; i < PL_LEN; i++)
+ {
+ if (playlist->_priv->shuffled[i] == indice)
+ {
+ playlist->_priv->current_shuffled = i;
+ break;
+ }
+ }
+ }
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+
+ if (playlist->_priv->drop_disabled) {
+ playlist->_priv->drop_disabled = FALSE;
+ totem_playlist_set_reorderable (playlist, FALSE);
+ gtk_drag_dest_set (GTK_WIDGET (treeview), GTK_DEST_DEFAULT_ALL,
+ target_table, G_N_ELEMENTS (target_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ g_signal_handlers_unblock_by_func (treeview,
+ (GFunc) drop_cb, playlist);
+ }
+}
+
+static void
+init_treeview (GtkWidget *treeview, TotemPlaylist *playlist)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+
+ /* the model */
+ model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING));
+
+ /* the treeview */
+ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), model);
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
+ g_object_unref (G_OBJECT (model));
+
+ init_columns (GTK_TREE_VIEW (treeview), playlist);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+ g_signal_connect (G_OBJECT (selection), "changed",
+ G_CALLBACK (selection_changed), playlist);
+ g_signal_connect (G_OBJECT (treeview), "row-activated",
+ G_CALLBACK (treeview_row_changed), playlist);
+ g_signal_connect (G_OBJECT (treeview), "button-press-event",
+ G_CALLBACK (treeview_button_pressed), playlist);
+ g_signal_connect (G_OBJECT (treeview), "popup-menu",
+ G_CALLBACK (playlist_treeview_popup_menu), playlist);
+
+ /* Drag'n'Drop */
+ g_signal_connect (G_OBJECT (treeview), "drag_data_received",
+ G_CALLBACK (drop_cb), playlist);
+ g_signal_connect (G_OBJECT (treeview), "button_press_event",
+ G_CALLBACK (button_press_cb), playlist);
+ g_signal_connect (G_OBJECT (treeview), "button_release_event",
+ G_CALLBACK (button_release_cb), playlist);
+ g_signal_connect (G_OBJECT (treeview), "drag_begin",
+ G_CALLBACK (drag_begin_cb), playlist);
+ g_signal_connect (G_OBJECT (treeview), "drag_end",
+ G_CALLBACK (drag_end_cb), playlist);
+ gtk_drag_dest_set (treeview, GTK_DEST_DEFAULT_ALL,
+ target_table, G_N_ELEMENTS (target_table),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ playlist->_priv->selection = selection;
+
+ gtk_widget_show (treeview);
+}
+
+static void
+update_repeat_cb (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, TotemPlaylist *playlist)
+{
+ gboolean repeat;
+
+ repeat = gconf_value_get_bool (entry->value);
+ playlist->_priv->repeat = (repeat != FALSE);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[REPEAT_TOGGLED], 0,
+ repeat, NULL);
+}
+
+typedef struct {
+ int random;
+ int index;
+} RandomData;
+
+static int
+compare_random (gconstpointer ptr_a, gconstpointer ptr_b)
+{
+ RandomData *a = (RandomData *) ptr_a;
+ RandomData *b = (RandomData *) ptr_b;
+
+ if (a->random < b->random)
+ return -1;
+ else if (a->random > b->random)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+ensure_shuffled (TotemPlaylist *playlist, gboolean shuffle)
+{
+ RandomData data;
+ GArray *array;
+ int i, current;
+ int *indices;
+
+ if (shuffle == FALSE || PL_LEN != playlist->_priv->shuffle_len)
+ {
+ g_free (playlist->_priv->shuffled);
+ playlist->_priv->shuffled = NULL;
+ }
+
+ if (shuffle == FALSE || PL_LEN == 0)
+ return;
+
+ if (playlist->_priv->current != NULL)
+ {
+ indices = gtk_tree_path_get_indices (playlist->_priv->current);
+ current = indices[0];
+ } else {
+ current = -1;
+ }
+
+ playlist->_priv->shuffled = g_new (int, PL_LEN);
+ playlist->_priv->shuffle_len = PL_LEN;
+
+ array = g_array_sized_new (FALSE, FALSE,
+ sizeof (RandomData), PL_LEN);
+
+ for (i = 0; i < PL_LEN; i++)
+ {
+ data.random = g_random_int_range (0, PL_LEN);
+ data.index = i;
+
+ g_array_append_val (array, data);
+ }
+
+ g_array_sort (array, compare_random);
+
+ for (i = 0; i < PL_LEN; i++)
+ {
+ playlist->_priv->shuffled[i]
+ = g_array_index (array, RandomData, i).index;
+
+ if (playlist->_priv->current != NULL
+ && playlist->_priv->shuffled[i] == current)
+ playlist->_priv->current_shuffled = i;
+ }
+
+ g_array_free (array, TRUE);
+}
+
+static void
+update_shuffle_cb (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, TotemPlaylist *playlist)
+{
+ gboolean shuffle;
+
+ shuffle = gconf_value_get_bool (entry->value);
+ playlist->_priv->shuffle = shuffle;
+ ensure_shuffled (playlist, shuffle);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[SHUFFLE_TOGGLED], 0,
+ shuffle, NULL);
+}
+
+static void
+update_lockdown (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, TotemPlaylist *playlist)
+{
+ playlist->_priv->disable_save_to_disk = gconf_client_get_bool
+ (playlist->_priv->gc,
+ "/desktop/gnome/lockdown/disable_save_to_disk", NULL) != FALSE;
+ totem_playlist_update_save_button (playlist);
+}
+
+static void
+init_config (TotemPlaylist *playlist)
+{
+ playlist->_priv->gc = gconf_client_get_default ();
+
+ playlist->_priv->disable_save_to_disk = gconf_client_get_bool
+ (playlist->_priv->gc,
+ "/desktop/gnome/lockdown/disable_save_to_disk", NULL) != FALSE;
+ totem_playlist_update_save_button (playlist);
+
+ gconf_client_add_dir (playlist->_priv->gc, GCONF_PREFIX,
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_notify_add (playlist->_priv->gc, GCONF_PREFIX"/repeat",
+ (GConfClientNotifyFunc) update_repeat_cb,
+ playlist, NULL, NULL);
+ gconf_client_notify_add (playlist->_priv->gc, GCONF_PREFIX"/shuffle",
+ (GConfClientNotifyFunc) update_shuffle_cb,
+ playlist, NULL, NULL);
+
+ gconf_client_add_dir (playlist->_priv->gc, "/desktop/gnome/lockdown",
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ gconf_client_notify_add (playlist->_priv->gc,
+ "/desktop/gnome/lockdown/disable_save_to_disk",
+ (GConfClientNotifyFunc) update_lockdown,
+ playlist, NULL, NULL);
+
+ playlist->_priv->repeat = gconf_client_get_bool (playlist->_priv->gc,
+ GCONF_PREFIX"/repeat", NULL) != FALSE;
+ playlist->_priv->shuffle = gconf_client_get_bool (playlist->_priv->gc,
+ GCONF_PREFIX"/shuffle", NULL) != FALSE;
+}
+
+static void
+totem_playlist_entry_parsed (TotemPlParser *parser,
+ const char *uri, const char *title,
+ const char *genre, TotemPlaylist *playlist)
+{
+ totem_playlist_add_one_mrl (playlist, uri, title);
+}
+
+static void
+totem_playlist_init (TotemPlaylist *playlist)
+{
+ playlist->_priv = g_new0 (TotemPlaylistPrivate, 1);
+ playlist->_priv->parser = totem_pl_parser_new ();
+
+ totem_pl_parser_add_ignored_scheme (playlist->_priv->parser, "dvd:");
+ totem_pl_parser_add_ignored_scheme (playlist->_priv->parser, "cdda:");
+ totem_pl_parser_add_ignored_scheme (playlist->_priv->parser, "vcd:");
+ totem_pl_parser_add_ignored_scheme (playlist->_priv->parser, "cd:");
+
+ g_signal_connect (G_OBJECT (playlist->_priv->parser),
+ "entry",
+ G_CALLBACK (totem_playlist_entry_parsed),
+ playlist);
+}
+
+static void
+totem_playlist_finalize (GObject *object)
+{
+ TotemPlaylist *playlist = TOTEM_PLAYLIST (object);
+
+ if (playlist->_priv->current != NULL)
+ gtk_tree_path_free (playlist->_priv->current);
+ g_object_unref (playlist->_priv->parser);
+
+ if (playlist->_priv->ui_manager != NULL) {
+ g_object_unref (G_OBJECT (playlist->_priv->ui_manager));
+ }
+
+ if (playlist->_priv->action_group != NULL) {
+ g_object_unref (G_OBJECT (playlist->_priv->action_group));
+ }
+
+ G_OBJECT_CLASS (totem_playlist_parent_class)->finalize (object);
+}
+
+GtkWidget*
+totem_playlist_new (void)
+{
+ TotemPlaylist *playlist;
+ GtkWidget *container, *item;
+ char *filename;
+
+ playlist = TOTEM_PLAYLIST (g_object_new (TOTEM_TYPE_PLAYLIST, NULL));
+
+ playlist->_priv->xml = totem_interface_load ("playlist.glade",
+ _("playlist"), TRUE, NULL);
+
+ if (playlist->_priv->xml == NULL)
+ {
+ totem_playlist_finalize (G_OBJECT (playlist));
+ return NULL;
+ }
+
+ /* popup menu */
+ playlist->_priv->action_group =
+ gtk_action_group_new ("playlist-action-group");
+ gtk_action_group_set_translation_domain (playlist->_priv->action_group,
+ GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (playlist->_priv->action_group, entries,
+ G_N_ELEMENTS (entries), playlist);
+
+ playlist->_priv->ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (playlist->_priv->ui_manager,
+ playlist->_priv->action_group, -1);
+
+ filename = totem_interface_get_full_path ("playlist-ui.xml");
+ if (!gtk_ui_manager_add_ui_from_file (playlist->_priv->ui_manager, filename, NULL)) {
+ totem_playlist_finalize (G_OBJECT (playlist));
+ return NULL;
+ }
+ g_free (filename);
+
+ /* Connect the buttons */
+ item = glade_xml_get_widget (playlist->_priv->xml, "add_button");
+ g_signal_connect (G_OBJECT (item), "clicked",
+ G_CALLBACK (totem_playlist_add_files),
+ playlist);
+ item = glade_xml_get_widget (playlist->_priv->xml, "remove_button");
+ g_signal_connect (G_OBJECT (item), "clicked",
+ G_CALLBACK (playlist_remove_button_clicked),
+ playlist);
+ item = glade_xml_get_widget (playlist->_priv->xml, "save_button");
+ g_signal_connect (G_OBJECT (item), "clicked",
+ G_CALLBACK (totem_playlist_save_files),
+ playlist);
+ item = glade_xml_get_widget (playlist->_priv->xml, "up_button");
+ g_signal_connect (G_OBJECT (item), "clicked",
+ G_CALLBACK (totem_playlist_up_files),
+ playlist);
+ item = glade_xml_get_widget (playlist->_priv->xml, "down_button");
+ g_signal_connect (G_OBJECT (item), "clicked",
+ G_CALLBACK (totem_playlist_down_files),
+ playlist);
+
+ gtk_widget_add_events (GTK_WIDGET (playlist), GDK_KEY_PRESS_MASK);
+ g_signal_connect (G_OBJECT (playlist), "key_press_event",
+ G_CALLBACK (totem_playlist_key_press), playlist);
+
+ /* Reparent the vbox */
+ item = glade_xml_get_widget (playlist->_priv->xml, "dialog-vbox1");
+ container = glade_xml_get_widget (playlist->_priv->xml, "vbox4");
+ g_object_ref (container);
+ gtk_container_remove (GTK_CONTAINER (item), container);
+ gtk_box_pack_start (GTK_BOX (playlist),
+ container,
+ TRUE, /* expand */
+ TRUE, /* fill */
+ 0); /* padding */
+ g_object_unref (container);
+
+ playlist->_priv->treeview = glade_xml_get_widget
+ (playlist->_priv->xml, "treeview1");
+ init_treeview (playlist->_priv->treeview, playlist);
+ playlist->_priv->model = gtk_tree_view_get_model
+ (GTK_TREE_VIEW (playlist->_priv->treeview));
+
+ /* The configuration */
+ init_config (playlist);
+
+ gtk_widget_show_all (GTK_WIDGET (playlist));
+
+ return GTK_WIDGET (playlist);
+}
+
+static gboolean
+totem_playlist_add_one_mrl (TotemPlaylist *playlist, const char *mrl,
+ const char *display_name)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ char *filename_for_display, *uri;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+ g_return_val_if_fail (mrl != NULL, FALSE);
+
+ if (display_name == NULL)
+ {
+ filename_for_display = totem_playlist_mrl_to_title (mrl);
+ } else {
+ filename_for_display = g_strdup (display_name);
+ }
+
+ uri = totem_playlist_create_full_path (mrl);
+
+ D("totem_playlist_add_one_mrl (): %s %s %s\n",
+ filename_for_display, uri, display_name);
+
+ store = GTK_LIST_STORE (playlist->_priv->model);
+ gtk_list_store_insert_with_values (store, &iter, G_MAXINT32,
+ PLAYING_COL, FALSE,
+ FILENAME_COL, filename_for_display,
+ URI_COL, uri,
+ TITLE_CUSTOM_COL, display_name ? TRUE : FALSE,
+ -1);
+
+ g_free (filename_for_display);
+ g_free (uri);
+
+ if (playlist->_priv->current == NULL
+ && playlist->_priv->shuffle == FALSE)
+ playlist->_priv->current = gtk_tree_model_get_path
+ (playlist->_priv->model, &iter);
+ ensure_shuffled (playlist, playlist->_priv->shuffle);
+
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+ totem_playlist_update_save_button (playlist);
+
+ return TRUE;
+}
+
+gboolean
+totem_playlist_add_mrl (TotemPlaylist *playlist, const char *mrl,
+ const char *display_name)
+{
+ TotemPlParserResult res;
+ GtkWidget *parent;
+
+ g_return_val_if_fail (mrl != NULL, FALSE);
+
+ parent = GTK_WIDGET (totem_playlist_get_toplevel (playlist));
+ totem_gdk_window_set_waiting_cursor (parent->window);
+ res = totem_pl_parser_parse (playlist->_priv->parser, mrl, TRUE);
+ gdk_window_set_cursor (parent->window, NULL);
+
+ if (res == TOTEM_PL_PARSER_RESULT_UNHANDLED)
+ return totem_playlist_add_one_mrl (playlist, mrl, display_name);
+ if (res == TOTEM_PL_PARSER_RESULT_ERROR)
+ {
+ totem_playlist_error (_("Playlist error"), _("The playlist '%s' could not be parsed, it might be damaged."), playlist);
+ return FALSE;
+ }
+ if (res == TOTEM_PL_PARSER_RESULT_IGNORED)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+totem_playlist_clear (TotemPlaylist *playlist)
+{
+ GtkListStore *store;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (PL_LEN == 0)
+ return FALSE;
+
+ store = GTK_LIST_STORE (playlist->_priv->model);
+ gtk_list_store_clear (store);
+
+ if (playlist->_priv->current != NULL)
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = NULL;
+
+ totem_playlist_update_save_button (playlist);
+
+ return TRUE;
+}
+
+static void
+totem_playlist_clear_with_compare (TotemPlaylist *playlist, GCompareFunc func,
+ gconstpointer data)
+{
+ GList *list = NULL, *l;
+ guint num_items, i;
+ gboolean has_items;
+
+ num_items = PL_LEN;
+ if (num_items == 0)
+ return;
+
+ for (i = 0; i < num_items; i++)
+ {
+ GtkTreeIter iter;
+ char *index;
+ char *mrl;
+
+ index = g_strdup_printf ("%d", i);
+ if (gtk_tree_model_get_iter_from_string
+ (playlist->_priv->model,
+ &iter, index) == FALSE)
+ {
+ g_free (index);
+ continue;
+ }
+ g_free (index);
+
+ gtk_tree_model_get (playlist->_priv->model, &iter,
+ URI_COL, &mrl, -1);
+
+ if ((* func) (mrl, data) != FALSE)
+ {
+ GtkTreePath *path;
+ GtkTreeRowReference *ref;
+
+ path = gtk_tree_path_new_from_indices (i, -1);
+ ref = gtk_tree_row_reference_new
+ (playlist->_priv->model, path);
+ list = g_list_prepend (list, ref);
+ gtk_tree_path_free (path);
+ }
+
+ g_free (mrl);
+ }
+
+ has_items = (list != NULL);
+
+ for (l = list; l != NULL; l = l->next)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_row_reference_get_path (l->data);
+ gtk_tree_model_get_iter (playlist->_priv->model, &iter, path);
+ gtk_list_store_remove (GTK_LIST_STORE (playlist->_priv->model),
+ &iter);
+ gtk_tree_path_free (path);
+ gtk_tree_row_reference_free (l->data);
+ }
+ g_list_free (list);
+
+ if (has_items != FALSE) {
+ playlist->_priv->current_shuffled = -1;
+
+ ensure_shuffled (playlist, playlist->_priv->shuffle);
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = NULL;
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CURRENT_REMOVED],
+ 0, NULL);
+ }
+}
+
+static int
+totem_playlist_compare_with_prefix (gconstpointer a, gconstpointer b)
+{
+ const char *mrl = (const char *) a;
+ const char *prefix = (const char *) b;
+
+ return g_str_has_prefix (mrl, prefix);
+}
+
+void
+totem_playlist_clear_with_prefix (TotemPlaylist *playlist, const char *prefix)
+{
+ totem_playlist_clear_with_compare (playlist,
+ (GCompareFunc) totem_playlist_compare_with_prefix,
+ (void *)prefix);
+}
+
+static int
+totem_playlist_compare_with_volume (gpointer a, gpointer b)
+{
+ GnomeVFSVolume *clear_volume = (GnomeVFSVolume *) b;
+ const char *mrl = (const char *) a;
+
+ char *filename;
+ GnomeVFSVolume *volume;
+ GnomeVFSVolumeMonitor *monitor;
+ gboolean retval = FALSE;
+
+ if (g_str_has_prefix (mrl, "file:///") == FALSE)
+ return FALSE;
+
+ monitor = gnome_vfs_get_volume_monitor ();
+
+ filename = g_filename_from_uri (mrl, NULL, NULL);
+ volume = gnome_vfs_volume_monitor_get_volume_for_path
+ (monitor, filename);
+ g_free (filename);
+
+ if (volume == clear_volume)
+ retval = TRUE;
+
+ gnome_vfs_volume_unref (volume);
+
+ return retval;
+}
+
+void
+totem_playlist_clear_with_gnome_vfs_volume (TotemPlaylist *playlist,
+ GnomeVFSVolume *volume)
+{
+ totem_playlist_clear_with_compare (playlist,
+ (GCompareFunc) totem_playlist_compare_with_volume,
+ volume);
+}
+
+char
+*totem_playlist_get_current_mrl (TotemPlaylist *playlist)
+{
+ GtkTreeIter iter;
+ char *path;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), NULL);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return NULL;
+
+ if (gtk_tree_model_get_iter (playlist->_priv->model, &iter,
+ playlist->_priv->current) == FALSE)
+ return NULL;
+
+ gtk_tree_model_get (playlist->_priv->model,
+ &iter,
+ URI_COL, &path,
+ -1);
+
+ return path;
+}
+
+char
+*totem_playlist_get_current_title (TotemPlaylist *playlist, gboolean *custom)
+{
+ GtkTreeIter iter;
+ char *path;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), NULL);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return NULL;
+
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ gtk_tree_model_get (playlist->_priv->model,
+ &iter,
+ FILENAME_COL, &path,
+ TITLE_CUSTOM_COL, custom,
+ -1);
+
+ return path;
+}
+
+gboolean
+totem_playlist_get_current_metadata (TotemPlaylist *playlist,
+ char **artist,
+ char **title,
+ char **album)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return FALSE;
+
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ *artist = NULL;
+ gtk_tree_model_get (playlist->_priv->model,
+ &iter,
+ CACHE_ARTIST_COL, artist,
+ CACHE_TITLE_COL, title,
+ CACHE_ALBUM_COL, album,
+ -1);
+
+ return (*artist != NULL);
+}
+
+gboolean
+totem_playlist_has_previous_mrl (TotemPlaylist *playlist)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return FALSE;
+
+ if (playlist->_priv->repeat != FALSE)
+ return TRUE;
+
+ if (playlist->_priv->shuffle == FALSE)
+ {
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ return totem_playlist_gtk_tree_model_iter_previous
+ (playlist->_priv->model, &iter);
+ } else {
+ if (playlist->_priv->current_shuffled == 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+totem_playlist_has_next_mrl (TotemPlaylist *playlist)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return FALSE;
+
+ if (playlist->_priv->repeat != FALSE)
+ return TRUE;
+
+ if (playlist->_priv->shuffle == FALSE)
+ {
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ return gtk_tree_model_iter_next (playlist->_priv->model, &iter);
+ } else {
+ if (playlist->_priv->current_shuffled == PL_LEN - 1)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+totem_playlist_set_title (TotemPlaylist *playlist, const char *title, gboolean force)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gboolean custom_title;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return FALSE;
+
+ store = GTK_LIST_STORE (playlist->_priv->model);
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ if (&iter == NULL)
+ return FALSE;
+
+ if (force == FALSE) {
+ gtk_tree_model_get (playlist->_priv->model, &iter,
+ TITLE_CUSTOM_COL, &custom_title,
+ -1);
+ if (custom_title != FALSE)
+ return TRUE;
+ }
+
+ gtk_list_store_set (store, &iter,
+ FILENAME_COL, title,
+ TITLE_CUSTOM_COL, TRUE,
+ -1);
+
+ g_signal_emit (playlist,
+ totem_playlist_table_signals[ACTIVE_NAME_CHANGED], 0);
+
+ return TRUE;
+}
+
+gboolean
+totem_playlist_set_playing (TotemPlaylist *playlist, gboolean state)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ if (update_current_from_playlist (playlist) == FALSE)
+ return FALSE;
+
+ store = GTK_LIST_STORE (playlist->_priv->model);
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ g_return_val_if_fail (&iter != NULL, FALSE);
+
+ gtk_list_store_set (store, &iter,
+ PLAYING_COL, state,
+ -1);
+
+ if (state == FALSE)
+ return TRUE;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (playlist->_priv->treeview),
+ path, NULL,
+ TRUE, 0.5, 0);
+ gtk_tree_path_free (path);
+
+ return TRUE;
+}
+
+void
+totem_playlist_set_previous (TotemPlaylist *playlist)
+{
+ GtkTreeIter iter;
+
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ if (totem_playlist_has_previous_mrl (playlist) == FALSE)
+ return;
+
+ totem_playlist_unset_playing (playlist);
+
+ if (playlist->_priv->shuffle == FALSE)
+ {
+ char *path;
+
+ path = gtk_tree_path_to_string (playlist->_priv->current);
+ if (strcmp (path, "0") == 0)
+ {
+ totem_playlist_set_at_end (playlist);
+ g_free (path);
+ return;
+ }
+ g_free (path);
+
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ totem_playlist_gtk_tree_model_iter_previous
+ (playlist->_priv->model, &iter);
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = gtk_tree_model_get_path
+ (playlist->_priv->model, &iter);
+ } else {
+ int indice;
+
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current_shuffled--;
+ if (playlist->_priv->current_shuffled < 0) {
+ indice = playlist->_priv->shuffled[PL_LEN -1];
+ playlist->_priv->current_shuffled = PL_LEN -1;
+ } else {
+ indice = playlist->_priv->shuffled[playlist->_priv->current_shuffled];
+ }
+ playlist->_priv->current = gtk_tree_path_new_from_indices
+ (indice, -1);
+ }
+}
+
+void
+totem_playlist_set_next (TotemPlaylist *playlist)
+{
+ GtkTreeIter iter;
+
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ if (totem_playlist_has_next_mrl (playlist) == FALSE)
+ {
+ totem_playlist_set_at_start (playlist);
+ return;
+ }
+
+ totem_playlist_unset_playing (playlist);
+
+ if (playlist->_priv->shuffle == FALSE)
+ {
+ gtk_tree_model_get_iter (playlist->_priv->model,
+ &iter,
+ playlist->_priv->current);
+
+ gtk_tree_model_iter_next (playlist->_priv->model, &iter);
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = gtk_tree_model_get_path
+ (playlist->_priv->model, &iter);
+ } else {
+ int indice;
+
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current_shuffled++;
+ if (playlist->_priv->current_shuffled == PL_LEN)
+ playlist->_priv->current_shuffled = 0;
+ indice = playlist->_priv->shuffled[playlist->_priv->current_shuffled];
+ playlist->_priv->current = gtk_tree_path_new_from_indices
+ (indice, -1);
+ }
+}
+
+gboolean
+totem_playlist_get_repeat (TotemPlaylist *playlist)
+{
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ return playlist->_priv->repeat;
+}
+
+void
+totem_playlist_set_repeat (TotemPlaylist *playlist, gboolean repeat)
+{
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ gconf_client_set_bool (playlist->_priv->gc, GCONF_PREFIX"/repeat",
+ repeat, NULL);
+}
+
+gboolean
+totem_playlist_get_shuffle (TotemPlaylist *playlist)
+{
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), FALSE);
+
+ return playlist->_priv->shuffle;
+}
+
+void
+totem_playlist_set_shuffle (TotemPlaylist *playlist, gboolean shuffle)
+{
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ gconf_client_set_bool (playlist->_priv->gc, GCONF_PREFIX"/shuffle",
+ shuffle, NULL);
+}
+
+void
+totem_playlist_set_at_start (TotemPlaylist *playlist)
+{
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ totem_playlist_unset_playing (playlist);
+
+ if (playlist->_priv->current != NULL)
+ {
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = NULL;
+ }
+ update_current_from_playlist (playlist);
+}
+
+void
+totem_playlist_set_at_end (TotemPlaylist *playlist)
+{
+ int indice;
+
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ totem_playlist_unset_playing (playlist);
+
+ if (playlist->_priv->current != NULL)
+ {
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = NULL;
+ }
+
+ if (PL_LEN)
+ {
+ if (playlist->_priv->shuffle == FALSE)
+ indice = PL_LEN - 1;
+ else
+ indice = playlist->_priv->shuffled[PL_LEN - 1];
+
+ playlist->_priv->current = gtk_tree_path_new_from_indices
+ (indice, -1);
+ }
+}
+
+guint
+totem_playlist_get_current (TotemPlaylist *playlist)
+{
+ char *path;
+ double index;
+
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), 0);
+
+ path = gtk_tree_path_to_string (playlist->_priv->current);
+ if (path == NULL)
+ return -1;
+
+ index = g_ascii_strtod (path, NULL);
+ g_free (path);
+
+ return index;
+}
+
+guint
+totem_playlist_get_last (TotemPlaylist *playlist)
+{
+ g_return_val_if_fail (TOTEM_IS_PLAYLIST (playlist), -1);
+
+ return PL_LEN - 1;
+}
+
+void
+totem_playlist_set_current (TotemPlaylist *playlist, guint index)
+{
+ g_return_if_fail (TOTEM_IS_PLAYLIST (playlist));
+
+ if (index >= (guint) PL_LEN)
+ return;
+
+ totem_playlist_unset_playing (playlist);
+ //FIXME problems when shuffled?
+ gtk_tree_path_free (playlist->_priv->current);
+ playlist->_priv->current = gtk_tree_path_new_from_indices (index, -1);
+}
+
+static void
+totem_playlist_class_init (TotemPlaylistClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = totem_playlist_finalize;
+
+ /* Signals */
+ totem_playlist_table_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ totem_playlist_table_signals[ITEM_ACTIVATED] =
+ g_signal_new ("item-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass, item_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ totem_playlist_table_signals[ACTIVE_NAME_CHANGED] =
+ g_signal_new ("active-name-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass, active_name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ totem_playlist_table_signals[CURRENT_REMOVED] =
+ g_signal_new ("current-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass,
+ current_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ totem_playlist_table_signals[REPEAT_TOGGLED] =
+ g_signal_new ("repeat-toggled",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass,
+ repeat_toggled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ totem_playlist_table_signals[SHUFFLE_TOGGLED] =
+ g_signal_new ("shuffle-toggled",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TotemPlaylistClass,
+ shuffle_toggled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+