diff options
Diffstat (limited to 'trunk/src/totem.c')
-rw-r--r-- | trunk/src/totem.c | 3566 |
1 files changed, 3566 insertions, 0 deletions
diff --git a/trunk/src/totem.c b/trunk/src/totem.c new file mode 100644 index 000000000..2dd904bc7 --- /dev/null +++ b/trunk/src/totem.c @@ -0,0 +1,3566 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2001-2006 Bastien Nocera <hadess@hadess.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Totem project hereby grant permission for non-gpl compatible GStreamer + * plugins to be used and distributed together with GStreamer and Totem. This + * permission are above and beyond the permissions granted by the GPL license + * Totem is covered by. + * + * Monday 7th February 2005: Christian Schaller: Add excemption clause. + * See license_change file for details. + * + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <stdlib.h> + +#ifndef HAVE_GTK_ONLY +#include <gnome.h> +#include "totem-gromit.h" +#endif /* !HAVE_GTK_ONLY */ + +#include <string.h> + +#ifdef GDK_WINDOWING_X11 +/* X11 headers */ +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#ifdef HAVE_XFREE +#include <X11/XF86keysym.h> +#endif +#endif + +#include "bacon-video-widget.h" +#include "bacon-video-widget-properties.h" +#include "totem-statusbar.h" +#include "totem-time-label.h" +#include "totem-session.h" +#include "totem-screenshot.h" +#include "totem-sidebar.h" +#include "totem-menu.h" +#include "totem-missing-plugins.h" +#include "totem-options.h" +#include "totem-uri.h" +#include "totem-interface.h" +#include "bacon-volume.h" +#include "video-utils.h" + +#include "totem.h" +#include "totem-private.h" +#include "totem-preferences.h" +#include "totem-disc.h" + +#include "debug.h" + +#define REWIND_OR_PREVIOUS 4000 + +#define SEEK_FORWARD_SHORT_OFFSET 15 +#define SEEK_BACKWARD_SHORT_OFFSET -5 + +#define SEEK_FORWARD_LONG_OFFSET 10*60 +#define SEEK_BACKWARD_LONG_OFFSET -3*60 + +#define ZOOM_UPPER 200 +#define ZOOM_RESET 100 +#define ZOOM_LOWER 10 +#define ZOOM_DISABLE (ZOOM_LOWER - 1) +#define ZOOM_ENABLE (ZOOM_UPPER + 1) + +#define FULLSCREEN_POPUP_TIMEOUT 5 * 1000 + +#define BVW_VBOX_BORDER_WIDTH 1 + +static const GtkTargetEntry target_table[] = { + { "text/uri-list", 0, 0 }, + { "_NETSCAPE_URL", 0, 1 }, +}; + +static const GtkTargetEntry source_table[] = { + { "text/uri-list", 0, 0 }, +}; + +static gboolean totem_action_open_files (Totem *totem, char **list); +static gboolean totem_action_open_files_list (Totem *totem, GSList *list); +static void update_fullscreen_size (Totem *totem); +static gboolean popup_hide (Totem *totem); +static void update_buttons (Totem *totem); +static void update_media_menu_items (Totem *totem); +static void update_seekable (Totem *totem, gboolean force_false); +static void playlist_changed_cb (GtkWidget *playlist, Totem *totem); +static void play_pause_set_label (Totem *totem, TotemStates state); +static gboolean on_video_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, Totem *totem); +static void popup_timeout_remove (Totem *totem); +static gint totem_compare_recent_stream_items (GtkRecentInfo *a, GtkRecentInfo *b); + +static void +long_action (void) +{ + while (gtk_events_pending ()) + gtk_main_iteration (); +} + +void +totem_action_error (const char *title, const char *reason, Totem *totem) +{ + totem_interface_error (title, reason, + GTK_WINDOW (totem->win)); +} + +static void +totem_action_error_and_exit (const char *title, + const char *reason, Totem *totem) +{ + totem_interface_error_blocking (title, reason, + GTK_WINDOW (totem->win)); + totem_action_exit (totem); +} + +static void +totem_action_save_size (Totem *totem) +{ + GtkWidget *item; + + if (totem->bvw == NULL) + return; + + if (totem_is_fullscreen (totem) != FALSE) + return; + + /* Save the size of the video widget */ + item = glade_xml_get_widget (totem->xml, "tmw_main_pane"); + gtk_window_get_size (GTK_WINDOW (totem->win), &totem->window_w, + &totem->window_h); + totem->sidebar_w = totem->window_w + - gtk_paned_get_position (GTK_PANED (item)); +} + +static void +totem_action_save_state (Totem *totem) +{ + GKeyFile *keyfile; + char *contents, *filename; + const char *page_id; + + keyfile = g_key_file_new (); + g_key_file_set_integer (keyfile, "State", + "window_w", totem->window_w); + g_key_file_set_integer (keyfile, "State", + "window_h", totem->window_h); + g_key_file_set_boolean (keyfile, "State", + "show_sidebar", totem_sidebar_is_visible (totem)); + g_key_file_set_boolean (keyfile, "State", + "maximised", totem->maximised); + g_key_file_set_integer (keyfile, "State", + "sidebar_w", totem->sidebar_w); + + page_id = totem_sidebar_get_current_page (totem); + g_key_file_set_string (keyfile, "State", + "sidebar_page", page_id); + + contents = g_key_file_to_data (keyfile, NULL, NULL); + g_key_file_free (keyfile); + filename = g_build_filename (g_get_home_dir (), ".gnome2", "totem", NULL); + g_file_set_contents (filename, contents, -1, NULL); + + g_free (filename); + g_free (contents); +} + +void +totem_action_exit (Totem *totem) +{ + GdkDisplay *display = NULL; + + if (totem != NULL) + popup_timeout_remove (totem); + + if (gtk_main_level () > 0) + gtk_main_quit (); + + if (totem == NULL) + exit (0); + +#ifdef HAVE_REMOTE + if (totem->remote != NULL) { + g_object_unref (G_OBJECT (totem->remote)); + } +#endif /* HAVE_REMOTE */ + + if (totem->win != NULL) { + gtk_widget_hide (totem->win); + display = gtk_widget_get_display (totem->win); + } + + if (totem->prefs != NULL) { + gtk_widget_hide (totem->prefs); + } + +#ifndef HAVE_GTK_ONLY + totem_gromit_clear (TRUE); +#endif /* !HAVE_GTK_ONLY */ + + if (display != NULL) + gdk_display_sync (display); + + if (totem->bvw) { + //FIXME move the volume to the static file? + gconf_client_set_int (totem->gc, + GCONF_PREFIX"/volume", + bacon_video_widget_get_volume (totem->bvw), + NULL); + totem_action_save_size (totem); + } + + bacon_message_connection_free (totem->conn); + totem_action_save_state (totem); + + totem_action_fullscreen (totem, FALSE); + + totem_sublang_exit (totem); + totem_destroy_file_filters (); + + if (totem->gc) + g_object_unref (G_OBJECT (totem->gc)); + + if (totem->win) + gtk_widget_destroy (GTK_WIDGET (totem->win)); + + gnome_vfs_shutdown (); + + exit (0); +} + +static void +totem_action_menu_popup (Totem *totem, guint button) +{ + GtkWidget *menu; + + menu = gtk_ui_manager_get_widget (totem->ui_manager, + "/totem-main-popup"); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, + button, gtk_get_current_event_time ()); + gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE); +} + +static gboolean +main_window_destroy_cb (GtkWidget *widget, GdkEvent *event, Totem *totem) +{ + totem_action_exit (totem); + + return FALSE; +} + +static void +play_pause_set_label (Totem *totem, TotemStates state) +{ + GtkAction *action; + const char *id, *tip; + GSList *l, *proxies; + + if (state == totem->state) + return; + + switch (state) + { + case STATE_PLAYING: + totem_statusbar_set_text (TOTEM_STATUSBAR (totem->statusbar), + _("Playing")); + id = GTK_STOCK_MEDIA_PAUSE; + tip = N_("Pause"); + break; + case STATE_PAUSED: + totem_statusbar_set_text (TOTEM_STATUSBAR (totem->statusbar), + _("Paused")); + id = GTK_STOCK_MEDIA_PLAY; + tip = N_("Play"); + break; + case STATE_STOPPED: + totem_statusbar_set_text (TOTEM_STATUSBAR (totem->statusbar), + _("Stopped")); + totem_statusbar_set_time_and_length + (TOTEM_STATUSBAR (totem->statusbar), 0, 0); + id = GTK_STOCK_MEDIA_PLAY; + tip = N_("Play"); + break; + default: + return; + } + + action = gtk_action_group_get_action (totem->main_action_group, "play"); + g_object_set (G_OBJECT (action), + "tooltip", _(tip), + "stock-id", id, NULL); + + proxies = gtk_action_get_proxies (action); + for (l = proxies; l != NULL; l = l->next) { + atk_object_set_name (gtk_widget_get_accessible (l->data), + _(tip)); + } + + totem->state = state; +} + +void +totem_action_eject (Totem *totem) +{ + GError *err = NULL; + char *cmd, *prefix; + const char *needle; + char *device; + + needle = strchr (totem->mrl, ':'); + g_assert (needle != NULL); + /* we want the ':' as well */ + prefix = g_strndup (totem->mrl, needle - totem->mrl + 1); + totem_playlist_clear_with_prefix (totem->playlist, prefix); + g_free (prefix); + + g_object_get (G_OBJECT (totem->bvw), + "mediadev", &device, NULL); + cmd = g_strdup_printf ("eject %s", device); + g_free (device); + + if (g_spawn_command_line_sync (cmd, NULL, NULL, NULL, &err) == FALSE) + { + totem_action_error (_("Totem could not eject the optical media."), err->message, totem); + g_error_free (err); + } + g_free (cmd); +} + +void +totem_action_show_properties (Totem *totem) +{ + totem_sidebar_set_current_page (totem, "properties"); +} + +void +totem_action_play (Totem *totem) +{ + GError *err = NULL; + int retval; + + if (totem->mrl == NULL) + return; + + if (bacon_video_widget_is_playing (totem->bvw) != FALSE) + return; + + retval = bacon_video_widget_play (totem->bvw, &err); + play_pause_set_label (totem, retval ? STATE_PLAYING : STATE_STOPPED); + if (totem_is_fullscreen (totem) != FALSE) + totem_scrsaver_set_state (totem->scr, !retval); + + if (retval == FALSE) + { + char *msg, *disp; + + disp = totem_uri_escape_for_display (totem->mrl); + msg = g_strdup_printf(_("Totem could not play '%s'."), disp); + g_free (disp); + + totem_playlist_set_playing (totem->playlist, FALSE); + totem_action_error (msg, err->message, totem); + totem_action_stop (totem); + g_free (msg); + g_error_free (err); + } +} + +static void +totem_action_seek (Totem *totem, double pos) +{ + GError *err = NULL; + int retval; + + if (totem->mrl == NULL) + return; + if (bacon_video_widget_is_seekable (totem->bvw) == FALSE) + return; + + retval = bacon_video_widget_seek (totem->bvw, pos, &err); + + if (retval == FALSE) + { + char *msg, *disp; + + disp = totem_uri_escape_for_display (totem->mrl); + msg = g_strdup_printf(_("Totem could not play '%s'."), disp); + g_free (disp); + + totem_playlist_set_playing (totem->playlist, FALSE); + totem_action_error (msg, err->message, totem); + totem_action_stop (totem); + g_free (msg); + g_error_free (err); + } +} + +void +totem_action_set_mrl_and_play (Totem *totem, const char *mrl) +{ + if (totem_action_set_mrl (totem, mrl) != FALSE) + totem_action_play (totem); +} + +static gboolean +totem_action_open_dialog (Totem *totem, const char *path, gboolean play) +{ + GSList *filenames; + gboolean playlist_modified; + + filenames = totem_add_files (GTK_WINDOW (totem->win), path); + + if (filenames == NULL) + return FALSE; + + playlist_modified = totem_action_open_files_list (totem, + filenames); + + if (playlist_modified == FALSE) { + g_slist_foreach (filenames, (GFunc) g_free, NULL); + g_slist_free (filenames); + return FALSE; + } + + g_slist_foreach (filenames, (GFunc) g_free, NULL); + g_slist_free (filenames); + + if (play != FALSE) { + char *mrl; + + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } + + return TRUE; +} + +static gboolean +totem_action_load_media (Totem *totem, MediaType type) +{ + char **mrls; + char *msg; + gboolean retval; + + if (bacon_video_widget_can_play (totem->bvw, type) == FALSE) + { + msg = g_strdup_printf (_("Totem cannot play this type of media (%s) because you do not have the appropriate plugins to handle it."), _(totem_cd_get_human_readable_name (type))); + totem_action_error (msg, _("Please install the necessary plugins and restart Totem to be able to play this media."), totem); + g_free (msg); + return FALSE; + } + + mrls = bacon_video_widget_get_mrls (totem->bvw, type); + if (mrls == NULL) + { + msg = g_strdup_printf (_("Totem could not play this media (%s) although a plugin is present to handle it."), _(totem_cd_get_human_readable_name (type))); + totem_action_error (msg, _("You might want to check that a disc is present in the drive and that it is correctly configured."), totem); + g_free (msg); + return FALSE; + } + + retval = totem_action_open_files (totem, mrls); + g_strfreev (mrls); + + return retval; +} + +static gboolean +totem_action_load_media_device (Totem *totem, const char *device) +{ + MediaType type; + GError *error = NULL; + char *device_path, *url; + gboolean retval; + + if (g_str_has_prefix (device, "file://") != FALSE) + device_path = g_filename_from_uri (device, NULL, NULL); + else + device_path = g_strdup (device); + + type = totem_cd_detect_type_with_url (device_path, &url, &error); + + switch (type) { + case MEDIA_TYPE_ERROR: + totem_action_error (_("Totem was not able to play this disc."), + error ? error->message : _("No reason."), + totem); + retval = FALSE; + break; + case MEDIA_TYPE_DATA: + /* Set default location to the mountpoint of + * this device */ + retval = totem_action_open_dialog (totem, url, FALSE); + break; + case MEDIA_TYPE_DVD: + case MEDIA_TYPE_VCD: + case MEDIA_TYPE_CDDA: + bacon_video_widget_set_media_device + (BACON_VIDEO_WIDGET (totem->bvw), device_path); + retval = totem_action_load_media (totem, type); + if (retval == FALSE) { + totem_action_set_mrl_and_play (totem, NULL); + } + break; + default: + g_assert_not_reached (); + } + + g_free (url); + g_free (device_path); + + return retval; +} + +void +totem_action_play_media_device (Totem *totem, const char *device) +{ + char *mrl; + + if (totem_action_load_media_device (totem, device) != FALSE) { + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } +} + +void +totem_action_play_media (Totem *totem, MediaType type) +{ + char *mrl; + + if (totem_action_load_media (totem, type) != FALSE) { + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } +} + +void +totem_action_stop (Totem *totem) +{ + bacon_video_widget_stop (totem->bvw); + totem_scrsaver_enable (totem->scr); +} + +void +totem_action_play_pause (Totem *totem) +{ + if (totem->mrl == NULL) + { + char *mrl; + + /* Try to pull an mrl from the playlist */ + mrl = totem_playlist_get_current_mrl (totem->playlist); + if (mrl == NULL) + { + play_pause_set_label (totem, STATE_STOPPED); + return; + } else { + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + return; + } + } + + if (bacon_video_widget_is_playing (totem->bvw) == FALSE) + { + bacon_video_widget_play (totem->bvw, NULL); + play_pause_set_label (totem, STATE_PLAYING); + if (totem_is_fullscreen (totem) != FALSE) + totem_scrsaver_disable (totem->scr); + } else { + bacon_video_widget_pause (totem->bvw); + play_pause_set_label (totem, STATE_PAUSED); + totem_scrsaver_enable (totem->scr); + } +} + +void +totem_action_pause (Totem *totem) +{ + if (bacon_video_widget_is_playing (totem->bvw) != FALSE) + { + bacon_video_widget_pause (totem->bvw); + play_pause_set_label (totem, STATE_PAUSED); + totem_scrsaver_enable (totem->scr); + } +} + +static void +totem_action_set_cursor (Totem *totem, gboolean state) +{ + totem->cursor_shown = state; + bacon_video_widget_set_show_cursor (totem->bvw, state); +} + +static gboolean +window_state_event_cb (GtkWidget *window, GdkEventWindowState *event, + Totem *totem) +{ + if (event->changed_mask == GDK_WINDOW_STATE_MAXIMIZED) { + totem->maximised = (event->new_window_state + & GDK_WINDOW_STATE_MAXIMIZED); + return FALSE; + } + + if (event->changed_mask != GDK_WINDOW_STATE_FULLSCREEN) { + return FALSE; + } + + if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) { + totem_action_save_size (totem); + update_fullscreen_size (totem); + bacon_video_widget_set_fullscreen (totem->bvw, TRUE); + totem_action_set_cursor (totem, FALSE); + + if (bacon_video_widget_is_playing (totem->bvw) != FALSE) + totem_scrsaver_disable (totem->scr); + + totem->controls_visibility = TOTEM_CONTROLS_FULLSCREEN; + show_controls (totem, FALSE); + totem_action_set_sensitivity ("fullscreen", FALSE); + } else { + GtkAction *action; + + popup_hide (totem); + bacon_video_widget_set_fullscreen (totem->bvw, FALSE); + totem_action_set_cursor (totem, TRUE); + + totem_scrsaver_enable (totem->scr); + + action = gtk_action_group_get_action (totem->main_action_group, + "show-controls"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) + totem->controls_visibility = TOTEM_CONTROLS_VISIBLE; + else + totem->controls_visibility = TOTEM_CONTROLS_HIDDEN; + + show_controls (totem, TRUE); + totem_action_set_sensitivity ("fullscreen", TRUE); + } + + return FALSE; +} + +void +totem_action_fullscreen_toggle (Totem *totem) +{ + if (totem_is_fullscreen (totem) != FALSE) { + gtk_window_unfullscreen (GTK_WINDOW (totem->win)); + } else { + gtk_window_fullscreen (GTK_WINDOW (totem->win)); + } +} + +void +totem_action_fullscreen (Totem *totem, gboolean state) +{ + if (totem_is_fullscreen (totem) == state) + return; + + totem_action_fullscreen_toggle (totem); +} + +void +totem_action_open (Totem *totem) +{ + totem_action_open_dialog (totem, NULL, TRUE); +} + +static gint +totem_compare_recent_stream_items (GtkRecentInfo *a, GtkRecentInfo *b) +{ + gboolean has_totem_a, has_totem_b; + + has_totem_a = gtk_recent_info_has_group (a, "TotemStreams"); + has_totem_b = gtk_recent_info_has_group (b, "TotemStreams"); + + 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; +} + +static char * +totem_open_location_set_from_clipboard (Totem *totem) +{ + GtkClipboard *clipboard; + gchar *clipboard_content; + + /* Initialize the clipboard and get its content */ + clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (totem->win), GDK_SELECTION_CLIPBOARD); + clipboard_content = gtk_clipboard_wait_for_text (clipboard); + + /* Check clipboard for "://". If it exists, return it */ + if (clipboard_content != NULL && strcmp (clipboard_content, "") != 0) + { + if (g_strrstr (clipboard_content, "://") != NULL) + return clipboard_content; + } + + g_free (clipboard_content); + return NULL; +} + +static gboolean +totem_open_location_match (GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data) +{ + /* Substring-match key against uri */ + char *uri, *match; + + g_return_val_if_fail (key != NULL, FALSE); + gtk_tree_model_get (user_data, iter, 0, &uri, -1); + g_return_val_if_fail (uri != NULL, FALSE); + match = strstr (uri, key); + g_free (uri); + + return (match != NULL); +} + +void +totem_action_open_location (Totem *totem) +{ + GladeXML *glade; + char *mrl, *clipboard_location; + GtkWidget *dialog, *entry; + int response; + const char *filenames[2]; + GtkEntryCompletion *completion; + GtkTreeModel *model; + GList *recent_items; + + glade = totem_interface_load ("uri.glade", _("Open Location..."), + FALSE, GTK_WINDOW (totem->win)); + if (glade == NULL) + return; + + /* Get item from clipboard to fill entry in Open Location... dialog */ + clipboard_location = totem_open_location_set_from_clipboard (totem); + if (clipboard_location != NULL && strcmp (clipboard_location, "") != 0) + gtk_entry_set_text (GTK_ENTRY (glade_xml_get_widget (glade, "uri")), clipboard_location); + g_free (clipboard_location); + + dialog = glade_xml_get_widget (glade, "open_uri_dialog"); + entry = glade_xml_get_widget (glade, "uri"); + + completion = gtk_entry_completion_new(); + model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING)); + gtk_entry_set_completion (GTK_ENTRY (entry), completion); + + /* Add items in Totem's GtkRecentManager to the uri_list GtkEntry's GtkEntryCompletion */ + recent_items = gtk_recent_manager_get_items (totem->recent_manager); + + if (recent_items != NULL) + { + GList *p; + GtkTreeIter iter; + + recent_items = g_list_sort (recent_items, (GCompareFunc) totem_compare_recent_stream_items); + + for (p = recent_items; p != NULL; p = p->next) + { + if (!gtk_recent_info_has_group ((GtkRecentInfo *) p->data, "TotemStreams")) + continue; + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, gtk_recent_info_get_uri ((GtkRecentInfo *) p->data), -1); + gtk_recent_info_unref ((GtkRecentInfo *) p->data); + } + } + + g_list_free (recent_items); + + gtk_entry_completion_set_model (completion, model); + gtk_entry_completion_set_text_column (completion, 0); + gtk_entry_completion_set_match_func (completion, (GtkEntryCompletionMatchFunc) totem_open_location_match, model, NULL); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_OK) + { + char *uri; + + uri = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + + if (uri != NULL && strcmp (uri, "") != 0) + { + if (g_strrstr (uri, "://") == NULL) + { + char *tmp; + tmp = g_strconcat ("http://", uri, NULL); + g_free (uri); + uri = tmp; + } + + filenames[0] = uri; + filenames[1] = NULL; + totem_action_open_files (totem, (char **) filenames); + + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } + g_free (uri); + } + + gtk_widget_destroy (dialog); + g_object_unref (glade); +} + +void +totem_action_take_screenshot (Totem *totem) +{ + GdkPixbuf *pixbuf; + GtkWidget *dialog; + char *filename; + GError *err = NULL; + + if (bacon_video_widget_get_logo_mode (totem->bvw) != FALSE) + return; + + if (bacon_video_widget_can_get_frames (totem->bvw, &err) == FALSE) + { + if (err == NULL) + return; + + totem_action_error (_("Totem could not get a screenshot of that film."), err->message, totem); + g_error_free (err); + return; + } + + pixbuf = bacon_video_widget_get_current_frame (totem->bvw); + if (pixbuf == NULL) + { + totem_action_error (_("Totem could not get a screenshot of that film."), _("This is not supposed to happen; please file a bug report."), totem); + return; + } + + filename = g_build_filename (DATADIR, + "totem", "screenshot.glade", NULL); + dialog = totem_screenshot_new (filename, pixbuf); + g_free (filename); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_object_unref (pixbuf); +} + +static char * +totem_get_nice_name_for_stream (Totem *totem) +{ + char *title, *artist, *retval; + int tracknum; + GValue value = { 0, }; + + bacon_video_widget_get_metadata (totem->bvw, BVW_INFO_TITLE, &value); + title = g_value_dup_string (&value); + g_value_unset (&value); + + if (title == NULL) + return NULL; + + bacon_video_widget_get_metadata (totem->bvw, BVW_INFO_ARTIST, &value); + artist = g_value_dup_string (&value); + g_value_unset (&value); + + if (artist == NULL) + return title; + + bacon_video_widget_get_metadata (totem->bvw, BVW_INFO_TRACK_NUMBER, + &value); + tracknum = g_value_get_int (&value); + + if (tracknum != 0) { + retval = g_strdup_printf ("%02d. %s - %s", + tracknum, artist, title); + } else { + retval = g_strdup_printf ("%s - %s", artist, title); + } + g_free (artist); + g_free (title); + + return retval; +} + +static void +update_skip_to (Totem *totem, gint64 time) +{ + if (totem->skipto != NULL) + totem_skipto_update_range (totem->skipto, time); +} + +static void +update_mrl_label (Totem *totem, const char *name) +{ + gint time; + char *text; + GtkWidget *widget; + + if (name != NULL) + { + char *escaped; + + /* Get the length of the stream */ + time = bacon_video_widget_get_stream_length (totem->bvw); + totem_statusbar_set_time_and_length (TOTEM_STATUSBAR + (totem->statusbar), 0, time / 1000); + + update_skip_to (totem, time); + + /* Update the mrl label */ + escaped = g_markup_escape_text (name, strlen (name)); + text = g_strdup_printf + ("<span size=\"medium\"><b>%s</b></span>", escaped); + g_free (escaped); + + widget = glade_xml_get_widget (totem->xml, "tcw_title_label"); + gtk_label_set_markup (GTK_LABEL (widget), text); + + g_free (text); + + /* Title */ + gtk_window_set_title (GTK_WINDOW (totem->win), name); + } else { + totem_statusbar_set_time_and_length (TOTEM_STATUSBAR + (totem->statusbar), 0, 0); + totem_statusbar_set_text (TOTEM_STATUSBAR (totem->statusbar), + _("Stopped")); + + update_skip_to (totem, 0); + + /* Update the mrl label */ + text = g_strdup_printf + ("<span size=\"medium\"><b>%s</b></span>", + _("No File")); + widget = glade_xml_get_widget (totem->xml, "tcw_title_label"); + gtk_label_set_markup (GTK_LABEL (widget), text); + + g_free (text); + + /* Title */ + gtk_window_set_title (GTK_WINDOW (totem->win), _("Totem Movie Player")); + } +} + +gboolean +totem_action_set_mrl_with_warning (Totem *totem, const char *mrl, + gboolean warn) +{ + gboolean retval = TRUE; + + if (totem->mrl != NULL) + { + g_free (totem->mrl); + totem->mrl = NULL; + bacon_video_widget_close (totem->bvw); + } + + /* Reset the properties and wait for the signal*/ + bacon_video_widget_properties_reset + (BACON_VIDEO_WIDGET_PROPERTIES (totem->properties)); + + if (mrl == NULL) + { + retval = FALSE; + + /* Play/Pause */ + totem_action_set_sensitivity ("play", FALSE); + + /* Seek bar and seek buttons */ + update_seekable (totem, FALSE); + + /* Volume */ + totem_main_set_sensitivity ("tcw_volume_button", FALSE); + totem_action_set_sensitivity ("volume-up", FALSE); + totem_action_set_sensitivity ("volume-down", FALSE); + totem->volume_sensitive = FALSE; + + /* Control popup */ + gtk_widget_set_sensitive (totem->fs_seek, FALSE); + totem_action_set_sensitivity ("next-chapter", FALSE); + totem_action_set_sensitivity ("previous-chapter", FALSE); + totem_main_set_sensitivity ("tcw_volume_hbox", FALSE); + + /* Take a screenshot */ + totem_action_set_sensitivity ("take-screenshot", FALSE); + + /* Clear the playlist */ + totem_action_set_sensitivity ("clear-playlist", FALSE); + + /* Set the logo */ + bacon_video_widget_set_logo_mode (totem->bvw, TRUE); + gtk_widget_set_sensitive (GTK_WIDGET (totem->properties), + FALSE); + update_mrl_label (totem, NULL); + } else { + gboolean caps; + char *subtitle_uri; + GError *err = NULL; + + bacon_video_widget_set_logo_mode (totem->bvw, FALSE); + + subtitle_uri = totem_uri_get_subtitle_uri (mrl); + totem_gdk_window_set_waiting_cursor (totem->win->window); + retval = bacon_video_widget_open_with_subtitle (totem->bvw, + mrl, subtitle_uri, &err); + gdk_window_set_cursor (totem->win->window, NULL); + totem->mrl = g_strdup (mrl); + + /* Play/Pause */ + totem_action_set_sensitivity ("play", TRUE); + + /* Seek bar */ + update_seekable (totem, + bacon_video_widget_is_seekable (totem->bvw)); + + /* Volume */ + caps = bacon_video_widget_can_set_volume (totem->bvw); + totem_main_set_sensitivity ("tcw_volume_button", caps); + totem_main_set_sensitivity ("tcw_volume_hbox", caps); + totem_action_set_sensitivity ("volume-up", caps && totem->prev_volume < 100); + totem_action_set_sensitivity ("volume-down", caps && totem->prev_volume > 0); + totem->volume_sensitive = caps; + + /* Take a screenshot */ + totem_action_set_sensitivity ("take-screenshot", retval); + + /* Clear the playlist */ + totem_action_set_sensitivity ("clear-playlist", retval); + + gtk_widget_set_sensitive + (GTK_WIDGET (totem->properties), retval); + + /* Set the playlist */ + totem_playlist_set_playing (totem->playlist, retval); + + if (retval == FALSE && warn != FALSE) + { + char *msg, *disp; + + disp = totem_uri_escape_for_display (totem->mrl); + msg = g_strdup_printf(_("Totem could not play '%s'."), disp); + g_free (disp); + if (err && err->message) { + totem_action_error (msg, err->message, totem); + } + else { + totem_action_error (msg, _("No error message"), totem); + } + g_free (msg); + } + + if (retval == FALSE) + { + if (err) { + g_error_free (err); + } + g_free (totem->mrl); + totem->mrl = NULL; + play_pause_set_label (totem, STATE_STOPPED); + bacon_video_widget_set_logo_mode (totem->bvw, TRUE); + } + } + update_buttons (totem); + update_media_menu_items (totem); + + return retval; +} + +gboolean +totem_action_set_mrl (Totem *totem, const char *mrl) +{ + return totem_action_set_mrl_with_warning (totem, mrl, TRUE); +} + +static gboolean +totem_time_within_seconds (Totem *totem) +{ + gint64 time; + + time = bacon_video_widget_get_current_time (totem->bvw); + + return (time < REWIND_OR_PREVIOUS); +} + +static void +totem_action_direction (Totem *totem, TotemPlaylistDirection dir) +{ + if (totem_playing_dvd (totem->mrl) == FALSE && + totem_playlist_has_direction (totem->playlist, dir) == FALSE + && totem_playlist_get_repeat (totem->playlist) == FALSE) + return; + + if (totem_playing_dvd (totem->mrl) != FALSE) + { + bacon_video_widget_dvd_event (totem->bvw, + dir == TOTEM_PLAYLIST_DIRECTION_NEXT ? + BVW_DVD_NEXT_CHAPTER : + BVW_DVD_PREV_CHAPTER); + return; + } + + if (dir == TOTEM_PLAYLIST_DIRECTION_NEXT + || bacon_video_widget_is_seekable (totem->bvw) == FALSE + || totem_time_within_seconds (totem) != FALSE) + { + char *mrl; + + totem_playlist_set_direction (totem->playlist, dir); + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } else { + totem_action_seek (totem, 0); + } +} + +void +totem_action_previous (Totem *totem) +{ + totem_action_direction (totem, TOTEM_PLAYLIST_DIRECTION_PREVIOUS); +} + +void +totem_action_next (Totem *totem) +{ + totem_action_direction (totem, TOTEM_PLAYLIST_DIRECTION_NEXT); +} + +void +totem_action_seek_relative (Totem *totem, int off_sec) +{ + GError *err = NULL; + gint64 off_msec, oldsec, sec; + + if (totem->mrl == NULL) + return; + if (bacon_video_widget_is_seekable (totem->bvw) == FALSE) + return; + + off_msec = off_sec * 1000; + oldsec = bacon_video_widget_get_current_time (totem->bvw); + sec = MAX (0, oldsec + off_msec); + + bacon_video_widget_seek_time (totem->bvw, sec, &err); + + if (err != NULL) + { + char *msg, *disp; + + disp = totem_uri_escape_for_display (totem->mrl); + msg = g_strdup_printf(_("Totem could not play '%s'."), totem->mrl); + g_free (disp); + + totem_playlist_set_playing (totem->playlist, FALSE); + totem_action_stop (totem); + totem_action_error (msg, err->message, totem); + g_free (msg); + g_error_free (err); + } +} + +static void +totem_action_zoom (Totem *totem, int zoom) +{ + GtkAction *action; + gboolean zoom_reset, zoom_in, zoom_out; + + if (zoom == ZOOM_ENABLE) + zoom = bacon_video_widget_get_zoom (totem->bvw); + + if (zoom == ZOOM_DISABLE) { + zoom_reset = zoom_in = zoom_out = FALSE; + } else if (zoom < ZOOM_LOWER || zoom > ZOOM_UPPER) { + return; + } else { + bacon_video_widget_set_zoom (totem->bvw, zoom); + zoom_reset = (zoom != ZOOM_RESET); + zoom_out = zoom != ZOOM_LOWER; + zoom_in = zoom != ZOOM_UPPER; + } + + action = gtk_action_group_get_action (totem->zoom_action_group, + "zoom-in"); + gtk_action_set_sensitive (action, zoom_in); + + action = gtk_action_group_get_action (totem->zoom_action_group, + "zoom-out"); + gtk_action_set_sensitive (action, zoom_out); + + action = gtk_action_group_get_action (totem->zoom_action_group, + "zoom-reset"); + gtk_action_set_sensitive (action, zoom_reset); +} + +void +totem_action_zoom_relative (Totem *totem, int off_pct) +{ + int zoom; + + zoom = bacon_video_widget_get_zoom (totem->bvw); + totem_action_zoom (totem, zoom + off_pct); +} + +void +totem_action_zoom_reset (Totem *totem) +{ + totem_action_zoom (totem, 100); +} + +void +totem_action_volume_relative (Totem *totem, int off_pct) +{ + int vol; + + if (bacon_video_widget_can_set_volume (totem->bvw) == FALSE) + return; + + vol = bacon_video_widget_get_volume (totem->bvw); + bacon_video_widget_set_volume (totem->bvw, vol + off_pct); +} + +void +totem_action_toggle_aspect_ratio (Totem *totem) +{ + GtkAction *action; + int tmp; + + tmp = totem_action_get_aspect_ratio (totem); + tmp++; + if (tmp > 4) + tmp = 0; + + action = gtk_action_group_get_action (totem->main_action_group, "aspect-ratio-auto"); + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), tmp); +} + +void +totem_action_set_aspect_ratio (Totem *totem, int ratio) +{ + bacon_video_widget_set_aspect_ratio (totem->bvw, ratio); +} + +int +totem_action_get_aspect_ratio (Totem *totem) +{ + return (bacon_video_widget_get_aspect_ratio (totem->bvw)); +} + +void +totem_action_set_scale_ratio (Totem *totem, gfloat ratio) +{ + bacon_video_widget_set_scale_ratio (totem->bvw, ratio); +} + +void +totem_action_show_help (Totem *totem) +{ +#ifndef HAVE_GTK_ONLY + GError *err = NULL; + + if (gnome_help_display ("totem.xml", NULL, &err) == FALSE) + { + totem_action_error (_("Totem could not display the help contents."), err->message, totem); + g_error_free (err); + } +#endif /* !HAVE_GTK_ONLY */ +} + +static gboolean +totem_action_drop_files (Totem *totem, GtkSelectionData *data, + int drop_type, gboolean empty_pl) +{ + GList *list, *p, *file_list; + gboolean cleared = FALSE; + + list = gnome_vfs_uri_list_parse ((const char *)data->data); + + if (list == NULL) + return FALSE; + + 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); + if (file_list == NULL) + return FALSE; + + if (drop_type != 1) + file_list = g_list_sort (file_list, (GCompareFunc) strcmp); + else + file_list = g_list_reverse (file_list); + + 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 (empty_pl != FALSE && cleared == FALSE) + { + /* The function that calls us knows better + * if we should be doing something with the + * changed playlist ... */ + g_signal_handlers_disconnect_by_func + (G_OBJECT (totem->playlist), + playlist_changed_cb, totem); + totem_playlist_clear (totem->playlist); + cleared = TRUE; + } + + /* Super _NETSCAPE_URL trick */ + if (drop_type == 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 (totem->playlist, filename, title); + + g_free (filename); + g_free (p->data); + } + + g_list_free (file_list); + + /* ... and reconnect because we're nice people */ + if (cleared != FALSE) + { + char *mrl; + + g_signal_connect (G_OBJECT (totem->playlist), + "changed", G_CALLBACK (playlist_changed_cb), + totem); + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); + } + + return TRUE; +} + +static void +drop_video_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + Totem *totem) +{ + gboolean retval; + + retval = totem_action_drop_files (totem, data, info, TRUE); + gtk_drag_finish (context, retval, FALSE, time); +} + +static void +drop_playlist_cb (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + Totem *totem) +{ + gboolean retval; + + retval = totem_action_drop_files (totem, data, info, FALSE); + gtk_drag_finish (context, retval, FALSE, time); +} + +static void +drag_video_cb (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time, + gpointer callback_data) +{ + Totem *totem = (Totem *) callback_data; + char *text; + int len; + + g_assert (selection_data != NULL); + + if (totem->mrl == NULL) + return; + + if (totem->mrl[0] == '/') + text = gnome_vfs_get_uri_from_local_path (totem->mrl); + else + text = g_strdup (totem->mrl); + + g_return_if_fail (text != NULL); + + len = strlen (text); + + gtk_selection_data_set (selection_data, + selection_data->target, + 8, (guchar *) text, len); + + g_free (text); +} + +static void +on_got_redirect (BaconVideoWidget *bvw, const char *mrl, Totem *totem) +{ + gchar *old_mrl, *new_mrl; + + old_mrl = totem_playlist_get_current_mrl (TOTEM_PLAYLIST (totem->playlist)); + new_mrl = totem_resolve_relative_link (old_mrl, mrl); + g_free (old_mrl); + + bacon_video_widget_close (totem->bvw); + totem_gdk_window_set_waiting_cursor (totem->win->window); + bacon_video_widget_open (totem->bvw, new_mrl, NULL); + gdk_window_set_cursor (totem->win->window, NULL); + bacon_video_widget_play (bvw, NULL); + g_free (new_mrl); +} + +/* This is only called when we are playing a DVD */ +static void +on_title_change_event (BaconVideoWidget *bvw, const char *string, Totem *totem) +{ + update_mrl_label (totem, string); + update_buttons (totem); + totem_playlist_set_title (TOTEM_PLAYLIST (totem->playlist), + string, TRUE); +} + +static void +on_channels_change_event (BaconVideoWidget *bvw, Totem *totem) +{ + gchar *name; + + totem_sublang_update (totem); + + /* updated stream info (new song) */ + name = totem_get_nice_name_for_stream (totem); + + bacon_video_widget_properties_update + (BACON_VIDEO_WIDGET_PROPERTIES (totem->properties), + totem->bvw); + + if (name != NULL) { + update_mrl_label (totem, name); + totem_playlist_set_title + (TOTEM_PLAYLIST (totem->playlist), name, TRUE); + g_free (name); + } +} + +static void +on_playlist_change_name (TotemPlaylist *playlist, Totem *totem) +{ + char *name, *artist, *album, *title; + gboolean cur; + + if ((name = totem_playlist_get_current_title (playlist, + &cur)) != NULL) { + update_mrl_label (totem, name); + g_free (name); + } + + if (totem_playlist_get_current_metadata (playlist, &artist, + &title, &album) != FALSE) { + bacon_video_widget_properties_from_metadata ( + BACON_VIDEO_WIDGET_PROPERTIES (totem->properties), + artist, title, album); + + g_free (artist); + g_free (album); + g_free (title); + } +} + +static void +on_got_metadata_event (BaconVideoWidget *bvw, Totem *totem) +{ + char *name = NULL; + + bacon_video_widget_properties_update + (BACON_VIDEO_WIDGET_PROPERTIES (totem->properties), + totem->bvw); + + name = totem_get_nice_name_for_stream (totem); + + if (name != NULL) { + totem_playlist_set_title + (TOTEM_PLAYLIST (totem->playlist), name, FALSE); + g_free (name); + } + on_playlist_change_name + (TOTEM_PLAYLIST (totem->playlist), totem); +} + +static void +on_error_event (BaconVideoWidget *bvw, char *message, + gboolean playback_stopped, gboolean fatal, Totem *totem) +{ + if (playback_stopped) + play_pause_set_label (totem, STATE_STOPPED); + + if (fatal == FALSE) { + totem_action_error (_("An error occurred"), message, totem); + } else { + totem_action_error_and_exit (_("An error occurred"), + message, totem); + } +} + +static void +on_buffering_event (BaconVideoWidget *bvw, int percentage, Totem *totem) +{ + totem_statusbar_push (TOTEM_STATUSBAR (totem->statusbar), percentage); +} + +static void +update_seekable (Totem *totem, gboolean seekable) +{ + if (totem->seekable == seekable) + return; + + totem->seekable = seekable; + + /* Check if the stream is seekable */ + gtk_widget_set_sensitive (totem->seek, seekable); + gtk_widget_set_sensitive (totem->fs_seek, seekable); + + totem_main_set_sensitivity ("tmw_seek_hbox", seekable); + + totem_main_set_sensitivity ("tcw_time_hbox", seekable); + + totem_action_set_sensitivity ("skip-forward", seekable); + totem_action_set_sensitivity ("skip-backwards", seekable); + totem_action_set_sensitivity ("skip-to", seekable); + if (totem->skipto) + totem_skipto_set_seekable (totem->skipto, seekable); +} + +static void +update_current_time (BaconVideoWidget *bvw, + gint64 current_time, + gint64 stream_length, + float current_position, + gboolean seekable, Totem *totem) +{ + update_skip_to (totem, stream_length); + update_seekable (totem, seekable); + + if (totem->seek_lock == FALSE) + { + gtk_adjustment_set_value (totem->seekadj, + current_position * 65535); + gtk_adjustment_set_value (totem->fs_seekadj, + current_position * 65535); + + if (stream_length == 0 && totem->mrl != NULL) + { + totem_statusbar_set_time_and_length + (TOTEM_STATUSBAR (totem->statusbar), + (int) (current_time / 1000), -1); + } else { + totem_statusbar_set_time_and_length + (TOTEM_STATUSBAR (totem->statusbar), + (int) (current_time / 1000), + (int) (stream_length / 1000)); + } + + totem_time_label_set_time + (TOTEM_TIME_LABEL (totem->tcw_time_label), + current_time, stream_length); + bacon_video_widget_properties_from_time + (BACON_VIDEO_WIDGET_PROPERTIES (totem->properties), + stream_length); + } +} + +static gboolean +vol_slider_pressed_cb (GtkWidget *widget, GdkEventButton *event, Totem *totem) +{ + totem->vol_fs_lock = TRUE; + return FALSE; +} + +static gboolean +vol_slider_released_cb (GtkWidget *widget, GdkEventButton *event, Totem *totem) +{ + totem->vol_fs_lock = FALSE; + return FALSE; +} + +static void +update_volume_sliders (Totem *totem) +{ + int volume; + GtkAction *action; + + volume = bacon_video_widget_get_volume (totem->bvw); + + if (totem->volume_first_time || (totem->prev_volume != volume && + totem->prev_volume != -1 && volume != -1)) + { + totem->volume_first_time = 0; + bacon_volume_button_set_value ( + BACON_VOLUME_BUTTON (totem->volume), (float) volume); + gtk_adjustment_set_value (totem->fs_voladj, + (float) volume); + + action = gtk_action_group_get_action (totem->main_action_group, "volume-down"); + gtk_action_set_sensitive (action, volume > 0 && totem->volume_sensitive); + + action = gtk_action_group_get_action (totem->main_action_group, "volume-up"); + gtk_action_set_sensitive (action, volume < 100 && totem->volume_sensitive); + } + + totem->prev_volume = volume; +} + +static void +property_notify_cb (BaconVideoWidget *bvw, GParamSpec *spec, Totem *totem) +{ + if (strcmp ("volume", spec->name) == 0) { + update_volume_sliders (totem); + } else if (strcmp ("logo-mode", spec->name) == 0) { + gboolean enabled; + enabled = bacon_video_widget_get_logo_mode (totem->bvw); + totem_action_zoom (totem, enabled ? ZOOM_DISABLE : ZOOM_ENABLE); + } +} + +static gboolean +seek_slider_pressed_cb (GtkWidget *widget, GdkEventButton *event, Totem *totem) +{ + totem->seek_lock = TRUE; + totem_statusbar_set_seeking (TOTEM_STATUSBAR (totem->statusbar), TRUE); + totem_time_label_set_seeking (TOTEM_TIME_LABEL (totem->tcw_time_label), TRUE); + + return FALSE; +} + +static void +seek_slider_changed_cb (GtkAdjustment *adj, Totem *totem) +{ + double pos; + gint time; + + if (totem->seek_lock == FALSE) + return; + + pos = gtk_adjustment_get_value (adj) / 65535; + time = bacon_video_widget_get_stream_length (totem->bvw); + totem_statusbar_set_time_and_length (TOTEM_STATUSBAR (totem->statusbar), + (int) (pos * time / 1000), time / 1000); + totem_time_label_set_time + (TOTEM_TIME_LABEL (totem->tcw_time_label), + (int) (pos * time), time); + + if (bacon_video_widget_can_direct_seek (totem->bvw) != FALSE) + totem_action_seek (totem, pos); +} + +static gboolean +seek_slider_released_cb (GtkWidget *widget, GdkEventButton *event, Totem *totem) +{ + GtkAdjustment *adj, *other_adj; + gdouble val; + + adj = gtk_range_get_adjustment (GTK_RANGE (widget)); + other_adj = (adj == totem->seekadj) ? totem->fs_seekadj : totem->seekadj; + + /* set to FALSE here to avoid triggering a final seek when + * syncing the adjustments while being in direct seek mode */ + totem->seek_lock = FALSE; + + /* sync both adjustments */ + val = gtk_adjustment_get_value (adj); + gtk_adjustment_set_value (other_adj, val); + + if (bacon_video_widget_can_direct_seek (totem->bvw) == FALSE) + { + totem_action_seek (totem, val / 65535.0); + } + + totem_statusbar_set_seeking (TOTEM_STATUSBAR (totem->statusbar), FALSE); + totem_time_label_set_seeking (TOTEM_TIME_LABEL (totem->tcw_time_label), + FALSE); + return FALSE; +} + +static void +vol_cb (GtkWidget *widget, Totem *totem) +{ + if (totem->vol_lock == FALSE) + { + totem->vol_lock = TRUE; + + if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "fs")) != FALSE) + { + bacon_video_widget_set_volume + (totem->bvw, (gint) totem->fs_voladj->value); + + /* Update the fullscreen volume adjustment */ + bacon_volume_button_set_value ( + BACON_VOLUME_BUTTON (totem->volume), + gtk_adjustment_get_value (totem->fs_voladj)); + } else { + int value = bacon_volume_button_get_value ( + BACON_VOLUME_BUTTON (totem->volume)); + + bacon_video_widget_set_volume (totem->bvw, value); + /* Update the volume adjustment */ + gtk_adjustment_set_value (totem->fs_voladj, value); + } + + totem->vol_lock = FALSE; + } +} + +static gboolean +totem_action_open_files (Totem *totem, char **list) +{ + GSList *slist = NULL; + int i, retval; + + for (i = 0 ; list[i] != NULL; i++) + slist = g_slist_prepend (slist, list[i]); + + slist = g_slist_reverse (slist); + retval = totem_action_open_files_list (totem, slist); + g_slist_free (slist); + + return retval; +} + +static gboolean +totem_action_open_files_list (Totem *totem, GSList *list) +{ + GSList *l; + gboolean changed; + gboolean cleared; + + changed = FALSE; + cleared = FALSE; + + if (list == NULL) + return changed; + + for (l = list ; l != NULL; l = l->next) + { + char *filename; + char *data = l->data; + + if (data == NULL) + continue; + + /* Ignore relatives paths that start with "--", tough luck */ + if (data[0] == '-' && data[1] == '-') + continue; + + /* Get the subtitle part out for our tests */ + filename = totem_create_full_path (data); + + if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) + || strstr (filename, "#") != NULL + || strstr (filename, "://") != NULL + || g_str_has_prefix (filename, "dvd:") != FALSE + || g_str_has_prefix (filename, "vcd:") != FALSE + || g_str_has_prefix (filename, "cdda:") != FALSE + || g_str_has_prefix (filename, "cd:") != FALSE) + { + if (cleared == FALSE) + { + /* The function that calls us knows better + * if we should be doing something with the + * changed playlist ... */ + g_signal_handlers_disconnect_by_func + (G_OBJECT (totem->playlist), + playlist_changed_cb, totem); + changed = totem_playlist_clear (totem->playlist); + bacon_video_widget_close (totem->bvw); + cleared = TRUE; + } + + if (totem_is_block_device (filename) != FALSE) { + totem_action_load_media_device (totem, data); + changed = TRUE; + } else if (g_str_has_prefix (filename, "cdda:/") != FALSE) { + totem_playlist_add_mrl (totem->playlist, data, NULL); + changed = TRUE; + } else if (totem_playlist_add_mrl (totem->playlist, + filename, NULL) != FALSE) { + totem_action_add_recent (totem, filename); + changed = TRUE; + } + } + + g_free (filename); + } + + /* ... and reconnect because we're nice people */ + if (cleared != FALSE) + { + g_signal_connect (G_OBJECT (totem->playlist), + "changed", G_CALLBACK (playlist_changed_cb), + totem); + } + + return changed; +} + +static void +commit_hide_skip_to (GtkDialog *dialog, gint response, Totem *totem) +{ + GError *err = NULL; + + if (response != GTK_RESPONSE_OK) + { + gtk_widget_destroy (GTK_WIDGET (totem->skipto)); + return; + } + + gtk_widget_hide (GTK_WIDGET (dialog)); + + bacon_video_widget_seek_time (totem->bvw, + totem_skipto_get_range (totem->skipto), &err); + + gtk_widget_destroy (GTK_WIDGET (totem->skipto)); + + if (err != NULL) + { + char *msg, *disp; + + disp = totem_uri_escape_for_display (totem->mrl); + msg = g_strdup_printf(_("Totem could not seek in '%s'."), disp); + g_free (disp); + totem_action_stop (totem); + totem_playlist_set_playing (totem->playlist, FALSE); + totem_action_error (msg, err->message, totem); + g_free (msg); + g_error_free (err); + } +} + +void +totem_action_skip_to (Totem *totem) +{ + char *filename; + TotemSkipto **skipto; + + if (totem->seekable == FALSE) + return; + + if (totem->skipto != NULL) + { + gtk_window_present (GTK_WINDOW (totem->skipto)); + return; + } + + filename = totem_interface_get_full_path ("skip_to.glade"); + totem->skipto = TOTEM_SKIPTO (totem_skipto_new (filename)); + g_free (filename); + + g_signal_connect (G_OBJECT (totem->skipto), "delete-event", + G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (G_OBJECT (totem->skipto), "response", + G_CALLBACK (commit_hide_skip_to), totem); + + skipto = &totem->skipto; + g_object_add_weak_pointer (G_OBJECT (totem->skipto), + (gpointer *) skipto); + + gtk_window_set_transient_for (GTK_WINDOW (totem->skipto), + GTK_WINDOW (totem->win)); + gtk_widget_show (GTK_WIDGET (totem->skipto)); +} + +static void +on_fs_exit1_activate (GtkButton *button, Totem *totem) +{ + totem_action_fullscreen_toggle (totem); +} + +void +show_controls (Totem *totem, gboolean was_fullscreen) +{ + GtkAction *action; + GtkWidget *menubar, *controlbar, *statusbar, *bvw_vbox, *widget; + int width = 0, height = 0; + + if (totem->bvw == NULL) + return; + + menubar = glade_xml_get_widget (totem->xml, "tmw_menubar_box"); + controlbar = glade_xml_get_widget (totem->xml, "tmw_controls_vbox"); + statusbar = glade_xml_get_widget (totem->xml, "tmw_statusbar"); + bvw_vbox = glade_xml_get_widget (totem->xml, "tmw_bvw_vbox"); + widget = GTK_WIDGET (totem->bvw); + + action = gtk_action_group_get_action (totem->main_action_group, "show-controls"); + gtk_action_set_sensitive (action, !totem_is_fullscreen (totem)); + + if (totem->controls_visibility == TOTEM_CONTROLS_VISIBLE) + { + if (was_fullscreen == FALSE) + { + height = widget->allocation.height; + width = widget->allocation.width; + } + + gtk_widget_set_sensitive (menubar, TRUE); + gtk_widget_show (menubar); + gtk_widget_show (controlbar); + gtk_widget_show (statusbar); + if (totem_sidebar_is_visible (totem) != FALSE) { + /* This is uglier then you might expect because of the + resize handle between the video and sidebar. There + is no convenience method to get the handle's width. + */ + GValue value = { 0, }; + GtkWidget *pane; + int handle_size; + + g_value_init (&value, G_TYPE_INT); + pane = glade_xml_get_widget (totem->xml, + "tmw_main_pane"); + gtk_widget_style_get_property (pane, "handle-size", + &value); + handle_size = g_value_get_int (&value); + + gtk_widget_show (totem->sidebar); + width += totem->sidebar->allocation.width + + handle_size; + } else { + gtk_widget_hide (totem->sidebar); + } + + gtk_container_set_border_width (GTK_CONTAINER (bvw_vbox), + BVW_VBOX_BORDER_WIDTH); + + if (was_fullscreen == FALSE) + { + height += menubar->allocation.height + + controlbar->allocation.height + + statusbar->allocation.height + + 2 * BVW_VBOX_BORDER_WIDTH; + width += 2 * BVW_VBOX_BORDER_WIDTH; + gtk_window_resize (GTK_WINDOW(totem->win), + width, height); + } + } else { + if (totem->controls_visibility == TOTEM_CONTROLS_HIDDEN) + { + width = widget->allocation.width; + height = widget->allocation.height; + } + + /* Hide and make the menubar unsensitive */ + gtk_widget_set_sensitive (menubar, FALSE); + gtk_widget_hide (menubar); + + gtk_widget_hide (controlbar); + gtk_widget_hide (statusbar); + gtk_widget_hide (totem->sidebar); + + /* We won't show controls in fullscreen */ + gtk_container_set_border_width (GTK_CONTAINER (bvw_vbox), 0); + + if (totem->controls_visibility == TOTEM_CONTROLS_HIDDEN) + { + gtk_window_resize (GTK_WINDOW(totem->win), + width, height); + } + } +} + +void +totem_action_toggle_controls (Totem *totem) +{ + GtkAction *action; + gboolean state; + + if (totem_is_fullscreen (totem) != FALSE) + return; + + action = gtk_action_group_get_action (totem->main_action_group, + "show-controls"); + state = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), !state); +} + +static void +on_volume_mute_button (GtkButton *button, Totem *totem) +{ + totem_action_volume_relative (totem, -100); +} + +static void +on_volume_max_button (GtkButton *button, Totem *totem) +{ + totem_action_volume_relative (totem, 100); +} + +static void +totem_action_remote (Totem *totem, TotemRemoteCommand cmd, const char *url) +{ + gboolean handled = TRUE; + + switch (cmd) { + case TOTEM_REMOTE_COMMAND_PLAY: + totem_action_play (totem); + break; + case TOTEM_REMOTE_COMMAND_PLAYPAUSE: + totem_action_play_pause (totem); + break; + case TOTEM_REMOTE_COMMAND_PAUSE: + totem_action_pause (totem); + break; + case TOTEM_REMOTE_COMMAND_SEEK_FORWARD: + totem_action_seek_relative (totem, SEEK_FORWARD_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_SEEK_BACKWARD: + totem_action_seek_relative (totem, + SEEK_BACKWARD_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_VOLUME_UP: + totem_action_volume_relative (totem, VOLUME_UP_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_VOLUME_DOWN: + totem_action_volume_relative (totem, VOLUME_DOWN_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_NEXT: + totem_action_next (totem); + break; + case TOTEM_REMOTE_COMMAND_PREVIOUS: + totem_action_previous (totem); + break; + case TOTEM_REMOTE_COMMAND_FULLSCREEN: + totem_action_fullscreen_toggle (totem); + break; + case TOTEM_REMOTE_COMMAND_QUIT: + totem_action_exit (totem); + break; + case TOTEM_REMOTE_COMMAND_ENQUEUE: + g_assert (url != NULL); + if (totem_playlist_add_mrl (totem->playlist, url, NULL) != FALSE) { + totem_action_add_recent (totem, url); + } + break; + case TOTEM_REMOTE_COMMAND_REPLACE: + g_assert (url != NULL); + totem_playlist_clear (totem->playlist); + if (strcmp (url, "dvd:") == 0) { + totem_action_play_media (totem, MEDIA_TYPE_DVD); + } else if (strcmp (url, "vcd:") == 0) { + totem_action_play_media (totem, MEDIA_TYPE_VCD); + } else if (g_str_has_prefix (url, "cd:") != FALSE) { + totem_action_play_media (totem, MEDIA_TYPE_CDDA); + } else if (g_str_has_prefix (url, "cdda:/") != FALSE) { + totem_playlist_add_mrl (totem->playlist, url, NULL); + } else if (totem_playlist_add_mrl (totem->playlist, + url, NULL) != FALSE) { + totem_action_add_recent (totem, url); + } + break; + case TOTEM_REMOTE_COMMAND_SHOW: + gtk_window_present (GTK_WINDOW (totem->win)); + break; + case TOTEM_REMOTE_COMMAND_TOGGLE_CONTROLS: + if (totem->controls_visibility != TOTEM_CONTROLS_FULLSCREEN) + { + GtkToggleAction *action; + gboolean state; + + action = GTK_TOGGLE_ACTION (gtk_action_group_get_action + (totem->main_action_group, + "show-controls")); + state = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, !state); + } + break; + case TOTEM_REMOTE_COMMAND_SHOW_PLAYING: + { + char *title; + gboolean custom; + + title = totem_playlist_get_current_title + (totem->playlist, &custom); + bacon_message_connection_send (totem->conn, + title ? title : SHOW_PLAYING_NO_TRACKS); + g_free (title); + } + break; + case TOTEM_REMOTE_COMMAND_UP: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_UP); + break; + case TOTEM_REMOTE_COMMAND_DOWN: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_DOWN); + break; + case TOTEM_REMOTE_COMMAND_LEFT: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_LEFT); + break; + case TOTEM_REMOTE_COMMAND_RIGHT: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_RIGHT); + break; + case TOTEM_REMOTE_COMMAND_SELECT: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_SELECT); + break; + case TOTEM_REMOTE_COMMAND_DVD_MENU: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU); + break; + case TOTEM_REMOTE_COMMAND_ZOOM_UP: + totem_action_zoom_relative (totem, ZOOM_IN_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_ZOOM_DOWN: + totem_action_zoom_relative (totem, ZOOM_OUT_OFFSET); + break; + case TOTEM_REMOTE_COMMAND_EJECT: + totem_action_eject (totem); + break; + case TOTEM_REMOTE_COMMAND_PLAY_DVD: + // TODO - how to see if can, and play the DVD (like the menu item) + break; + case TOTEM_REMOTE_COMMAND_MUTE: + totem_action_volume_relative (totem, -100); + break; + default: + handled = FALSE; + break; + } + + if (handled != FALSE && + gtk_window_is_active (GTK_WINDOW (totem->win))) { + on_video_motion_notify_event (NULL, NULL, totem); + } +} + +#ifdef HAVE_REMOTE +static void +totem_button_pressed_remote_cb (TotemRemote *remote, TotemRemoteCommand cmd, + Totem *totem) +{ + totem_action_remote (totem, cmd, NULL); +} +#endif /* HAVE_REMOTE */ + +static void +playlist_changed_cb (GtkWidget *playlist, Totem *totem) +{ + char *mrl; + + update_buttons (totem); + mrl = totem_playlist_get_current_mrl (totem->playlist); + + if (mrl == NULL) + return; + + //FIXME we shouldn't compare the 2 URLs + // http://bugzilla.gnome.org/show_bug.cgi?id=364311 + if (totem->mrl == NULL + || (totem->mrl != NULL && mrl != NULL + && strcmp (totem->mrl, mrl) != 0)) + { + totem_action_set_mrl_and_play (totem, mrl); + } else if (totem->mrl != NULL) { + totem_playlist_set_playing (totem->playlist, TRUE); + } + + g_free (mrl); +} + +static void +item_activated_cb (GtkWidget *playlist, Totem *totem) +{ + totem_action_seek (totem, 0); +} + +static void +current_removed_cb (GtkWidget *playlist, Totem *totem) +{ + char *mrl; + + /* Set play button status */ + play_pause_set_label (totem, STATE_STOPPED); + mrl = totem_playlist_get_current_mrl (totem->playlist); + + if (mrl == NULL) + { + totem_playlist_set_at_start (totem->playlist); + update_buttons (totem); + mrl = totem_playlist_get_current_mrl (totem->playlist); + } else { + update_buttons (totem); + } + + totem_action_set_mrl_and_play (totem, mrl); + g_free (mrl); +} + +static void +playlist_repeat_toggle_cb (TotemPlaylist *playlist, gboolean repeat, Totem *totem) +{ + GtkAction *action; + + action = gtk_action_group_get_action (totem->main_action_group, "repeat-mode"); + + g_signal_handlers_block_matched (G_OBJECT (action), G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, totem); + + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), repeat); + + g_signal_handlers_unblock_matched (G_OBJECT (action), G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, totem); +} + +static void +playlist_shuffle_toggle_cb (TotemPlaylist *playlist, gboolean shuffle, Totem *totem) +{ + GtkAction *action; + + action = gtk_action_group_get_action (totem->main_action_group, "shuffle-mode"); + + g_signal_handlers_block_matched (G_OBJECT (action), G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, totem); + + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), shuffle); + + g_signal_handlers_unblock_matched (G_OBJECT (action), G_SIGNAL_MATCH_DATA, 0, 0, + NULL, NULL, totem); +} + +static void +update_fullscreen_size (Totem *totem) +{ + GdkScreen *screen; + + screen = gtk_window_get_screen (GTK_WINDOW (totem->win)); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_window + (screen, totem->win->window), + &totem->fullscreen_rect); +} + +gboolean +totem_is_fullscreen (Totem *totem) +{ + return (totem->controls_visibility == TOTEM_CONTROLS_FULLSCREEN); +} + +static void +move_popups (Totem *totem) +{ + int control_width, control_height; + int exit_width, exit_height; + + update_fullscreen_size (totem); + + gtk_window_get_size (GTK_WINDOW (totem->control_popup), + &control_width, &control_height); + gtk_window_get_size (GTK_WINDOW (totem->exit_popup), + &exit_width, &exit_height); + + /* We take the full width of the screen */ + gtk_window_resize (GTK_WINDOW (totem->control_popup), + totem->fullscreen_rect.width, + control_height); + + if (gtk_widget_get_direction (totem->exit_popup) == GTK_TEXT_DIR_RTL) + { + gtk_window_move (GTK_WINDOW (totem->exit_popup), + totem->fullscreen_rect.x, + totem->fullscreen_rect.y); + gtk_window_move (GTK_WINDOW (totem->control_popup), + totem->fullscreen_rect.width - control_width, + totem->fullscreen_rect.height + totem->fullscreen_rect.y - control_height); + } else { + gtk_window_move (GTK_WINDOW (totem->exit_popup), + totem->fullscreen_rect.width + totem->fullscreen_rect.x - exit_width, + totem->fullscreen_rect.y); + gtk_window_move (GTK_WINDOW (totem->control_popup), + totem->fullscreen_rect.x, + totem->fullscreen_rect.height + totem->fullscreen_rect.y - control_height); + } +} + +static void +size_changed_cb (GdkScreen *screen, Totem *totem) +{ + move_popups (totem); +} + +static void +theme_changed_cb (GtkIconTheme *icon_theme, Totem *totem) +{ + move_popups (totem); +} + +static void +window_realize_cb (GtkWidget *widget, Totem *totem) +{ + GdkScreen *screen; + + screen = gtk_widget_get_screen (widget); + g_signal_connect (G_OBJECT (screen), "size-changed", + G_CALLBACK (size_changed_cb), totem); + g_signal_connect (G_OBJECT (gtk_icon_theme_get_for_screen (screen)), + "changed", G_CALLBACK (theme_changed_cb), totem); +} + +static void +popup_timeout_add(Totem* totem) +{ + totem->popup_timeout = g_timeout_add (FULLSCREEN_POPUP_TIMEOUT, + (GSourceFunc) popup_hide, totem); +} + +static void +popup_timeout_remove (Totem *totem) +{ + if (totem->popup_timeout != 0) + { + g_source_remove (totem->popup_timeout); + totem->popup_timeout = 0; + } +} + +static gboolean +popup_hide (Totem *totem) +{ + if (totem->bvw == NULL || totem_is_fullscreen (totem) == FALSE) + return TRUE; + + if (totem->seek_lock != FALSE || totem->vol_fs_lock != FALSE) + return TRUE; + + gtk_widget_hide (GTK_WIDGET (totem->exit_popup)); + gtk_widget_hide (GTK_WIDGET (totem->control_popup)); + + popup_timeout_remove (totem); + + totem_action_set_cursor (totem, FALSE); + + return FALSE; +} + +static void +on_mouse_click_fullscreen (GtkWidget *widget, Totem *totem) +{ + popup_timeout_remove (totem); + popup_timeout_add (totem); +} + +static gboolean +on_video_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, + Totem *totem) +{ + GtkWidget *item; + + if (totem_is_fullscreen (totem) == FALSE) + return FALSE; + + if (totem->popup_in_progress != FALSE) + return FALSE; + + totem->popup_in_progress = TRUE; + + popup_timeout_remove (totem); + + item = glade_xml_get_widget (totem->xml, "tcw_hbox"); + gtk_widget_show_all (item); + gdk_flush (); + + move_popups (totem); + + gtk_widget_show_all (totem->exit_popup); + gtk_widget_show_all (totem->control_popup); + totem_action_set_cursor (totem, TRUE); + + popup_timeout_add (totem); + totem->popup_in_progress = FALSE; + + return FALSE; +} + +static gboolean +on_video_button_press_event (BaconVideoWidget *bvw, GdkEventButton *event, + Totem *totem) +{ + if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { + totem_action_fullscreen_toggle(totem); + return TRUE; + } else if (event->type == GDK_BUTTON_PRESS && event->button == 2) { + totem_action_play_pause(totem); + return TRUE; + } else if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + totem_action_menu_popup (totem, event->button); + return TRUE; + } + + return FALSE; +} + +static gboolean +on_eos_event (GtkWidget *widget, Totem *totem) +{ + if (bacon_video_widget_get_logo_mode (totem->bvw) != FALSE) + return FALSE; + + if (totem_playlist_has_next_mrl (totem->playlist) == FALSE + && totem_playlist_get_repeat (totem->playlist) == FALSE) + { + char *mrl; + + /* Set play button status */ + play_pause_set_label (totem, STATE_PAUSED); + totem_playlist_set_at_start (totem->playlist); + update_buttons (totem); + mrl = totem_playlist_get_current_mrl (totem->playlist); + totem_action_stop (totem); + totem_action_set_mrl_with_warning (totem, mrl, FALSE); + bacon_video_widget_pause (totem->bvw); + g_free (mrl); + } else { + totem_action_next (totem); + } + + return FALSE; +} + +static gboolean +totem_action_handle_key_release (Totem *totem, GdkEventKey *event) +{ + gboolean retval = TRUE; + + switch (event->keyval) { + case GDK_Left: + case GDK_Right: + totem_statusbar_set_seeking + (TOTEM_STATUSBAR (totem->statusbar), FALSE); + break; + } + + return retval; +} + +static void +totem_action_handle_seek (Totem *totem, GdkEventKey *event, gboolean is_forward) +{ + if (is_forward != FALSE) { + if (event->state & GDK_SHIFT_MASK) { + totem_action_seek_relative (totem, + SEEK_FORWARD_SHORT_OFFSET); + } else if (event->state & GDK_CONTROL_MASK) { + totem_action_seek_relative (totem, + SEEK_FORWARD_LONG_OFFSET); + } else { + totem_action_seek_relative (totem, + SEEK_FORWARD_OFFSET); + } + } else { + if (event->state & GDK_SHIFT_MASK) { + totem_action_seek_relative (totem, + SEEK_BACKWARD_SHORT_OFFSET); + } else if (event->state & GDK_CONTROL_MASK) { + totem_action_seek_relative (totem, + SEEK_BACKWARD_LONG_OFFSET); + } else { + totem_action_seek_relative (totem, + SEEK_BACKWARD_OFFSET); + } + } +} + +static gboolean +totem_action_handle_key_press (Totem *totem, GdkEventKey *event) +{ + gboolean retval = TRUE, playlist_focused = FALSE; + GtkWidget *focused; + + focused = gtk_window_get_focus (GTK_WINDOW (totem->win)); + if (focused != NULL && gtk_widget_is_ancestor + (focused, GTK_WIDGET (totem->playlist)) != FALSE) { + playlist_focused = TRUE; + } + + switch (event->keyval) { + case GDK_A: + case GDK_a: + totem_action_toggle_aspect_ratio (totem); + break; +#ifdef HAVE_XFREE + case XF86XK_AudioPrev: +#endif /* HAVE_XFREE */ + case GDK_B: + case GDK_b: + totem_action_previous (totem); + break; + case GDK_C: + case GDK_c: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_CHAPTER_MENU); + break; +#ifndef HAVE_GTK_ONLY + case GDK_D: + case GDK_d: + totem_gromit_toggle (); + break; + case GDK_E: + case GDK_e: + totem_gromit_clear (FALSE); + break; +#endif /* !HAVE_GTK_ONLY */ + case GDK_F11: + case GDK_f: + case GDK_F: + totem_action_fullscreen_toggle (totem); + break; + case GDK_g: + case GDK_G: + if (totem_playing_dvd (totem->mrl) != FALSE) { + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_NEXT_ANGLE); + } + break; + case GDK_h: + case GDK_H: + totem_action_toggle_controls (totem); + break; + case GDK_i: + case GDK_I: + { + GtkToggleAction *action; + gboolean state; + + action = GTK_TOGGLE_ACTION (gtk_action_group_get_action + (totem->main_action_group, + "deinterlace")); + state = gtk_toggle_action_get_active (action); + gtk_toggle_action_set_active (action, !state); + } + break; + case GDK_M: + case GDK_m: + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_ROOT_MENU); + break; +#ifdef HAVE_XFREE + case XF86XK_AudioNext: +#endif /* HAVE_XFREE */ + case GDK_N: + case GDK_n: + totem_action_next (totem); + break; + case GDK_O: + case GDK_o: + totem_action_fullscreen (totem, FALSE); + totem_action_open (totem); + break; +#ifdef HAVE_XFREE + case XF86XK_AudioPlay: + case XF86XK_AudioPause: +#endif /* HAVE_XFREE */ + case GDK_p: + case GDK_P: + if (event->state & GDK_CONTROL_MASK) + totem_action_show_properties (totem); + else + totem_action_play_pause (totem); + break; + case GDK_q: + case GDK_Q: + totem_action_exit (totem); + break; + case GDK_r: + case GDK_R: + totem_action_zoom_relative (totem, ZOOM_IN_OFFSET); + break; + case GDK_s: + case GDK_S: + if (event->state & GDK_CONTROL_MASK) { + totem_action_take_screenshot (totem); + } else { + totem_action_skip_to (totem); + } + break; + case GDK_t: + case GDK_T: + totem_action_zoom_relative (totem, ZOOM_OUT_OFFSET); + break; + case GDK_Escape: + if (event->state & GDK_SUPER_MASK) + bacon_video_widget_dvd_event (totem->bvw, BVW_DVD_ROOT_MENU); + else + totem_action_fullscreen (totem, FALSE); + break; + case GDK_Left: + if (playlist_focused != FALSE) + return FALSE; + + totem_statusbar_set_seeking + (TOTEM_STATUSBAR (totem->statusbar), TRUE); + if (gtk_widget_get_direction (totem->win) == GTK_TEXT_DIR_RTL) + totem_action_handle_seek (totem, event, TRUE); + else + totem_action_handle_seek (totem, event, FALSE); + break; + case GDK_Right: + if (playlist_focused != FALSE) + return FALSE; + + totem_statusbar_set_seeking + (TOTEM_STATUSBAR (totem->statusbar), TRUE); + if (gtk_widget_get_direction (totem->win) == GTK_TEXT_DIR_RTL) + totem_action_handle_seek (totem, event, FALSE); + else + totem_action_handle_seek (totem, event, TRUE); + break; + case GDK_space: + if (totem_is_fullscreen (totem) != FALSE || gtk_widget_is_focus (GTK_WIDGET (totem->bvw)) != FALSE) + totem_action_play_pause (totem); + else + retval = FALSE; + break; + case GDK_Up: + if (playlist_focused != FALSE) + return FALSE; + totem_action_volume_relative (totem, VOLUME_UP_OFFSET); + break; + case GDK_Down: + if (playlist_focused != FALSE) + return FALSE; + totem_action_volume_relative (totem, VOLUME_DOWN_OFFSET); + break; + case GDK_0: + if (event->state & GDK_CONTROL_MASK) + totem_action_zoom_reset (totem); + else + totem_action_set_scale_ratio (totem, 0.5); + break; + case GDK_onehalf: + totem_action_set_scale_ratio (totem, 0.5); + break; + case GDK_1: + totem_action_set_scale_ratio (totem, 1); + break; + case GDK_2: + totem_action_set_scale_ratio (totem, 2); + break; + case GDK_Menu: + if (playlist_focused != FALSE) + return FALSE; + totem_action_menu_popup (totem, 0); + break; + case GDK_F10: + if (playlist_focused != FALSE) + return FALSE; + if (!(event->state & GDK_SHIFT_MASK)) + return FALSE; + + totem_action_menu_popup (totem, 0); + break; + case GDK_plus: + case GDK_KP_Add: + if (!(event->state & GDK_CONTROL_MASK)) + return FALSE; + + totem_action_zoom_relative (totem, ZOOM_IN_OFFSET); + break; + case GDK_minus: + case GDK_KP_Subtract: + if (!(event->state & GDK_CONTROL_MASK)) + return FALSE; + + totem_action_zoom_relative (totem, ZOOM_OUT_OFFSET); + break; + case GDK_KP_Up: + case GDK_KP_8: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_UP); + break; + case GDK_KP_Down: + case GDK_KP_2: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_DOWN); + break; + case GDK_KP_Right: + case GDK_KP_6: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_RIGHT); + break; + case GDK_KP_Left: + case GDK_KP_4: + bacon_video_widget_dvd_event (totem->bvw, + BVW_DVD_ROOT_MENU_LEFT); + break; + default: + retval = FALSE; + } + + return retval; +} + +static gboolean +totem_action_handle_scroll (Totem *totem, GdkScrollDirection direction) +{ + gboolean retval = TRUE; + + on_video_motion_notify_event (NULL, NULL, totem); + + switch (direction) { + case GDK_SCROLL_UP: + totem_action_seek_relative + (totem, SEEK_FORWARD_SHORT_OFFSET); + break; + case GDK_SCROLL_DOWN: + totem_action_seek_relative + (totem, SEEK_BACKWARD_SHORT_OFFSET); + break; + default: + retval = FALSE; + } + + return retval; +} + +static gboolean +totem_action_handle_volume_scroll (Totem *totem, GdkScrollDirection direction) +{ + gboolean retval = TRUE; + + on_video_motion_notify_event (NULL, NULL, totem); + + switch (direction) { + case GDK_SCROLL_UP: + totem_action_volume_relative (totem, VOLUME_UP_OFFSET); + break; + case GDK_SCROLL_DOWN: + totem_action_volume_relative (totem, VOLUME_DOWN_OFFSET); + break; + default: + retval = FALSE; + } + + return retval; +} + +static int +on_window_key_press_event (GtkWidget *win, GdkEventKey *event, Totem *totem) +{ + /* Special case Eject, Open, Open URI and + * seeking keyboard shortcuts */ + if (event->state != 0 + && (event->state & GDK_CONTROL_MASK)) + { + switch (event->keyval) + case GDK_E: + case GDK_e: + case GDK_O: + case GDK_o: + case GDK_L: + case GDK_l: + case GDK_q: + case GDK_Q: + case GDK_S: + case GDK_s: + case GDK_Right: + case GDK_Left: + case GDK_plus: + case GDK_KP_Add: + case GDK_minus: + case GDK_KP_Subtract: + case GDK_0: + if (event->type == GDK_KEY_PRESS) { + return totem_action_handle_key_press (totem, event); + } else { + return totem_action_handle_key_release (totem, event); + } + } + + if (event->state != 0 + && (event->state & GDK_SUPER_MASK)) { + switch (event->keyval) + case GDK_Escape: + if (event->type == GDK_KEY_PRESS) { + return totem_action_handle_key_press (totem, event); + } else { + return totem_action_handle_key_release (totem, event); + } + } + + + /* 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->type == GDK_KEY_PRESS) { + return totem_action_handle_key_press (totem, event); + } else { + return totem_action_handle_key_release (totem, event); + } +} + +static int +on_window_scroll_event (GtkWidget *win, GdkEventScroll *event, Totem *totem) +{ + return totem_action_handle_scroll (totem, event->direction); +} + +static int +on_volume_scroll_event (GtkWidget *win, GdkEventScroll *event, Totem *totem) +{ + return totem_action_handle_volume_scroll (totem, event->direction); +} + +#ifdef HAVE_MEDIA_PLAYER_KEYS +static gboolean +on_window_focus_in_event (GtkWidget *win, GdkEventFocus *event, Totem *totem) +{ + if (totem->remote != NULL) { + totem_remote_window_activated (totem->remote); + } + + return FALSE; +} +#endif + +static void +update_media_menu_items (Totem *totem) +{ + gboolean playing; + + playing = totem_playing_dvd (totem->mrl); + + totem_action_set_sensitivity ("dvd-root-menu", playing); + totem_action_set_sensitivity ("dvd-title-menu", playing); + totem_action_set_sensitivity ("dvd-audio-menu", playing); + totem_action_set_sensitivity ("dvd-angle-menu", playing); + totem_action_set_sensitivity ("dvd-chapter-menu", playing); + /* FIXME we should only show that if we have multiple angles */ + totem_action_set_sensitivity ("next-angle", playing); + + playing = totem_is_media (totem->mrl); + totem_action_set_sensitivity ("eject", playing); +} + +static void +update_buttons (Totem *totem) +{ + gboolean has_item; + + /* Previous */ + if (totem_playing_dvd (totem->mrl) != FALSE) + has_item = bacon_video_widget_has_previous_track (totem->bvw); + else + has_item = totem_playlist_has_previous_mrl (totem->playlist); + + totem_action_set_sensitivity ("previous-chapter", has_item); + + /* Next */ + if (totem_playing_dvd (totem->mrl) != FALSE) + has_item = bacon_video_widget_has_next_track (totem->bvw); + else + has_item = totem_playlist_has_next_mrl (totem->playlist); + + totem_action_set_sensitivity ("next-chapter", has_item); +} + +static void +main_pane_size_allocated (GtkWidget *main_pane, GtkAllocation *allocation, Totem *totem) +{ + gulong handler_id; + + if (!totem->maximised || GTK_WIDGET_MAPPED (totem->win)) { + handler_id = g_signal_handler_find (main_pane, + G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + main_pane_size_allocated, totem); + g_signal_handler_disconnect (main_pane, handler_id); + + gtk_paned_set_position (GTK_PANED (main_pane), allocation->width - totem->sidebar_w); + } +} + +static void +totem_setup_window (Totem *totem) +{ + GKeyFile *keyfile; + int w, h; + gboolean show_sidebar; + char *filename, *page_id; + GError *err = NULL; + GtkWidget *main_pane; + + filename = g_build_filename (g_get_home_dir (), ".gnome2", "totem", NULL); + keyfile = g_key_file_new (); + if (g_key_file_load_from_file (keyfile, filename, + G_KEY_FILE_NONE, NULL) == FALSE) { + totem->sidebar_w = w = h = 0; + show_sidebar = TRUE; + page_id = NULL; + g_free (filename); + } else { + g_free (filename); + + w = g_key_file_get_integer (keyfile, "State", "window_w", &err); + if (err != NULL) { + w = 0; + g_error_free (err); + err = NULL; + } + + h = g_key_file_get_integer (keyfile, "State", "window_h", &err); + if (err != NULL) { + h = 0; + g_error_free (err); + err = NULL; + } + + show_sidebar = g_key_file_get_boolean (keyfile, "State", + "show_sidebar", &err); + if (err != NULL) { + show_sidebar = TRUE; + g_error_free (err); + err = NULL; + } + + totem->maximised = g_key_file_get_boolean (keyfile, "State", + "maximised", &err); + if (err != NULL) { + g_error_free (err); + err = NULL; + } + + page_id = g_key_file_get_string (keyfile, "State", + "sidebar_page", &err); + if (err != NULL) { + g_error_free (err); + page_id = NULL; + err = NULL; + } + + totem->sidebar_w = g_key_file_get_integer (keyfile, "State", + "sidebar_w", &err); + if (err != NULL) { + g_error_free (err); + totem->sidebar_w = 0; + } + g_key_file_free (keyfile); + } + + if (w > 0 && h > 0 && totem->maximised == FALSE) { + gtk_window_set_default_size (GTK_WINDOW (totem->win), + w, h); + totem->window_w = w; + totem->window_h = h; + } else if (totem->maximised != FALSE) { + gtk_window_maximize (GTK_WINDOW (totem->win)); + } + + main_pane = glade_xml_get_widget (totem->xml, "tmw_main_pane"); + g_signal_connect (G_OBJECT (main_pane), "size-allocate", G_CALLBACK (main_pane_size_allocated), totem); + + totem_sidebar_setup (totem, show_sidebar, page_id); +} + +static void +totem_callback_connect (Totem *totem) +{ + GtkWidget *item, *box, *arrow; + GtkAction *action; + + /* Menu items */ + gtk_action_group_set_visible (totem->zoom_action_group, + bacon_video_widget_can_set_zoom (totem->bvw)); + + action = gtk_action_group_get_action (totem->main_action_group, "repeat-mode"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + totem_playlist_get_repeat (totem->playlist)); + action = gtk_action_group_get_action (totem->main_action_group, "shuffle-mode"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + totem_playlist_get_shuffle (totem->playlist)); + + /* Controls */ + box = glade_xml_get_widget (totem->xml, "tmw_buttons_hbox"); + + action = gtk_action_group_get_action (totem->main_action_group, + "previous-chapter"); + item = gtk_action_create_tool_item (action); + atk_object_set_name (gtk_widget_get_accessible (item), + _("Previous Chapter/Movie")); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + + action = gtk_action_group_get_action (totem->main_action_group, "play"); + item = gtk_action_create_tool_item (action); + atk_object_set_name (gtk_widget_get_accessible (item), + _("Play / Pause")); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + + action = gtk_action_group_get_action (totem->main_action_group, + "next-chapter"); + item = gtk_action_create_tool_item (action); + atk_object_set_name (gtk_widget_get_accessible (item), + _("Next Chapter/Movie")); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + + /* Sidebar button (Drag'n'Drop) */ + box = glade_xml_get_widget (totem->xml, "tmw_sidebar_button_hbox"); + action = gtk_action_group_get_action (totem->main_action_group, "sidebar"); + item = gtk_toggle_button_new (); + gtk_action_connect_proxy (action, item); + arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE); + gtk_widget_show (arrow); + gtk_button_set_image (GTK_BUTTON (item), arrow); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (item), "drag_data_received", + G_CALLBACK (drop_playlist_cb), totem); + gtk_drag_dest_set (item, GTK_DEST_DEFAULT_ALL, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + /* Main Window */ + g_signal_connect (G_OBJECT (totem->win), "delete-event", + G_CALLBACK (main_window_destroy_cb), totem); + g_object_notify (G_OBJECT (totem->win), "is-active"); + g_signal_connect_swapped (G_OBJECT (totem->win), "notify", + G_CALLBACK (popup_hide), totem); + g_signal_connect (G_OBJECT (totem->win), "window-state-event", + G_CALLBACK (window_state_event_cb), totem); + + /* Screen size and Theme changes */ + g_signal_connect (totem->win, "realize", + G_CALLBACK (window_realize_cb), totem); + + /* Motion notify for the Popups */ + item = glade_xml_get_widget (totem->xml, + "totem_exit_fullscreen_window"); + gtk_widget_add_events (item, GDK_POINTER_MOTION_MASK); + g_signal_connect (G_OBJECT (item), "motion-notify-event", + G_CALLBACK (on_video_motion_notify_event), totem); + item = glade_xml_get_widget (totem->xml, "totem_controls_window"); + gtk_widget_add_events (item, GDK_POINTER_MOTION_MASK); + g_signal_connect (G_OBJECT (item), "motion-notify-event", + G_CALLBACK (on_video_motion_notify_event), totem); + + /* Popup */ + item = glade_xml_get_widget (totem->xml, "tefw_fs_exit_button"); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_fs_exit1_activate), totem); + g_signal_connect (G_OBJECT (item), "motion-notify-event", + G_CALLBACK (on_video_motion_notify_event), totem); + + /* Control Popup */ + box = glade_xml_get_widget (totem->xml, "tcw_buttons_hbox"); + + action = gtk_action_group_get_action (totem->main_action_group, "previous-chapter"); + item = gtk_action_create_tool_item (action); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_mouse_click_fullscreen), totem); + + action = gtk_action_group_get_action (totem->main_action_group, "play"); + item = gtk_action_create_tool_item (action); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_mouse_click_fullscreen), totem); + + action = gtk_action_group_get_action (totem->main_action_group, "next-chapter"); + item = gtk_action_create_tool_item (action); + gtk_box_pack_start (GTK_BOX (box), item, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_mouse_click_fullscreen), totem); + + + item = glade_xml_get_widget (totem->xml, "tcw_volume_mute_button"); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_volume_mute_button), totem); + item = glade_xml_get_widget (totem->xml, "tcw_volume_max_button"); + g_signal_connect (G_OBJECT (item), "clicked", + G_CALLBACK (on_volume_max_button), totem); + + /* Control Popup Sliders */ + g_signal_connect (G_OBJECT(totem->fs_seek), "button_press_event", + G_CALLBACK (seek_slider_pressed_cb), totem); + g_signal_connect (G_OBJECT(totem->fs_seek), "button_release_event", + G_CALLBACK (seek_slider_released_cb), totem); + g_signal_connect (G_OBJECT (totem->fs_seekadj), "value-changed", + G_CALLBACK (seek_slider_changed_cb), totem); + g_signal_connect (G_OBJECT(totem->fs_volume), "value-changed", + G_CALLBACK (vol_cb), totem); + g_signal_connect (G_OBJECT(totem->fs_volume), "button_press_event", + G_CALLBACK (vol_slider_pressed_cb), totem); + g_signal_connect (G_OBJECT(totem->fs_volume), "button_release_event", + G_CALLBACK (vol_slider_released_cb), totem); + + /* Connect the keys */ + gtk_widget_add_events (totem->win, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + g_signal_connect (G_OBJECT(totem->win), "key_press_event", + G_CALLBACK (on_window_key_press_event), totem); + g_signal_connect (G_OBJECT(totem->win), "key_release_event", + G_CALLBACK (on_window_key_press_event), totem); + + /* Connect the mouse wheel */ + gtk_widget_add_events (totem->win, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT(totem->win), "scroll_event", + G_CALLBACK (on_window_scroll_event), totem); + gtk_widget_add_events (totem->seek, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT (totem->seek), "scroll_event", + G_CALLBACK (on_window_scroll_event), totem); + gtk_widget_add_events (totem->fs_seek, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT (totem->fs_seek), "scroll_event", + G_CALLBACK (on_window_scroll_event), totem); + gtk_widget_add_events (totem->volume, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT (totem->volume), "scroll_event", + G_CALLBACK (on_volume_scroll_event), totem); + gtk_widget_add_events (totem->fs_volume, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT (totem->fs_volume), "scroll_event", + G_CALLBACK (on_volume_scroll_event), totem); + + /* Sliders */ + g_signal_connect (G_OBJECT (totem->seek), "button_press_event", + G_CALLBACK (seek_slider_pressed_cb), totem); + g_signal_connect (G_OBJECT (totem->seek), "button_release_event", + G_CALLBACK (seek_slider_released_cb), totem); + g_signal_connect (G_OBJECT (totem->seekadj), "value_changed", + G_CALLBACK (seek_slider_changed_cb), totem); + g_signal_connect (G_OBJECT (totem->volume), "value-changed", + G_CALLBACK (vol_cb), totem); + + /* Set sensitivity of the toolbar buttons */ + totem_action_set_sensitivity ("play", FALSE); + totem_action_set_sensitivity ("next-chapter", FALSE); + totem_action_set_sensitivity ("previous-chapter", FALSE); + +#ifdef HAVE_MEDIA_PLAYER_KEYS + g_signal_connect (G_OBJECT(totem->win), "focus-in-event", + G_CALLBACK (on_window_focus_in_event), totem); +#endif +} + +static void +playlist_widget_setup (Totem *totem) +{ + totem->playlist = TOTEM_PLAYLIST (totem_playlist_new ()); + + if (totem->playlist == NULL) + totem_action_exit (totem); + + gtk_widget_show_all (GTK_WIDGET (totem->playlist)); + + g_signal_connect (G_OBJECT (totem->playlist), "active-name-changed", + G_CALLBACK (on_playlist_change_name), totem); + g_signal_connect (G_OBJECT (totem->playlist), "item-activated", + G_CALLBACK (item_activated_cb), totem); + g_signal_connect (G_OBJECT (totem->playlist), + "changed", G_CALLBACK (playlist_changed_cb), + totem); + g_signal_connect (G_OBJECT (totem->playlist), + "current-removed", G_CALLBACK (current_removed_cb), + totem); + g_signal_connect (G_OBJECT (totem->playlist), + "repeat-toggled", + G_CALLBACK (playlist_repeat_toggle_cb), + totem); + g_signal_connect (G_OBJECT (totem->playlist), + "shuffle-toggled", + G_CALLBACK (playlist_shuffle_toggle_cb), + totem); + +} + +static void +video_widget_create (Totem *totem) +{ + GError *err = NULL; + GtkWidget *container; + BaconVideoWidget **bvw; + + totem->scr = totem_scrsaver_new (); + + totem->bvw = BACON_VIDEO_WIDGET + (bacon_video_widget_new (-1, -1, BVW_USE_TYPE_VIDEO, &err)); + + if (totem->bvw == NULL) + { + totem_playlist_set_playing (totem->playlist, FALSE); + + gtk_widget_hide (totem->win); + + totem_action_error_and_exit (_("Totem could not startup."), err != NULL ? err->message : _("No reason."), totem); + if (err != NULL) + g_error_free (err); + } + + totem_preferences_tvout_setup (totem); + totem_preferences_visuals_setup (totem); + totem_action_zoom (totem, ZOOM_RESET); + + g_signal_connect (G_OBJECT (totem->bvw), + "motion-notify-event", + G_CALLBACK (on_video_motion_notify_event), + totem); + g_signal_connect_after (G_OBJECT (totem->bvw), + "button-press-event", + G_CALLBACK (on_video_button_press_event), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "eos", + G_CALLBACK (on_eos_event), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "got-redirect", + G_CALLBACK (on_got_redirect), + totem); + g_signal_connect (G_OBJECT(totem->bvw), + "title-change", + G_CALLBACK (on_title_change_event), + totem); + g_signal_connect (G_OBJECT(totem->bvw), + "channels-change", + G_CALLBACK (on_channels_change_event), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "tick", + G_CALLBACK (update_current_time), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "got-metadata", + G_CALLBACK (on_got_metadata_event), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "buffering", + G_CALLBACK (on_buffering_event), + totem); + g_signal_connect (G_OBJECT (totem->bvw), + "error", + G_CALLBACK (on_error_event), + totem); + + totem_missing_plugins_setup (totem); + + container = glade_xml_get_widget (totem->xml, "tmw_bvw_vbox"); + gtk_container_add (GTK_CONTAINER (container), + GTK_WIDGET (totem->bvw)); + + /* Events for the widget video window as well */ + gtk_widget_add_events (GTK_WIDGET (totem->bvw), + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + g_signal_connect (G_OBJECT(totem->bvw), "key_press_event", + G_CALLBACK (on_window_key_press_event), totem); + g_signal_connect (G_OBJECT(totem->bvw), "key_release_event", + G_CALLBACK (on_window_key_press_event), totem); + + g_signal_connect (G_OBJECT (totem->bvw), "drag_data_received", + G_CALLBACK (drop_video_cb), totem); + gtk_drag_dest_set (GTK_WIDGET (totem->bvw), GTK_DEST_DEFAULT_ALL, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + g_signal_connect (G_OBJECT (totem->bvw), "drag_data_get", + G_CALLBACK (drag_video_cb), totem); + gtk_drag_source_set (GTK_WIDGET (totem->bvw), + GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, + source_table, G_N_ELEMENTS (source_table), + GDK_ACTION_LINK); + + bvw = &(totem->bvw); + g_object_add_weak_pointer (G_OBJECT (totem->bvw), + (gpointer *) bvw); + + gtk_widget_show (GTK_WIDGET (totem->bvw)); + + bacon_video_widget_set_volume (totem->bvw, + gconf_client_get_int (totem->gc, + GCONF_PREFIX"/volume", NULL)); + g_signal_connect (G_OBJECT (totem->bvw), "notify", + G_CALLBACK (property_notify_cb), totem); + update_volume_sliders (totem); +} + +static void +totem_message_connection_receive_cb (const char *msg, Totem *totem) +{ + char *command_str, *url; + int command; + + if (strlen (msg) < 4) + return; + + command_str = g_strndup (msg, 3); + sscanf (command_str, "%d", &command); + g_free (command_str); + + if (msg[4] != '\0') + url = g_strdup (msg + 4); + else + url = NULL; + + totem_action_remote (totem, command, url); + + g_free (url); +} + +GtkWidget * +totem_volume_create (void) +{ + GtkWidget *widget; + + widget = bacon_volume_button_new (GTK_ICON_SIZE_SMALL_TOOLBAR, + 0, 100, 1); + gtk_widget_set_sensitive (widget, FALSE); + gtk_widget_show (widget); + + return widget; +} + +int +main (int argc, char **argv) +{ + Totem *totem; + GConfClient *gc; +#ifndef HAVE_GTK_ONLY + GnomeProgram *program; +#else + GError *error = NULL; +#endif + GOptionContext *context; + GOptionGroup *baconoptiongroup; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + +#ifdef GDK_WINDOWING_X11 + if (XInitThreads () == 0) + { + gtk_init (&argc, &argv); + g_set_application_name (_("Totem Movie Player")); + totem_action_error_and_exit (_("Could not initialize the thread-safe libraries."), _("Verify your system installation. Totem will now exit."), NULL); + } +#endif + + g_thread_init (NULL); + + /* Handle command line arguments */ + context = g_option_context_new (_("- Play movies and songs")); + baconoptiongroup = bacon_video_widget_get_option_group(); + g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE); + g_option_context_add_group (context, baconoptiongroup); + +#ifdef HAVE_GTK_ONLY + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) { + totem_action_error_and_exit (_("Totem could not parse the command-line options"), error->message, NULL); + } +#else + program = gnome_program_init (PACKAGE, VERSION, + LIBGNOMEUI_MODULE, + argc, argv, + GNOME_PARAM_APP_DATADIR, DATADIR, + GNOME_PARAM_GOPTION_CONTEXT, context, + GNOME_PARAM_NONE); +#endif /* HAVE_GTK_ONLY */ + + g_set_application_name (_("Totem Movie Player")); + gtk_window_set_default_icon_name ("totem"); + + gnome_vfs_init (); + + gc = gconf_client_get_default (); + if (gc == NULL) + { + totem_action_error_and_exit (_("Totem could not initialize the configuration engine."), _("Make sure that GNOME is properly installed."), NULL); + } + +#ifndef HAVE_GTK_ONLY + gnome_authentication_manager_init (); +#endif /* !HAVE_GTK_ONLY */ + + totem = g_new0 (Totem, 1); + + /* IPC stuff */ + totem->conn = bacon_message_connection_new (GETTEXT_PACKAGE); + if (bacon_message_connection_get_is_server (totem->conn) == FALSE) + { + totem_options_process_for_server (totem->conn, &optionstate); + bacon_message_connection_free (totem->conn); + g_free (totem); + gdk_notify_startup_complete (); + exit (0); + } else { + totem_options_process_early (gc, &optionstate); + } + + /* Init totem itself */ + totem->prev_volume = -1; + totem->gc = gc; + totem->cursor_shown = TRUE; + + /* Main window */ + totem->xml = totem_interface_load ("totem.glade", _("main window"), + TRUE, NULL); + if (totem->xml == NULL) + totem_action_exit (NULL); + + totem->win = glade_xml_get_widget (totem->xml, "totem_main_window"); + + /* Menubar */ + totem_ui_manager_setup (totem); + + /* The sidebar */ + playlist_widget_setup (totem); + totem->properties = bacon_video_widget_properties_new (); + + /* The rest of the widgets */ + totem->state = STATE_STOPPED; + totem->seek = glade_xml_get_widget (totem->xml, "tmw_seek_hscale"); + totem->seekadj = gtk_range_get_adjustment (GTK_RANGE (totem->seek)); + g_object_set_data (G_OBJECT (totem->seek), "fs", GINT_TO_POINTER (0)); + totem->volume = glade_xml_get_widget (totem->xml, "tcw_volume_button"); + g_object_set_data (G_OBJECT (totem->volume), "fs", GINT_TO_POINTER (0)); + totem->exit_popup = glade_xml_get_widget + (totem->xml, "totem_exit_fullscreen_window"); + totem->control_popup = glade_xml_get_widget + (totem->xml, "totem_controls_window"); + totem->fs_seek = glade_xml_get_widget (totem->xml, "tcw_seek_hscale"); + totem->fs_seekadj = gtk_range_get_adjustment + (GTK_RANGE (totem->fs_seek)); + g_object_set_data (G_OBJECT (totem->fs_seek), "fs", GINT_TO_POINTER (1)); + totem->fs_volume = glade_xml_get_widget + (totem->xml, "tcw_volume_hscale"); + totem->fs_voladj = gtk_range_get_adjustment + (GTK_RANGE (totem->fs_volume)); + totem->tooltip = gtk_tooltips_new (); + g_object_set_data (G_OBJECT (totem->fs_volume), "fs", GINT_TO_POINTER (1)); + totem->volume_first_time = 1; + totem->statusbar = glade_xml_get_widget (totem->xml, "tmw_statusbar"); + totem->tcw_time_label = glade_xml_get_widget (totem->xml, + "tcw_time_display_label"); + totem->seek_lock = totem->vol_lock = totem->vol_fs_lock = FALSE; + + totem_session_setup (totem, argv); + totem_setup_recent (totem); + totem_setup_file_monitoring (totem); + totem_setup_file_filters (); + totem_setup_play_disc (totem); + totem_callback_connect (totem); + totem_setup_window (totem); + + /* Show ! gtk_main_iteration trickery to show all the widgets + * we have so far */ + gtk_widget_show (totem->win); + totem_gdk_window_set_waiting_cursor (totem->win->window); + update_fullscreen_size (totem); + long_action (); + + totem->controls_visibility = TOTEM_CONTROLS_VISIBLE; + + /* Show ! (again) the video widget this time. */ + video_widget_create (totem); + long_action (); + gtk_widget_grab_focus (GTK_WIDGET (totem->bvw)); + bacon_video_widget_set_logo (totem->bvw, LOGO_PATH); + + /* The prefs after the video widget is connected */ + totem_setup_preferences (totem); + + /* Command-line handling */ + totem_options_process_late (totem, &optionstate); + + if (totem->session_restored != FALSE) + { + totem_session_restore (totem, optionstate.filenames); + } else if (optionstate.filenames != NULL && totem_action_open_files (totem, optionstate.filenames)) { + totem_action_play_pause (totem); + } else { + totem_action_set_mrl (totem, NULL); + } + gdk_window_set_cursor (totem->win->window, NULL); + + if (bacon_message_connection_get_is_server (totem->conn) != FALSE) + { + bacon_message_connection_set_callback (totem->conn, + (BaconMessageReceivedFunc) + totem_message_connection_receive_cb, totem); + } + +#ifdef HAVE_REMOTE + totem->remote = totem_remote_new (); + g_signal_connect (totem->remote, "button_pressed", + G_CALLBACK (totem_button_pressed_remote_cb), totem); +#endif /* HAVE_REMOTE */ + + gtk_main (); + +#ifndef HAVE_GTK_ONLY + /* Will destroy GOption allocated data automatically */ + g_object_unref (program); +#endif + return 0; +} |