108
100
G_MODULE_EXPORT gboolean seek_slider_released_cb (GtkWidget *widget, GdkEventButton *event, TotemObject *totem);
109
101
G_MODULE_EXPORT void volume_button_value_changed_cb (GtkScaleButton *button, gdouble value, TotemObject *totem);
110
102
G_MODULE_EXPORT gboolean window_key_press_event_cb (GtkWidget *win, GdkEventKey *event, TotemObject *totem);
111
G_MODULE_EXPORT int window_scroll_event_cb (GtkWidget *win, GdkEventScroll *event, TotemObject *totem);
103
G_MODULE_EXPORT int window_scroll_event_cb (GtkWidget *win, GdkEvent *event, TotemObject *totem);
112
104
G_MODULE_EXPORT void main_pane_size_allocated (GtkWidget *main_pane, GtkAllocation *allocation, TotemObject *totem);
113
105
G_MODULE_EXPORT void fs_exit1_activate_cb (GtkButton *button, TotemObject *totem);
144
140
static int totem_table_signals[LAST_SIGNAL] = { 0 };
146
G_DEFINE_TYPE(TotemObject, totem_object, G_TYPE_OBJECT)
142
G_DEFINE_TYPE(TotemObject, totem_object, GTK_TYPE_APPLICATION)
145
totem_object_local_command_line (GApplication *application,
149
GOptionContext *context;
150
GError *error = NULL;
154
/* Dupe so that the remote arguments are listed, but
155
* not removed from the list */
156
argv = g_strdupv (*arguments);
157
argc = g_strv_length (argv);
159
context = totem_options_get_context ();
160
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
161
g_print (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
162
error->message, argv[0]);
163
g_error_free (error);
168
/* Replace relative paths with absolute URIs */
169
if (optionstate.filenames != NULL) {
173
n_args = g_strv_length (*arguments);
174
n_files = g_strv_length (optionstate.filenames);
176
i = n_args - n_files;
177
for ( ; i < n_args; i++) {
180
new_path = totem_create_full_path ((*arguments)[i]);
181
if (new_path == NULL)
184
g_free ((*arguments)[i]);
185
(*arguments)[i] = new_path;
189
g_strfreev (optionstate.filenames);
190
optionstate.filenames = NULL;
194
g_option_context_free (context);
201
accumulator_first_non_null_wins (GSignalInvocationHint *ihint,
203
const GValue *handler_return,
208
str = g_value_get_string (handler_return);
211
g_value_set_string (return_accu, str);
149
217
totem_object_class_init (TotemObjectClass *klass)
151
219
GObjectClass *object_class;
220
GApplicationClass *app_class;
153
222
object_class = (GObjectClass *) klass;
223
app_class = (GApplicationClass *) klass;
155
225
object_class->set_property = totem_object_set_property;
156
226
object_class->get_property = totem_object_get_property;
157
227
object_class->finalize = totem_object_finalize;
229
app_class->local_command_line = totem_object_local_command_line;
160
232
* TotemObject:fullscreen:
213
285
NULL, G_PARAM_READABLE));
216
* TotemObject:autoload-subtitles:
218
* If %TRUE, Totem will automatically load any subtitle files it finds for each newly opened video.
220
g_object_class_install_property (object_class, PROP_AUTOLOAD_SUBTITLES,
221
g_param_spec_boolean ("autoload-subtitles", "Autoload subtitles?",
222
"Whether to automatically load any subtitle files Totem finds.",
223
FALSE, G_PARAM_READWRITE));
288
* TotemObject:current-content-type:
290
* The content-type of the current stream.
292
g_object_class_install_property (object_class, PROP_CURRENT_CONTENT_TYPE,
293
g_param_spec_string ("current-content-type",
294
"Current stream's content-type",
295
"Current stream's content-type.",
296
NULL, G_PARAM_READABLE));
299
* TotemObject:current-display-name:
301
* The display name of the current stream.
303
g_object_class_install_property (object_class, PROP_CURRENT_DISPLAY_NAME,
304
g_param_spec_string ("current-display-name",
305
"Current stream's display name",
306
"Current stream's display name.",
307
NULL, G_PARAM_READABLE));
226
310
* TotemObject:remember-position:
249
333
G_TYPE_NONE, 1, G_TYPE_STRING);
336
* TotemObject::file-has-played:
337
* @totem: the #TotemObject which received the signal
338
* @mrl: the MRL of the opened stream
340
* The #TotemObject::file-has-played signal is emitted when a new stream has started playing in Totem.
342
totem_table_signals[FILE_HAS_PLAYED] =
343
g_signal_new ("file-has-played",
344
G_TYPE_FROM_CLASS (object_class),
346
G_STRUCT_OFFSET (TotemObjectClass, file_has_played),
348
g_cclosure_marshal_VOID__STRING,
349
G_TYPE_NONE, 1, G_TYPE_STRING);
252
352
* TotemObject::file-closed:
253
353
* @totem: the #TotemObject which received the signal
280
380
G_SIGNAL_RUN_LAST,
281
381
G_STRUCT_OFFSET (TotemObjectClass, metadata_updated),
283
totemobject_marshal_VOID__STRING_STRING_STRING_UINT,
383
g_cclosure_marshal_generic,
284
384
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
387
* TotemObject::get-user-agent:
388
* @totem: the #TotemObject which received the signal
389
* @mrl: the MRL of the opened stream
391
* The #TotemObject::get-user-agent signal is emitted before opening a stream, so that plugins
392
* have the opportunity to return the user-agent to be set.
394
* Return value: allocated string representing the user-agent to use for @mrl
396
totem_table_signals[GET_USER_AGENT] =
397
g_signal_new ("get-user-agent",
398
G_TYPE_FROM_CLASS (object_class),
400
G_STRUCT_OFFSET (TotemObjectClass, get_user_agent),
401
accumulator_first_non_null_wins, NULL,
402
g_cclosure_marshal_generic,
403
G_TYPE_STRING, 1, G_TYPE_STRING);
406
* TotemObject::get-text-subtitle:
407
* @totem: the #TotemObject which received the signal
408
* @mrl: the MRL of the opened stream
410
* The #TotemObject::get-text-subtitle signal is emitted before opening a stream, so that plugins
411
* have the opportunity to detect or download text subtitles for the stream if necessary.
413
* Return value: allocated string representing the URI of the subtitle to use for @mrl
415
totem_table_signals[GET_TEXT_SUBTITLE] =
416
g_signal_new ("get-text-subtitle",
417
G_TYPE_FROM_CLASS (object_class),
419
G_STRUCT_OFFSET (TotemObjectClass, get_text_subtitle),
420
accumulator_first_non_null_wins, NULL,
421
g_cclosure_marshal_generic,
422
G_TYPE_STRING, 1, G_TYPE_STRING);
985
1110
gdk_display_sync (display);
987
1112
if (totem->bvw) {
988
totem_action_save_size (totem);
989
1113
totem_save_position (totem);
990
1114
bacon_video_widget_close (totem->bvw);
993
if (totem->app != NULL)
994
g_object_unref (totem->app);
995
1117
totem_action_save_state (totem, page_id);
996
1118
g_free (page_id);
998
1120
totem_sublang_exit (totem);
999
1121
totem_destroy_file_filters ();
1001
if (totem->settings)
1002
g_object_unref (totem->settings);
1005
g_object_unref (totem->fs);
1123
g_clear_object (&totem->settings);
1124
g_clear_object (&totem->fs);
1007
1126
if (totem->win)
1008
1127
gtk_widget_destroy (GTK_WIDGET (totem->win));
1233
totem_action_load_media (TotemObject *totem, TotemDiscMediaType type, const char *device)
1236
GError *error = NULL;
1237
const char *uri, *link_text, *secondary;
1240
mrls = bacon_video_widget_get_mrls (totem->bvw, type, device, &error);
1244
/* No errors? Weird */
1245
if (error == NULL) {
1246
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)));
1247
totem_action_error (totem, msg, _("You might want to check that a disc is present in the drive and that it is correctly configured."));
1252
/* No plugin for the media type */
1253
if (g_error_matches (error, BVW_ERROR, BVW_ERROR_NO_PLUGIN_FOR_FILE) != FALSE) {
1254
uri = "http://projects.gnome.org/totem/#codecs";
1255
link_text = _("More information about media plugins");
1256
secondary = _("Please install the necessary plugins and restart Totem to be able to play this media.");
1257
if (type == MEDIA_TYPE_DVD || type == MEDIA_TYPE_VCD)
1258
msg = g_strdup_printf (_("Totem cannot play this type of media (%s) because it does not have the appropriate plugins to be able to read from the disc."), _(totem_cd_get_human_readable_name (type)));
1260
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)));
1261
/* Unsupported type (ie. CDDA) */
1262
} else if (g_error_matches (error, BVW_ERROR, BVW_ERROR_INVALID_LOCATION) != FALSE) {
1263
msg = g_strdup_printf(_("Totem cannot play this type of media (%s) because it is not supported."), _(totem_cd_get_human_readable_name (type)));
1264
totem_action_error (totem, msg, _("Please insert another disc to play back."));
1268
g_assert_not_reached ();
1271
totem_interface_error_with_link (msg, secondary, uri, link_text, GTK_WINDOW (totem->win));
1276
retval = totem_action_open_files (totem, mrls);
1283
totem_action_load_media_device (TotemObject *totem, const char *device)
1285
TotemDiscMediaType type;
1286
GError *error = NULL;
1287
char *device_path, *url;
1290
if (g_str_has_prefix (device, "file://") != FALSE)
1291
device_path = g_filename_from_uri (device, NULL, NULL);
1293
device_path = g_strdup (device);
1295
type = totem_cd_detect_type_with_url (device_path, &url, &error);
1298
case MEDIA_TYPE_ERROR:
1299
totem_action_error (totem,
1300
_("Totem was not able to play this disc."),
1301
error ? error->message : _("No reason."));
1304
case MEDIA_TYPE_DATA:
1305
/* Set default location to the mountpoint of
1307
retval = totem_action_open_dialog (totem, url, FALSE);
1309
case MEDIA_TYPE_DVD:
1310
case MEDIA_TYPE_VCD:
1311
retval = totem_action_load_media (totem, type, device_path);
1313
case MEDIA_TYPE_CDDA:
1314
totem_action_error (totem,
1315
_("Totem does not support playback of Audio CDs"),
1316
_("Please consider using a music player or a CD extractor to play this CD"));
1319
case MEDIA_TYPE_DVB:
1321
g_assert_not_reached ();
1325
g_free (device_path);
1331
* totem_action_play_media_device:
1332
* @totem: a #TotemObject
1333
* @device: the media device's path
1335
* Attempts to play the media device (for example, a DVD drive or CD drive)
1336
* with the given @device path by first adding it to the playlist, then
1339
* An error dialog will be displayed if Totem cannot read or play what's on
1343
totem_action_play_media_device (TotemObject *totem, const char *device)
1347
if (totem_action_load_media_device (totem, device) != FALSE) {
1348
mrl = totem_playlist_get_current_mrl (totem->playlist, NULL);
1349
totem_action_set_mrl_and_play (totem, mrl, NULL);
1355
* totem_action_play_media:
1356
* @totem: a #TotemObject
1357
* @type: the type of disc media
1358
* @device: the media's device path
1360
* Attempts to play the media found on @device (for example, a DVD in a drive or a DVB
1361
* tuner) by first adding it to the playlist, then playing it.
1363
* An error dialog will be displayed if Totem cannot support media of @type.
1366
totem_action_play_media (TotemObject *totem, TotemDiscMediaType type, const char *device)
1370
if (totem_action_load_media (totem, type, device) != FALSE) {
1371
mrl = totem_playlist_get_current_mrl (totem->playlist, NULL);
1372
totem_action_set_mrl_and_play (totem, mrl, NULL);
1378
1358
* totem_object_action_stop:
1379
1359
* @totem: a #TotemObject
1746
1734
gdouble volume;
1747
char *autoload_sub = NULL;
1748
1737
GdkWindowState window_state;
1749
1738
GError *err = NULL;
1751
1740
bacon_video_widget_set_logo_mode (totem->bvw, FALSE);
1753
if (subtitle == NULL && totem->autoload_subs != FALSE)
1754
autoload_sub = totem_uri_get_subtitle_uri (mrl);
1742
autoload_sub = NULL;
1743
if (subtitle == NULL)
1744
g_signal_emit (G_OBJECT (totem), totem_table_signals[GET_TEXT_SUBTITLE], 0, mrl, &autoload_sub);
1756
/* HACK: Bad bad Apple */
1757
if (g_str_has_prefix (mrl, "http://movies.apple.com")
1758
|| g_str_has_prefix (mrl, "http://trailers.apple.com"))
1759
bacon_video_widget_set_user_agent (totem->bvw, "Quicktime/7.2.0");
1761
bacon_video_widget_set_user_agent (totem->bvw, NULL);
1747
g_signal_emit (G_OBJECT (totem), totem_table_signals[GET_USER_AGENT], 0, mrl, &user_agent);
1748
bacon_video_widget_set_user_agent (totem->bvw, user_agent);
1749
g_free (user_agent);
1763
1751
totem_gdk_window_set_waiting_cursor (gtk_widget_get_window (totem->win));
1764
1752
totem_try_restore_position (totem, mrl);
1765
retval = bacon_video_widget_open (totem->bvw, mrl, subtitle ? subtitle : autoload_sub, &err);
1753
retval = bacon_video_widget_open (totem->bvw, mrl, &err);
1754
bacon_video_widget_set_text_subtitle (totem->bvw, subtitle ? subtitle : autoload_sub);
1766
1755
g_free (autoload_sub);
1767
1756
gdk_window_set_cursor (gtk_widget_get_window (totem->win), NULL);
1768
1757
totem->mrl = g_strdup (mrl);
1871
1854
totem_action_direction (TotemObject *totem, TotemPlaylistDirection dir)
1873
if (totem_playing_dvd (totem->mrl) == FALSE &&
1874
totem_playlist_has_direction (totem->playlist, dir) == FALSE
1875
&& totem_playlist_get_repeat (totem->playlist) == FALSE)
1856
if (bacon_video_widget_has_next_track (totem->bvw) == FALSE &&
1857
totem_playlist_has_direction (totem->playlist, dir) == FALSE &&
1858
totem_playlist_get_repeat (totem->playlist) == FALSE)
1878
if (totem_playing_dvd (totem->mrl) != FALSE)
1880
bacon_video_widget_dvd_event (totem->bvw,
1881
dir == TOTEM_PLAYLIST_DIRECTION_NEXT ?
1882
BVW_DVD_NEXT_CHAPTER :
1883
BVW_DVD_PREV_CHAPTER);
1861
if (bacon_video_widget_has_next_track (totem->bvw) != FALSE) {
1863
event = (dir == TOTEM_PLAYLIST_DIRECTION_NEXT ? BVW_DVD_NEXT_CHAPTER : BVW_DVD_PREV_CHAPTER);
1864
bacon_video_widget_dvd_event (totem->bvw, event);
1887
if (dir == TOTEM_PLAYLIST_DIRECTION_NEXT
1888
|| bacon_video_widget_is_seekable (totem->bvw) == FALSE
1889
|| totem_time_within_seconds (totem) != FALSE)
1868
if (dir == TOTEM_PLAYLIST_DIRECTION_NEXT ||
1869
bacon_video_widget_is_seekable (totem->bvw) == FALSE ||
1870
totem_time_within_seconds (totem) != FALSE) {
1891
1871
char *mrl, *subtitle;
1893
1873
totem_playlist_set_direction (totem->playlist, dir);
2423
2403
bacon_video_widget_close (totem->bvw);
2424
2404
totem_file_closed (totem);
2405
totem->has_played_emitted = FALSE;
2425
2406
totem_gdk_window_set_waiting_cursor (gtk_widget_get_window (totem->win));
2426
bacon_video_widget_open (totem->bvw, new_mrl ? new_mrl : mrl, NULL, NULL);
2407
bacon_video_widget_open (totem->bvw, new_mrl ? new_mrl : mrl, NULL);
2427
2408
totem_file_opened (totem, new_mrl ? new_mrl : mrl);
2428
2409
gdk_window_set_cursor (gtk_widget_get_window (totem->win), NULL);
2429
bacon_video_widget_play (bvw, NULL);
2410
if (bacon_video_widget_play (bvw, NULL) != FALSE) {
2411
totem_file_has_played (totem, totem->mrl);
2412
totem->has_played_emitted = TRUE;
2430
2414
g_free (new_mrl);
3093
3071
if (url == NULL) {
3094
3072
bacon_video_widget_close (totem->bvw);
3095
3073
totem_file_closed (totem);
3074
totem->has_played_emitted = FALSE;
3096
3075
totem_action_set_mrl (totem, NULL, NULL);
3099
if (strcmp (url, "dvd:") == 0) {
3101
totem_action_play_media (totem, MEDIA_TYPE_DVD, NULL);
3102
} else if (strcmp (url, "vcd:") == 0) {
3104
totem_action_play_media (totem, MEDIA_TYPE_VCD, NULL);
3106
totem_playlist_add_mrl (totem->playlist, url, NULL, TRUE, NULL, NULL, NULL);
3078
totem_playlist_add_mrl (totem->playlist, url, NULL, TRUE, NULL, NULL, NULL);
3109
3080
case TOTEM_REMOTE_COMMAND_SHOW:
3110
gtk_window_present (GTK_WINDOW (totem->win));
3081
gtk_window_present_with_time (GTK_WINDOW (totem->win), GDK_CURRENT_TIME);
3112
3083
case TOTEM_REMOTE_COMMAND_TOGGLE_CONTROLS:
3113
3084
if (totem->controls_visibility != TOTEM_CONTROLS_FULLSCREEN)
3419
3388
totem_action_fullscreen_toggle(totem);
3421
3390
} else if (event->type == GDK_BUTTON_PRESS && event->button == 2) {
3422
if (totem_is_fullscreen (totem) != FALSE) {
3423
const char *icon_name;
3424
if (bacon_video_widget_is_playing (totem->bvw) == FALSE)
3425
icon_name = "media-playback-start-symbolic";
3427
icon_name = "media-playback-pause-symbolic";
3428
totem_fullscreen_show_popups_or_osd (totem->fs, icon_name, FALSE);
3391
const char *icon_name;
3392
if (bacon_video_widget_is_playing (totem->bvw) == FALSE)
3393
icon_name = "media-playback-start-symbolic";
3395
icon_name = "media-playback-pause-symbolic";
3396
totem_fullscreen_show_popups_or_osd (totem->fs, icon_name, FALSE);
3430
3397
totem_action_play_pause (totem);
3432
3399
} else if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
3772
3740
static gboolean
3773
totem_action_handle_scroll (TotemObject *totem, GdkScrollDirection direction)
3741
totem_action_handle_scroll (TotemObject *totem,
3742
const GdkEvent *event)
3775
3744
gboolean retval = TRUE;
3745
GdkEventScroll *sevent = (GdkEventScroll *) event;
3746
GdkScrollDirection direction;
3748
direction = sevent->direction;
3777
3750
if (totem_fullscreen_is_fullscreen (totem->fs) != FALSE)
3778
3751
totem_fullscreen_show_popups (totem->fs, TRUE);
3753
if (direction == GDK_SCROLL_SMOOTH) {
3755
gdk_event_get_scroll_deltas (event, NULL, &y);
3756
direction = y >= 0.0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
3780
3759
switch (direction) {
3781
3760
case GDK_SCROLL_UP:
3782
3761
totem_action_seek_relative (totem, SEEK_FORWARD_SHORT_OFFSET * 1000, FALSE);
3915
3890
gboolean has_item;
3918
if (totem_playing_dvd (totem->mrl) != FALSE)
3919
has_item = bacon_video_widget_has_previous_track (totem->bvw);
3921
has_item = totem_playlist_has_previous_mrl (totem->playlist);
3893
has_item = bacon_video_widget_has_previous_track (totem->bvw) ||
3894
totem_playlist_has_previous_mrl (totem->playlist) ||
3895
totem_playlist_get_repeat (totem->playlist);
3923
3896
totem_action_set_sensitivity ("previous-chapter", has_item);
3926
if (totem_playing_dvd (totem->mrl) != FALSE)
3927
has_item = bacon_video_widget_has_next_track (totem->bvw);
3929
has_item = totem_playlist_has_next_mrl (totem->playlist);
3899
has_item = bacon_video_widget_has_next_track (totem->bvw) ||
3900
totem_playlist_has_next_mrl (totem->playlist) ||
3901
totem_playlist_get_repeat (totem->playlist);
3931
3902
totem_action_set_sensitivity ("next-chapter", has_item);
4149
4120
gtk_widget_add_events (totem->win, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
4151
4122
/* Connect the mouse wheel */
4152
gtk_widget_add_events (totem->win, GDK_SCROLL_MASK);
4153
gtk_widget_add_events (totem->seek, GDK_SCROLL_MASK);
4154
gtk_widget_add_events (totem->fs->seek, GDK_SCROLL_MASK);
4123
gtk_widget_add_events (GTK_WIDGET (gtk_builder_get_object (totem->xml, "tmw_main_vbox")), GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK);
4124
gtk_widget_add_events (totem->seek, GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK);
4125
gtk_widget_add_events (totem->fs->seek, GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK);
4156
4127
/* FIXME Hack to fix bug #462286 and #563894 */
4157
4128
g_signal_connect (G_OBJECT (totem->fs->seek), "button-press-event",