diff options
Diffstat (limited to 'trunk/src/totem-menu.c')
-rw-r--r-- | trunk/src/totem-menu.c | 1388 |
1 files changed, 1388 insertions, 0 deletions
diff --git a/trunk/src/totem-menu.c b/trunk/src/totem-menu.c new file mode 100644 index 000000000..10ae4b5de --- /dev/null +++ b/trunk/src/totem-menu.c @@ -0,0 +1,1388 @@ +/* totem-menu.c + + Copyright (C) 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 <gtk/gtk.h> +#include <glib/gi18n.h> +#include <glade/glade.h> +#include <string.h> + +#include "totem-menu.h" +#include "totem.h" +#include "totem-interface.h" +#include "totem-private.h" +#include "totem-sidebar.h" +#include "bacon-video-widget.h" + +#include "debug.h" + +/* Helper function to escape underscores in labels + * before putting them in menu items */ +static char * +escape_label_for_menu (const char *name) +{ + char *new, **a; + + a = g_strsplit (name, "_", -1); + new = g_strjoinv ("__", a); + g_strfreev (a); + + return new; +} + +/* ISO-639 helpers */ +static GHashTable *lang_table; + +static void +totem_lang_table_free (void) +{ + g_hash_table_destroy (lang_table); + lang_table = NULL; +} + +static void +totem_lang_table_parse_start_tag (GMarkupParseContext *ctx, + const gchar *element_name, + const gchar **attr_names, + const gchar **attr_values, + gpointer data, + GError **error) +{ + const char *ccode_longB, *ccode_longT, *ccode, *lang_name; + + if (!g_str_equal (element_name, "iso_639_entry") + || attr_names == NULL + || attr_values == NULL) + return; + + ccode = NULL; + ccode_longB = NULL; + ccode_longT = NULL; + lang_name = NULL; + + while (*attr_names && *attr_values) + { + if (g_str_equal (*attr_names, "iso_639_1_code")) + { + /* skip if empty */ + if (**attr_values) + { + g_return_if_fail (strlen (*attr_values) == 2); + ccode = *attr_values; + } + } else if (g_str_equal (*attr_names, "iso_639_2B_code")) { + /* skip if empty */ + if (**attr_values) + { + g_return_if_fail (strlen (*attr_values) == 3 || strcmp (*attr_values, "qaa-qtz") == 0); + ccode_longB = *attr_values; + } + } else if (g_str_equal (*attr_names, "iso_639_2T_code")) { + /* skip if empty */ + if (**attr_values) + { + g_return_if_fail (strlen (*attr_values) == 3 || strcmp (*attr_values, "qaa-qtz") == 0); + ccode_longT = *attr_values; + } + } else if (g_str_equal (*attr_names, "name")) { + lang_name = *attr_values; + } + + ++attr_names; + ++attr_values; + } + + if (lang_name == NULL) + return; + + if (ccode != NULL) + { + g_hash_table_insert (lang_table, + g_strdup (ccode), + g_strdup (lang_name)); + } + if (ccode_longB != NULL) + { + g_hash_table_insert (lang_table, + g_strdup (ccode_longB), + g_strdup (lang_name)); + } + if (ccode_longT != NULL) + { + g_hash_table_insert (lang_table, + g_strdup (ccode_longT), + g_strdup (lang_name)); + } +} + +#define ISO_CODES_DATADIR ISO_CODES_PREFIX"/share/xml/iso-codes" +#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX"/share/locale" + +static void +totem_lang_table_init (void) +{ + GError *err = NULL; + char *buf; + gsize buf_len; + + lang_table = g_hash_table_new_full + (g_str_hash, g_str_equal, g_free, g_free); + + g_atexit (totem_lang_table_free); + + bindtextdomain ("iso_639", ISO_CODES_LOCALESDIR); + bind_textdomain_codeset ("iso_639", "UTF-8"); + + if (g_file_get_contents (ISO_CODES_DATADIR "/iso_639.xml", + &buf, &buf_len, &err)) + { + GMarkupParseContext *ctx; + GMarkupParser parser = + { totem_lang_table_parse_start_tag, NULL, NULL, NULL, NULL }; + + ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL); + + if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) + { + g_warning ("Failed to parse '%s': %s\n", + ISO_CODES_DATADIR"/iso_639.xml", + err->message); + g_error_free (err); + } + + g_markup_parse_context_free (ctx); + g_free (buf); + } else { + g_warning ("Failed to load '%s': %s\n", + ISO_CODES_DATADIR"/iso_639.xml", err->message); + g_error_free (err); + } +} + +static const char * +totem_lang_get_full (const char *lang) +{ + const char *lang_name; + int len; + + g_return_val_if_fail (lang != NULL, NULL); + + len = strlen (lang); + if (len != 2 && len != 3) + return NULL; + if (lang_table == NULL) + totem_lang_table_init (); + + lang_name = (const gchar*) g_hash_table_lookup (lang_table, lang); + + if (lang_name) + return dgettext ("iso_639", lang_name); + + return NULL; +} + +/* Subtitle and language menus */ +static void +totem_g_list_deep_free (GList *list) +{ + GList *l; + + for (l = list; l != NULL; l = l->next) + g_free (l->data); + g_list_free (list); +} + +static void +subtitles_changed_callback (GtkRadioAction *action, GtkRadioAction *current, + Totem *totem) +{ + int rank; + + rank = gtk_radio_action_get_current_value (current); + + bacon_video_widget_set_subtitle (totem->bvw, rank); +} + + +static void +languages_changed_callback (GtkRadioAction *action, GtkRadioAction *current, + Totem *totem) +{ + int rank; + + rank = gtk_radio_action_get_current_value (current); + + bacon_video_widget_set_language (totem->bvw, rank); +} + +static GtkAction * +add_lang_action (Totem *totem, GtkActionGroup *action_group, guint ui_id, + const char *path, const char *prefix, const char *lang, + int lang_id, GSList **group) +{ + const char *full_lang; + char *label; + char *name; + GtkAction *action; + + full_lang = totem_lang_get_full (lang); + label = escape_label_for_menu (full_lang ? full_lang : lang); + name = g_strdup_printf ("%s-%d", prefix, lang_id); + + action = g_object_new (GTK_TYPE_RADIO_ACTION, + "name", name, + "label", label, + "value", lang_id, + NULL); + g_free (label); + + gtk_radio_action_set_group (GTK_RADIO_ACTION (action), *group); + *group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action)); + gtk_action_group_add_action (action_group, action); + g_object_unref (action); + gtk_ui_manager_add_ui (totem->ui_manager, ui_id, + path, name, name, GTK_UI_MANAGER_MENUITEM, FALSE); + g_free (name); + + return action; +} + +static GtkAction * +create_lang_actions (Totem *totem, GtkActionGroup *action_group, guint ui_id, + const char *path, const char *prefix, GList *list, + gboolean is_lang) +{ + GtkAction *action = NULL; + unsigned int i, *hash_value; + GList *l; + GSList *group = NULL; + GHashTable *lookup; + char *action_data; + + if (is_lang == FALSE) { + add_lang_action (totem, action_group, ui_id, path, prefix, + _("None"), -2, &group); + } + + action = add_lang_action (totem, action_group, ui_id, path, prefix, + _("Auto"), -1, &group); + + i = 0; + lookup = g_hash_table_new_full (g_str_hash, g_int_equal, g_free, NULL); + + for (l = list; l != NULL; l = l->next) + { + hash_value = g_hash_table_lookup (lookup, l->data); + if (hash_value == NULL) { + action_data = g_strdup (l->data); + g_hash_table_insert (lookup, l->data, (unsigned int *)1); + } else { + action_data = g_strdup_printf ("%s #%u", (char *)l->data, (unsigned int)hash_value+1); + g_hash_table_replace (lookup, l->data, (unsigned int *)((unsigned int)hash_value+1)); + } + + add_lang_action (totem, action_group, ui_id, path, prefix, + action_data, i, &group); + i++; + } + + g_hash_table_destroy (lookup); + + return action; +} + +static gboolean +totem_sublang_equal_lists (GList *orig, GList *new) +{ + GList *o, *n; + gboolean retval; + + if ((orig == NULL && new != NULL) || (orig != NULL && new == NULL)) + return FALSE; + if (orig == NULL && new == NULL) + return TRUE; + + if (g_list_length (orig) != g_list_length (new)) + return FALSE; + + retval = TRUE; + o = orig; + n = new; + while (o != NULL && n != NULL && retval != FALSE) + { + if (g_str_equal (o->data, n->data) == FALSE) + retval = FALSE; + o = g_list_next (o); + n = g_list_next (n); + } + + return retval; +} + +static void +totem_languages_update (Totem *totem, GList *list) +{ + GtkAction *action; + int current; + + /* Remove old UI */ + gtk_ui_manager_remove_ui (totem->ui_manager, totem->languages_ui_id); + gtk_ui_manager_ensure_update (totem->ui_manager); + + /* Create new ActionGroup */ + if (totem->languages_action_group) { + gtk_ui_manager_remove_action_group (totem->ui_manager, + totem->languages_action_group); + g_object_unref (totem->languages_action_group); + } + totem->languages_action_group = gtk_action_group_new ("languages-action-group"); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->languages_action_group, -1); + + if (list != NULL) + { + action = create_lang_actions (totem, totem->languages_action_group, + totem->languages_ui_id, + "/tmw-menubar/sound/languages/placeholder", + "languages", list, TRUE); + gtk_ui_manager_ensure_update (totem->ui_manager); + + current = bacon_video_widget_get_language (totem->bvw); + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), + current); + g_signal_connect (G_OBJECT (action), "changed", + G_CALLBACK (languages_changed_callback), totem); + } + + totem_g_list_deep_free (totem->language_list); + totem->language_list = list; +} + +static void +totem_subtitles_update (Totem *totem, GList *list) +{ + GtkAction *action; + int current; + + /* Remove old UI */ + gtk_ui_manager_remove_ui (totem->ui_manager, totem->subtitles_ui_id); + gtk_ui_manager_ensure_update (totem->ui_manager); + + /* Create new ActionGroup */ + if (totem->subtitles_action_group) { + gtk_ui_manager_remove_action_group (totem->ui_manager, + totem->subtitles_action_group); + g_object_unref (totem->subtitles_action_group); + } + totem->subtitles_action_group = gtk_action_group_new ("subtitles-action-group"); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->subtitles_action_group, -1); + + + if (list != NULL) + { + action = create_lang_actions (totem, totem->subtitles_action_group, + totem->subtitles_ui_id, + "/tmw-menubar/view/subtitles/placeholder", + "subtitles", list, FALSE); + gtk_ui_manager_ensure_update (totem->ui_manager); + + current = bacon_video_widget_get_subtitle (totem->bvw); + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), + current); + g_signal_connect (G_OBJECT (action), "changed", + G_CALLBACK (subtitles_changed_callback), totem); + } + + totem_g_list_deep_free (totem->subtitles_list); + totem->subtitles_list = list; +} + +void +totem_sublang_update (Totem *totem) +{ + GList *list; + + list = bacon_video_widget_get_languages (totem->bvw); + if (totem_sublang_equal_lists (totem->language_list, list) == TRUE) { + totem_g_list_deep_free (list); + } else { + totem_languages_update (totem, list); + } + + list = bacon_video_widget_get_subtitles (totem->bvw); + if (totem_sublang_equal_lists (totem->subtitles_list, list) == TRUE) { + totem_g_list_deep_free (list); + } else { + totem_subtitles_update (totem, list); + } +} + +void +totem_sublang_exit (Totem *totem) +{ + totem_g_list_deep_free (totem->subtitles_list); + totem_g_list_deep_free (totem->language_list); +} + +/* Recent files */ +static void +on_recent_file_item_activated (GtkAction *action, + Totem *totem) +{ + GtkRecentInfo *recent_info; + const gchar *uri; + gboolean playlist_changed; + guint end; + + recent_info = g_object_get_data (G_OBJECT (action), "recent-info"); + uri = gtk_recent_info_get_uri (recent_info); + + totem_signal_block_by_data (totem->playlist, totem); + + end = totem_playlist_get_last (totem->playlist); + playlist_changed = totem_playlist_add_mrl (totem->playlist, uri, NULL); + gtk_recent_manager_add_item (totem->recent_manager, uri); + + totem_signal_unblock_by_data (totem->playlist, totem); + + if (playlist_changed) + { + char *mrl; + + totem_playlist_set_current (totem->playlist, end + 1); + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } +} + +static gint +totem_compare_recent_items (GtkRecentInfo *a, GtkRecentInfo *b) +{ + gboolean has_totem_a, has_totem_b; + + has_totem_a = gtk_recent_info_has_group (a, "Totem"); + has_totem_b = gtk_recent_info_has_group (b, "Totem"); + + if (has_totem_a && has_totem_b) { + time_t time_a, time_b; + + time_a = gtk_recent_info_get_modified (a); + time_b = gtk_recent_info_get_modified (b); + + return (time_b - time_a); + } else if (has_totem_a) { + return -1; + } else if (has_totem_b) { + return 1; + } + + return 0; +} + +/* Copied from eel so we don't have a dependency on them. + * It's being consolidated into glib eventually anyway. + */ +static char * +totem_str_middle_truncate (const char *string, + guint truncate_length) +{ + char *truncated; + guint length; + guint num_left_chars; + guint num_right_chars; + + const char delimter[] = "..."; + const guint delimter_length = strlen (delimter); + const guint min_truncate_length = delimter_length + 2; + + if (string == NULL) { + return NULL; + } + + /* It doesnt make sense to truncate strings to less than + * the size of the delimiter plus 2 characters (one on each + * side) + */ + if (truncate_length < min_truncate_length) { + return g_strdup (string); + } + + length = strlen (string); + + /* Make sure the string is not already small enough. */ + if (length <= truncate_length) { + return g_strdup (string); + } + + /* Find the 'middle' where the truncation will occur. */ + num_left_chars = (truncate_length - delimter_length) / 2; + num_right_chars = truncate_length - num_left_chars - delimter_length + 1; + + truncated = g_new (char, truncate_length + 1); + + strncpy (truncated, string, num_left_chars); + strncpy (truncated + num_left_chars, delimter, delimter_length); + strncpy (truncated + num_left_chars + delimter_length, string + length - num_right_chars + 1, num_right_chars); + + return truncated; +} + +static void +totem_recent_manager_changed_callback (GtkRecentManager *recent_manager, Totem *totem) +{ + GList *items, *l; + guint n_items = 0; + static guint i = 0; + + gtk_ui_manager_remove_ui (totem->ui_manager, totem->recent_ui_id); + gtk_ui_manager_ensure_update (totem->ui_manager); + + if (totem->recent_action_group) { + gtk_ui_manager_remove_action_group (totem->ui_manager, + totem->recent_action_group); + g_object_unref (totem->recent_action_group); + } + totem->recent_action_group = gtk_action_group_new ("recent-action-group"); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->recent_action_group, -1); + + items = gtk_recent_manager_get_items (recent_manager); + items = g_list_sort (items, (GCompareFunc) totem_compare_recent_items); + + for (l = items; l && l->data; l = g_list_next (l)) { + GtkRecentInfo *info; + GtkAction *action; + char *action_name; + char *label; + char *escaped_label; + char *label_trunc; + const char *uri; + + info = (GtkRecentInfo *) l->data; + + if (!gtk_recent_info_has_group (info, "Totem")) + continue; + + action_name = g_strdup_printf ("recent-file%u", i++); + uri = gtk_recent_info_get_uri (info); + + /* Munge the URI for display */ + if (g_str_has_prefix (uri, "file:///") == FALSE) { + label_trunc = totem_str_middle_truncate (uri, TOTEM_MAX_RECENT_ITEM_LEN); + } else { + label_trunc = totem_str_middle_truncate + (gtk_recent_info_get_display_name (info), + TOTEM_MAX_RECENT_ITEM_LEN); + } + escaped_label = escape_label_for_menu (label_trunc); + g_free (label_trunc); + label = g_strdup_printf ("_%d. %s", n_items + 1, escaped_label); + g_free (escaped_label); + + action = g_object_new (GTK_TYPE_ACTION, + "name", action_name, + "label", label, + NULL); + + g_object_set_data_full (G_OBJECT (action), "recent-info", + gtk_recent_info_ref (info), + (GDestroyNotify) gtk_recent_info_unref); + g_signal_connect (G_OBJECT (action), "activate", + G_CALLBACK (on_recent_file_item_activated), + totem); + + gtk_action_group_add_action (totem->recent_action_group, + action); + g_object_unref (action); + + gtk_ui_manager_add_ui (totem->ui_manager, totem->recent_ui_id, + "/tmw-menubar/movie/recent-placeholder", + label, action_name, GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + g_free (label); + + if (++n_items == 5) + break; + } + gtk_ui_manager_ensure_update (totem->ui_manager); + + g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL); + g_list_free (items); +} + +void +totem_setup_recent (Totem *totem) +{ + totem->recent_manager = gtk_recent_manager_get_default (); + totem->recent_action_group = NULL; + totem->recent_ui_id = gtk_ui_manager_new_merge_id + (totem->ui_manager); + + g_signal_connect (G_OBJECT (totem->recent_manager), "changed", + G_CALLBACK (totem_recent_manager_changed_callback), + totem); + + totem_recent_manager_changed_callback (totem->recent_manager, totem); +} + +void +totem_action_add_recent (Totem *totem, const char *filename) +{ + GtkRecentData data; + char *groups[] = { NULL, NULL }; + + data.mime_type = gnome_vfs_get_mime_type (filename); + + if (strstr (filename, "file:///") == NULL) { + /* It's a URI/stream */ + groups[0] = "TotemStreams"; + data.display_name = NULL; + } else { + char *display; + + /* Local files with no mime-type probably don't exist */ + if (data.mime_type == NULL) + return; + + /* It's a local file */ + display = g_filename_from_uri (filename, NULL, NULL); + if (display) { + data.display_name = g_filename_display_basename (display); + g_free (display); + } + groups[0] = "Totem"; + } + + data.description = NULL; + data.app_name = g_strdup (g_get_application_name ()); + data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); + data.groups = groups; + gtk_recent_manager_add_full (totem->recent_manager, + filename, &data); + + if (data.display_name != NULL) + g_free (data.display_name); + g_free (data.mime_type); + g_free (data.app_name); + g_free (data.app_exec); +} + +/* Play Disc menu items */ + +static void +on_play_disc_activate (GtkAction *action, Totem *totem) +{ + char *device_path; + + device_path = g_object_get_data (G_OBJECT (action), "device_path"); + totem_action_play_media_device (totem, device_path); +} + +/* A GnomeVFSDrive and GnomeVFSVolume share many similar methods, but do not + share a base class other than GObject. */ +static char * +fake_gnome_vfs_device_get_something (GObject *device, + char *(*volume_function) (GnomeVFSVolume *), + char *(*drive_function) (GnomeVFSDrive *)) { + if (GNOME_IS_VFS_VOLUME (device)) { + return (*volume_function) (GNOME_VFS_VOLUME (device)); + } else if (GNOME_IS_VFS_DRIVE (device)) { + return (*drive_function) (GNOME_VFS_DRIVE (device)); + } else { + g_warning ("neither a GnomeVFSVolume or a GnomeVFSDrive"); + return NULL; + } +} + +static char * +my_gnome_vfs_volume_get_mount_path (GnomeVFSVolume *volume) +{ + char *uri, *path; + + uri = gnome_vfs_volume_get_activation_uri (volume); + path = g_filename_from_uri (uri, NULL, NULL); + g_free (uri); + + if (path == NULL) + return gnome_vfs_volume_get_device_path (volume); + return path; +} + +static void +add_device_to_menu (GObject *device, guint position, Totem *totem) +{ + char *name, *escaped_name, *icon_name, *device_path; + char *label, *activation_uri; + GtkAction *action; + gboolean disabled = FALSE; + + /* Add devices with blank CDs and audio CDs in them, but disable them */ + activation_uri = fake_gnome_vfs_device_get_something (device, + &gnome_vfs_volume_get_activation_uri, + &gnome_vfs_drive_get_activation_uri); + if (activation_uri != NULL) { + if (g_str_has_prefix (activation_uri, "burn://") != FALSE || g_str_has_prefix (activation_uri, "cdda://") != FALSE) { + disabled = TRUE; + } + g_free (activation_uri); + } else { + if (GNOME_IS_VFS_DRIVE (device)) { + device_path = gnome_vfs_drive_get_device_path + (GNOME_VFS_DRIVE (device)); + disabled = !totem_cd_has_medium (device_path); + g_free (device_path); + } + } + + name = fake_gnome_vfs_device_get_something (device, + &gnome_vfs_volume_get_display_name, + &gnome_vfs_drive_get_display_name); + icon_name = fake_gnome_vfs_device_get_something (device, + &gnome_vfs_volume_get_icon, &gnome_vfs_drive_get_icon); + device_path = fake_gnome_vfs_device_get_something (device, + &my_gnome_vfs_volume_get_mount_path, + &gnome_vfs_drive_get_device_path); + + g_strstrip (name); + escaped_name = escape_label_for_menu (name); + g_free (name); + label = g_strdup_printf (_("Play Disc '%s'"), escaped_name); + g_free (escaped_name); + + name = g_strdup_printf (_("device%d"), position); + action = gtk_action_new (name, label, NULL, NULL); + g_object_set (G_OBJECT (action), "icon-name", icon_name, + "sensitive", !disabled, NULL); + gtk_action_group_add_action (totem->devices_action_group, action); + g_object_unref (action); + gtk_ui_manager_add_ui (totem->ui_manager, totem->devices_ui_id, + "/tmw-menubar/movie/devices-placeholder", name, name, + GTK_UI_MANAGER_MENUITEM, FALSE); + g_free (name); + g_free (label); + g_free (icon_name); + + g_object_set_data_full (G_OBJECT (action), "device_path", + device_path, (GDestroyNotify) g_free); + if (GNOME_IS_VFS_VOLUME (device)) { + g_object_set_data_full (G_OBJECT (action), "activation_uri", + gnome_vfs_volume_get_activation_uri (GNOME_VFS_VOLUME (device)), + (GDestroyNotify) g_free); + } + + g_signal_connect (G_OBJECT (action), "activate", + G_CALLBACK (on_play_disc_activate), totem); +} + +static void +on_movie_menu_select (GtkMenuItem *movie_menuitem, Totem *totem) +{ + GList *devices, *volumes, *drives, *i; + guint position; + + if (totem->drives_changed == FALSE) + return; + + /* Remove old UI */ + gtk_ui_manager_remove_ui (totem->ui_manager, totem->devices_ui_id); + gtk_ui_manager_ensure_update (totem->ui_manager); + + /* Create new ActionGroup */ + if (totem->devices_action_group) { + gtk_ui_manager_remove_action_group (totem->ui_manager, + totem->devices_action_group); + g_object_unref (totem->devices_action_group); + } + totem->devices_action_group = gtk_action_group_new ("devices-action-group"); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->devices_action_group, -1); + + /* Create a list of suitable devices */ + devices = NULL; + + volumes = gnome_vfs_volume_monitor_get_mounted_volumes + (totem->monitor); + for (i = volumes; i != NULL; i = i->next) { + if (gnome_vfs_volume_get_device_type (i->data) != GNOME_VFS_DEVICE_TYPE_CDROM) + continue; + + gnome_vfs_volume_ref (i->data); + devices = g_list_append (devices, i->data); + } + gnome_vfs_drive_volume_list_free (volumes); + + drives = gnome_vfs_volume_monitor_get_connected_drives (totem->monitor); + for (i = drives; i != NULL; i = i->next) { + if (gnome_vfs_drive_get_device_type (i->data) != GNOME_VFS_DEVICE_TYPE_CDROM) + continue; + else if (gnome_vfs_drive_is_mounted (i->data)) + continue; + + gnome_vfs_volume_ref (i->data); + devices = g_list_append (devices, i->data); + } + gnome_vfs_drive_volume_list_free (drives); + + /* Add the devices to the menu */ + position = 0; + + for (i = devices; i != NULL; i = i->next) + { + position++; + add_device_to_menu (i->data, position, totem); + } + gtk_ui_manager_ensure_update (totem->ui_manager); + + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); +} + +static void +on_gnome_vfs_monitor_event (GnomeVFSVolumeMonitor *monitor, + GnomeVFSDrive *drive, + Totem *totem) +{ + totem->drives_changed = TRUE; +} + +void +totem_setup_play_disc (Totem *totem) +{ + GtkWidget *item; + + item = gtk_ui_manager_get_widget (totem->ui_manager, "/tmw-menubar/movie"); + g_signal_connect (G_OBJECT (item), "select", + G_CALLBACK (on_movie_menu_select), totem); + + g_signal_connect (G_OBJECT (totem->monitor), + "drive-connected", + G_CALLBACK (on_gnome_vfs_monitor_event), totem); + g_signal_connect (G_OBJECT (totem->monitor), + "drive-disconnected", + G_CALLBACK (on_gnome_vfs_monitor_event), totem); + g_signal_connect (G_OBJECT (totem->monitor), + "volume-mounted", + G_CALLBACK (on_gnome_vfs_monitor_event), totem); + g_signal_connect (G_OBJECT (totem->monitor), + "volume-unmounted", + G_CALLBACK (on_gnome_vfs_monitor_event), totem); + + totem->drives_changed = TRUE; +} + +static void +open_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_open (totem); +} + +static void +open_location_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_open_location (totem); +} + +static void +eject_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_eject (totem); +} + +static void +properties_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_show_properties (totem); +} + +static void +play_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_play_pause (totem); +} + +static void +quit_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_exit (totem); +} + +static void +take_screenshot_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_take_screenshot (totem); +} + +static void +preferences_action_callback (GtkAction *action, Totem *totem) +{ + gtk_widget_show (totem->prefs); +} + +static void +fullscreen_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_fullscreen_toggle (totem); +} + +static void +zoom_1_2_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_set_scale_ratio (totem, 0.5); +} + +static void +zoom_1_1_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_set_scale_ratio (totem, 1); +} + +static void +zoom_2_1_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_set_scale_ratio (totem, 2); +} + +static void +zoom_in_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_zoom_relative (totem, ZOOM_IN_OFFSET); +} + +static void +zoom_reset_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_zoom_reset (totem); +} + +static void +zoom_out_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_zoom_relative (totem, ZOOM_OUT_OFFSET); +} + +static void +next_angle_action_callback (GtkAction *action, Totem *totem) +{ +} + +static void +dvd_root_menu_action_callback (GtkAction *action, Totem *totem) +{ + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_ROOT_MENU); +} + +static void +dvd_title_menu_action_callback (GtkAction *action, Totem *totem) +{ + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_TITLE_MENU); +} + +static void +dvd_audio_menu_action_callback (GtkAction *action, Totem *totem) +{ + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_AUDIO_MENU); +} + +static void +dvd_angle_menu_action_callback (GtkAction *action, Totem *totem) +{ + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_ANGLE_MENU); +} + +static void +dvd_chapter_menu_action_callback (GtkAction *action, Totem *totem) +{ + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_CHAPTER_MENU); +} + +static void +next_chapter_action_callback (GtkAction *action, Totem *totem) +{ + TOTEM_PROFILE (totem_action_next (totem)); +} + +static void +previous_chapter_action_callback (GtkAction *action, Totem *totem) +{ + TOTEM_PROFILE (totem_action_previous (totem)); +} + +static void +skip_to_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_skip_to (totem); +} + +static void +skip_forward_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_seek_relative (totem, SEEK_FORWARD_OFFSET); +} + +static void +skip_backwards_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_seek_relative (totem, SEEK_BACKWARD_OFFSET); +} + +static void +volume_up_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_volume_relative (totem, VOLUME_UP_OFFSET); +} + +static void +volume_down_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_volume_relative (totem, VOLUME_DOWN_OFFSET); +} + +static void +contents_action_callback (GtkAction *action, Totem *totem) +{ + totem_action_show_help (totem); +} + +static void +about_action_callback (GtkAction *action, Totem *totem) +{ + char *backend_version, *description; + const char *frontend_type; + + const char *authors[] = + { + "Bastien Nocera <hadess@hadess.net>", + "Ronald Bultje <rbultje@ronald.bitfreak.net>", + "Julien Moutte <julien@moutte.net> (GStreamer backend)", + NULL + }; + const char *artists[] = { "Jakub Steiner <jimmac@ximian.com>", NULL }; + const char *documenters[] = + { + "Chee Bin Hoh <cbhoh@gnome.org>", + NULL + }; + char *license = totem_interface_get_license (); + +#ifdef HAVE_GTK_ONLY + frontend_type = N_("GTK+"); +#else + frontend_type = N_("GNOME"); +#endif + + backend_version = bacon_video_widget_get_backend_name (totem->bvw); + /* This lists the back-end and front-end types and versions, such as + * Movie Player using GStreamer 0.10.1 and GNOME */ + description = g_strdup_printf (_("Movie Player using %s and %s"), + backend_version, _(frontend_type)); + + gtk_show_about_dialog (GTK_WINDOW (totem->win), + "version", VERSION, + "copyright", _("Copyright \xc2\xa9 2002-2006 Bastien Nocera"), + "comments", description, + "authors", authors, + "documenters", documenters, + "artists", artists, + "translator-credits", _("translator-credits"), + "logo-icon-name", "totem", + "license", license, + "wrap-license", TRUE, + NULL); + + g_free (backend_version); + g_free (description); + g_free (license); +} + +static void +repeat_mode_action_callback (GtkToggleAction *action, Totem *totem) +{ + totem_playlist_set_repeat (totem->playlist, + gtk_toggle_action_get_active (action)); +} + +static void +shuffle_mode_action_callback (GtkToggleAction *action, Totem *totem) +{ + totem_playlist_set_shuffle (totem->playlist, + gtk_toggle_action_get_active (action)); +} + +static void +deinterlace_action_callback (GtkToggleAction *action, Totem *totem) +{ + gboolean value; + + value = gtk_toggle_action_get_active (action); + bacon_video_widget_set_deinterlacing (totem->bvw, value); + gconf_client_set_bool (totem->gc, GCONF_PREFIX"/deinterlace", + value, NULL); +} + +static void +always_on_top_action_callback (GtkToggleAction *action, Totem *totem) +{ + gtk_window_set_keep_above (GTK_WINDOW (totem->win), + gtk_toggle_action_get_active (action)); + gconf_client_set_bool (totem->gc, + GCONF_PREFIX"/window_on_top", + gtk_toggle_action_get_active (action), NULL); +} + +static void +show_controls_action_callback (GtkToggleAction *action, Totem *totem) +{ + gboolean show; + + show = gtk_toggle_action_get_active (action); + + /* Let's update our controls visibility */ + if (show) + totem->controls_visibility = TOTEM_CONTROLS_VISIBLE; + else + totem->controls_visibility = TOTEM_CONTROLS_HIDDEN; + + show_controls (totem, FALSE); +} + +static void +show_sidebar_action_callback (GtkToggleAction *action, Totem *totem) +{ + if (totem_is_fullscreen (totem)) + return; + + totem_sidebar_toggle (totem, gtk_toggle_action_get_active (action)); +} + +static void +aspect_ratio_changed_callback (GtkRadioAction *action, GtkRadioAction *current, Totem *totem) +{ + totem_action_set_aspect_ratio (totem, gtk_radio_action_get_current_value (current)); +} + +static void +clear_playlist_action_callback (GtkAction *action, Totem *totem) +{ + totem_playlist_clear (totem->playlist); + totem_action_set_mrl (totem, NULL); +} + +static const GtkActionEntry entries[] = { + { "movie-menu", NULL, N_("_Movie") }, + { "open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), G_CALLBACK (open_action_callback) }, + { "open-location", NULL, N_("Open _Location..."), "<control>L", N_("Open a non-local file"), G_CALLBACK (open_location_action_callback) }, + { "eject", "media-eject", N_("_Eject"), "<control>E", NULL, G_CALLBACK (eject_action_callback) }, + { "properties", GTK_STOCK_PROPERTIES, N_("_Properties"), "<control>P", NULL, G_CALLBACK (properties_action_callback) }, + { "play", GTK_STOCK_MEDIA_PLAY, N_("Play / Pa_use"), "P", N_("Play or pause the movie"), G_CALLBACK (play_action_callback) }, + { "quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", N_("Quit the program"), G_CALLBACK (quit_action_callback) }, + + { "edit-menu", NULL, N_("_Edit") }, + { "take-screenshot", "camera-photo", N_("Take _Screenshot..."), "<control>S", N_("Take a screenshot"), G_CALLBACK (take_screenshot_action_callback) }, + { "clear-playlist", NULL, N_("_Clear Playlist"), NULL, N_("Clear playlist"), G_CALLBACK (clear_playlist_action_callback) }, + { "preferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces"), NULL, NULL, G_CALLBACK (preferences_action_callback) }, + + { "view-menu", NULL, N_("_View") }, + { "fullscreen", "view-fullscreen", N_("_Fullscreen"), "F", N_("Switch to fullscreen"), G_CALLBACK (fullscreen_action_callback) }, + { "zoom-window-menu", NULL, N_("Fit Window to Movie") }, + { "zoom-1-2", NULL, N_("_Resize 1:2"), "0", N_("Resize to half the video size"), G_CALLBACK (zoom_1_2_action_callback) }, + { "zoom-1-1", NULL, N_("Resize _1:1"), "1", N_("Resize to video size"), G_CALLBACK (zoom_1_1_action_callback) }, + { "zoom-2-1", NULL, N_("Resize _2:1"), "2", N_("Resize to twice the video size"), G_CALLBACK (zoom_2_1_action_callback) }, + { "aspect-ratio-menu", NULL, N_("_Aspect Ratio") }, + { "next-angle", NULL, N_("Switch An_gles"), "G", N_("Switch angles"), G_CALLBACK (next_angle_action_callback) }, +/* { "subtitles-menu", NULL, N_("S_ubtitles") },*/ + + { "go-menu", NULL, N_("_Go") }, + { "dvd-root-menu", GTK_STOCK_INDEX, N_("_DVD Menu"), "m", N_("Go to the DVD menu"), G_CALLBACK (dvd_root_menu_action_callback) }, + { "dvd-title-menu", NULL, N_("_Title Menu"), NULL, N_("Go to the title menu"), G_CALLBACK (dvd_title_menu_action_callback) }, + { "dvd-audio-menu", NULL, N_("A_udio Menu"), NULL, N_("Go to the audio menu"), G_CALLBACK (dvd_audio_menu_action_callback) }, + { "dvd-angle-menu", NULL, N_("_Angle Menu"), NULL, N_("Go to the angle menu"), G_CALLBACK (dvd_angle_menu_action_callback) }, + { "dvd-chapter-menu", GTK_STOCK_INDEX, N_("_Chapter Menu"), "c", N_("Go to the chapter menu"), G_CALLBACK (dvd_chapter_menu_action_callback) }, + { "next-chapter", GTK_STOCK_MEDIA_NEXT, N_("_Next Chapter/Movie"), "n", N_("Next chapter or movie"), G_CALLBACK (next_chapter_action_callback) }, + { "previous-chapter", GTK_STOCK_MEDIA_PREVIOUS, N_("_Previous Chapter/Movie"), "b", N_("Previous chapter or movie"), G_CALLBACK (previous_chapter_action_callback) }, + { "skip-to", GTK_STOCK_JUMP_TO, N_("_Skip to..."), "S", N_("Skip to a specific time"), G_CALLBACK (skip_to_action_callback) }, + + { "sound-menu", NULL, N_("_Sound") }, +/* { "languages-menu", NULL, N_("_Languages") }, */ + { "volume-up", "audio-volume-high", N_("Volume _Up"), "Up", N_("Volume up"), G_CALLBACK (volume_up_action_callback) }, + { "volume-down", "audio-volume-low", N_("Volume _Down"), "Down", N_("Volume down"), G_CALLBACK (volume_down_action_callback) }, + + { "help-menu", NULL, N_("_Help") }, + { "contents", GTK_STOCK_HELP, N_("_Contents"), "F1", N_("Help contents"), G_CALLBACK (contents_action_callback) }, + { "about", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, G_CALLBACK (about_action_callback) } +}; + +static const GtkActionEntry zoom_entries[] = { + { "zoom-in", GTK_STOCK_ZOOM_IN, N_("Zoom In"), "R", N_("Zoom in"), G_CALLBACK (zoom_in_action_callback) }, + { "zoom-reset", GTK_STOCK_ZOOM_100, N_("Zoom Reset"), NULL, N_("Zoom reset"), G_CALLBACK (zoom_reset_action_callback) }, + { "zoom-out", GTK_STOCK_ZOOM_OUT, N_("Zoom Out"), "T", N_("Zoom out"), G_CALLBACK (zoom_out_action_callback) } +}; + +static const GtkActionEntry seek_entries_ltr[] = { + { "skip-forward", GTK_STOCK_MEDIA_FORWARD, N_("Skip _Forward"), "Right", N_("Skip forward"), G_CALLBACK (skip_forward_action_callback) }, + { "skip-backwards", GTK_STOCK_MEDIA_REWIND, N_("Skip _Backwards"), "Left", N_("Skip backwards"), G_CALLBACK (skip_backwards_action_callback) } +}; + +static const GtkActionEntry seek_entries_rtl[] = { + { "skip-forward", GTK_STOCK_MEDIA_FORWARD, N_("Skip _Forward"), "Left", N_("Skip forward"), G_CALLBACK (skip_forward_action_callback) }, + { "skip-backwards", GTK_STOCK_MEDIA_REWIND, N_("Skip _Backwards"), "Right", N_("Skip backwards"), G_CALLBACK (skip_backwards_action_callback) } +}; + +static const GtkToggleActionEntry toggle_entries[] = { + { "repeat-mode", NULL, N_("_Repeat Mode"), NULL, N_("Set the repeat mode"), G_CALLBACK (repeat_mode_action_callback), FALSE }, + { "shuffle-mode", NULL, N_("Shuff_le Mode"), NULL, N_("Set the shuffle mode"), G_CALLBACK (shuffle_mode_action_callback), FALSE }, + { "deinterlace", NULL, N_("_Deinterlace"), "I", N_("Deinterlace"), G_CALLBACK (deinterlace_action_callback), FALSE }, + { "always-on-top", NULL, N_("Always on _Top"), NULL, N_("Always on top"), G_CALLBACK (always_on_top_action_callback), FALSE }, + { "show-controls", NULL, N_("Show _Controls"), "H", N_("Show controls"), G_CALLBACK (show_controls_action_callback), TRUE }, + { "sidebar", NULL, N_("_Sidebar"), "F9", N_("Show or hide the sidebar"), G_CALLBACK (show_sidebar_action_callback), TRUE } +}; + +static const GtkRadioActionEntry aspect_ratio_entries[] = { + { "aspect-ratio-auto", NULL, N_("Auto"), NULL, N_("Sets automatic aspect ratio"), BVW_RATIO_AUTO }, + { "aspect-ratio-square", NULL, N_("Square"), NULL, N_("Sets square aspect ratio"), BVW_RATIO_SQUARE }, + { "aspect-ratio-fbt", NULL, N_("4:3 (TV)"), NULL, N_("Sets 4:3 (TV) aspect ratio"), BVW_RATIO_FOURBYTHREE }, + { "aspect-ratio-anamorphic", NULL, N_("16:9 (Widescreen)"), NULL, N_("Sets 16:9 (Anamorphic) aspect ratio"), BVW_RATIO_ANAMORPHIC }, + { "aspect-ratio-dvb", NULL, N_("2.11:1 (DVB)"), NULL, N_("Sets 2.11:1 (DVB) aspect ratio"), BVW_RATIO_DVB } +}; + +static void +totem_ui_manager_connect_proxy_callback (GtkUIManager *ui_manager, + GtkAction *action, GtkWidget *widget, Totem *totem) +{ + GtkRecentInfo *recent_info; + GdkPixbuf *icon; + GtkWidget *image; + gint w, h; + + recent_info = g_object_get_data (G_OBJECT (action), "recent-info"); + + if (recent_info == NULL) { + return; + } + + if (GTK_IS_IMAGE_MENU_ITEM (widget)) { + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); + icon = gtk_recent_info_get_icon (recent_info, w); + + if (icon != NULL) { + image = gtk_image_new_from_pixbuf (icon); + gtk_image_menu_item_set_image + (GTK_IMAGE_MENU_ITEM (widget), image); + } + } +} + +void +totem_ui_manager_setup (Totem *totem) +{ + char *filename; + GtkWidget *menubar; + GtkWidget *menubar_box; + GtkAction *action; + + totem->main_action_group = gtk_action_group_new ("main-action-group"); + gtk_action_group_set_translation_domain (totem->main_action_group, + GETTEXT_PACKAGE); + gtk_action_group_add_actions (totem->main_action_group, entries, + G_N_ELEMENTS (entries), totem); + if (gtk_widget_get_direction (totem->win) == GTK_TEXT_DIR_RTL) { + gtk_action_group_add_actions (totem->main_action_group, + seek_entries_rtl, + G_N_ELEMENTS (seek_entries_rtl), totem); + } else { + gtk_action_group_add_actions (totem->main_action_group, + seek_entries_ltr, + G_N_ELEMENTS (seek_entries_ltr), totem); + } + gtk_action_group_add_toggle_actions (totem->main_action_group, + toggle_entries, G_N_ELEMENTS (toggle_entries), totem); + gtk_action_group_add_radio_actions (totem->main_action_group, + aspect_ratio_entries, G_N_ELEMENTS (aspect_ratio_entries), 0, + G_CALLBACK (aspect_ratio_changed_callback), totem); + + action = g_object_new (GTK_TYPE_ACTION, + "name", "subtitles-menu", + "label", _("S_ubtitles"), + "hide-if-empty", FALSE, NULL); + gtk_action_group_add_action (totem->main_action_group, action); + g_object_unref (action); + action = g_object_new (GTK_TYPE_ACTION, + "name", "languages-menu", + "label", _("_Languages"), + "hide-if-empty", FALSE, NULL); + gtk_action_group_add_action (totem->main_action_group, action); + g_object_unref (action); + + /* Hide help if we're using GTK+ only */ +#ifdef HAVE_GTK_ONLY + action = gtk_action_group_get_action + (totem->main_action_group, "contents"); + gtk_action_set_visible (action, FALSE); +#endif /* HAVE_GTK_ONLY */ + + totem->zoom_action_group = gtk_action_group_new ("zoom-action-group"); + gtk_action_group_set_translation_domain (totem->zoom_action_group, + GETTEXT_PACKAGE); + gtk_action_group_add_actions (totem->zoom_action_group, zoom_entries, + G_N_ELEMENTS (zoom_entries), totem); + + totem->ui_manager = gtk_ui_manager_new (); + g_signal_connect (G_OBJECT (totem->ui_manager), "connect-proxy", + G_CALLBACK (totem_ui_manager_connect_proxy_callback), + totem); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->main_action_group, 0); + gtk_ui_manager_insert_action_group (totem->ui_manager, + totem->zoom_action_group, -1); + + totem->devices_action_group = NULL; + totem->devices_ui_id = gtk_ui_manager_new_merge_id (totem->ui_manager); + totem->languages_action_group = NULL; + totem->languages_ui_id = gtk_ui_manager_new_merge_id + (totem->ui_manager); + totem->subtitles_action_group = NULL; + totem->subtitles_ui_id = gtk_ui_manager_new_merge_id + (totem->ui_manager); + + gtk_window_add_accel_group (GTK_WINDOW (totem->win), + gtk_ui_manager_get_accel_group (totem->ui_manager)); + + filename = totem_interface_get_full_path ("totem-ui.xml"); + if (gtk_ui_manager_add_ui_from_file (totem->ui_manager, + filename, NULL) == 0) { + totem_interface_error_blocking ( + _("Couldn't load the 'ui description' file"), + _("Make sure that Totem is properly installed."), + GTK_WINDOW (totem->win)); + totem_action_exit (NULL); + } + g_free (filename); + + menubar = gtk_ui_manager_get_widget (totem->ui_manager, "/tmw-menubar"); + menubar_box = glade_xml_get_widget (totem->xml, "tmw_menubar_box"); + gtk_box_pack_start (GTK_BOX (menubar_box), menubar, FALSE, FALSE, 0); +} + |