1 /*
2  * * Copyright (C) 2009-2011 Ali <aliov@xfce.org>
3  * * Copyright (C) 2012-2017 Simon Steinbeiß <ochosi@xfce.org>
4  * * Copyright (C) 2012-2020 Sean Davis <bluesabre@xfce.org>
5  *
6  * Licensed under the GNU General Public License Version 2
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #ifdef HAVE_XF86_KEYSYM
32 #include <X11/XF86keysym.h>
33 #endif
34 
35 #include <X11/Xatom.h>
36 
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <gtk/gtk.h>
40 
41 #ifdef XFCE_DISABLE_DEPRECATED
42 #undef XFCE_DISABLE_DEPRECATED
43 #endif
44 #include <libxfce4util/libxfce4util.h>
45 #include <libxfce4ui/libxfce4ui.h>
46 
47 #include <dbus/dbus-glib.h>
48 
49 #include "src/common/parole-common.h"
50 #include "src/common/parole-rc-utils.h"
51 #include "src/common/parole-screensaver.h"
52 #include "src/common/parole-powermanager.h"
53 
54 #include "src/dbus/parole-dbus.h"
55 
56 #include "src/gst/gst-enum-types.h"
57 #include "src/gst/parole-gst.h"
58 
59 #include "src/misc/parole-debug.h"
60 #include "src/misc/parole-file.h"
61 #include "src/misc/parole-filters.h"
62 
63 #include "src/enum-gtypes.h"
64 #include "src/parole-about.h"
65 #include "src/parole-builder.h"
66 #include "src/parole-button.h"
67 
68 #ifdef HAVE_CLUTTER
69 #include "src/parole-clutter.h"
70 #endif
71 
72 #include "src/parole-conf.h"
73 #include "src/parole-conf-dialog.h"
74 #include "src/parole-disc.h"
75 #include "src/parole-mediachooser.h"
76 #include "src/parole-medialist.h"
77 #include "src/parole-shortcuts.h"
78 #include "src/parole-utils.h"
79 
80 #include "src/parole-player.h"
81 
82 int GTK_ICON_SIZE_ARTWORK_FALLBACK;
83 
84 GSimpleAction *playpause_action;
85 GSimpleAction *previous_action;
86 GSimpleAction *next_action;
87 gboolean block_playlist_updates = FALSE;
88 
89 /* Temporary reference to ParolePlayer to cover for GSimpleAction. */
90 ParolePlayer *parole_player = NULL;
91 
92 static void
get_time_string(gchar * timestring,gint total_seconds)93 get_time_string(gchar *timestring, gint total_seconds) {
94     gint  hours;
95     gint  minutes;
96     gint  seconds;
97 
98     minutes =  total_seconds / 60;
99     seconds = total_seconds % 60;
100     hours = minutes / 60;
101     minutes = minutes % 60;
102 
103     if ( hours == 0 ) {
104         g_snprintf(timestring, 128, "%02i:%02i", minutes, seconds);
105     } else {
106         g_snprintf(timestring, 128, "%i:%02i:%02i", hours, minutes, seconds);
107     }
108 }
109 
110 /*
111  * DBus Glib init
112  */
113 static void parole_player_dbus_class_init(ParolePlayerClass *klass);
114 static void parole_player_dbus_init(ParolePlayer *player);
115 
116 static void parole_player_disc_selected_cb(ParoleDisc *disc,
117                                                      const gchar *uri,
118                                                      const gchar *device,
119                                                      ParolePlayer *player);
120 
121 static void parole_player_select_custom_subtitle(GtkMenuItem *widget, gpointer data);
122 
123 static gboolean parole_overlay_expose_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
124 
125 static gboolean parole_audiobox_expose_event(GtkWidget *w, GdkEventExpose *ev, ParolePlayer *player);
126 
127 /*
128  * GtkBuilder Callbacks
129  */
130 void        on_content_area_size_allocate(GtkWidget *widget,
131                                                      GtkAllocation *allocation,
132                                                      ParolePlayer *player);
133 
134 
135 gboolean    parole_player_configure_event_cb(GtkWidget *widget,
136                                                      GdkEventConfigure *ev,
137                                                      ParolePlayer *player);
138 
139 gboolean    parole_player_range_button_press(GtkWidget *widget,
140                                                      GdkEventButton *ev,
141                                                      ParolePlayer *player);
142 
143 gboolean    parole_player_range_button_release(GtkWidget *widget,
144                                                      GdkEventButton *ev,
145                                                      ParolePlayer *player);
146 
147 void        parole_player_range_value_changed(GtkRange *range,
148                                                      ParolePlayer *player);
149 
150 void        parole_player_playpause_action_cb(GSimpleAction *action);
151 
152 void        parole_player_pause_clicked(GtkButton *button,
153                                                      ParolePlayer *player);
154 
155 void        parole_player_playlist_toggled_cb(GtkWidget *button,
156                                               ParolePlayer    *player);
157 
158 void        parole_player_playlist_menu_toggled_cb(GtkWidget *button,
159                                               ParolePlayer    *player);
160 
161 void        parole_player_next_action_cb(GSimpleAction *action);
162 
163 void        parole_player_previous_action_cb(GSimpleAction *action);
164 
165 void        parole_player_fullscreen_action_cb(GSimpleAction *action);
166 
167 void        parole_player_seekf_cb(GtkWidget *widget,
168                                                      ParolePlayer *player,
169                                                      gdouble seek);
170 
171 void        parole_player_seekb_cb(GtkWidget *widget,
172                                                      ParolePlayer *player,
173                                                      gdouble seek);
174 
175 gboolean    parole_player_window_state_event(GtkWidget *widget,
176                                                      GdkEventWindowState *event,
177                                                      ParolePlayer *player);
178 
179 void        parole_player_destroy_cb(GObject *window,
180                                                      ParolePlayer *player);
181 
182 gboolean    parole_player_delete_event_cb(GtkWidget *widget,
183                                                      GdkEvent *ev,
184                                                      ParolePlayer *player);
185 
186 void        parole_player_reset_controls(ParolePlayer *player,
187                                                      gboolean fullscreen);
188 
189 /*Menu items callbacks*/
190 void        parole_player_menu_open_location_cb(GtkWidget *widget,
191                                                      ParolePlayer *player);
192 
193 void        parole_player_menu_add_cb(GtkWidget *widget,
194                                                      ParolePlayer *player);
195 
196 void        parole_player_media_menu_select_cb(GtkMenuItem *widget,
197                                                      ParolePlayer *player);
198 
199 void        parole_player_save_playlist_cb(GtkWidget *widget,
200                                                      ParolePlayer *player);
201 
202 void        parole_player_menu_exit_cb(GtkWidget *widget,
203                                                      ParolePlayer *player);
204 
205 void        parole_player_volume_up(GtkWidget *widget,
206                                                      ParolePlayer *player);
207 
208 void        parole_player_volume_down(GtkWidget *widget,
209                                                      ParolePlayer *player);
210 
211 void        parole_player_volume_mute(GtkWidget *widget,
212                                                      ParolePlayer *player);
213 
214 void        parole_player_open_preferences_cb(GtkWidget *widget,
215                                                      ParolePlayer *player);
216 
217 void        parole_player_volume_value_changed_cb(GtkScaleButton *widget,
218                                                      gdouble value,
219                                                      ParolePlayer *player);
220 
221 gboolean    parole_player_volume_scroll_event_cb(GtkWidget *widget,
222                                                      GdkEventScroll *ev,
223                                                      ParolePlayer *player);
224 
225 static void parole_player_volume_popdown_cb(GtkWidget *widget,
226                                             ParolePlayer *player);
227 
228 static void parole_player_clear_subtitles(ParolePlayer *player);
229 
230 static void parole_player_clear_audio_tracks(ParolePlayer *player);
231 
232 /*
233  * Aspect ratio
234  */
235 void        ratio_none_toggled_cb(GtkWidget *widget,
236                                                      ParolePlayer *player);
237 
238 void        ratio_auto_toggled_cb(GtkWidget *widget,
239                                                      ParolePlayer *player);
240 
241 void        ratio_square_toggled_cb(GtkWidget *widget,
242                                                      ParolePlayer *player);
243 
244 void        ratio_4_3_toggled_cb(GtkWidget *widget,
245                                                      ParolePlayer *player);
246 
247 void        ratio_16_9_toggled_cb(GtkWidget *widget,
248                                                      ParolePlayer *player);
249 
250 void        ratio_20_9_toggled_cb(GtkWidget *widget,
251                                                      ParolePlayer *player);
252 
253 void        parole_player_set_playlist_visible(ParolePlayer *player,
254                                                      gboolean visibility);
255 
256 gboolean    parole_player_gst_widget_button_press(GtkWidget *widget,
257                                                      GdkEventButton *ev,
258                                                      ParolePlayer *player);
259 
260 gboolean    parole_player_gst_widget_button_release(GtkWidget *widget,
261                                                      GdkEventButton *ev,
262                                                      ParolePlayer *player);
263 
264 void
265 parole_player_schedule_hide_controls                (ParolePlayer *player,
266                                                      gint seconds);
267 
268 gboolean
269 parole_player_gst_widget_motion_notify_event(GtkWidget *widget,
270                                                      GdkEventMotion *ev,
271                                                      ParolePlayer *player);
272 
273 void        parole_show_about(GtkWidget *widget,
274                                                      ParolePlayer *player);
275 
276 void        parole_show_shortcuts(GtkWidget *widget,
277                                                      ParolePlayer *player);
278 
279 void        parole_player_set_audiotrack_radio_menu_item_selected(
280                                                      ParolePlayer *player,
281                                                      gint audio_index);
282 
283 void        parole_player_set_subtitle_radio_menu_item_selected(
284                                                      ParolePlayer *player,
285                                                      gint sub_index);
286 
287 void
288 parole_player_combo_box_audiotrack_changed_cb(GtkWidget *widget,
289                                                      ParolePlayer *player);
290 
291 void
292 parole_player_combo_box_subtitles_changed_cb(GtkWidget *widget,
293                                                      ParolePlayer *player);
294 
295 static void
296 parole_player_audiotrack_radio_menu_item_changed_cb(GtkWidget *widget,
297                                                      ParolePlayer *player);
298 
299 static void
300 parole_player_subtitles_radio_menu_item_changed_cb(GtkWidget *widget,
301                                                      ParolePlayer *player);
302 
303 static void
304 parole_player_dvd_chapter_count_change_cb(ParoleGst *gst,
305                                                      gint chapter_count,
306                                                      ParolePlayer *player);
307 
308 static void parole_player_dvd_chapter_change_cb(ParoleGst *gst,
309                                                      gint chapter_count,
310                                                      ParolePlayer *player);
311 
312 void        parole_player_dvd_menu_activated(GtkMenuItem *widget,
313                                                      ParolePlayer *player);
314 
315 void        parole_player_dvd_title_activated(GtkMenuItem *widget,
316                                                      ParolePlayer *player);
317 
318 void        parole_player_dvd_subpicture_activated(GtkMenuItem *widget,
319                                                      ParolePlayer *player);
320 
321 void        parole_player_dvd_audio_activated(GtkMenuItem *widget,
322                                                      ParolePlayer *player);
323 
324 void        parole_player_dvd_angle_activated(GtkMenuItem *widget,
325                                                      ParolePlayer *player);
326 
327 void        parole_player_dvd_chapter_activated(GtkMenuItem *widget,
328                                                      ParolePlayer *player);
329 
330 gboolean    parole_player_key_press(GtkWidget *widget,
331                                                      GdkEventKey *ev,
332                                                      ParolePlayer *player);
333 
334 void        parole_player_widget_activate_action(GtkWidget *widget,
335                                                      GSimpleAction *action);
336 
337 static void parole_player_set_cursor_visible(ParolePlayer *player,
338                                                      gboolean visible);
339 
340 static gboolean
341 parole_player_handle_key_value                      (guint keyval,
342                                                      guint state,
343                                                      ParolePlayer *player);
344 
345 gboolean parole_player_hide_controls(gpointer data);
346 
347 static GtkTargetEntry target_entry[] = {
348     { "STRING",        0, 0 },
349     { "text/uri-list", 0, 1 },
350 };
351 
352 /*
353  * End of GtkBuilder Callbacks
354  */
355 
356 struct ParolePlayerPrivate {
357     DBusGConnection    *bus;
358     ParoleMediaList    *list;
359     ParoleDisc         *disc;
360     ParoleScreenSaver  *screen_saver;
361     GDBusConnection    *connection;
362     guint32             inhibit_cookie;
363 
364     ParoleConf         *conf;
365     ParoleConfDialog   *settings_dialog;
366 
367     XfceSMClient       *sm_client;
368     gchar              *client_id;
369 
370 #ifdef HAVE_XF86_KEYSYM
371     ParoleButton       *button;
372 #endif
373 
374     GtkFileFilter      *video_filter;
375     GtkRecentManager   *recent;
376 
377     gdouble             last_volume;
378 
379     GtkWidget          *window;
380     GtkWidget          *playlist_nt;
381     GtkWidget          *playlist_popover;
382     /* Parole Player layouts */
383     gboolean            embedded;
384     gboolean            full_screen;
385     gboolean            mini_mode;
386     /* Remembered window sizes */
387     gint                last_h, last_w;
388 
389     /* Menubar */
390     GtkWidget          *menu_bar;
391     GtkWidget          *recent_menu;
392     GtkWidget          *save_playlist;
393     GtkWidget          *dvd_menu;
394     GtkWidget          *chapters_menu;
395     GtkWidget          *goto_position;
396     gboolean            show_menubar;
397 
398     /* Media Controls */
399     GtkWidget          *control;
400     GtkWidget          *previous_button;
401     GtkWidget          *next_button;
402     GtkWidget          *playpause_button;
403     GtkWidget          *playpause_image;
404     GtkWidget          *fullscreen_button;
405     GtkWidget          *fullscreen_image;
406     GtkWidget          *fullscreen_menu_item;
407     GtkWidget          *label_elapsed;
408     GtkWidget          *label_duration;
409     GtkWidget          *label_divider;
410     GtkWidget          *range;
411     GtkWidget          *progressbar_buffering;
412     GtkWidget          *volume;
413     GtkWidget          *mute;
414     GtkWidget          *showhide_playlist_menu_item;
415     GtkWidget          *showhide_playlist_button;
416     GtkWidget          *repeat_menu_item;
417     GtkWidget          *shuffle_menu_item;
418     GtkWidget          *revealer;
419 
420     /* Infobar */
421     GtkWidget          *infobar;
422     /* Audio Track */
423     GtkWidget          *combobox_audiotrack;
424     GtkListStore       *liststore_audiotrack;
425     GList              *audio_list;
426     gboolean            update_languages;
427     GtkWidget          *audio_group;
428     GtkWidget          *languages_menu;
429     /* Subtitle Track */
430     GtkWidget          *combobox_subtitles;
431     GtkListStore       *liststore_subtitles;
432     GList              *subtitle_list;
433     gboolean            updated_subs;
434     GtkWidget          *subtitles_group;
435     GtkWidget          *subtitles_menu_custom;
436     GtkWidget          *subtitles_menu;
437 
438     /* Output Widgets */
439     GtkWidget          *eventbox_output;
440     /* Idle Logo */
441     GtkWidget          *logo_image;
442     /* VideoBox (Gst Video Output) Widget */
443     GtkWidget          *videobox;
444     /* AudioBox (Artwork, Title, Track, Album) Widgets */
445     GtkWidget          *audiobox;
446     GtkWidget          *audiobox_cover;
447     GtkWidget          *audiobox_text;
448     GtkWidget          *audiobox_title;
449     GtkWidget          *audiobox_album;
450     GtkWidget          *audiobox_artist;
451 
452     /* Current media-list row reference */
453     GtkTreeRowReference *row;
454 
455     /* GStreamer */
456     GtkWidget          *gst;
457     ParoleMediaType     current_media_type;
458     ParoleState         state;
459     gboolean            user_seeking;
460     gboolean            internal_range_change;
461     gboolean            buffering;
462     gboolean            wait_for_gst_disc_info;
463 
464 #ifdef HAVE_CLUTTER
465     GtkWidget          *clutter;
466     gboolean            use_clutter;
467 #endif
468 
469     /* Actions */
470     GSimpleAction      *media_next_action;
471     GSimpleAction      *media_playpause_action;
472     GSimpleAction      *media_previous_action;
473     GSimpleAction      *media_fullscreen_action;
474     GSimpleAction      *toggle_repeat_action;
475     GSimpleAction      *toggle_shuffle_action;
476 
477     gboolean            exit;
478 };
479 
480 enum {
481     PROP_0,
482     PROP_CLIENT_ID
483 };
484 
G_DEFINE_TYPE_WITH_PRIVATE(ParolePlayer,parole_player,G_TYPE_OBJECT)485 G_DEFINE_TYPE_WITH_PRIVATE(ParolePlayer, parole_player, G_TYPE_OBJECT)
486 
487 void parole_show_about(GtkWidget *widget, ParolePlayer *player) {
488     parole_about(GTK_WINDOW(player->priv->window));
489 }
490 
parole_show_shortcuts(GtkWidget * widget,ParolePlayer * player)491 void parole_show_shortcuts(GtkWidget *widget, ParolePlayer *player) {
492     parole_shortcuts(GTK_WIDGET(player->priv->window));
493 }
494 
ratio_none_toggled_cb(GtkWidget * widget,ParolePlayer * player)495 void ratio_none_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
496     g_object_set(G_OBJECT(player->priv->conf),
497                  "aspect-ratio", PAROLE_ASPECT_RATIO_NONE,
498                  NULL);
499 }
500 
ratio_auto_toggled_cb(GtkWidget * widget,ParolePlayer * player)501 void ratio_auto_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
502     g_object_set(G_OBJECT(player->priv->conf),
503                  "aspect-ratio", PAROLE_ASPECT_RATIO_AUTO,
504                  NULL);
505 }
506 
ratio_square_toggled_cb(GtkWidget * widget,ParolePlayer * player)507 void ratio_square_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
508      g_object_set(G_OBJECT(player->priv->conf),
509                   "aspect-ratio", PAROLE_ASPECT_RATIO_SQUARE,
510                   NULL);
511 }
512 
ratio_4_3_toggled_cb(GtkWidget * widget,ParolePlayer * player)513 void ratio_4_3_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
514     g_object_set(G_OBJECT(player->priv->conf),
515                  "aspect-ratio", PAROLE_ASPECT_RATIO_4_3,
516                  NULL);
517 }
518 
ratio_16_9_toggled_cb(GtkWidget * widget,ParolePlayer * player)519 void ratio_16_9_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
520     g_object_set(G_OBJECT(player->priv->conf),
521                  "aspect-ratio", PAROLE_ASPECT_RATIO_16_9,
522                  NULL);
523 }
524 
ratio_20_9_toggled_cb(GtkWidget * widget,ParolePlayer * player)525 void ratio_20_9_toggled_cb(GtkWidget *widget, ParolePlayer *player) {
526     g_object_set(G_OBJECT(player->priv->conf),
527                  "aspect-ratio", PAROLE_ASPECT_RATIO_DVB,
528                  NULL);
529 }
530 
parole_player_widget_activate_action(GtkWidget * widget,GSimpleAction * action)531 void parole_player_widget_activate_action(GtkWidget *widget, GSimpleAction *action) {
532     g_action_activate(G_ACTION(action), NULL);
533 }
534 
toggle_action_cb(GtkWidget * widget,GSimpleAction * action)535 static void toggle_action_cb(GtkWidget *widget, GSimpleAction *action) {
536     if (!block_playlist_updates)
537     g_simple_toggle_action_set_active(action,
538                                        !g_simple_toggle_action_get_active(action));
539 }
540 
parole_player_get_playlist_visible(ParolePlayer * player)541 static gboolean parole_player_get_playlist_visible(ParolePlayer *player) {
542     return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(player->priv->showhide_playlist_button));
543 }
544 
parole_player_get_volume_visible(ParolePlayer * player)545 static gboolean parole_player_get_volume_visible(ParolePlayer *player) {
546     GtkWidget *popup;
547 
548     popup = gtk_scale_button_get_popup (GTK_SCALE_BUTTON (player->priv->volume));
549 
550     return gtk_widget_get_visible (popup);
551 }
552 
parole_player_set_playlist_visible(ParolePlayer * player,gboolean visibility)553 void parole_player_set_playlist_visible(ParolePlayer *player, gboolean visibility) {
554     if (visibility && player->priv->control != NULL) {
555         gtk_widget_show(gtk_widget_get_parent(player->priv->control));
556         gtk_revealer_set_reveal_child(GTK_REVEALER(player->priv->revealer), TRUE);
557         parole_player_set_cursor_visible(player, TRUE);
558     }
559 
560     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(player->priv->showhide_playlist_menu_item)) != visibility)
561         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(player->priv->showhide_playlist_menu_item), visibility);
562 
563     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(player->priv->showhide_playlist_button)) != visibility)
564         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(player->priv->showhide_playlist_button), visibility);
565 
566     if (!visibility)
567         parole_player_schedule_hide_controls (player, 1);
568 }
569 
parole_player_playlist_menu_toggled_cb(GtkWidget * menu_item,ParolePlayer * player)570 void parole_player_playlist_menu_toggled_cb(GtkWidget *menu_item, ParolePlayer *player) {
571     parole_player_set_playlist_visible(player, gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item)));
572 }
573 
parole_player_playlist_toggled_cb(GtkWidget * button,ParolePlayer * player)574 void parole_player_playlist_toggled_cb(GtkWidget *button, ParolePlayer *player) {
575     parole_player_set_playlist_visible(player, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
576 }
577 
578 static void
parole_player_change_range_value(ParolePlayer * player,gdouble value)579 parole_player_change_range_value(ParolePlayer *player, gdouble value) {
580     gchar pos_text[128];
581 
582     if ( !player->priv->user_seeking ) {
583         player->priv->internal_range_change = TRUE;
584 
585         gtk_range_set_value(GTK_RANGE(player->priv->range), value);
586 
587         player->priv->internal_range_change = FALSE;
588     }
589 
590     get_time_string(pos_text, value);
591     gtk_label_set_text(GTK_LABEL(player->priv->label_elapsed), pos_text);
592 }
593 
594 static void
parole_player_reset(ParolePlayer * player)595 parole_player_reset(ParolePlayer *player) {
596     parole_gst_stop(PAROLE_GST(player->priv->gst));
597     player->priv->update_languages = TRUE;
598     gtk_window_set_title(GTK_WINDOW(player->priv->window), _("Parole Media Player"));
599     gtk_widget_hide(GTK_WIDGET(player->priv->dvd_menu));
600     player->priv->audio_list = NULL;
601     player->priv->subtitle_list = NULL;
602 
603     gtk_widget_hide(GTK_WIDGET(player->priv->infobar));
604     parole_player_change_range_value(player, 0);
605 
606     if ( player->priv->row ) {
607         parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
608         gtk_tree_row_reference_free(player->priv->row);
609         player->priv->row = NULL;
610     }
611 
612     if (player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD) {
613         TRACE("CLEAR DVD LIST");
614         parole_media_list_clear_disc_list(player->priv->list);
615         TRACE("END CLEAR DVD LIST");
616     }
617     player->priv->current_media_type = PAROLE_MEDIA_TYPE_UNKNOWN;
618 
619     parole_media_list_set_playlist_view(player->priv->list, PAROLE_MEDIA_LIST_PLAYLIST_VIEW_STANDARD);
620 
621     g_simple_action_set_enabled(player->priv->toggle_repeat_action, TRUE);
622     g_simple_action_set_enabled(player->priv->toggle_shuffle_action, TRUE);
623 }
624 
625 static void
parole_player_dvd_reset(ParolePlayer * player)626 parole_player_dvd_reset(ParolePlayer *player) {
627     if ( player->priv->row ) {
628         parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
629         gtk_tree_row_reference_free(player->priv->row);
630         player->priv->row = NULL;
631     }
632 }
633 
634 void
parole_player_dvd_menu_activated(GtkMenuItem * widget,ParolePlayer * player)635 parole_player_dvd_menu_activated(GtkMenuItem *widget, ParolePlayer *player) {
636     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_ROOT_MENU);
637 }
638 
639 void
parole_player_dvd_title_activated(GtkMenuItem * widget,ParolePlayer * player)640 parole_player_dvd_title_activated(GtkMenuItem *widget, ParolePlayer *player) {
641     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_TITLE_MENU);
642 }
643 
644 void
parole_player_dvd_subpicture_activated(GtkMenuItem * widget,ParolePlayer * player)645 parole_player_dvd_subpicture_activated(GtkMenuItem *widget, ParolePlayer *player) {
646     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_SUBPICTURE_MENU);
647 }
648 
649 void
parole_player_dvd_audio_activated(GtkMenuItem * widget,ParolePlayer * player)650 parole_player_dvd_audio_activated(GtkMenuItem *widget, ParolePlayer *player) {
651     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_AUDIO_MENU);
652 }
653 
654 void
parole_player_dvd_angle_activated(GtkMenuItem * widget,ParolePlayer * player)655 parole_player_dvd_angle_activated(GtkMenuItem *widget, ParolePlayer *player) {
656     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_ANGLE_MENU);
657 }
658 
659 void
parole_player_dvd_chapter_activated(GtkMenuItem * widget,ParolePlayer * player)660 parole_player_dvd_chapter_activated(GtkMenuItem *widget, ParolePlayer *player) {
661     parole_gst_send_navigation_command(PAROLE_GST(player->priv->gst), GST_DVD_CHAPTER_MENU);
662 }
663 
664 static gboolean
parole_sublang_equal_lists(GList * orig,GList * new)665 parole_sublang_equal_lists (GList *orig, GList *new) {
666     GList *o, *n;
667     gboolean retval;
668 
669     if ((orig == NULL && new != NULL) || (orig != NULL && new == NULL))
670         return FALSE;
671     if (orig == NULL && new == NULL)
672         return TRUE;
673 
674     if (g_list_length (orig) != g_list_length (new))
675         return FALSE;
676 
677     retval = TRUE;
678     o = orig;
679     n = new;
680     while (o != NULL && n != NULL && retval != FALSE) {
681         if (g_str_equal(o->data, n->data) == FALSE) {
682             retval = FALSE;
683         }
684         o = g_list_next(o);
685         n = g_list_next(n);
686     }
687 
688     return retval;
689 }
690 
691 static void
parole_player_clear_chapters(ParolePlayer * player)692 parole_player_clear_chapters(ParolePlayer *player) {
693     GList *menu_items, *menu_iter;
694     gint counter = 0;
695 
696     /* Clear the chapter menu options */
697     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->chapters_menu));
698 
699     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
700         if (counter >= 2)
701             gtk_widget_destroy(GTK_WIDGET(menu_iter->data));
702         counter++;
703     }
704     g_list_free(menu_items);
705 }
706 
707 static void
parole_player_chapter_selection_changed_cb(GtkWidget * widget,ParolePlayer * player)708 parole_player_chapter_selection_changed_cb(GtkWidget *widget, ParolePlayer *player) {
709     gint chapter_id = atoi((char*)g_object_get_data(G_OBJECT(widget), "chapter-id"));
710     parole_gst_set_dvd_chapter(PAROLE_GST(player->priv->gst)    , chapter_id);
711 }
712 
713 static void
parole_player_update_chapters(ParolePlayer * player,gint chapter_count)714 parole_player_update_chapters(ParolePlayer *player, gint chapter_count) {
715     int chapter_id;
716     GtkWidget *menu_item;
717     parole_player_clear_chapters(player);
718 
719     for (chapter_id=0; chapter_id < chapter_count; chapter_id++) {
720         menu_item = GTK_WIDGET(gtk_menu_item_new_with_label(g_strdup_printf(_("Chapter %i"), chapter_id+1)));
721         gtk_widget_show(menu_item);
722 
723         g_object_set_data(G_OBJECT(menu_item), "chapter-id", g_strdup_printf("%i", chapter_id+1));
724 
725         gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->chapters_menu), menu_item);
726         g_signal_connect(menu_item, "activate",
727                             G_CALLBACK(parole_player_chapter_selection_changed_cb), player);
728     }
729 }
730 
731 static void
parole_player_clear_subtitles(ParolePlayer * player)732 parole_player_clear_subtitles(ParolePlayer *player) {
733     GtkTreeIter iter;
734     GList *menu_items, *menu_iter;
735     gint counter = 0;
736 
737     /* Clear the InfoBar Combobox */
738     gtk_list_store_clear(player->priv->liststore_subtitles);
739     gtk_list_store_append(GTK_LIST_STORE(player->priv->liststore_subtitles), &iter);
740     gtk_list_store_set(GTK_LIST_STORE(player->priv->liststore_subtitles), &iter, 0, _("None"), -1);
741     gtk_combo_box_set_active(GTK_COMBO_BOX(player->priv->combobox_subtitles), 0);
742 
743     /* Clear the subtitle menu options */
744     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->subtitles_menu));
745     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(player->priv->subtitles_group), TRUE);
746 
747     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
748         if (counter >= 4)
749             gtk_widget_destroy(GTK_WIDGET(menu_iter->data));
750         counter++;
751     }
752     g_list_free(menu_items);
753 }
754 
755 static void
parole_player_set_subtitles_list(ParolePlayer * player,GList * subtitle_list)756 parole_player_set_subtitles_list(ParolePlayer *player, GList *subtitle_list) {
757     GList *l;
758     gchar* language;
759 
760     GtkTreeIter iter;
761     gint counter = 0;
762 
763     GtkWidget *menu_item;
764 
765     parole_player_clear_subtitles(player);
766 
767     player->priv->subtitle_list = subtitle_list;
768 
769     for (l = subtitle_list; l != NULL; l = l->next) {
770         language = g_strdup(l->data);
771 
772         gtk_list_store_append(GTK_LIST_STORE(player->priv->liststore_subtitles), &iter);
773         gtk_list_store_set(GTK_LIST_STORE(player->priv->liststore_subtitles), &iter, 0, language, -1);
774 
775         menu_item = gtk_radio_menu_item_new_with_label_from_widget(
776             GTK_RADIO_MENU_ITEM(player->priv->subtitles_group), language);
777         gtk_widget_show(menu_item);
778 
779         gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->subtitles_menu), menu_item);
780         g_signal_connect(menu_item, "activate",
781                             G_CALLBACK(parole_player_subtitles_radio_menu_item_changed_cb), player);
782 
783         g_free(language);
784 
785         counter++;
786     }
787 
788     if (g_list_length(subtitle_list) != 1) {
789         gtk_widget_show(player->priv->infobar);
790     }
791 }
792 
793 static void
parole_player_clear_audio_tracks(ParolePlayer * player)794 parole_player_clear_audio_tracks(ParolePlayer *player) {
795     GList *menu_items, *menu_iter;
796     GtkWidget *empty_item;
797 
798     gtk_list_store_clear(player->priv->liststore_audiotrack);
799     player->priv->audio_group = NULL;
800 
801     /* Clear the subtitle menu options */
802 
803     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->languages_menu));
804 
805     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter))
806         gtk_widget_destroy(GTK_WIDGET(menu_iter->data));
807     g_list_free(menu_items);
808 
809     empty_item = gtk_menu_item_new_with_label(_("Empty"));
810     gtk_widget_set_sensitive(empty_item, FALSE);
811     gtk_widget_show(empty_item);
812 
813     gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->languages_menu), empty_item);
814 }
815 
816 static void
parole_player_set_audio_list(ParolePlayer * player,GList * audio_list)817 parole_player_set_audio_list(ParolePlayer *player, GList *audio_list) {
818     GList *menu_iter;
819     GList *l;
820     gchar* language;
821 
822     GtkTreeIter iter;
823 
824     GtkWidget *menu_item;
825 
826     parole_player_clear_audio_tracks(player);
827 
828     menu_iter = gtk_container_get_children(GTK_CONTAINER(player->priv->languages_menu));
829 
830     gtk_widget_destroy(GTK_WIDGET(menu_iter->data));
831     g_list_free(menu_iter);
832 
833     player->priv->audio_list = audio_list;
834 
835     for (l = audio_list; l != NULL; l = l->next) {
836         language = g_strdup(l->data);
837 
838         gtk_list_store_append(GTK_LIST_STORE(player->priv->liststore_audiotrack), &iter);
839         gtk_list_store_set(GTK_LIST_STORE(player->priv->liststore_audiotrack), &iter, 0, language, -1);
840 
841         if (player->priv->audio_group == NULL) {
842             player->priv->audio_group = GTK_WIDGET(gtk_radio_menu_item_new_with_label(NULL, language));
843             gtk_widget_show(GTK_WIDGET(player->priv->audio_group));
844             gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->languages_menu), GTK_WIDGET(player->priv->audio_group));
845             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(player->priv->audio_group), TRUE);
846 
847             g_signal_connect(player->priv->audio_group, "activate",
848                               G_CALLBACK(parole_player_audiotrack_radio_menu_item_changed_cb), player);
849         } else {
850             menu_item = gtk_radio_menu_item_new_with_label_from_widget(
851                 GTK_RADIO_MENU_ITEM(player->priv->audio_group), language);
852             gtk_widget_show(menu_item);
853             gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->languages_menu), menu_item);
854 
855             g_signal_connect(menu_item, "activate",
856                               G_CALLBACK(parole_player_audiotrack_radio_menu_item_changed_cb), player);
857         }
858 
859         g_free(language);
860     }
861 
862     gtk_combo_box_set_active(GTK_COMBO_BOX(player->priv->combobox_audiotrack), 0);
863 
864     if (g_list_length(audio_list) >= 2) {
865         gtk_widget_set_sensitive(GTK_WIDGET(player->priv->combobox_audiotrack), TRUE);
866         gtk_widget_show(player->priv->infobar);
867     } else {
868         gtk_widget_set_sensitive(GTK_WIDGET(player->priv->combobox_audiotrack), FALSE);
869     }
870 }
871 
872 static void
parole_player_update_audio_tracks(ParolePlayer * player,ParoleGst * gst)873 parole_player_update_audio_tracks(ParolePlayer *player, ParoleGst *gst) {
874     GList * list = gst_get_lang_list_for_type(gst, "AUDIO");
875 
876     if (parole_sublang_equal_lists(player->priv->audio_list, list) == TRUE) {
877         return;
878     }
879 
880     parole_player_set_audio_list(player, list);
881 
882     g_free(list->data);
883     g_list_free(list);
884     list = NULL;
885 }
886 
887 static void
parole_player_update_subtitles(ParolePlayer * player,ParoleGst * gst)888 parole_player_update_subtitles(ParolePlayer *player, ParoleGst *gst) {
889     GList * list = gst_get_lang_list_for_type(gst, "TEXT");
890 
891     guint64 sub_index;
892     gboolean sub_enabled;
893 
894     sub_index = 0;
895 
896     g_object_get(G_OBJECT(player->priv->conf),
897                   "enable-subtitle", &sub_enabled,
898                   NULL);
899 
900     if (sub_enabled)
901         sub_index = 1;
902 
903     if (parole_sublang_equal_lists(player->priv->subtitle_list, list) == TRUE) {
904         if (g_list_length(list) == 0) {
905             parole_player_clear_subtitles(player);
906         }
907         return;
908     }
909 
910     parole_player_set_subtitles_list(player, list);
911 
912     gtk_combo_box_set_active(GTK_COMBO_BOX(player->priv->combobox_subtitles), sub_index);
913 
914     if (g_list_length(list) != 1) {
915         gtk_widget_show(player->priv->infobar);
916     }
917     g_free(list->data);
918     g_list_free(list);
919     list = NULL;
920 }
921 
922 static void
parole_player_update_languages(ParolePlayer * player,ParoleGst * gst)923 parole_player_update_languages(ParolePlayer *player, ParoleGst *gst) {
924     if (player->priv->update_languages == TRUE) {
925         if (gst_get_has_video(PAROLE_GST(player->priv->gst))) {
926             parole_player_update_audio_tracks(player, gst);
927             parole_player_update_subtitles(player, gst);
928 
929             /* Enable custom subtitles for video as long as its not a DVD. */
930             gtk_widget_set_sensitive(player->priv->subtitles_menu_custom,
931                 player->priv->current_media_type != PAROLE_MEDIA_TYPE_DVD);
932         } else {
933             gtk_widget_set_sensitive(player->priv->subtitles_menu_custom, FALSE);
934         }
935 
936         player->priv->update_languages = FALSE;
937     }
938 }
939 
940 static void
parole_player_show_audiobox(ParolePlayer * player)941 parole_player_show_audiobox(ParolePlayer *player) {
942     /* Only show the audiobox if we're sure there's no video playing and
943        visualizations are disabled. */
944     gtk_widget_hide(player->priv->logo_image);
945     if (!gst_get_has_video(PAROLE_GST(player->priv->gst)) &&
946         !gst_get_has_vis(PAROLE_GST(player->priv->gst))) {
947         gtk_widget_show(player->priv->audiobox);
948         gtk_widget_hide(player->priv->videobox);
949     } else {
950         gtk_widget_hide(player->priv->audiobox);
951         gtk_widget_show(player->priv->videobox);
952     }
953 }
954 
955 /**
956  * parole_player_select_custom_subtitle:
957  * @widget : The #GtkMenuItem for selecting a custom subtitle file
958  * @data   : The #ParolePlayer instance passed by the callback function
959  *
960  * Display the #FileChooserDialog for selecting a custom subtitle file and load
961  * the subtitles selected.
962  **/
963 static void
parole_player_select_custom_subtitle(GtkMenuItem * widget,gpointer data)964 parole_player_select_custom_subtitle(GtkMenuItem *widget, gpointer data) {
965     ParolePlayer  *player;
966     ParoleFile    *file;
967 
968     GtkWidget     *chooser;
969     GtkFileFilter *filter, *all_files;
970     gint           response;
971 
972     const gchar   *folder;
973     gchar         *sub = NULL;
974     gchar         *uri = NULL;
975 
976     player = PAROLE_PLAYER(data);
977 
978     /* Build the FileChooser dialog for subtitle selection. */
979     chooser = gtk_file_chooser_dialog_new(_("Select Subtitle File"), NULL,
980                                            GTK_FILE_CHOOSER_ACTION_OPEN,
981                                            NULL,
982                                            NULL);
983     gtk_window_set_icon_name(GTK_WINDOW(chooser), "org.xfce.parole");
984     gtk_dialog_add_buttons(GTK_DIALOG(chooser),
985                            _("Cancel"), GTK_RESPONSE_CANCEL,
986                            _("Open"), GTK_RESPONSE_OK,
987                            NULL);
988     gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_OK);
989 
990     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), FALSE);
991     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chooser), FALSE);
992 
993     g_object_get(G_OBJECT(player->priv->conf),
994                     "media-chooser-folder", &folder,
995                     NULL);
996 
997     if ( folder )
998         gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), folder);
999 
1000     /* Subtitle format filter */
1001     filter = gtk_file_filter_new();
1002     gtk_file_filter_set_name(filter, _("Subtitle Files"));
1003     gtk_file_filter_add_pattern(filter, "*.asc");
1004     gtk_file_filter_add_pattern(filter, "*.txt");
1005     gtk_file_filter_add_pattern(filter, "*.sub");
1006     gtk_file_filter_add_pattern(filter, "*.srt");
1007     gtk_file_filter_add_pattern(filter, "*.smi");
1008     gtk_file_filter_add_pattern(filter, "*.ssa");
1009     gtk_file_filter_add_pattern(filter, "*.ass");
1010     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter);
1011 
1012     /* All files filter */
1013     all_files = gtk_file_filter_new();
1014     gtk_file_filter_set_name(all_files, _("All files"));
1015     gtk_file_filter_add_pattern(all_files, "*");
1016     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), all_files);
1017 
1018     gtk_window_set_default_size(GTK_WINDOW(chooser), 680, 480);
1019 
1020     /* Run the dialog, get the selected filename. */
1021     response = gtk_dialog_run(GTK_DIALOG(chooser));
1022     if ( response == GTK_RESPONSE_OK )
1023         sub = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1024 
1025     gtk_widget_destroy(chooser);
1026 
1027     if ( sub ) {
1028         /* Get the current playing file uri. */
1029         uri = parole_gst_get_file_uri(PAROLE_GST(player->priv->gst));
1030 
1031         /* Reset the player. */
1032         parole_player_reset(player);
1033 
1034         if (g_str_has_prefix(uri, "file:/")) {
1035             TRACE("Trying to play media file %s", uri);
1036             TRACE("Trying to use subtitle file %s", sub);
1037             player->priv->updated_subs = TRUE;
1038 
1039             file = parole_media_list_get_selected_file(player->priv->list);
1040 
1041             /* Set the subtitles in gst as well as in the media list, for later
1042                retrieval. */
1043             if ( file ) {
1044                 parole_file_set_custom_subtitles(file, sub);
1045                 parole_gst_set_custom_subtitles(PAROLE_GST(player->priv->gst), sub);
1046                 parole_gst_play_uri(PAROLE_GST(player->priv->gst), uri, sub);
1047             }
1048         }
1049 
1050         g_free(sub);
1051         g_free(uri);
1052     }
1053 }
1054 
1055 static void
parole_player_media_activated_cb(ParoleMediaList * list,GtkTreeRowReference * row,ParolePlayer * player)1056 parole_player_media_activated_cb(ParoleMediaList *list, GtkTreeRowReference *row, ParolePlayer *player) {
1057     ParoleFile *file;
1058     GtkTreeIter iter;
1059     GtkTreeModel *model;
1060 
1061     model = gtk_tree_row_reference_get_model(row);
1062 
1063     if (gtk_tree_model_get_iter(model, &iter, gtk_tree_row_reference_get_path(row))) {
1064         gtk_tree_model_get(model, &iter, DATA_COL, &file, -1);
1065 
1066         if ( file ) {
1067             const gchar *sub = NULL;
1068             const gchar *uri;
1069             const gchar *directory = NULL;
1070             gint dvd_chapter;
1071 
1072             uri = parole_file_get_uri(file);
1073             directory = parole_file_get_directory(file);
1074 
1075             if (g_str_has_prefix(uri, "dvd")) {
1076                 parole_player_dvd_reset(player);
1077                 player->priv->row = gtk_tree_row_reference_copy(row);
1078                 dvd_chapter = parole_file_get_dvd_chapter(file);
1079                 parole_gst_set_dvd_chapter(PAROLE_GST(player->priv->gst), dvd_chapter);
1080                 g_object_unref(file);
1081                 return;
1082             }
1083             parole_player_reset(player);
1084             player->priv->row = gtk_tree_row_reference_copy(row);
1085 
1086             if (g_str_has_prefix(uri, "file:/")) {
1087                 if (parole_file_filter(player->priv->video_filter, file)) {
1088                         sub = parole_file_get_custom_subtitles(file);
1089                         parole_gst_set_custom_subtitles(PAROLE_GST(player->priv->gst), sub);
1090                         if (sub == NULL)
1091                             sub = parole_get_subtitle_path(uri);
1092                 }
1093             }
1094             TRACE("Trying to play media file %s", uri);
1095             TRACE("File content type %s", parole_file_get_content_type(file));
1096 
1097 
1098             parole_gst_play_uri(PAROLE_GST(player->priv->gst),
1099                                  parole_file_get_uri(file),
1100                                  sub);
1101 
1102             gtk_window_set_title(GTK_WINDOW(player->priv->window),
1103                                  parole_media_list_get_row_name(player->priv->list, player->priv->row));
1104 
1105             if ( directory ) {
1106                 g_object_set(G_OBJECT(player->priv->conf),
1107                     "media-chooser-folder", directory,
1108                     NULL);
1109             }
1110 
1111             g_object_unref(file);
1112         }
1113     }
1114 }
1115 
1116 static void
parole_player_disc_selected_cb(ParoleDisc * disc,const gchar * uri,const gchar * device,ParolePlayer * player)1117 parole_player_disc_selected_cb(ParoleDisc *disc, const gchar *uri, const gchar *device, ParolePlayer *player) {
1118     parole_player_reset(player);
1119     parole_gst_play_device_uri(PAROLE_GST(player->priv->gst), uri, device);
1120     player->priv->current_media_type = parole_gst_get_current_stream_type(PAROLE_GST(player->priv->gst));
1121 
1122     if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_CDDA ) {
1123         player->priv->wait_for_gst_disc_info = TRUE;
1124         if ( player->priv->row ) {
1125             parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
1126             gtk_tree_row_reference_free(player->priv->row);
1127             player->priv->row = NULL;
1128         }
1129         TRACE("CLEAR PLAYLIST");
1130         parole_media_list_clear_list(player->priv->list);
1131         TRACE("END CLEAR PLAYLIST");
1132     } else if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD ) {
1133         parole_media_list_set_playlist_view(player->priv->list, PAROLE_MEDIA_LIST_PLAYLIST_VIEW_DISC);
1134         gtk_widget_show(GTK_WIDGET(player->priv->dvd_menu));
1135         g_simple_action_set_enabled(player->priv->toggle_repeat_action, FALSE);
1136         g_simple_action_set_enabled(player->priv->toggle_shuffle_action, FALSE);
1137     }
1138 }
1139 
1140 static void
parole_player_uri_opened_cb(ParoleMediaList * list,const gchar * uri,ParolePlayer * player)1141 parole_player_uri_opened_cb(ParoleMediaList *list, const gchar *uri, ParolePlayer *player) {
1142     parole_player_reset(player);
1143     parole_gst_play_uri(PAROLE_GST(player->priv->gst), uri, NULL);
1144 }
1145 
1146 static void
parole_player_iso_opened_cb(ParoleMediaList * list,const gchar * uri,ParolePlayer * player)1147 parole_player_iso_opened_cb(ParoleMediaList *list, const gchar *uri, ParolePlayer *player) {
1148     parole_player_reset(player);
1149     parole_player_disc_selected_cb(NULL, uri, NULL, player);
1150 }
1151 
1152 static void
parole_player_key_forward_cb(ParoleMediaList * list,GdkEventKey * event,ParolePlayer * player)1153 parole_player_key_forward_cb(ParoleMediaList *list, GdkEventKey *event, ParolePlayer *player) {
1154     parole_player_handle_key_value(event->keyval, event->state, player);
1155     gdk_event_free ((GdkEvent *)event);
1156 }
1157 
1158 static void
parole_player_recent_menu_clear_activated_cb(GtkWidget * widget,ParolePlayer * player)1159 parole_player_recent_menu_clear_activated_cb(GtkWidget *widget, ParolePlayer *player) {
1160     GtkWidget *dlg;
1161     GtkWidget *clear_button;
1162     gint response;
1163 
1164     dlg = gtk_message_dialog_new(GTK_WINDOW(player->priv->window),
1165                                   GTK_DIALOG_DESTROY_WITH_PARENT,
1166                                   GTK_MESSAGE_QUESTION,
1167                                   GTK_BUTTONS_NONE,
1168                                   NULL);
1169 
1170     gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dlg),
1171                                     g_strdup_printf("<big><b>%s</b></big>",
1172                                     _("Clear Recent Items")));
1173     gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dlg),
1174     _("Are you sure you wish to clear your recent items history?  This cannot be undone."));
1175 
1176     gtk_dialog_add_button(GTK_DIALOG(dlg), _("Cancel"), 0);
1177     clear_button = gtk_dialog_add_button(GTK_DIALOG(dlg),
1178                                          "edit-clear",
1179                                          1);
1180     gtk_button_set_label(GTK_BUTTON(clear_button), _("Clear Recent Items"));
1181 
1182     gtk_widget_show_all(dlg);
1183 
1184     response = gtk_dialog_run(GTK_DIALOG(dlg));
1185     if (response == 1) {
1186         GList *items, *l;
1187 
1188         items = gtk_recent_manager_get_items(player->priv->recent);
1189         for (l = items; l != NULL; l = l->next) {
1190             GtkRecentInfo *info = l->data;
1191 
1192             if (gtk_recent_info_has_application(info, "parole"))
1193                 gtk_recent_manager_remove_item(player->priv->recent,
1194                                                 gtk_recent_info_get_uri(info),
1195                                                 NULL);
1196         }
1197 
1198         g_list_free_full(items, (GDestroyNotify)gtk_recent_info_unref);
1199     }
1200     gtk_widget_destroy(dlg);
1201 }
1202 
1203 static void
parole_player_recent_menu_item_activated_cb(GtkWidget * widget,ParolePlayer * player)1204 parole_player_recent_menu_item_activated_cb(GtkWidget *widget, ParolePlayer *player) {
1205     gchar *uri;
1206     gchar *filename;
1207     gchar *filenames[] = {NULL, NULL};
1208     ParoleMediaList *list;
1209 
1210     uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(widget));
1211 
1212     filename = g_filename_from_uri(uri, NULL, NULL);
1213 
1214     if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
1215         gtk_recent_manager_add_item(player->priv->recent, uri);
1216 
1217         filenames[0] = g_strdup(filename);
1218 
1219         list = parole_player_get_media_list(player);
1220         parole_media_list_add_files(list, filenames, FALSE);
1221 
1222         g_free(filenames[0]);
1223     }
1224 
1225     g_free(filename);
1226     g_free(uri);
1227 }
1228 
1229 static void
parole_player_media_cursor_changed_cb(ParoleMediaList * list,gboolean media_selected,ParolePlayer * player)1230 parole_player_media_cursor_changed_cb(ParoleMediaList *list, gboolean media_selected, ParolePlayer *player) {
1231     gboolean enabled;
1232 
1233     /* Play/Pause */
1234     if (player->priv->state < PAROLE_STATE_PAUSED) {
1235         enabled = media_selected || !parole_media_list_is_empty(player->priv->list);
1236         gtk_widget_set_sensitive(player->priv->playpause_button, enabled);
1237         g_simple_action_set_enabled(player->priv->media_playpause_action, enabled);
1238     }
1239 
1240     enabled = parole_media_list_get_playlist_count(player->priv->list) > 1;
1241 
1242     /* Previous */
1243     gtk_widget_set_sensitive(player->priv->previous_button, enabled);
1244     g_simple_action_set_enabled(player->priv->media_previous_action, enabled);
1245 
1246     /* Next */
1247     gtk_widget_set_sensitive(player->priv->next_button, enabled);
1248     g_simple_action_set_enabled(player->priv->media_next_action, enabled);
1249 }
1250 
1251 static void
parole_player_media_progressed_cb(ParoleGst * gst,const ParoleStream * stream,gint64 value,ParolePlayer * player)1252 parole_player_media_progressed_cb(ParoleGst *gst, const ParoleStream *stream, gint64 value, ParolePlayer *player) {
1253 #ifdef DEBUG
1254     g_return_if_fail(value > 0);
1255 #endif
1256 
1257     if (!player->priv->user_seeking) {
1258         parole_player_change_range_value(player, value);
1259     }
1260 }
1261 
1262 static void
parole_player_seekable_notify(ParoleStream * stream,GParamSpec * spec,ParolePlayer * player)1263 parole_player_seekable_notify(ParoleStream *stream, GParamSpec *spec, ParolePlayer *player) {
1264     gboolean seekable;
1265 
1266     g_object_get(G_OBJECT(stream),
1267                  "seekable", &seekable,
1268                  NULL);
1269 
1270     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->range), seekable ? NULL : _("Media stream is not seekable"));
1271     gtk_widget_set_sensitive(GTK_WIDGET(player->priv->range), seekable);
1272 }
1273 
1274 static void
parole_player_set_playpause_button_from_stock(ParolePlayer * player,const gchar * stock_id)1275 parole_player_set_playpause_button_from_stock(ParolePlayer *player, const gchar *stock_id) {
1276     gchar *icon_name = NULL, *tooltip = NULL;
1277 
1278     if (g_strcmp0(stock_id, "gtk-media-play") == 0) {
1279         icon_name = g_strdup("media-playback-start-symbolic");
1280         tooltip = _("Play");
1281     } else if (g_strcmp0(stock_id, "gtk-media-pause") == 0) {
1282         icon_name = g_strdup("media-playback-pause-symbolic");
1283         tooltip = _("Pause");
1284     }
1285 
1286     gtk_image_set_from_icon_name(GTK_IMAGE(player->priv->playpause_image), icon_name, 24);
1287     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->playpause_button), tooltip);
1288 }
1289 
1290 static void
parole_player_save_uri(ParolePlayer * player,const ParoleStream * stream)1291 parole_player_save_uri(ParolePlayer *player, const ParoleStream *stream) {
1292     ParoleMediaType media_type;
1293     gchar *uri;
1294     gboolean save = TRUE;
1295     gchar **lines = NULL;
1296     guint i;
1297 
1298     g_object_get(G_OBJECT(stream),
1299                  "uri", &uri,
1300                  NULL);
1301 
1302     g_object_get(G_OBJECT(stream),
1303                  "media-type", &media_type,
1304                  NULL);
1305 
1306     if ( media_type == PAROLE_MEDIA_TYPE_LOCAL_FILE ) {
1307         gtk_recent_manager_add_item(player->priv->recent, uri);
1308         goto out;
1309     }
1310 
1311     lines = parole_get_history();
1312 
1313     if (lines) {
1314         for (i = 0; lines[i]; i++) {
1315             if (!g_strcmp0(lines[i], uri)) {
1316                 save = FALSE;
1317                 break;
1318             }
1319         }
1320     }
1321 
1322     if ( media_type != PAROLE_MEDIA_TYPE_CDDA && media_type != PAROLE_MEDIA_TYPE_DVD ) {
1323         if ( save ) {
1324             parole_insert_line_history(uri);
1325         }
1326     }
1327 
1328     g_strfreev(lines);
1329 out:
1330     g_free(uri);
1331 }
1332 
1333 static void
parole_player_playing(ParolePlayer * player,const ParoleStream * stream)1334 parole_player_playing(ParolePlayer *player, const ParoleStream *stream) {
1335     gint64 duration;
1336     gboolean seekable;
1337     gboolean live;
1338     gint height, width;
1339 
1340     gtk_widget_set_sensitive(player->priv->playpause_button, TRUE);
1341 
1342     parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_PLAYING);
1343 
1344     g_object_get(G_OBJECT(stream),
1345                  "seekable", &seekable,
1346                  "duration", &duration,
1347                  "video-width", &width,
1348                  "video-height", &height,
1349                  "live", &live,
1350                  NULL);
1351 
1352 #ifdef HAVE_CLUTTER
1353     if (player->priv->use_clutter)
1354         parole_clutter_set_video_dimensions(PAROLE_CLUTTER(player->priv->clutter), width, height);
1355 #endif
1356 
1357     if (player->priv->wait_for_gst_disc_info == TRUE) {
1358         parole_media_list_add_cdda_tracks(player->priv->list, parole_gst_get_num_tracks(PAROLE_GST(player->priv->gst)));
1359         player->priv->wait_for_gst_disc_info = FALSE;
1360     }
1361 
1362     g_simple_action_set_enabled(player->priv->media_playpause_action, TRUE);
1363 
1364     parole_player_set_playpause_button_from_stock(player, "gtk-media-pause");
1365 
1366     gtk_widget_set_sensitive(player->priv->range, seekable);
1367 
1368     player->priv->internal_range_change = TRUE;
1369     if (live || duration == 0) {
1370         parole_player_change_range_value(player, 0);
1371         gtk_widget_set_visible(player->priv->label_duration, FALSE);
1372         gtk_widget_set_visible(player->priv->label_divider, FALSE);
1373         gtk_widget_set_visible(player->priv->label_elapsed, FALSE);
1374     } else {
1375         gtk_range_set_range(GTK_RANGE(player->priv->range), 0, duration);
1376         gtk_widget_set_visible(player->priv->label_duration, TRUE);
1377         gtk_widget_set_visible(player->priv->label_divider, player->priv->mini_mode);
1378         gtk_widget_set_visible(player->priv->label_elapsed, TRUE);
1379     }
1380 
1381     if (duration != 0) {
1382         gchar dur_text[128];
1383         get_time_string(dur_text, duration);
1384 
1385         gtk_label_set_text(GTK_LABEL(player->priv->label_duration), dur_text);
1386         if ( player->priv->current_media_type != PAROLE_MEDIA_TYPE_DVD ) {
1387             parole_media_list_set_row_length(player->priv->list,
1388                                               player->priv->row,
1389                                               dur_text);
1390         }
1391     }
1392 
1393     player->priv->internal_range_change = FALSE;
1394 
1395     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->range), seekable ? NULL : _("Media stream is not seekable"));
1396 
1397     parole_player_save_uri(player, stream);
1398     parole_media_list_select_row(player->priv->list, player->priv->row);
1399     if (!parole_player_get_playlist_visible (player)) {
1400         gtk_widget_grab_focus(player->priv->gst);
1401     }
1402     parole_player_update_languages(player, PAROLE_GST(player->priv->gst));
1403 
1404     parole_player_schedule_hide_controls(player, -1);
1405 }
1406 
1407 static void
parole_player_paused(ParolePlayer * player)1408 parole_player_paused(ParolePlayer *player) {
1409     TRACE("Player paused");
1410 
1411     parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_PAUSED);
1412 
1413     g_simple_action_set_enabled(player->priv->media_playpause_action, TRUE);
1414 
1415     if (player->priv->user_seeking == FALSE) {
1416         parole_player_set_playpause_button_from_stock(player, "gtk-media-play");
1417     }
1418 }
1419 
1420 static void
parole_player_quit(ParolePlayer * player)1421 parole_player_quit(ParolePlayer *player) {
1422     parole_media_list_save_list(player->priv->list);
1423     parole_gst_shutdown(PAROLE_GST(player->priv->gst));
1424     gtk_widget_destroy(player->priv->window);
1425     g_object_unref(player);
1426     gtk_main_quit();
1427 }
1428 
1429 static void
parole_player_stopped(ParolePlayer * player)1430 parole_player_stopped(ParolePlayer *player) {
1431     GdkPixbuf *logo;
1432     gchar dur_text[128];
1433     TRACE("Player stopped");
1434 
1435     g_simple_action_set_enabled(player->priv->media_playpause_action,
1436                               parole_media_list_is_selected_row(player->priv->list) ||
1437                               !parole_media_list_is_empty(player->priv->list));
1438 
1439     gtk_window_set_title(GTK_WINDOW(player->priv->window), _("Parole Media Player"));
1440 
1441     gtk_widget_hide(player->priv->videobox);
1442     gtk_widget_hide(player->priv->audiobox);
1443 
1444     logo = gdk_pixbuf_new_from_file(g_strdup_printf("%s/replay.png", PIXMAPS_DIR), NULL);
1445     gtk_image_set_from_pixbuf(GTK_IMAGE(player->priv->logo_image), logo);
1446     gtk_widget_show(player->priv->logo_image);
1447 
1448     get_time_string(dur_text, 0);
1449     gtk_label_set_text(GTK_LABEL(player->priv->label_duration), dur_text);
1450 
1451     parole_player_change_range_value(player, 0);
1452     gtk_widget_set_sensitive(player->priv->range, FALSE);
1453 
1454     parole_player_set_playpause_button_from_stock(player, "gtk-media-play");
1455 
1456     parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
1457 
1458     if ( player->priv->exit ) {
1459         parole_player_quit(player);
1460     }
1461 }
1462 
1463 static void
parole_player_play_selected_row(ParolePlayer * player)1464 parole_player_play_selected_row(ParolePlayer *player) {
1465     GtkTreeRowReference *row;
1466 
1467     row = parole_media_list_get_selected_row(player->priv->list);
1468 
1469     if ( row == NULL )
1470         row = parole_media_list_get_first_row(player->priv->list);
1471 
1472     if ( row )
1473         parole_player_media_activated_cb(player->priv->list, row, player);
1474 }
1475 
1476 static void
parole_player_play_next(ParolePlayer * player,gboolean allow_shuffle)1477 parole_player_play_next(ParolePlayer *player, gboolean allow_shuffle) {
1478     gboolean repeat, shuffle;
1479     GtkTreeRowReference *row;
1480 
1481     if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD ) {
1482         parole_gst_next_dvd_chapter(PAROLE_GST(player->priv->gst));
1483         return;
1484     }
1485 
1486     g_object_get(G_OBJECT(player->priv->conf),
1487                   "shuffle", &shuffle,
1488                   "repeat", &repeat,
1489                   NULL);
1490 
1491     if ( player->priv->row ) {
1492         parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
1493 
1494         row = parole_media_list_get_next_row(player->priv->list, player->priv->row, repeat);
1495 
1496         if ( row ) {
1497             parole_player_media_activated_cb(player->priv->list, row, player);
1498             return;
1499         } else {
1500             TRACE("No remaining media in the list");
1501             gtk_tree_row_reference_free(player->priv->row);
1502             player->priv->row = NULL;
1503         }
1504     }
1505 
1506     parole_gst_stop(PAROLE_GST(player->priv->gst));
1507 }
1508 
1509 static void
parole_player_play_prev(ParolePlayer * player)1510 parole_player_play_prev(ParolePlayer *player) {
1511     gboolean repeat;
1512     GtkTreeRowReference *row;
1513 
1514     if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD ) {
1515         parole_gst_prev_dvd_chapter(PAROLE_GST(player->priv->gst));
1516         return;
1517     }
1518 
1519     g_object_get(G_OBJECT(player->priv->conf),
1520                  "repeat", &repeat,
1521                  NULL);
1522 
1523     if ( player->priv->row ) {
1524         parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
1525 
1526         row = parole_media_list_get_prev_row(player->priv->list, player->priv->row, repeat);
1527 
1528         if ( row ) {
1529             parole_player_media_activated_cb(player->priv->list, row, player);
1530             return;
1531         } else {
1532             TRACE("No remaining media in the list");
1533             gtk_tree_row_reference_free(player->priv->row);
1534             player->priv->row = NULL;
1535         }
1536     }
1537 
1538     parole_gst_stop(PAROLE_GST(player->priv->gst));
1539 }
1540 
1541 static void
parole_player_reset_saver_changed(ParolePlayer * player,const ParoleStream * stream)1542 parole_player_reset_saver_changed(ParolePlayer *player, const ParoleStream *stream) {
1543     gboolean reset_saver;
1544 
1545     TRACE("Start");
1546 
1547     g_object_get(G_OBJECT(player->priv->conf),
1548                   "reset-saver", &reset_saver,
1549                   NULL);
1550 
1551     if ( !reset_saver ) {
1552         parole_screen_saver_uninhibit(player->priv->screen_saver, GTK_WINDOW(player->priv->window));
1553         if (player->priv->inhibit_cookie > 0)
1554             parole_power_manager_uninhibit (player->priv->connection, player->priv->inhibit_cookie);
1555         player->priv->inhibit_cookie = 0;
1556     } else if ( player->priv->state ==  PAROLE_STATE_PLAYING ) {
1557         gboolean has_video;
1558 
1559         g_object_get(G_OBJECT(stream),
1560                       "has-video", &has_video,
1561                       NULL);
1562 
1563         if ( has_video ) {
1564             parole_screen_saver_inhibit (player->priv->screen_saver, GTK_WINDOW(player->priv->window));
1565             if (player->priv->inhibit_cookie == 0)
1566                 player->priv->inhibit_cookie = parole_power_manager_inhibit (player->priv->connection);
1567         }
1568     } else {
1569         parole_screen_saver_uninhibit(player->priv->screen_saver, GTK_WINDOW(player->priv->window));
1570         if (player->priv->inhibit_cookie > 0)
1571             parole_power_manager_uninhibit (player->priv->connection, player->priv->inhibit_cookie);
1572         player->priv->inhibit_cookie = 0;
1573     }
1574 }
1575 
1576 static void
parole_player_media_state_cb(ParoleGst * gst,const ParoleStream * stream,ParoleState state,ParolePlayer * player)1577 parole_player_media_state_cb(ParoleGst *gst, const ParoleStream *stream, ParoleState state, ParolePlayer *player) {
1578     PAROLE_DEBUG_ENUM("State callback", state, PAROLE_ENUM_TYPE_STATE);
1579 
1580     player->priv->state = state;
1581     parole_player_reset_saver_changed(player, stream);
1582 
1583     if ( state == PAROLE_STATE_PLAYING ) {
1584         parole_player_playing(player, stream);
1585         parole_player_show_audiobox(player);
1586     } else if ( state == PAROLE_STATE_PAUSED ) {
1587         parole_player_paused(player);
1588     } else if ( state == PAROLE_STATE_STOPPED ) {
1589         parole_player_stopped(player);
1590     /* PAROLE_STATE_ABOUT_TO_FINISH is used for continuous playback of audio CDs */
1591     } else if ( state == PAROLE_STATE_ABOUT_TO_FINISH ) {
1592 #ifdef DEBUG
1593         TRACE("***Playback about to finish***");
1594 #endif
1595         if ( player->priv->current_media_type == PAROLE_MEDIA_TYPE_DVD )
1596             parole_player_play_next(player, TRUE);
1597     } else if ( state == PAROLE_STATE_PLAYBACK_FINISHED ) {
1598 #ifdef DEBUG
1599         TRACE("***Playback finished***");
1600 #endif
1601         parole_player_play_next(player, TRUE);
1602     }
1603 }
1604 
1605 static void
on_infobar_close_clicked(GtkButton * button,ParolePlayer * player)1606 on_infobar_close_clicked(GtkButton *button, ParolePlayer *player) {
1607     gtk_widget_hide(player->priv->infobar);
1608 }
1609 
1610 static void
parole_player_repeat_state_changed(GSimpleAction * simple,GVariant * value,ParolePlayer * player)1611 parole_player_repeat_state_changed(GSimpleAction *simple, GVariant *value, ParolePlayer *player) {
1612     gboolean active = g_simple_toggle_action_get_active(simple);
1613     block_playlist_updates = TRUE;
1614 
1615     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(player->priv->repeat_menu_item)) != active) {
1616         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(player->priv->repeat_menu_item), active);
1617     }
1618 
1619     block_playlist_updates = FALSE;
1620 }
1621 
1622 static void
parole_player_shuffle_state_changed(GSimpleAction * simple,GVariant * value,ParolePlayer * player)1623 parole_player_shuffle_state_changed(GSimpleAction *simple, GVariant *value, ParolePlayer *player) {
1624     gboolean active = g_simple_toggle_action_get_active(simple);
1625     block_playlist_updates = TRUE;
1626 
1627     if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(player->priv->shuffle_menu_item)) != active) {
1628         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(player->priv->shuffle_menu_item), active);
1629     }
1630 
1631     block_playlist_updates = FALSE;
1632 }
1633 
1634 static void
parole_player_toggle_playpause(ParolePlayer * player)1635 parole_player_toggle_playpause(ParolePlayer *player) {
1636     if ( player->priv->state == PAROLE_STATE_PLAYING )
1637         parole_gst_pause(PAROLE_GST(player->priv->gst));
1638     else if ( player->priv->state == PAROLE_STATE_PAUSED )
1639         parole_gst_resume(PAROLE_GST(player->priv->gst));
1640     else
1641         parole_player_play_selected_row(player);
1642 }
1643 
1644 void
parole_player_playpause_action_cb(GSimpleAction * action)1645 parole_player_playpause_action_cb(GSimpleAction *action) {
1646     parole_player_toggle_playpause(parole_player);
1647 }
1648 
1649 void
parole_player_pause_clicked(GtkButton * button,ParolePlayer * player)1650 parole_player_pause_clicked(GtkButton *button, ParolePlayer *player) {
1651     parole_gst_pause(PAROLE_GST(player->priv->gst));
1652 }
1653 
parole_player_next_action_cb(GSimpleAction * action)1654 void parole_player_next_action_cb(GSimpleAction *action) {
1655     parole_player_play_next(parole_player, TRUE);
1656 }
1657 
parole_player_previous_action_cb(GSimpleAction * action)1658 void parole_player_previous_action_cb(GSimpleAction *action) {
1659     parole_player_play_prev(parole_player);
1660 }
1661 
parole_player_get_action(ParolePlayerAction action)1662 GSimpleAction *parole_player_get_action(ParolePlayerAction action) {
1663     switch (action) {
1664         case PAROLE_PLAYER_ACTION_PLAYPAUSE:
1665             return playpause_action;
1666             break;
1667         case PAROLE_PLAYER_ACTION_PREVIOUS:
1668             return previous_action;
1669             break;
1670         case PAROLE_PLAYER_ACTION_NEXT:
1671             return next_action;
1672             break;
1673         default:
1674             return NULL;
1675     }
1676 }
1677 
parole_player_seekf_cb(GtkWidget * widget,ParolePlayer * player,gdouble seek)1678 void parole_player_seekf_cb(GtkWidget *widget, ParolePlayer *player, gdouble seek) {
1679     seek = parole_gst_get_stream_position(PAROLE_GST(player->priv->gst) ) + seek;
1680     parole_gst_seek(PAROLE_GST(player->priv->gst), seek);
1681     parole_player_change_range_value(player, seek);
1682 }
1683 
parole_player_seekb_cb(GtkWidget * widget,ParolePlayer * player,gdouble seek)1684 void parole_player_seekb_cb(GtkWidget *widget, ParolePlayer *player, gdouble seek) {
1685     seek = parole_gst_get_stream_position(PAROLE_GST(player->priv->gst) ) - seek;
1686     if ( seek < 0 ) { seek = 0; }
1687     parole_gst_seek(PAROLE_GST(player->priv->gst), seek);
1688     parole_player_change_range_value(player, seek);
1689 }
1690 
1691 gboolean
parole_player_range_button_release(GtkWidget * widget,GdkEventButton * ev,ParolePlayer * player)1692 parole_player_range_button_release(GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player) {
1693     ev->button = 2;
1694 
1695     player->priv->user_seeking = FALSE;
1696 
1697     return FALSE;
1698 }
1699 
1700 gboolean
parole_player_range_button_press(GtkWidget * widget,GdkEventButton * ev,ParolePlayer * player)1701 parole_player_range_button_press(GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player) {
1702     ev->button = 2;
1703 
1704     player->priv->user_seeking = TRUE;
1705 
1706     return FALSE;
1707 }
1708 
1709 void
parole_player_range_value_changed(GtkRange * range,ParolePlayer * player)1710 parole_player_range_value_changed(GtkRange *range, ParolePlayer *player) {
1711     gdouble value;
1712 
1713     if ( !player->priv->internal_range_change ) {
1714         value = gtk_range_get_value(GTK_RANGE(range));
1715         player->priv->user_seeking = TRUE;
1716         TRACE("Sending a seek request with value :%e", value);
1717         parole_gst_seek(PAROLE_GST(player->priv->gst), value);
1718         player->priv->user_seeking = FALSE;
1719     }
1720 }
1721 
1722 static void
parole_player_error_cb(ParoleGst * gst,const gchar * error,ParolePlayer * player)1723 parole_player_error_cb(ParoleGst *gst, const gchar *error, ParolePlayer *player) {
1724     parole_dialog_error(GTK_WINDOW(player->priv->window), _("GStreamer backend error"), error);
1725     parole_screen_saver_uninhibit(player->priv->screen_saver, GTK_WINDOW(player->priv->window));
1726     parole_player_stopped(player);
1727 }
1728 
1729 static gchar *
parole_player_get_filename_from_uri(gchar * uri)1730 parole_player_get_filename_from_uri(gchar *uri) {
1731     gchar *filename = NULL;
1732     gchar *scheme;
1733 
1734     scheme = g_uri_parse_scheme(uri);
1735     if (strcmp(scheme, "http") == 0 || strcmp(scheme, "https") == 0 || strcmp(scheme, "ftp") == 0) {
1736         GRegex *regex;
1737         GMatchInfo *match_info;
1738 
1739         regex = g_regex_new("^.*://.*/(?<filename>[^?#/]+)", 0, 0, NULL);
1740         g_regex_match(regex, uri, 0, &match_info);
1741         if (g_match_info_matches(match_info)) {
1742             gchar *word = g_match_info_fetch_named(match_info, "filename");
1743             filename = g_strdup(word);
1744             g_free(word);
1745         }
1746         g_match_info_free(match_info);
1747         g_regex_unref(regex);
1748     } else {
1749         gchar *decoded;
1750         decoded = g_filename_from_uri(uri, NULL, NULL);
1751         filename = g_path_get_basename(decoded);
1752         g_free(decoded);
1753     }
1754 
1755     g_free(scheme);
1756 
1757     return filename;
1758 }
1759 
1760 static void
parole_player_media_tag_cb(ParoleGst * gst,const ParoleStream * stream,ParolePlayer * player)1761 parole_player_media_tag_cb(ParoleGst *gst, const ParoleStream *stream, ParolePlayer *player) {
1762     gchar *title;
1763     gchar *album;
1764     gchar *artist;
1765     gchar *year;
1766     gchar *uri;
1767     gchar *filename;
1768     GdkPixbuf *image = NULL;
1769 
1770     if ( player->priv->row ) {
1771         g_object_get(G_OBJECT(stream),
1772                       "title", &title,
1773                       "album", &album,
1774                       "artist", &artist,
1775                       "year", &year,
1776                       "uri", &uri,
1777                       NULL);
1778 
1779         if ( title ) {
1780             parole_media_list_set_row_name(player->priv->list, player->priv->row, title);
1781             gtk_window_set_title(GTK_WINDOW(player->priv->window), title);
1782             gtk_label_set_markup(
1783                 GTK_LABEL(player->priv->audiobox_title),
1784                 g_markup_printf_escaped(
1785                     "<span color='#F4F4F4'><b><big>%s</big></b></span>",
1786                     title));
1787             g_free(title);
1788         } else {
1789             /* No ID3, no problem! Show the filename instead */
1790             filename = parole_player_get_filename_from_uri(uri);
1791             if ( filename ) {
1792                 gtk_window_set_title(GTK_WINDOW(player->priv->window), filename);
1793                 gtk_label_set_markup(
1794                     GTK_LABEL(player->priv->audiobox_title),
1795                     g_markup_printf_escaped(
1796                         "<span color='#F4F4F4'><b><big>%s</big></b></span>",
1797                         filename));
1798                 g_free(filename);
1799             } else {
1800                 gtk_window_set_title(GTK_WINDOW(player->priv->window), _("Parole Media Player"));
1801                 gtk_label_set_markup(
1802                     GTK_LABEL(player->priv->audiobox_title),
1803                     g_markup_printf_escaped(
1804                         "<span color='#F4F4F4'><b><big>%s</big></b></span>",
1805                         _("Unknown Song")));
1806             }
1807         }
1808         g_free(uri);
1809 
1810         if ( album ) {
1811             if (year)
1812                 gtk_label_set_markup(
1813                     GTK_LABEL(player->priv->audiobox_album),
1814                     g_markup_printf_escaped(
1815                         "<big><span color='#BBBBBB'><i>%s</i></span> <span color='#F4F4F4'>%s (%s)</span></big>",
1816                         _("on"), album, year));
1817             else
1818                 gtk_label_set_markup(
1819                     GTK_LABEL(player->priv->audiobox_album),
1820                     g_markup_printf_escaped(
1821                         "<big><span color='#BBBBBB'><i>%s</i></span> <span color='#F4F4F4'>%s</span></big>",
1822                         _("on"), album));
1823 
1824             g_free(album);
1825         } else {
1826             gtk_label_set_markup(
1827                 GTK_LABEL(player->priv->audiobox_album),
1828                 g_markup_printf_escaped(
1829                     "<big><span color='#BBBBBB'><i>%s</i></span> <span color='#F4F4F4'>%s</span></big>",
1830                     _("on"), _("Unknown Album")));
1831         }
1832 
1833         if (year)
1834             g_free(year);
1835 
1836         if ( artist ) {
1837             gtk_label_set_markup(
1838                 GTK_LABEL(player->priv->audiobox_artist),
1839                 g_markup_printf_escaped(
1840                     "<big><span color='#BBBBBB'><i>%s</i></span> <span color='#F4F4F4'>%s</span></big>",
1841                     _("by"), artist));
1842             g_free(artist);
1843         } else {
1844             gtk_label_set_markup(
1845                 GTK_LABEL(player->priv->audiobox_artist),
1846                 g_markup_printf_escaped(
1847                     "<big><span color='#BBBBBB'><i>%s</i></span> <span color='#F4F4F4'>%s</span></big>",
1848                     _("by"), _("Unknown Artist")));
1849         }
1850 
1851         image = parole_stream_get_image(G_OBJECT(stream));
1852         if (image) {
1853             gtk_image_set_from_pixbuf(GTK_IMAGE(player->priv->audiobox_cover), image);
1854             g_object_unref(image);
1855         } else {
1856             gtk_image_set_from_icon_name(GTK_IMAGE(player->priv->audiobox_cover),
1857                                          "audio-x-generic-symbolic",
1858                                          GTK_ICON_SIZE_ARTWORK_FALLBACK);
1859         }
1860     }
1861 }
1862 
1863 static void
parole_player_buffering_cb(ParoleGst * gst,const ParoleStream * stream,gint percentage,ParolePlayer * player)1864 parole_player_buffering_cb(ParoleGst *gst, const ParoleStream *stream, gint percentage, ParolePlayer *player) {
1865     gchar *buff;
1866 
1867     if ( percentage == 100 ) {
1868         player->priv->buffering = FALSE;
1869         parole_gst_resume(PAROLE_GST(player->priv->gst));
1870         gtk_widget_hide(player->priv->progressbar_buffering);
1871         gtk_widget_show(player->priv->label_duration);
1872         if ( player->priv->mini_mode )
1873             gtk_widget_show(player->priv->label_divider);
1874         else
1875             gtk_widget_show(player->priv->range);
1876         gtk_widget_show(player->priv->label_elapsed);
1877     } else {
1878         player->priv->buffering = TRUE;
1879 
1880         if ( player->priv->state == PAROLE_STATE_PLAYING )
1881             parole_gst_pause(PAROLE_GST(player->priv->gst));
1882 
1883         buff = g_strdup_printf("%s(%d%%)", _("Buffering"), percentage);
1884 
1885         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(player->priv->progressbar_buffering), buff);
1886         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(player->priv->progressbar_buffering), (gdouble)percentage / 100);
1887         gtk_widget_hide(player->priv->label_duration);
1888         gtk_widget_hide(player->priv->label_divider);
1889         gtk_widget_hide(player->priv->range);
1890         gtk_widget_hide(player->priv->label_elapsed);
1891         gtk_widget_show(player->priv->progressbar_buffering);
1892         g_free(buff);
1893     }
1894 }
1895 
1896 static void
parole_player_dvd_chapter_count_change_cb(ParoleGst * gst,gint chapter_count,ParolePlayer * player)1897 parole_player_dvd_chapter_count_change_cb(ParoleGst *gst, gint chapter_count, ParolePlayer *player) {
1898     gtk_tree_row_reference_free(player->priv->row);
1899     player->priv->row = NULL;
1900 
1901     /* FIXME Cannot clear list prior to adding new chapters. */
1902     // parole_media_list_clear_list (player->priv->list);
1903 
1904     parole_media_list_add_dvd_chapters(player->priv->list, chapter_count);
1905     parole_player_update_chapters(player, chapter_count);
1906 }
1907 
1908 static void
parole_player_dvd_chapter_change_cb(ParoleGst * gst,gint chapter_count,ParolePlayer * player)1909 parole_player_dvd_chapter_change_cb(ParoleGst *gst, gint chapter_count, ParolePlayer *player) {
1910     parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_NONE);
1911 
1912     player->priv->row = parole_media_list_get_row_n(player->priv->list, chapter_count-1);
1913 
1914     parole_media_list_set_row_playback_state(player->priv->list, player->priv->row, PAROLE_MEDIA_STATE_PLAYING);
1915     parole_media_list_select_row(player->priv->list, player->priv->row);
1916 }
1917 
parole_player_delete_event_cb(GtkWidget * widget,GdkEvent * ev,ParolePlayer * player)1918 gboolean parole_player_delete_event_cb(GtkWidget *widget, GdkEvent *ev, ParolePlayer *player) {
1919     parole_window_busy_cursor(gtk_widget_get_window(GTK_WIDGET(player->priv->window)));
1920 
1921     player->priv->exit = TRUE;
1922     parole_gst_terminate(PAROLE_GST(player->priv->gst));
1923 
1924     return TRUE;
1925 }
1926 
1927 void
parole_player_destroy_cb(GObject * window,ParolePlayer * player)1928 parole_player_destroy_cb(GObject *window, ParolePlayer *player) {
1929 }
1930 
1931 gboolean
parole_player_window_state_event(GtkWidget * widget,GdkEventWindowState * event,ParolePlayer * player)1932 parole_player_window_state_event(GtkWidget *widget,
1933                                  GdkEventWindowState *event,
1934                                  ParolePlayer *player) {
1935     gboolean fullscreen = FALSE;
1936 
1937     if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
1938         /* Restore the previously saved window size if maximized */
1939         g_object_set(G_OBJECT(player->priv->conf),
1940                       "window-width", player->priv->last_w,
1941                       "window-height", player->priv->last_h,
1942                       "window-maximized", TRUE,
1943                       NULL);
1944     } else {
1945         g_object_set(G_OBJECT(player->priv->conf),
1946                       "window-maximized", FALSE,
1947                       NULL);
1948     }
1949 
1950     if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
1951         fullscreen = TRUE;
1952 
1953     if (player->priv->full_screen != fullscreen)
1954         parole_player_reset_controls(player, fullscreen);
1955 
1956     return TRUE;
1957 }
1958 
1959 /**
1960  * parole_player_reset_controls:
1961  * @player     : the #ParolePlayer instance
1962  * @fullscreen : %TRUE if player should be fullscreen, else %FALSE
1963  *
1964  * Reset the player controls based on existing conditions.
1965  **/
1966 void
parole_player_reset_controls(ParolePlayer * player,gboolean fullscreen)1967 parole_player_reset_controls(ParolePlayer *player, gboolean fullscreen) {
1968     static gint current_page = 0;
1969 
1970     gboolean always_hide_menubar = FALSE;
1971 
1972     gint h, w;
1973 
1974     g_object_get(G_OBJECT(player->priv->conf),
1975                  "always-hide-menubar", &always_hide_menubar,
1976                  "window-height", &h,
1977                  "window-width", &w,
1978                  NULL);
1979 
1980     if ( player->priv->full_screen != fullscreen ) {
1981         /* If the player is in fullscreen mode, change to windowed mode. */
1982         if ( player->priv->full_screen ) {
1983             if (player->priv->show_menubar == TRUE)
1984                 gtk_widget_show(player->priv->menu_bar);
1985             else if (always_hide_menubar == TRUE)
1986                 gtk_widget_hide(player->priv->menu_bar);
1987 
1988             gtk_menu_item_set_label(GTK_MENU_ITEM(player->priv->fullscreen_menu_item), _("_Fullscreen"));
1989             gtk_widget_set_tooltip_text(player->priv->fullscreen_button, _("Fullscreen"));
1990             gtk_image_set_from_icon_name(GTK_IMAGE(player->priv->fullscreen_image), "view-fullscreen-symbolic", 24);
1991 
1992             gtk_window_unfullscreen(GTK_WINDOW(player->priv->window));
1993             gtk_notebook_set_current_page(GTK_NOTEBOOK(player->priv->playlist_nt), current_page);
1994             player->priv->full_screen = FALSE;
1995         } else {
1996             gtk_widget_hide(player->priv->menu_bar);
1997             gtk_menu_item_set_label(GTK_MENU_ITEM(player->priv->fullscreen_menu_item), _("Leave _Fullscreen"));
1998             gtk_widget_set_tooltip_text(player->priv->fullscreen_button, _("Leave Fullscreen"));
1999             gtk_image_set_from_icon_name(GTK_IMAGE(player->priv->fullscreen_image), "view-restore-symbolic", 24);
2000 
2001             current_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(player->priv->playlist_nt));
2002 
2003             gtk_window_fullscreen(GTK_WINDOW(player->priv->window));
2004             player->priv->full_screen = TRUE;
2005         }
2006     }
2007 
2008     if ( player->priv->embedded ) {
2009         gtk_widget_hide(player->priv->menu_bar);
2010         gtk_widget_hide(player->priv->fullscreen_button);
2011         gtk_widget_hide(player->priv->showhide_playlist_button);
2012     } else {
2013         if ( player->priv->mini_mode ) {
2014             gtk_widget_hide(player->priv->menu_bar);
2015             gtk_widget_hide(player->priv->fullscreen_button);
2016             gtk_widget_hide(player->priv->audiobox_text);
2017             gtk_widget_hide(player->priv->showhide_playlist_button);
2018             gtk_widget_set_halign(player->priv->audiobox_cover, GTK_ALIGN_CENTER);
2019 
2020             gtk_widget_hide(player->priv->range);
2021             gtk_widget_show(player->priv->label_divider);
2022 
2023             gtk_window_resize(GTK_WINDOW(player->priv->window), 256, 256);
2024             gtk_widget_set_size_request(GTK_WIDGET(player->priv->window), 256, 256);
2025             gtk_window_set_default_size(GTK_WINDOW(player->priv->window), 256, 256);
2026             gtk_window_set_resizable(GTK_WINDOW(player->priv->window), FALSE);
2027         } else {
2028             gtk_widget_set_size_request(GTK_WIDGET(player->priv->window), -1, -1);
2029             gtk_window_set_default_size(GTK_WINDOW(player->priv->window), -1, -1);
2030             gtk_window_set_resizable(GTK_WINDOW(player->priv->window), TRUE);
2031 
2032             gtk_widget_show(player->priv->audiobox_text);
2033             gtk_widget_show(player->priv->fullscreen_button);
2034             gtk_widget_hide(player->priv->label_divider);
2035             gtk_widget_show(player->priv->range);
2036             gtk_widget_show(player->priv->showhide_playlist_button);
2037             gtk_widget_set_halign(player->priv->audiobox_cover, GTK_ALIGN_END);
2038 
2039             if ( !player->priv->full_screen ) {
2040                 gtk_widget_show(player->priv->menu_bar);
2041                 gtk_window_resize(GTK_WINDOW(player->priv->window), w, h);
2042             }
2043         }
2044     }
2045 }
2046 
2047 void
parole_player_embedded(ParolePlayer * player)2048 parole_player_embedded(ParolePlayer *player) {
2049     if ( player->priv->embedded == TRUE )
2050         return;
2051 
2052     player->priv->embedded = TRUE;
2053 
2054     parole_player_reset_controls(player, player->priv->full_screen);
2055 }
2056 
2057 void
parole_player_full_screen(ParolePlayer * player,gboolean fullscreen)2058 parole_player_full_screen(ParolePlayer *player, gboolean fullscreen) {
2059     if ( player->priv->full_screen == fullscreen )
2060         return;
2061 
2062     parole_player_reset_controls(player, fullscreen);
2063 }
2064 
parole_player_fullscreen_action_cb(GSimpleAction * action)2065 void parole_player_fullscreen_action_cb(GSimpleAction *action) {
2066     parole_player_full_screen(parole_player, !parole_player->priv->full_screen);
2067 }
2068 
parole_player_hide_menubar_cb(GtkWidget * widget,ParolePlayer * player)2069 static void parole_player_hide_menubar_cb(GtkWidget *widget, ParolePlayer *player) {
2070     player->priv->show_menubar = !gtk_widget_get_visible(player->priv->menu_bar);
2071     if (!player->priv->full_screen)
2072         gtk_widget_set_visible(player->priv->menu_bar, player->priv->show_menubar);
2073 }
2074 
parole_player_toggle_mini_mode_cb(GtkWidget * widget,ParolePlayer * player)2075 static void parole_player_toggle_mini_mode_cb(GtkWidget *widget, ParolePlayer *player) {
2076     gboolean active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
2077     player->priv->mini_mode = active;
2078     parole_player_reset_controls(player, FALSE);
2079 }
2080 
2081 static void
parole_player_show_menu(ParolePlayer * player,guint button,guint activate_time)2082 parole_player_show_menu(ParolePlayer *player, guint button, guint activate_time) {
2083     GtkWidget *menu, *mi;
2084     gboolean sensitive;
2085     GtkAccelGroup *accels = gtk_accel_group_new();
2086 
2087     gtk_window_add_accel_group(GTK_WINDOW(player->priv->window), accels);
2088 
2089     player->priv->current_media_type = parole_gst_get_current_stream_type(PAROLE_GST(player->priv->gst));
2090 
2091     menu = gtk_menu_new();
2092 
2093     /*Play menu item
2094      */
2095     mi = gtk_menu_item_new_with_mnemonic(player->priv->state == PAROLE_STATE_PLAYING ? _("_Pause"):_("_Play"));
2096 
2097     g_object_get(G_OBJECT(player->priv->playpause_button),
2098         "sensitive", &sensitive,
2099         NULL);
2100 
2101     gtk_widget_set_sensitive(mi, sensitive);
2102     gtk_widget_show(mi);
2103     g_signal_connect(G_OBJECT(mi), "activate",
2104                      G_CALLBACK(parole_player_widget_activate_action),
2105                      player->priv->media_playpause_action);
2106     gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2107 
2108     /*
2109      * Previous item in playlist.
2110      */
2111     mi = gtk_menu_item_new_with_mnemonic(_("_Previous"));
2112     gtk_widget_set_sensitive(mi, (player->priv->state >= PAROLE_STATE_PAUSED));
2113     gtk_widget_show(mi);
2114     g_signal_connect(G_OBJECT(mi), "activate",
2115                      G_CALLBACK(parole_player_widget_activate_action),
2116                      player->priv->media_previous_action);
2117     gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2118 
2119     /*
2120      * Next item in playlist.
2121      */
2122     mi = gtk_menu_item_new_with_mnemonic(_("_Next"));
2123     gtk_widget_set_sensitive(mi, (player->priv->state >= PAROLE_STATE_PAUSED));
2124     gtk_widget_show(mi);
2125     g_signal_connect(G_OBJECT(mi), "activate",
2126                      G_CALLBACK(parole_player_widget_activate_action),
2127                      player->priv->media_next_action);
2128     gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2129 
2130     /*
2131      * Un/Full screen
2132      */
2133     mi = gtk_menu_item_new_with_mnemonic(player->priv->full_screen ? _("_Leave Fullscreen"):_("_Fullscreen"));
2134     gtk_widget_show(mi);
2135     g_signal_connect(G_OBJECT(mi), "activate",
2136                      G_CALLBACK(parole_player_widget_activate_action),
2137                      player->priv->media_fullscreen_action);
2138     gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2139 
2140     if (!player->priv->full_screen) {
2141         mi = gtk_separator_menu_item_new();
2142         gtk_widget_show(GTK_WIDGET(mi));
2143         gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2144 
2145         /*
2146          * Un/Hide menubar
2147          */
2148         mi = gtk_check_menu_item_new_with_label(_("Show menubar"));
2149         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), gtk_widget_get_visible(player->priv->menu_bar));
2150         g_signal_connect(mi, "activate",
2151             G_CALLBACK(parole_player_hide_menubar_cb), player);
2152         gtk_widget_add_accelerator(mi, "activate", accels,
2153                                GDK_KEY_m, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
2154         gtk_widget_show(mi);
2155         gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2156 
2157         if ( player->priv->mini_mode ) {
2158             gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
2159         }
2160 
2161         mi = gtk_separator_menu_item_new();
2162         gtk_widget_show(GTK_WIDGET(mi));
2163         gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2164 
2165         mi = gtk_check_menu_item_new_with_label(_("Mini Mode"));
2166         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), player->priv->mini_mode);
2167         g_signal_connect(mi, "activate",
2168             G_CALLBACK(parole_player_toggle_mini_mode_cb), player);
2169         gtk_widget_show(mi);
2170         gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2171     }
2172 
2173     g_signal_connect_swapped(menu, "selection-done",
2174                               G_CALLBACK(gtk_widget_destroy), menu);
2175 
2176 #if GTK_CHECK_VERSION(3, 22, 0)
2177     gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL);
2178 #else
2179     gtk_menu_popup(GTK_MENU(menu),
2180                     NULL, NULL,
2181                     NULL, NULL,
2182                     button, activate_time);
2183 #endif
2184 }
2185 
2186 gboolean
parole_player_gst_widget_button_press(GtkWidget * widget,GdkEventButton * ev,ParolePlayer * player)2187 parole_player_gst_widget_button_press(GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player) {
2188     gboolean ret_val = FALSE;
2189     gboolean sensitive = FALSE;
2190     gint w, h;
2191 
2192     if ( ev->type == GDK_2BUTTON_PRESS ) {
2193         g_action_activate(G_ACTION(player->priv->media_fullscreen_action), NULL);
2194         ret_val = TRUE;
2195     } else if (ev->button == 1 && gtk_widget_get_visible(player->priv->logo_image)) {
2196         /* Clicking on the play or replay logo will activate appropriate functionality */
2197         h = gtk_widget_get_allocated_height(widget);
2198         w = gtk_widget_get_allocated_width(widget);
2199 
2200         if ( (ev->x > ((w / 2) - 128) && ev->x < ((w / 2) + 128)) &&
2201              (ev->y > ((h / 2) - 128) && ev->y < ((h / 2) + 128)) ) {
2202             g_object_get(G_OBJECT(player->priv->playpause_button),
2203                 "sensitive", &sensitive,
2204                 NULL);
2205 
2206             if (sensitive)
2207                 parole_player_toggle_playpause(player);
2208             else
2209                 parole_media_list_open(player->priv->list);
2210         }
2211     }
2212 
2213     return ret_val;
2214 }
2215 
2216 gboolean
parole_player_gst_widget_button_release(GtkWidget * widget,GdkEventButton * ev,ParolePlayer * player)2217 parole_player_gst_widget_button_release(GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player) {
2218     gboolean ret_val = FALSE;
2219 
2220     if ( ev->button == 3 ) {
2221         parole_player_show_menu(player, ev->button, ev->time);
2222         gtk_widget_grab_focus(widget);
2223         ret_val = TRUE;
2224     } else if ( ev->button == 1 ) {
2225         gtk_widget_grab_focus(widget);
2226         ret_val = TRUE;
2227     }
2228 
2229     return ret_val;
2230 }
2231 
parole_player_hide_controls(gpointer data)2232 gboolean parole_player_hide_controls(gpointer data) {
2233     ParolePlayer *player;
2234 
2235     TRACE("start");
2236 
2237     player = PAROLE_PLAYER(data);
2238 
2239     if (parole_player_get_playlist_visible (player))
2240         return FALSE;
2241 
2242     if (parole_player_get_volume_visible (player))
2243         return FALSE;
2244 
2245     gtk_revealer_set_reveal_child(GTK_REVEALER(player->priv->revealer), FALSE);
2246 
2247     parole_player_set_cursor_visible(player, FALSE);
2248 
2249     return FALSE;
2250 }
2251 
2252 void
parole_player_schedule_hide_controls(ParolePlayer * player,gint seconds)2253 parole_player_schedule_hide_controls (ParolePlayer *player, gint seconds) {
2254     static gulong hide_timeout;
2255     int hide_controls_timeout;
2256 
2257     if (hide_timeout != 0) {
2258         g_source_remove_by_user_data(player);
2259         hide_timeout = 0;
2260     }
2261 
2262     if (player->priv->revealer == NULL)
2263         return;
2264     if (!gtk_revealer_get_reveal_child(GTK_REVEALER(player->priv->revealer)))
2265         return;
2266     if ( player->priv->state != PAROLE_STATE_PLAYING )
2267         return;
2268 
2269     g_object_get(G_OBJECT(player->priv->conf),
2270                     "hide-controls-timeout", &hide_controls_timeout,
2271                     NULL);
2272     if (hide_controls_timeout == 0)
2273         return;
2274 
2275     if (seconds < 0) {
2276         seconds = (gint)hide_controls_timeout;
2277     }
2278 
2279     hide_timeout = g_timeout_add_seconds((guint)seconds,
2280                                          (GSourceFunc) parole_player_hide_controls, player);
2281 
2282     return;
2283 }
2284 
2285 gboolean
parole_player_gst_widget_motion_notify_event(GtkWidget * widget,GdkEventMotion * ev,ParolePlayer * player)2286 parole_player_gst_widget_motion_notify_event(GtkWidget *widget, GdkEventMotion *ev, ParolePlayer *player) {
2287     gtk_widget_show(gtk_widget_get_parent(player->priv->control));
2288 
2289     parole_player_set_cursor_visible(player, TRUE);
2290 
2291     if (!gtk_revealer_get_reveal_child(GTK_REVEALER(player->priv->revealer))) {
2292         gtk_revealer_set_reveal_child(GTK_REVEALER(player->priv->revealer), TRUE);
2293     }
2294 
2295     parole_player_schedule_hide_controls (player, -1);
2296 
2297     return FALSE;
2298 }
2299 
2300 void
parole_player_menu_open_location_cb(GtkWidget * widget,ParolePlayer * player)2301 parole_player_menu_open_location_cb(GtkWidget *widget, ParolePlayer *player) {
2302     parole_media_list_open_location(player->priv->list);
2303 }
2304 
2305 void
parole_player_menu_add_cb(GtkWidget * widget,ParolePlayer * player)2306 parole_player_menu_add_cb(GtkWidget *widget, ParolePlayer *player) {
2307     parole_media_list_open(player->priv->list);
2308 }
2309 
2310 void
parole_player_save_playlist_cb(GtkWidget * widget,ParolePlayer * player)2311 parole_player_save_playlist_cb(GtkWidget *widget, ParolePlayer *player) {
2312     parole_media_list_save_cb(widget, player->priv->list);
2313 }
2314 
2315 void
parole_player_media_menu_select_cb(GtkMenuItem * widget,ParolePlayer * player)2316 parole_player_media_menu_select_cb(GtkMenuItem *widget, ParolePlayer *player) {
2317     gtk_widget_set_sensitive(player->priv->save_playlist,
2318                   !parole_media_list_is_empty(player->priv->list));
2319 }
2320 
2321 static void
parole_player_playback_menu_select_cb(GtkMenuItem * widget,ParolePlayer * player)2322 parole_player_playback_menu_select_cb(GtkMenuItem *widget, ParolePlayer *player) {
2323     gtk_widget_set_sensitive(player->priv->goto_position,
2324                   !parole_media_list_is_empty(player->priv->list));
2325 }
2326 
parole_player_open_preferences_cb(GtkWidget * widget,ParolePlayer * player)2327 void parole_player_open_preferences_cb(GtkWidget *widget, ParolePlayer *player) {
2328     parole_conf_dialog_open(player->priv->settings_dialog, player->priv->window);
2329 }
2330 
2331 void
parole_player_menu_exit_cb(GtkWidget * widget,ParolePlayer * player)2332 parole_player_menu_exit_cb(GtkWidget *widget, ParolePlayer *player) {
2333     parole_player_delete_event_cb(NULL, NULL, player);
2334 }
2335 
2336 static void
parole_property_notify_cb_volume(ParoleGst * gst,GParamSpec * spec,ParolePlayer * player)2337 parole_property_notify_cb_volume(ParoleGst *gst, GParamSpec *spec, ParolePlayer *player) {
2338     gdouble volume;
2339     volume = parole_gst_get_volume(PAROLE_GST(player->priv->gst));
2340     gtk_scale_button_set_value(GTK_SCALE_BUTTON(player->priv->volume), volume);
2341 }
2342 
2343 static void
parole_player_change_volume(ParolePlayer * player,gdouble value)2344 parole_player_change_volume(ParolePlayer *player, gdouble value) {
2345     parole_gst_set_volume(PAROLE_GST(player->priv->gst), value);
2346 }
2347 
parole_player_volume_popdown_cb(GtkWidget * widget,ParolePlayer * player)2348 static void parole_player_volume_popdown_cb(GtkWidget *widget, ParolePlayer *player) {
2349     parole_player_schedule_hide_controls (player, 1);
2350 }
2351 
2352 gboolean
parole_player_volume_scroll_event_cb(GtkWidget * widget,GdkEventScroll * ev,ParolePlayer * player)2353 parole_player_volume_scroll_event_cb(GtkWidget *widget, GdkEventScroll *ev, ParolePlayer *player) {
2354     gboolean ret_val = FALSE;
2355 
2356     if ( ev->direction == GDK_SCROLL_UP ) {
2357         parole_player_volume_up(NULL, player);
2358         ret_val = TRUE;
2359     } else if ( ev->direction == GDK_SCROLL_DOWN ) {
2360         parole_player_volume_down(NULL, player);
2361         ret_val = TRUE;
2362     }
2363 
2364     return ret_val;
2365 }
2366 
2367 void
parole_player_volume_value_changed_cb(GtkScaleButton * widget,gdouble value,ParolePlayer * player)2368 parole_player_volume_value_changed_cb(GtkScaleButton *widget, gdouble value, ParolePlayer *player) {
2369     parole_player_change_volume(player, value);
2370 
2371     /* Do not update the value unless it has changed! */
2372     if ((int)(value*100) != (int)(player->priv->last_volume*100)) {
2373         player->priv->last_volume = value;
2374         if ( value > 0.0 )
2375             g_object_set(G_OBJECT(player->priv->conf),
2376                           "volume", (gint)(value * 100),
2377                           NULL);
2378     }
2379 }
2380 
2381 void
parole_player_volume_up(GtkWidget * widget,ParolePlayer * player)2382 parole_player_volume_up(GtkWidget *widget, ParolePlayer *player) {
2383     gdouble value;
2384     value = gtk_scale_button_get_value(GTK_SCALE_BUTTON(player->priv->volume));
2385     gtk_scale_button_set_value(GTK_SCALE_BUTTON(player->priv->volume), value + 0.05);
2386 }
2387 
2388 void
parole_player_volume_down(GtkWidget * widget,ParolePlayer * player)2389 parole_player_volume_down(GtkWidget *widget, ParolePlayer *player) {
2390     gdouble value;
2391     value = gtk_scale_button_get_value(GTK_SCALE_BUTTON(player->priv->volume));
2392     gtk_scale_button_set_value(GTK_SCALE_BUTTON(player->priv->volume), value - 0.05);
2393 }
2394 
parole_player_volume_mute(GtkWidget * widget,ParolePlayer * player)2395 void parole_player_volume_mute(GtkWidget *widget, ParolePlayer *player) {
2396     gint value;
2397     if (gtk_scale_button_get_value(GTK_SCALE_BUTTON(player->priv->volume)) == 0.0) {
2398         g_object_get(G_OBJECT(player->priv->conf),
2399                       "volume", &value,
2400                       NULL);
2401         gtk_menu_item_set_label(GTK_MENU_ITEM(player->priv->mute), _("Mute"));
2402     } else {
2403         value = 0;
2404         gtk_menu_item_set_label(GTK_MENU_ITEM(player->priv->mute), _("Unmute"));
2405     }
2406     gtk_scale_button_set_value (GTK_SCALE_BUTTON (player->priv->volume), (gdouble)(value)/100);
2407 }
2408 
2409 static void
parole_player_sm_quit_requested_cb(ParolePlayer * player)2410 parole_player_sm_quit_requested_cb(ParolePlayer *player) {
2411     player->priv->exit = TRUE;
2412     parole_gst_terminate(PAROLE_GST(player->priv->gst));
2413 }
2414 
2415 static void
parole_player_finalize(GObject * object)2416 parole_player_finalize(GObject *object) {
2417     ParolePlayer *player;
2418 
2419     player = PAROLE_PLAYER(object);
2420 
2421     TRACE("start");
2422 
2423     dbus_g_connection_unref(player->priv->bus);
2424 
2425     g_object_unref(player->priv->conf);
2426     g_object_unref(player->priv->video_filter);
2427     g_object_unref(player->priv->disc);
2428     g_object_unref(player->priv->screen_saver);
2429     g_object_unref(player->priv->connection);
2430 
2431     if ( player->priv->client_id )
2432         g_free(player->priv->client_id);
2433 
2434     g_object_unref(player->priv->sm_client);
2435 
2436 #ifdef HAVE_XF86_KEYSYM
2437     if (player->priv->button)
2438         g_object_unref(player->priv->button);
2439 #endif
2440 
2441     G_OBJECT_CLASS(parole_player_parent_class)->finalize(object);
2442 }
2443 
parole_player_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2444 static void parole_player_set_property(GObject *object,
2445                                        guint prop_id,
2446                                        const GValue *value,
2447                                        GParamSpec *pspec) {
2448     ParolePlayer *player;
2449     player = PAROLE_PLAYER(object);
2450 
2451     switch (prop_id) {
2452         case PROP_CLIENT_ID:
2453             player->priv->client_id = g_value_dup_string(value);
2454             break;
2455         default:
2456             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2457             break;
2458     }
2459 }
2460 
parole_player_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2461 static void parole_player_get_property(GObject *object,
2462                                        guint prop_id,
2463                                        GValue *value,
2464                                        GParamSpec *pspec) {
2465     ParolePlayer *player;
2466     player = PAROLE_PLAYER(object);
2467 
2468     switch (prop_id) {
2469         case PROP_CLIENT_ID:
2470             g_value_set_string(value, player->priv->client_id);
2471             break;
2472         default:
2473             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2474             break;
2475     }
2476 }
2477 
2478 /**
2479  * Get the SM client
2480  **/
2481 static void
parole_player_constructed(GObject * object)2482 parole_player_constructed(GObject *object) {
2483     ParolePlayer *player;
2484     gchar *current_dir;
2485 
2486     const gchar *restart_command[] = {
2487         "parole",
2488         "--new-instance",
2489         NULL
2490     };
2491 
2492     player = PAROLE_PLAYER(object);
2493 
2494     current_dir = g_get_current_dir();
2495 
2496     player->priv->sm_client =   xfce_sm_client_get_full(XFCE_SM_CLIENT_RESTART_NORMAL,
2497                                 XFCE_SM_CLIENT_PRIORITY_DEFAULT,
2498                                 player->priv->client_id,
2499                                 current_dir,
2500                                 restart_command,
2501                                 DESKTOPDIR "/org.xfce.Parole.desktop");
2502 
2503     if (xfce_sm_client_connect(player->priv->sm_client, NULL)) {
2504         g_signal_connect_swapped(player->priv->sm_client, "quit-requested",
2505                       G_CALLBACK(parole_player_sm_quit_requested_cb), player);
2506     }
2507 
2508     g_free(current_dir);
2509 }
2510 
2511 static void
parole_player_class_init(ParolePlayerClass * klass)2512 parole_player_class_init(ParolePlayerClass *klass) {
2513     GObjectClass *object_class = G_OBJECT_CLASS(klass);
2514 
2515     object_class->constructed = parole_player_constructed;
2516     object_class->finalize = parole_player_finalize;
2517     object_class->set_property = parole_player_set_property;
2518     object_class->get_property = parole_player_get_property;
2519 
2520 
2521     /**
2522      * ParolePlayer:client-id:
2523      *
2524      * Sm Manager client ID
2525      * Since: 0.2.2
2526      **/
2527     g_object_class_install_property(object_class,
2528                      PROP_CLIENT_ID,
2529                      g_param_spec_string("client-id",
2530                               NULL,
2531                               NULL,
2532                               NULL,
2533                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
2534 
2535     parole_player_dbus_class_init(klass);
2536 }
2537 
2538 /**
2539  * Configuration changed regarding
2540  * whether to Reset the screen saver counter
2541  * while playing movies or not.
2542  *
2543  */
2544 static void
parole_player_reset_saver_changed_cb(ParolePlayer * player)2545 parole_player_reset_saver_changed_cb(ParolePlayer *player) {
2546     const ParoleStream *stream;
2547 
2548     stream = parole_gst_get_stream(PAROLE_GST(player->priv->gst));
2549     TRACE("Reset saver configuration changed");
2550     parole_player_reset_saver_changed(player, stream);
2551 }
2552 
2553 static gboolean
parole_player_handle_key_value(guint keyval,guint state,ParolePlayer * player)2554 parole_player_handle_key_value(guint keyval, guint state, ParolePlayer *player) {
2555     /* Seek duration in seconds */
2556     gdouble seek_short = 10, seek_medium = 60, seek_long = 600;
2557 
2558     gboolean ret_val = FALSE;
2559 
2560     if (state & GDK_MOD1_MASK)
2561         return FALSE;
2562 
2563     switch (keyval) {
2564         case GDK_KEY_f:
2565         case GDK_KEY_F:
2566             if ( player->priv->embedded != TRUE ) {
2567                 g_action_activate(G_ACTION(player->priv->media_fullscreen_action), NULL);
2568             }
2569             ret_val = TRUE;
2570             break;
2571         case GDK_KEY_space:
2572         case GDK_KEY_p:
2573         case GDK_KEY_P:
2574             parole_player_toggle_playpause(player);
2575             ret_val = TRUE;
2576             break;
2577         case GDK_KEY_Right:
2578             /* Media seekable ?*/
2579             if (gtk_widget_get_sensitive(player->priv->range)) {
2580                 if (state & GDK_CONTROL_MASK) {
2581                     parole_player_seekf_cb(NULL, player, seek_medium);
2582                 } else {
2583                     parole_player_seekf_cb(NULL, player, seek_short);
2584                 }
2585             }
2586             ret_val = TRUE;
2587             break;
2588         case GDK_KEY_Left:
2589             if (gtk_widget_get_sensitive(player->priv->range)) {
2590                 if (state & GDK_CONTROL_MASK) {
2591                     parole_player_seekb_cb(NULL, player, seek_medium);
2592                 } else {
2593                     parole_player_seekb_cb(NULL, player, seek_short);
2594                 }
2595             }
2596             ret_val = TRUE;
2597             break;
2598         case GDK_KEY_Page_Down:
2599             if ( gtk_widget_get_sensitive(player->priv->range) ) {
2600                 parole_player_seekb_cb(NULL, player, seek_long);
2601             }
2602             ret_val = TRUE;
2603             break;
2604         case GDK_KEY_Page_Up:
2605             if ( gtk_widget_get_sensitive(player->priv->range) ) {
2606                 parole_player_seekf_cb(NULL, player, seek_long);
2607             }
2608             ret_val = TRUE;
2609             break;
2610         case GDK_KEY_Escape:
2611             parole_player_full_screen(player, FALSE);
2612             break;
2613         case GDK_KEY_m:
2614             if (state & GDK_CONTROL_MASK) {
2615                 parole_player_hide_menubar_cb(NULL, player);
2616             }
2617             ret_val = TRUE;
2618             break;
2619         case GDK_KEY_n:
2620         case GDK_KEY_N:
2621             parole_player_play_next(player, TRUE);
2622             ret_val = TRUE;
2623             break;
2624         case GDK_KEY_b:
2625         case GDK_KEY_B:
2626             parole_player_play_prev(player);
2627             ret_val = TRUE;
2628             break;
2629         case GDK_KEY_q:
2630         case GDK_KEY_Q:
2631             if (state & GDK_CONTROL_MASK) {
2632                 parole_player_quit(player);
2633                 ret_val = TRUE;
2634             }
2635             break;
2636 #ifdef HAVE_XF86_KEYSYM
2637         case XF86XK_OpenURL:
2638             parole_player_full_screen(player, FALSE);
2639             parole_media_list_open_location(player->priv->list);
2640             break;
2641 #endif
2642         /*
2643          * Pass these to the media list and tell it to
2644          * grab the focus
2645          */
2646         case GDK_KEY_Up:
2647         case GDK_KEY_Down:
2648             if (!player->priv->full_screen && gtk_widget_get_visible(player->priv->playlist_nt)) {
2649                 parole_media_list_grab_focus(player->priv->list);
2650             }
2651             break;
2652         case GDK_KEY_plus:
2653         case GDK_KEY_equal:
2654             parole_player_volume_up(NULL, player);
2655             break;
2656         case GDK_KEY_minus:
2657             parole_player_volume_down(NULL, player);
2658             break;
2659         case GDK_KEY_0:
2660             parole_player_volume_mute(NULL, player);
2661             break;
2662         default:
2663             break;
2664     }
2665 
2666     return ret_val;
2667 }
2668 
2669 static gboolean
parole_player_handle_key_press(GdkEventKey * ev,ParolePlayer * player)2670 parole_player_handle_key_press(GdkEventKey *ev, ParolePlayer *player) {
2671     GtkWidget *focused;
2672 
2673     focused = gtk_window_get_focus(GTK_WINDOW(player->priv->window));
2674 
2675     if ( focused ) {
2676         if (gtk_widget_is_ancestor(focused, player->priv->playlist_nt)) {
2677             return FALSE;
2678         }
2679     }
2680 
2681     return parole_player_handle_key_value(ev->keyval, ev->state, player);
2682 }
2683 
2684 gboolean
parole_player_key_press(GtkWidget * widget,GdkEventKey * ev,ParolePlayer * player)2685 parole_player_key_press(GtkWidget *widget, GdkEventKey *ev, ParolePlayer *player) {
2686     gboolean enabled;
2687 
2688     switch (ev->keyval) {
2689         case GDK_KEY_F11:
2690             if ( player->priv->embedded != TRUE ) {
2691                 g_action_activate(G_ACTION(player->priv->media_fullscreen_action), NULL);
2692             }
2693             return TRUE;
2694         case GDK_KEY_F9:
2695             if ( player->priv->full_screen == TRUE ) {
2696                 enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(player->priv->showhide_playlist_button));
2697                 parole_player_set_playlist_visible(player, !enabled);
2698                 return TRUE;
2699             }
2700         default:
2701             break;
2702     }
2703 
2704 #ifdef HAVE_XF86_KEYSYM
2705     g_object_get(G_OBJECT(player->priv->conf),
2706                  "multimedia-keys", &enabled,
2707                  NULL);
2708     if (enabled) {
2709         switch (ev->keyval) {
2710             case XF86XK_AudioPlay:
2711                 parole_player_toggle_playpause(player);
2712                 return TRUE;
2713             case XF86XK_AudioStop:
2714                 parole_player_pause_clicked(NULL, player);
2715                 return TRUE;
2716             case XF86XK_AudioRaiseVolume:
2717                 parole_player_volume_up(NULL, player);
2718                 return TRUE;
2719             case XF86XK_AudioLowerVolume:
2720                 parole_player_volume_down(NULL, player);
2721                 return TRUE;
2722             case XF86XK_AudioMute:
2723                 parole_player_volume_mute(NULL, player);
2724                 return TRUE;
2725             case XF86XK_AudioPrev:
2726                 parole_player_play_prev(player);
2727                 return TRUE;
2728             case XF86XK_AudioNext:
2729                 parole_player_play_next(player, TRUE);
2730                 return TRUE;
2731             default:
2732                 break;
2733         }
2734     }
2735 #endif /* HAVE_XF86_KEYSYM */
2736 
2737     return parole_player_handle_key_press (ev, player);
2738 }
2739 
2740 #ifdef HAVE_XF86_KEYSYM
2741 static void
parole_player_button_pressed_cb(ParoleButton * button,ParoleButtonKey key,ParolePlayer * player)2742 parole_player_button_pressed_cb(ParoleButton *button, ParoleButtonKey key, ParolePlayer *player) {
2743     gboolean enabled;
2744 
2745     g_object_get(G_OBJECT(player->priv->conf),
2746                  "multimedia-keys", &enabled,
2747                  NULL);
2748 
2749     if (!enabled)
2750         return;
2751 
2752     PAROLE_DEBUG_ENUM("Button Press:", key, ENUM_GTYPE_BUTTON_KEY);
2753 
2754     switch (key) {
2755         case PAROLE_KEY_AUDIO_PLAY:
2756             parole_player_toggle_playpause(player);
2757             break;
2758         case PAROLE_KEY_AUDIO_STOP:
2759             parole_player_pause_clicked(NULL, player);
2760             break;
2761         case PAROLE_KEY_AUDIO_PREV:
2762             parole_player_play_prev(player);
2763             break;
2764         case PAROLE_KEY_AUDIO_NEXT:
2765             parole_player_play_next(player, TRUE);
2766             break;
2767         default:
2768             break;
2769     }
2770 }
2771 #endif
2772 
2773 static void
parole_gst_set_default_aspect_ratio(ParolePlayer * player,GtkBuilder * builder)2774 parole_gst_set_default_aspect_ratio(ParolePlayer *player, GtkBuilder *builder) {
2775     ParoleAspectRatio ratio;
2776     const gchar *widget_name;
2777 
2778     g_object_get(G_OBJECT(player->priv->conf),
2779                  "aspect-ratio", &ratio,
2780                  NULL);
2781 
2782     switch (ratio) {
2783         case PAROLE_ASPECT_RATIO_NONE:
2784             widget_name = "ratio_none";
2785             break;
2786         case PAROLE_ASPECT_RATIO_AUTO:
2787             widget_name = "ratio_auto";
2788             break;
2789         case PAROLE_ASPECT_RATIO_SQUARE:
2790             widget_name = "ratio_square";
2791             break;
2792         case PAROLE_ASPECT_RATIO_16_9:
2793             widget_name = "ratio_16_9";
2794             break;
2795         case PAROLE_ASPECT_RATIO_4_3:
2796             widget_name = "ratio_4_3";
2797             break;
2798         case PAROLE_ASPECT_RATIO_DVB:
2799             widget_name = "ratio_20_9";
2800             break;
2801         default:
2802             g_warn_if_reached();
2803             return;
2804     }
2805 
2806     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_builder_get_object(builder, widget_name)), TRUE);
2807 }
2808 
2809 static void
on_bug_report_clicked(GtkWidget * w,ParolePlayer * player)2810 on_bug_report_clicked(GtkWidget *w, ParolePlayer *player) {
2811     GtkWidget *dialog;
2812 #if GTK_CHECK_VERSION(3, 22, 0)
2813     if (!gtk_show_uri_on_window(GTK_WINDOW(player->priv->window),
2814                                 "https://docs.xfce.org/apps/parole/bugs",
2815                                 GDK_CURRENT_TIME,
2816                                 NULL)) {
2817 #else
2818     if (!gtk_show_uri(NULL, "https://docs.xfce.org/apps/parole/bugs", GDK_CURRENT_TIME, NULL)) {
2819 #endif
2820         dialog = gtk_message_dialog_new(GTK_WINDOW(player->priv->window),
2821                                         GTK_DIALOG_DESTROY_WITH_PARENT,
2822                                         GTK_MESSAGE_ERROR,
2823                                         GTK_BUTTONS_CLOSE,
2824                                         _("Unable to open default web browser"));
2825         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
2826                 _("Please go to https://docs.xfce.org/apps/parole/bugs to report your bug."));
2827         gtk_dialog_run(GTK_DIALOG(dialog));
2828         gtk_widget_destroy(dialog);
2829     }
2830 }
2831 
2832 static void
2833 on_contents_clicked(GtkWidget *w, ParolePlayer *player) {
2834     xfce_dialog_show_help_with_version(NULL, "parole", "start", NULL, PAROLE_VERSION_SHORT);
2835 }
2836 
2837 static gboolean
2838 on_goto_position_clicked(GtkWidget *w, ParolePlayer *player) {
2839     GtkWidget *dialog;
2840     GtkWidget *vbox, *hbox, *cbox;
2841     GtkWidget *spin_hrs, *spin_mins, *spin_secs;
2842     GtkAdjustment *adjustment;
2843     gint response;
2844     gdouble position, duration = 0, current_position = 0;
2845     int hrs, mins, secs;
2846     int max_hrs, max_mins = 59;
2847     int cur_hrs, cur_mins, cur_secs;
2848 
2849     /* Create dialog */
2850     dialog = gtk_dialog_new_with_buttons(_("Go to position"),
2851                                          GTK_WINDOW(player->priv->window),
2852                                          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
2853                                          _("Cancel"), GTK_RESPONSE_CANCEL,
2854                                          _("Go"), GTK_RESPONSE_OK,
2855                                          NULL);
2856     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2857     gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
2858 
2859     /* pack boxes and spinbutton */
2860     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
2861     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox, TRUE, TRUE, 0);
2862     gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
2863 
2864     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
2865     gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2866 
2867     cbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
2868     gtk_box_set_center_widget(GTK_BOX(hbox), cbox);
2869 
2870     /* Get the stream length and set that as maximum for hours and minutes */
2871     adjustment = gtk_range_get_adjustment(GTK_RANGE(player->priv->range));
2872     duration = gtk_adjustment_get_upper(adjustment);
2873     current_position = gtk_range_get_value(GTK_RANGE(player->priv->range));
2874     max_hrs = (int) duration/3600;
2875     if ( duration/60 <= 59 )
2876         max_mins = (int) duration/60;
2877 
2878     spin_hrs = gtk_spin_button_new_with_range(0, max_hrs, 1);
2879     gtk_orientable_set_orientation (GTK_ORIENTABLE(spin_hrs), GTK_ORIENTATION_VERTICAL);
2880     gtk_entry_set_activates_default (GTK_ENTRY(spin_hrs), TRUE);
2881     spin_mins = gtk_spin_button_new_with_range(0, max_mins, 1);
2882     gtk_orientable_set_orientation (GTK_ORIENTABLE(spin_mins), GTK_ORIENTATION_VERTICAL);
2883     gtk_entry_set_activates_default (GTK_ENTRY(spin_mins), TRUE);
2884     spin_secs = gtk_spin_button_new_with_range(0, 59, 1);
2885     gtk_orientable_set_orientation (GTK_ORIENTABLE(spin_secs), GTK_ORIENTATION_VERTICAL);
2886     gtk_entry_set_activates_default (GTK_ENTRY(spin_secs), TRUE);
2887     gtk_box_pack_start(GTK_BOX(cbox), spin_hrs, FALSE, FALSE, 0);
2888     gtk_box_pack_start(GTK_BOX(cbox), gtk_label_new(":"), FALSE, FALSE, 0);
2889     gtk_box_pack_start(GTK_BOX(cbox), spin_mins, FALSE, FALSE, 0);
2890     gtk_box_pack_start(GTK_BOX(cbox), gtk_label_new(":"), FALSE, FALSE, 0);
2891     gtk_box_pack_start(GTK_BOX(cbox), spin_secs, FALSE, FALSE, 0);
2892 
2893     if ( duration < 3600 )
2894         gtk_widget_set_sensitive(GTK_WIDGET(spin_hrs), FALSE);
2895     if ( duration < 60 )
2896         gtk_widget_set_sensitive(GTK_WIDGET(spin_mins), FALSE);
2897     if ( duration == 0 )
2898         gtk_widget_set_sensitive(GTK_WIDGET(spin_secs), FALSE);
2899 
2900     if ( current_position != 0 ) {
2901         cur_hrs = (int) current_position/3600;
2902         cur_mins = (int) current_position/60 - cur_hrs*60;
2903         cur_secs = (int) current_position - cur_hrs*3600 - cur_mins*60;
2904         gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_hrs), cur_hrs);
2905         gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_mins), cur_mins);
2906         gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_secs), cur_secs);
2907     }
2908 
2909     // FIXME: Prevent the mouse-cursor and controls from hiding
2910     /* show dialog */
2911     gtk_widget_show_all(dialog);
2912     response = gtk_dialog_run(GTK_DIALOG(dialog));
2913     if (response == GTK_RESPONSE_OK) {
2914         /* update range according to the value */
2915         hrs = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_hrs));
2916         mins = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_mins));
2917         secs = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_secs));
2918         position = hrs*3600 + mins*60 + secs;
2919         parole_gst_seek(PAROLE_GST(player->priv->gst), position);
2920         parole_player_change_range_value(player, position);
2921     }
2922 
2923     gtk_widget_destroy(GTK_WIDGET(dialog));
2924 
2925     return (response == GTK_RESPONSE_OK);
2926 }
2927 
2928 /**
2929  *
2930  * Draw a simple rectangular GtkOverlay
2931  * using the theme's background and border-color
2932  * to keep it on top of the gst-video-widget with Gtk3.8 and above
2933  *
2934  * NOTE: Transparency is not supported, so there's also no fadeout.
2935  **/
2936 static gboolean
2937 parole_overlay_expose_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) {
2938     GtkAllocation *allocation = g_new0(GtkAllocation, 1);
2939     GtkStyleContext *context;
2940 
2941     gtk_widget_get_allocation(widget, allocation);
2942     cairo_rectangle(cr, 0, 0, allocation->width, allocation->height);
2943     g_free(allocation);
2944 
2945     context = gtk_widget_get_style_context(GTK_WIDGET(widget));
2946     gtk_style_context_add_class(context, "background");
2947     gtk_style_context_add_class(context, "osd");
2948 
2949     return FALSE;
2950 }
2951 
2952 /* This function allows smoothly adjusting the window alignment with coverart */
2953 static gboolean
2954 parole_audiobox_expose_event(GtkWidget *w, GdkEventExpose *ev, ParolePlayer *player) {
2955     GtkAllocation *allocation = g_new0(GtkAllocation, 1);
2956     gboolean homogeneous;
2957 
2958     /* Float the cover and text together in the middle if there is space */
2959     gtk_widget_get_allocation(w, allocation);
2960     homogeneous = allocation->width > 536;
2961     g_free(allocation);
2962 
2963     /* Nothing to do if the homogeneous setting is already good */
2964     if (gtk_box_get_homogeneous(GTK_BOX(w)) == homogeneous)
2965         return FALSE;
2966 
2967     gtk_box_set_homogeneous(GTK_BOX(w), homogeneous);
2968 
2969     /* Expand the coverart if the parent box packing is homogeneous */
2970     gtk_box_set_child_packing(GTK_BOX(w),
2971                                player->priv->audiobox_cover,
2972                                homogeneous,
2973                                TRUE,
2974                                0,
2975                                GTK_PACK_START);
2976 
2977     return FALSE;
2978 }
2979 
2980 void
2981 on_content_area_size_allocate(GtkWidget *widget, GtkAllocation *allocation, ParolePlayer *player) {
2982     g_return_if_fail(allocation != NULL);
2983 
2984     gtk_widget_set_allocation(widget, allocation);
2985 
2986     if (gtk_widget_get_realized(widget)) {
2987         gtk_widget_queue_draw(widget);
2988     }
2989 }
2990 
2991 gboolean
2992 parole_player_configure_event_cb(GtkWidget *widget, GdkEventConfigure *ev, ParolePlayer *player) {
2993     gint old_w, old_h, new_w, new_h;
2994 
2995     /* Get the current window size */
2996     gtk_window_get_size(GTK_WINDOW(widget), &new_w, &new_h);
2997 
2998     if ( !player->priv->full_screen ) {
2999         /* Store the previously saved window size in case of maximize */
3000         g_object_get(G_OBJECT(player->priv->conf),
3001                      "window-width", &old_w,
3002                      "window-height", &old_h,
3003                      NULL);
3004 
3005         /* Configure gets run twice, only change on update */
3006         if (old_w != new_w || old_h != new_h) {
3007             player->priv->last_w = old_w;
3008             player->priv->last_h = old_h;
3009 
3010             if ( !player->priv->mini_mode )
3011                 g_object_set(G_OBJECT(player->priv->conf),
3012                               "window-width", new_w,
3013                               "window-height", new_h,
3014                               NULL);
3015         }
3016     }
3017 
3018     gtk_widget_set_size_request(player->priv->playlist_popover, -1, new_h - 60);
3019 
3020     return FALSE;
3021 }
3022 
3023 static void
3024 parole_player_drag_data_received_cb(GtkWidget *widget,
3025                                     GdkDragContext *drag_context,
3026                                     gint x,
3027                                     gint y,
3028                                     GtkSelectionData *data,
3029                                     guint info,
3030                                     guint drag_time,
3031                                     ParolePlayer *player) {
3032     gchar **uri_list;
3033     guint added  = 0;
3034     guint i;
3035 
3036     parole_window_busy_cursor(gtk_widget_get_window(widget));
3037 
3038     uri_list = g_uri_list_extract_uris((const gchar *)gtk_selection_data_get_data(data));
3039     for (i = 0; uri_list[i] != NULL; i++) {
3040         gchar *path;
3041         path = g_filename_from_uri(uri_list[i], NULL, NULL);
3042         added += parole_media_list_add_by_path(player->priv->list, path, i == 0 ? TRUE : FALSE);
3043 
3044         g_free(path);
3045     }
3046 
3047     g_strfreev(uri_list);
3048 
3049     parole_player_set_cursor_visible(player, TRUE);
3050     gtk_drag_finish(drag_context, added == i ? TRUE : FALSE, FALSE, drag_time);
3051 }
3052 
3053 static void
3054 parole_player_set_cursor_visible(ParolePlayer *player, gboolean visible) {
3055     GdkWindow *gdkwindow;
3056     gdkwindow = gtk_widget_get_window(GTK_WIDGET(player->priv->eventbox_output));
3057 
3058     if (visible) {
3059         parole_gst_set_cursor_visible(PAROLE_GST(player->priv->gst), TRUE);
3060         parole_window_normal_cursor(gdkwindow);
3061     } else if (gtk_window_is_active(GTK_WINDOW(player->priv->window))) {
3062         parole_gst_set_cursor_visible(PAROLE_GST(player->priv->gst), FALSE);
3063         parole_window_invisible_cursor(gdkwindow);
3064     }
3065 }
3066 
3067 static void
3068 parole_player_window_notify_is_active(ParolePlayer *player) {
3069     if (!gtk_window_is_active(GTK_WINDOW(player->priv->window))) {
3070         parole_player_set_cursor_visible(player, TRUE);
3071     } else {
3072         parole_player_set_cursor_visible(player, FALSE);
3073     }
3074 }
3075 
3076 /**
3077  *
3078  * Sets the _NET_WM_WINDOW_OPACITY_LOCKED wm hint
3079  * so window manager keep us opaque.
3080  *
3081  * Currently it is only supported by xfwm.
3082  *
3083  * NOTE: The widget has to be realized first.
3084  **/
3085 static void
3086 parole_player_set_wm_opacity_hint(GtkWidget *widget) {
3087     GdkScreen *gdkscreen;
3088     GdkDisplay *gdkdisplay;
3089     GdkWindow *gdkwindow;
3090     Display *xdisplay;
3091     Atom atom;
3092     char mode = 1;
3093 
3094     gdkscreen = gtk_widget_get_screen(widget);
3095     gdkdisplay = gdk_screen_get_display(gdkscreen);
3096 
3097     xdisplay = GDK_DISPLAY_XDISPLAY(gdkdisplay);
3098 
3099     atom = XInternAtom(xdisplay, "_NET_WM_WINDOW_OPACITY_LOCKED", TRUE);
3100 
3101     if ( atom == None )
3102         return;
3103 
3104     gdkwindow = gtk_widget_get_window(widget);
3105 
3106     XChangeProperty(xdisplay, gdk_x11_window_get_xid(gdkwindow),
3107                      atom, XA_CARDINAL,
3108                      32, PropModeAppend,
3109                      (guchar *) &mode,
3110                      1);
3111 }
3112 
3113 static void
3114 parole_player_init(ParolePlayer *player) {
3115     /* Builder */
3116     GtkBuilder *builder;
3117 
3118     /* Recent Menu */
3119     GtkRecentFilter *recent_filter;
3120     GtkWidget *recent_menu;
3121     GtkWidget *recent_separator;
3122     GtkWidget *clear_recent;
3123 
3124     /* Help Menu */
3125     GtkWidget *bug_report, *contents;
3126 
3127     /* Language Infobar */
3128     GtkWidget *hbox_infobar;
3129     GtkWidget *content_area;
3130     GtkWidget *audiotrack_box, *audiotrack_label;
3131     GtkWidget *subtitle_box, *subtitle_label;
3132     GtkWidget *infobar_close, *close_icon;
3133     GtkCellRenderer *cell, *sub_cell;
3134 
3135     /* Content Area */
3136     GtkWidget *controls_overlay;
3137     GtkWidget *controls_parent;
3138     GtkWidget *play_box;
3139     GdkPixbuf *logo;
3140 
3141     /* Player Controls */
3142     GList *widgets;
3143 
3144     /* Properties */
3145     gchar *videosink = NULL;
3146     gint w, h;
3147     gboolean maximized;
3148     gboolean always_hide_menubar = FALSE;
3149     gint volume;
3150 
3151 
3152     g_setenv("PULSE_PROP_media.role", "video", TRUE);
3153 
3154     player->priv = parole_player_get_instance_private(player);
3155 
3156     player->priv->client_id = NULL;
3157     player->priv->sm_client = NULL;
3158 
3159     player->priv->bus = parole_g_session_bus_get();
3160 
3161     player->priv->current_media_type = PAROLE_MEDIA_TYPE_UNKNOWN;
3162 
3163     player->priv->video_filter = parole_get_supported_video_filter();
3164     g_object_ref_sink(player->priv->video_filter);
3165 
3166     builder = parole_builder_get_main_interface();
3167 
3168     /* ParoleConf */
3169     player->priv->conf = parole_conf_new();
3170     player->priv->settings_dialog = parole_conf_dialog_new();
3171 
3172     g_signal_connect_swapped(player->priv->conf, "notify::reset-saver",
3173             G_CALLBACK(parole_player_reset_saver_changed_cb), player);
3174 
3175     /* ParoleGst */
3176     player->priv->gst = parole_gst_new(player->priv->conf);
3177 
3178     /* ParoleDisc */
3179     player->priv->disc = parole_disc_new();
3180     g_signal_connect(player->priv->disc, "disc-selected",
3181             G_CALLBACK(parole_player_disc_selected_cb), player);
3182 
3183     /* ParoleButton */
3184     #ifdef HAVE_XF86_KEYSYM
3185         player->priv->button = parole_button_new();
3186         g_signal_connect(player->priv->button, "button-pressed",
3187                 G_CALLBACK(parole_player_button_pressed_cb), player);
3188     #endif
3189 
3190     /* ParoleScreenSaver */
3191     player->priv->screen_saver = parole_screen_saver_new();
3192 
3193     /* PowerManagement Inhibit cookie */
3194     player->priv->connection = parole_power_manager_dbus_init ();
3195     player->priv->inhibit_cookie = 0;
3196 
3197     /* ParoleMediaList */
3198     player->priv->list = PAROLE_MEDIA_LIST(parole_media_list_get());
3199 
3200     player->priv->state = PAROLE_STATE_STOPPED;
3201     player->priv->user_seeking = FALSE;
3202     player->priv->internal_range_change = FALSE;
3203     player->priv->exit = FALSE;
3204     player->priv->full_screen = FALSE;
3205     player->priv->buffering = FALSE;
3206     player->priv->row = NULL;
3207     player->priv->wait_for_gst_disc_info = FALSE;
3208 
3209     player->priv->recent = gtk_recent_manager_get_default();
3210 
3211     /* Get properties from ParoleConf */
3212     g_object_get(G_OBJECT(player->priv->conf),
3213                   "videosink", &videosink,
3214                   "volume", &volume,
3215                   "window-width", &w,
3216                   "window-height", &h,
3217                   "window-maximized", &maximized,
3218                   NULL);
3219 
3220     player->priv->last_w = w;
3221     player->priv->last_h = h;
3222 
3223     /*
3224      * GTK Widgets
3225      */
3226     /* ParolePlayer Window */
3227     player->priv->window = GTK_WIDGET(gtk_builder_get_object(builder, "main-window"));
3228     g_signal_connect_after(G_OBJECT(player->priv->window),
3229                             "window-state-event",
3230                             G_CALLBACK(parole_player_window_state_event),
3231                             PAROLE_PLAYER(player));
3232 
3233     // Playlist notebook
3234     player->priv->playlist_nt = GTK_WIDGET(gtk_builder_get_object(builder, "notebook-playlist"));
3235     gtk_notebook_append_page(GTK_NOTEBOOK(player->priv->playlist_nt),
3236                               GTK_WIDGET(player->priv->list),
3237                               gtk_label_new(_("Playlist")));
3238 
3239     player->priv->playlist_popover = GTK_WIDGET(gtk_builder_get_object(builder, "notebook-playlist-popover"));
3240 
3241     /* Menu Bar */
3242     player->priv->menu_bar = GTK_WIDGET(gtk_builder_get_object(builder, "menubar"));
3243 
3244     /* Media Menu */
3245     // Save Playlist
3246     player->priv->save_playlist = GTK_WIDGET(gtk_builder_get_object(builder, "menu-save-playlist"));
3247     g_signal_connect(player->priv->save_playlist,
3248                       "activate",
3249                       G_CALLBACK(parole_player_save_playlist_cb),
3250                       PAROLE_PLAYER(player));
3251     g_signal_connect(gtk_builder_get_object(builder, "media-menu"), "select",
3252             G_CALLBACK(parole_player_media_menu_select_cb), player);
3253 
3254     /* Recent Submenu */
3255     recent_menu = GTK_WIDGET(gtk_builder_get_object(builder, "recent_menu"));
3256 
3257     // Initialize the Recent Menu settings
3258     player->priv->recent_menu = gtk_recent_chooser_menu_new_for_manager(player->priv->recent);
3259     gtk_recent_chooser_menu_set_show_numbers(GTK_RECENT_CHOOSER_MENU(player->priv->recent_menu), TRUE);
3260     gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(player->priv->recent_menu), GTK_RECENT_SORT_MRU);
3261     gtk_recent_chooser_set_show_private(GTK_RECENT_CHOOSER(player->priv->recent_menu), FALSE);
3262     gtk_recent_chooser_set_show_not_found(GTK_RECENT_CHOOSER(player->priv->recent_menu), FALSE);
3263     gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(player->priv->recent_menu), TRUE);
3264 
3265     // Recent Menu file filter
3266     recent_filter = parole_get_supported_recent_media_filter();
3267     gtk_recent_filter_add_application(recent_filter, "parole");
3268     gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(player->priv->recent_menu), recent_filter);
3269 
3270     // Recent Menu Separator
3271     recent_separator = gtk_separator_menu_item_new();
3272     gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->recent_menu), recent_separator);
3273 
3274     // Clear Recent Menu Item
3275     clear_recent = gtk_menu_item_new_with_mnemonic(_("_Clear recent items..."));
3276     gtk_menu_shell_append(GTK_MENU_SHELL(player->priv->recent_menu), clear_recent);
3277 
3278     // Recent Menu signals
3279     g_signal_connect(player->priv->recent_menu, "item-activated",
3280             G_CALLBACK(parole_player_recent_menu_item_activated_cb), player);
3281     g_signal_connect(clear_recent, "activate",
3282             G_CALLBACK(parole_player_recent_menu_clear_activated_cb), player);
3283 
3284     /* Attach the Recent Menu */
3285     gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_menu), player->priv->recent_menu);
3286 
3287     /* DVD Menu */
3288     player->priv->dvd_menu = GTK_WIDGET(gtk_builder_get_object(builder, "dvd-menu"));
3289     player->priv->chapters_menu = GTK_WIDGET(gtk_builder_get_object(builder, "chapters-menu"));
3290 
3291     /* Language Menus */
3292     player->priv->subtitles_menu = GTK_WIDGET(gtk_builder_get_object(builder, "subtitles-menu"));
3293     player->priv->languages_menu = GTK_WIDGET(gtk_builder_get_object(builder, "languages-menu"));
3294 
3295     player->priv->subtitles_group = GTK_WIDGET(gtk_builder_get_object(builder, "subtitles-menu-none"));
3296     player->priv->subtitles_menu_custom = GTK_WIDGET(gtk_builder_get_object(builder, "subtitles-menu-custom"));
3297 
3298     g_signal_connect(player->priv->subtitles_menu_custom, "activate",
3299             G_CALLBACK(parole_player_select_custom_subtitle), player);
3300 
3301     player->priv->audio_group = NULL;
3302 
3303     /* Additional Menu Items */
3304     bug_report = GTK_WIDGET(gtk_builder_get_object(builder, "bug-report"));
3305     g_signal_connect(bug_report, "activate",
3306             G_CALLBACK(on_bug_report_clicked), player);
3307 
3308     contents = GTK_WIDGET(gtk_builder_get_object(builder, "contents"));
3309     g_signal_connect(contents, "activate",
3310             G_CALLBACK(on_contents_clicked), player);
3311 
3312     player->priv->goto_position = GTK_WIDGET(gtk_builder_get_object(builder, "goto_position"));
3313     g_signal_connect(player->priv->goto_position, "activate",
3314             G_CALLBACK(on_goto_position_clicked), player);
3315 
3316     g_signal_connect(gtk_builder_get_object(builder, "playback-menu"), "select",
3317             G_CALLBACK(parole_player_playback_menu_select_cb), player);
3318 
3319     player->priv->show_menubar = TRUE;
3320     /* End Menu Bar */
3321 
3322 
3323     /* Content Area (Background, Audio, Video) */
3324     player->priv->eventbox_output = GTK_WIDGET(gtk_builder_get_object(builder, "content_area"));
3325 
3326     // Enable motion-notify event to show/hide controls on mouseover
3327     gtk_widget_add_events(GTK_WIDGET(player->priv->eventbox_output),
3328             GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
3329 
3330     // Enable DND for files onto output widget
3331     gtk_drag_dest_set(player->priv->eventbox_output, GTK_DEST_DEFAULT_ALL,
3332                         target_entry, G_N_ELEMENTS(target_entry),
3333                         GDK_ACTION_COPY | GDK_ACTION_MOVE);
3334     g_signal_connect(player->priv->eventbox_output, "drag-data-received",
3335                         G_CALLBACK(parole_player_drag_data_received_cb), player);
3336 
3337     // Background Image
3338     logo = gdk_pixbuf_new_from_file(g_strdup_printf("%s/play.png", PIXMAPS_DIR), NULL);
3339     player->priv->logo_image = GTK_WIDGET(gtk_builder_get_object(builder, "logo"));
3340     gtk_image_set_from_pixbuf(GTK_IMAGE(player->priv->logo_image), logo);
3341 
3342     // Video Box
3343     player->priv->videobox = GTK_WIDGET(gtk_builder_get_object(builder, "video_output"));
3344 
3345     // Audio Box
3346     player->priv->audiobox = GTK_WIDGET(gtk_builder_get_object(builder, "audio_output"));
3347     player->priv->audiobox_cover = GTK_WIDGET(gtk_builder_get_object(builder, "audio_cover"));
3348     player->priv->audiobox_text = GTK_WIDGET(gtk_builder_get_object(builder, "audio_text"));
3349     player->priv->audiobox_title = GTK_WIDGET(gtk_builder_get_object(builder, "audio_title"));
3350     player->priv->audiobox_album = GTK_WIDGET(gtk_builder_get_object(builder, "audio_album"));
3351     player->priv->audiobox_artist = GTK_WIDGET(gtk_builder_get_object(builder, "audio_artist"));
3352 
3353     g_signal_connect(player->priv->audiobox, "draw",
3354             G_CALLBACK(parole_audiobox_expose_event), player);
3355     /* End Content Area */
3356 
3357 
3358     /* Play / Pause */
3359     // GSimpleAction
3360     player->priv->media_playpause_action = g_simple_action_new("playpause_action", NULL);
3361     g_simple_action_set_enabled(player->priv->media_playpause_action, FALSE);
3362     playpause_action = player->priv->media_playpause_action;
3363 
3364     // Button & Image
3365     player->priv->playpause_button = GTK_WIDGET(gtk_builder_get_object(builder, "media_playpause"));
3366     player->priv->playpause_image = GTK_WIDGET(gtk_builder_get_object(builder, "image_media_playpause"));
3367 
3368     // Signals
3369     g_signal_connect(G_OBJECT(player->priv->media_playpause_action), "activate",
3370             G_CALLBACK(parole_player_playpause_action_cb), NULL);
3371     g_signal_connect(G_OBJECT(player->priv->playpause_button), "clicked",
3372             G_CALLBACK(parole_player_widget_activate_action), player->priv->media_playpause_action);
3373 
3374 
3375     /* Previous Track */
3376     // GSimpleAction
3377     player->priv->media_previous_action = g_simple_action_new("previous_action", NULL);
3378     g_simple_action_set_enabled(player->priv->media_previous_action, FALSE);
3379     previous_action = player->priv->media_previous_action;
3380 
3381     // Button
3382     player->priv->previous_button = GTK_WIDGET(gtk_builder_get_object(builder, "media_previous"));
3383     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->previous_button), _("Previous Track"));
3384     gtk_widget_set_sensitive(GTK_WIDGET(player->priv->previous_button), FALSE);
3385 
3386     // Signals
3387     g_signal_connect(G_OBJECT(player->priv->media_previous_action), "activate",
3388             G_CALLBACK(parole_player_previous_action_cb), NULL);
3389     g_signal_connect(G_OBJECT(player->priv->previous_button), "clicked",
3390             G_CALLBACK(parole_player_widget_activate_action), player->priv->media_previous_action);
3391 
3392 
3393     /* Next Track */
3394     // GSimpleAction
3395     player->priv->media_next_action = g_simple_action_new("next_action", NULL);
3396     g_simple_action_set_enabled(player->priv->media_next_action, FALSE);
3397     next_action = player->priv->media_next_action;
3398 
3399     // Button
3400     player->priv->next_button = GTK_WIDGET(gtk_builder_get_object(builder, "media_next"));
3401     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->next_button), _("Next Track"));
3402     gtk_widget_set_sensitive(GTK_WIDGET(player->priv->next_button), FALSE);
3403 
3404     // Signals
3405     g_signal_connect(G_OBJECT(player->priv->media_next_action), "activate",
3406             G_CALLBACK(parole_player_next_action_cb), NULL);
3407     g_signal_connect(G_OBJECT(player->priv->next_button), "clicked",
3408             G_CALLBACK(parole_player_widget_activate_action), player->priv->media_next_action);
3409 
3410 
3411     /* Fullscreen */
3412     // GSimpleAction
3413     player->priv->media_fullscreen_action = g_simple_action_new("fullscreen_action", NULL);
3414     g_simple_action_set_enabled(player->priv->media_fullscreen_action, TRUE);
3415 
3416     // Button
3417     player->priv->fullscreen_button = GTK_WIDGET(gtk_builder_get_object(builder, "media_fullscreen"));
3418     gtk_widget_set_tooltip_text(GTK_WIDGET(player->priv->fullscreen_button), _("Fullscreen"));
3419 
3420     // Menu Item
3421     player->priv->fullscreen_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "fullscreen-menu"));
3422     player->priv->fullscreen_image = GTK_WIDGET(gtk_builder_get_object(builder, "image_media_fullscreen"));
3423 
3424     // Signals
3425     g_signal_connect(G_OBJECT(player->priv->media_fullscreen_action), "activate",
3426             G_CALLBACK(parole_player_fullscreen_action_cb), NULL);
3427     g_signal_connect(G_OBJECT(player->priv->fullscreen_button), "clicked",
3428             G_CALLBACK(parole_player_widget_activate_action), player->priv->media_fullscreen_action);
3429     g_signal_connect(G_OBJECT(player->priv->fullscreen_menu_item), "activate",
3430             G_CALLBACK(parole_player_widget_activate_action), player->priv->media_fullscreen_action);
3431 
3432 
3433     /* Toggle Playlist */
3434 
3435     // Menu Item
3436     player->priv->showhide_playlist_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "show-hide-list"));
3437 
3438     // Button
3439     player->priv->showhide_playlist_button = GTK_WIDGET(gtk_builder_get_object(builder, "media_playlist"));
3440 
3441     // Signals
3442     g_signal_connect(G_OBJECT(player->priv->showhide_playlist_menu_item), "activate",
3443             G_CALLBACK(parole_player_playlist_menu_toggled_cb), player);
3444     g_signal_connect(G_OBJECT(player->priv->showhide_playlist_button), "toggled",
3445             G_CALLBACK(parole_player_playlist_toggled_cb), player);
3446 
3447     /* Toggle Repeat */
3448     // GSimpleAction
3449     player->priv->toggle_repeat_action = g_simple_toggle_action_new("toggle_repeat_action", NULL);
3450     g_simple_action_set_enabled(player->priv->toggle_repeat_action, TRUE);
3451 
3452     // Menu Item
3453     player->priv->repeat_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "repeat"));
3454 
3455     // Signals
3456     g_signal_connect(G_OBJECT(player->priv->repeat_menu_item), "activate",
3457             G_CALLBACK(toggle_action_cb), player->priv->toggle_repeat_action);
3458     g_signal_connect(G_OBJECT(player->priv->toggle_repeat_action), "notify::state",
3459             G_CALLBACK(parole_player_repeat_state_changed), player);
3460     parole_media_list_connect_repeat_action(player->priv->list, player->priv->toggle_repeat_action);
3461     g_object_bind_property(G_OBJECT(player->priv->conf), "repeat",
3462                             player->priv->repeat_menu_item, "active",
3463                             G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
3464 
3465 
3466     /* Toggle Shuffle */
3467     // GSimpleAction
3468     player->priv->toggle_shuffle_action = g_simple_toggle_action_new("toggle_shuffle_action", NULL);
3469     g_simple_action_set_enabled(player->priv->toggle_shuffle_action, TRUE);
3470 
3471     // Menu Item
3472     player->priv->shuffle_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "shuffle"));
3473 
3474     // Signals
3475     g_signal_connect(G_OBJECT(player->priv->shuffle_menu_item), "activate",
3476             G_CALLBACK(toggle_action_cb), player->priv->toggle_shuffle_action);
3477     g_signal_connect(G_OBJECT(player->priv->toggle_shuffle_action), "notify::state",
3478             G_CALLBACK(parole_player_shuffle_state_changed), player);
3479     parole_media_list_connect_shuffle_action(player->priv->list, player->priv->toggle_shuffle_action);
3480     g_object_bind_property(G_OBJECT(player->priv->conf), "shuffle",
3481                             player->priv->shuffle_menu_item, "active",
3482                             G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
3483 
3484 
3485 
3486     /* Media Controls */
3487     controls_overlay = GTK_WIDGET(gtk_overlay_new());
3488 
3489     player->priv->control = GTK_WIDGET(gtk_builder_get_object(builder, "control"));
3490 
3491     play_box = GTK_WIDGET(gtk_builder_get_object(builder, "media_controls"));
3492     controls_parent = GTK_WIDGET(gtk_builder_get_object(builder, "box2"));
3493     gtk_box_pack_start(GTK_BOX(controls_parent), controls_overlay, TRUE, TRUE, 0);
3494 
3495     parole_widget_reparent(GTK_WIDGET(player->priv->eventbox_output), controls_overlay);
3496 
3497     player->priv->revealer = gtk_revealer_new();
3498     gtk_widget_set_vexpand(GTK_WIDGET(player->priv->revealer), FALSE);
3499     gtk_widget_set_hexpand(GTK_WIDGET(player->priv->revealer), FALSE);
3500     gtk_revealer_set_transition_duration(GTK_REVEALER(player->priv->revealer), 250);
3501     gtk_revealer_set_transition_type(GTK_REVEALER(player->priv->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
3502     gtk_revealer_set_reveal_child(GTK_REVEALER(player->priv->revealer), TRUE);
3503     gtk_widget_set_valign(player->priv->revealer, GTK_ALIGN_END);
3504 
3505     parole_widget_reparent(GTK_WIDGET(player->priv->control), player->priv->revealer);
3506 
3507     gtk_overlay_add_overlay(GTK_OVERLAY(controls_overlay), player->priv->revealer);
3508     gtk_widget_show_all(player->priv->revealer);
3509 
3510     gtk_box_set_child_packing(GTK_BOX(player->priv->control), GTK_WIDGET(play_box), TRUE, TRUE, 2, GTK_PACK_START);
3511     gtk_container_set_border_width(GTK_CONTAINER(play_box), 3);
3512     gtk_widget_show_all(controls_parent);
3513 
3514     /* Enable motion-notify event to prevent hiding controls on mouseover */
3515     gtk_widget_add_events(GTK_WIDGET(player->priv->control), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
3516     g_signal_connect(G_OBJECT(player->priv->control), "motion-notify-event",
3517                      G_CALLBACK(parole_player_gst_widget_motion_notify_event), player);
3518 
3519     gtk_widget_add_events(GTK_WIDGET(play_box), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
3520     g_signal_connect(G_OBJECT(play_box), "motion-notify-event",
3521                      G_CALLBACK(parole_player_gst_widget_motion_notify_event), player);
3522     for (widgets = gtk_container_get_children(GTK_CONTAINER(play_box));
3523          widgets != NULL;
3524          widgets = g_list_next(widgets)) {
3525         gtk_widget_add_events(GTK_WIDGET(widgets->data), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
3526         g_signal_connect(G_OBJECT(widgets->data), "motion-notify-event",
3527                      G_CALLBACK(parole_player_gst_widget_motion_notify_event), player);
3528     }
3529 
3530     /* Elapsed/Duration labels */
3531     player->priv->label_duration = GTK_WIDGET(gtk_builder_get_object(builder, "media_time_duration"));
3532     player->priv->label_elapsed = GTK_WIDGET(gtk_builder_get_object(builder, "media_time_elapsed"));
3533     player->priv->label_divider = GTK_WIDGET(gtk_builder_get_object(builder, "media_time_divider"));
3534     gtk_widget_hide (player->priv->label_divider);
3535 
3536     /* Time Slider */
3537     player->priv->range = GTK_WIDGET(gtk_builder_get_object(builder, "media_progress_slider"));
3538     gtk_widget_set_name(player->priv->range, "ParoleScale");
3539 
3540     /* Buffering Progressbar */
3541     player->priv->progressbar_buffering = GTK_WIDGET(gtk_builder_get_object(builder, "media_buffering_progressbar"));
3542 
3543     /* Volume Button */
3544     player->priv->volume = GTK_WIDGET(gtk_builder_get_object(builder, "media_volumebutton"));
3545     player->priv->mute = GTK_WIDGET(gtk_builder_get_object(builder, "volume-mute-menu"));
3546     parole_player_change_volume(player, (gdouble)(volume/100.));
3547 
3548     g_signal_connect(gtk_scale_button_get_popup(GTK_SCALE_BUTTON(player->priv->volume)), "hide", G_CALLBACK(parole_player_volume_popdown_cb), player);
3549 
3550     gtk_widget_set_direction(GTK_WIDGET(gtk_builder_get_object(builder, "ltrbox")), GTK_TEXT_DIR_LTR);
3551     g_signal_connect(player->priv->control, "draw", G_CALLBACK(parole_overlay_expose_event), NULL);
3552     /* End Media Controls */
3553 
3554     /* Info Bar */
3555     // Placeholder widget
3556     hbox_infobar = GTK_WIDGET(gtk_builder_get_object(builder, "infobar_placeholder"));
3557 
3558     // Initialize the InfoBar
3559     player->priv->infobar = gtk_info_bar_new();
3560     gtk_info_bar_set_message_type(GTK_INFO_BAR(player->priv->infobar),
3561                                   GTK_MESSAGE_QUESTION);
3562 
3563     gtk_widget_set_no_show_all(player->priv->infobar, TRUE);
3564 
3565     content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(player->priv->infobar));
3566     g_signal_connect(content_area, "size-allocate",
3567               G_CALLBACK(on_content_area_size_allocate), player);
3568 
3569     gtk_box_pack_start(GTK_BOX(hbox_infobar), player->priv->infobar, TRUE, TRUE, 0);
3570 
3571     // Initialize the Audio Track combobox
3572     player->priv->liststore_audiotrack = gtk_list_store_new(1, G_TYPE_STRING);
3573     player->priv->combobox_audiotrack = gtk_combo_box_new_with_model(
3574         GTK_TREE_MODEL(player->priv->liststore_audiotrack));
3575     player->priv->audio_list = NULL;
3576 
3577     cell = gtk_cell_renderer_text_new();
3578     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(player->priv->combobox_audiotrack), cell, TRUE);
3579     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(player->priv->combobox_audiotrack), cell, "text", 0, NULL);
3580 
3581     g_signal_connect(player->priv->combobox_audiotrack, "changed",
3582             G_CALLBACK(parole_player_combo_box_audiotrack_changed_cb), player);
3583 
3584     // Humanize and pack the Audio Track combobox
3585     audiotrack_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
3586     audiotrack_label = gtk_label_new(_("Audio Track:"));
3587     gtk_box_pack_start(GTK_BOX(audiotrack_box), audiotrack_label, FALSE, FALSE, 0);
3588     gtk_box_pack_end(GTK_BOX(audiotrack_box), player->priv->combobox_audiotrack, FALSE, FALSE, 0);
3589     gtk_container_add(GTK_CONTAINER(content_area), audiotrack_box);
3590 
3591     // Initialize the Subtitles combobox
3592     player->priv->liststore_subtitles = gtk_list_store_new(1, G_TYPE_STRING);
3593     player->priv->combobox_subtitles = gtk_combo_box_new_with_model(GTK_TREE_MODEL(player->priv->liststore_subtitles));
3594     player->priv->subtitle_list = NULL;
3595 
3596     sub_cell = gtk_cell_renderer_text_new();
3597     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(player->priv->combobox_subtitles), sub_cell, TRUE);
3598     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(player->priv->combobox_subtitles), sub_cell, "text", 0, NULL);
3599 
3600     g_signal_connect(player->priv->combobox_subtitles, "changed",
3601             G_CALLBACK(parole_player_combo_box_subtitles_changed_cb), player);
3602 
3603     // Humanize and pack the Subtitles combobox
3604     subtitle_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
3605     subtitle_label = gtk_label_new(_("Subtitles:"));
3606     gtk_box_pack_start(GTK_BOX(subtitle_box), subtitle_label, FALSE, FALSE, 0);
3607     gtk_box_pack_end(GTK_BOX(subtitle_box), player->priv->combobox_subtitles, FALSE, FALSE, 0);
3608     gtk_container_add(GTK_CONTAINER(content_area), subtitle_box);
3609 
3610     // Add a close button to the Infobar
3611     infobar_close = gtk_button_new_with_label(_("Close"));
3612     close_icon = gtk_image_new_from_icon_name("gtk-close", GTK_ICON_SIZE_BUTTON);
3613     gtk_button_set_image(GTK_BUTTON(infobar_close), close_icon);
3614     g_signal_connect(infobar_close, "clicked",
3615               G_CALLBACK(on_infobar_close_clicked), player);
3616     gtk_box_pack_end(GTK_BOX(content_area), infobar_close, FALSE, FALSE, 0);
3617 
3618     gtk_widget_show_all(content_area);
3619 
3620     player->priv->update_languages = FALSE;
3621     player->priv->updated_subs = FALSE;
3622     /* End Info Bar */
3623 
3624     #ifdef HAVE_CLUTTER
3625         player->priv->use_clutter = g_strcmp0(videosink, "cluttersink") == 0;
3626         if (player->priv->use_clutter) {
3627             GtkWidget *clutterbox;
3628             GstElement *video_sink;
3629 
3630             player->priv->clutter = parole_clutter_new(player->priv->conf);
3631             clutterbox = parole_clutter_get_embed_widget(PAROLE_CLUTTER(player->priv->clutter));
3632 
3633             gtk_box_pack_start(GTK_BOX(player->priv->videobox),
3634                                          clutterbox,
3635                                          TRUE, TRUE, 0);
3636 
3637             video_sink = parole_gst_video_sink(PAROLE_GST(player->priv->gst));
3638             parole_clutter_apply_texture(PAROLE_CLUTTER(player->priv->clutter), &video_sink);
3639 
3640             gtk_widget_show(player->priv->clutter);
3641             gtk_widget_grab_focus(player->priv->clutter);
3642         } else {
3643             gtk_box_pack_start(GTK_BOX(player->priv->videobox),
3644                                          player->priv->gst,
3645                                          TRUE, TRUE, 0);
3646 
3647             gtk_widget_realize(player->priv->gst);
3648             gtk_widget_show(player->priv->gst);
3649 
3650             gtk_widget_grab_focus(player->priv->gst);
3651         }
3652     #else
3653         gtk_box_pack_start(GTK_BOX(player->priv->videobox),
3654                                      player->priv->gst,
3655                                      TRUE, TRUE, 0);
3656 
3657         gtk_widget_realize(player->priv->gst);
3658         gtk_widget_show(player->priv->gst);
3659 
3660         gtk_widget_grab_focus(player->priv->gst);
3661     #endif
3662 
3663     parole_gst_set_default_aspect_ratio(player, builder);
3664 
3665     /*
3666      * Gst signals
3667      */
3668     g_signal_connect(G_OBJECT(player->priv->gst), "media-state",
3669             G_CALLBACK(parole_player_media_state_cb), player);
3670 
3671     g_signal_connect(G_OBJECT(player->priv->gst), "media-progressed",
3672             G_CALLBACK(parole_player_media_progressed_cb), player);
3673 
3674     g_signal_connect(G_OBJECT(player->priv->gst), "media-tag",
3675             G_CALLBACK(parole_player_media_tag_cb), player);
3676 
3677     g_signal_connect(G_OBJECT(player->priv->gst), "error",
3678             G_CALLBACK(parole_player_error_cb), player);
3679 
3680     g_signal_connect(G_OBJECT(player->priv->gst), "buffering",
3681             G_CALLBACK(parole_player_buffering_cb), player);
3682 
3683     g_signal_connect(G_OBJECT(player->priv->gst), "dvd-chapter-count-change",
3684             G_CALLBACK(parole_player_dvd_chapter_count_change_cb), player);
3685 
3686     g_signal_connect(G_OBJECT(player->priv->gst), "dvd-chapter-change",
3687             G_CALLBACK(parole_player_dvd_chapter_change_cb), player);
3688 
3689     g_signal_connect(G_OBJECT(player->priv->gst), "notify::volume",
3690             G_CALLBACK(parole_property_notify_cb_volume), player);
3691 
3692     g_signal_connect(G_OBJECT(parole_gst_get_stream(PAROLE_GST(player->priv->gst))), "notify::seekable",
3693             G_CALLBACK(parole_player_seekable_notify), player);
3694 
3695     /*
3696      * Media List Signals
3697      */
3698     g_signal_connect(player->priv->list, "media_activated",
3699               G_CALLBACK(parole_player_media_activated_cb), player);
3700 
3701     g_signal_connect(player->priv->list, "media_cursor_changed",
3702               G_CALLBACK(parole_player_media_cursor_changed_cb), player);
3703 
3704     g_signal_connect(player->priv->list, "uri-opened",
3705               G_CALLBACK(parole_player_uri_opened_cb), player);
3706 
3707     g_signal_connect(player->priv->list, "iso-opened",
3708               G_CALLBACK(parole_player_iso_opened_cb), player);
3709 
3710     g_signal_connect(player->priv->list, "key-forward",
3711               G_CALLBACK(parole_player_key_forward_cb), player);
3712 
3713     /*
3714      * Load auto saved media list.
3715      */
3716     parole_media_list_load(player->priv->list);
3717 
3718     /*
3719      * Connect remaining signals.
3720      */
3721     gtk_builder_connect_signals(builder, player);
3722     g_object_unref(builder);
3723 
3724 
3725 
3726     /* Set up DBUS */
3727     parole_player_dbus_init(player);
3728 
3729     /* Finish preparing the window. */
3730     g_signal_connect_swapped(player->priv->window, "notify::is-active",
3731             G_CALLBACK(parole_player_window_notify_is_active), player);
3732 
3733     gtk_window_set_default_size(GTK_WINDOW(player->priv->window), w, h);
3734     gtk_window_resize(GTK_WINDOW(player->priv->window), w, h);
3735     if (maximized)
3736         gtk_window_maximize(GTK_WINDOW(player->priv->window));
3737 
3738     gtk_widget_show_all(player->priv->window);
3739 
3740     g_object_get(G_OBJECT(player->priv->conf),
3741               "always-hide-menubar", &always_hide_menubar,
3742               NULL);
3743     if (always_hide_menubar == TRUE)
3744         parole_player_hide_menubar_cb(NULL, player);
3745 
3746     parole_player_set_wm_opacity_hint(player->priv->window);
3747 }
3748 
3749 ParolePlayer *
3750 parole_player_new(const gchar *client_id) {
3751     parole_player = g_object_new(PAROLE_TYPE_PLAYER, "client-id", client_id, NULL);
3752     return parole_player;
3753 }
3754 
3755 ParoleMediaList *parole_player_get_media_list(ParolePlayer *player) {
3756     return player->priv->list;
3757 }
3758 
3759 void parole_player_play_uri_disc(ParolePlayer *player, const gchar *uri, const gchar *device) {
3760     if ( uri ) {
3761         parole_player_disc_selected_cb(NULL, uri, device, player);
3762     } else if (device) {
3763         gchar *uri_local = parole_get_uri_from_unix_device(device);
3764         if ( uri_local ) {
3765             parole_player_disc_selected_cb(NULL, uri_local, device, player);
3766             g_free(uri_local);
3767         }
3768     }
3769 }
3770 
3771 void parole_player_set_audiotrack_radio_menu_item_selected(ParolePlayer *player, gint audio_index) {
3772     GList *menu_items, *menu_iter;
3773     gint counter = 0;
3774 
3775     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->languages_menu));
3776 
3777     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
3778         if (counter == audio_index) {
3779             gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_iter->data), TRUE);
3780             break;
3781         }
3782         counter++;
3783     }
3784     g_list_free(menu_items);
3785 }
3786 
3787 void parole_player_set_subtitle_radio_menu_item_selected(ParolePlayer *player, gint sub_index) {
3788     GList *menu_items, *menu_iter;
3789     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->subtitles_menu));
3790 
3791     if (sub_index <= 0) {
3792         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_items->data), TRUE);
3793     } else {
3794         gint counter = -3;
3795         for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
3796             if (counter == sub_index) {
3797                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_iter->data), TRUE);
3798                 break;
3799             }
3800             counter++;
3801         }
3802     }
3803     g_list_free(menu_items);
3804 }
3805 
3806 void parole_player_audiotrack_radio_menu_item_changed_cb(GtkWidget *widget, ParolePlayer *player) {
3807     gint radio_index = 0;
3808     GList *menu_items, *menu_iter;
3809     gint counter = 0;
3810     gint combobox_index;
3811 
3812     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->languages_menu));
3813 
3814     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
3815         if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_iter->data)) == TRUE) {
3816             radio_index = counter;
3817             break;
3818         }
3819         counter++;
3820     }
3821     g_list_free(menu_items);
3822 
3823     combobox_index = gtk_combo_box_get_active(GTK_COMBO_BOX(player->priv->combobox_audiotrack));
3824     if (radio_index != combobox_index)
3825         gtk_combo_box_set_active(GTK_COMBO_BOX(player->priv->combobox_audiotrack), radio_index);
3826 }
3827 
3828 void parole_player_subtitles_radio_menu_item_changed_cb(GtkWidget *widget, ParolePlayer *player) {
3829     gint radio_index = 0;
3830     gint combobox_index = 0;
3831     gint counter = 0;
3832 
3833     GList *menu_items, *menu_iter;
3834     menu_items = gtk_container_get_children(GTK_CONTAINER(player->priv->subtitles_menu));
3835 
3836     for (menu_iter = menu_items; menu_iter != NULL; menu_iter = g_list_next(menu_iter)) {
3837         if (counter == 0 || counter > 3) {
3838             if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_iter->data)) == TRUE) {
3839                 radio_index = counter;
3840                 break;
3841             }
3842         }
3843         counter++;
3844     }
3845     g_list_free(menu_items);
3846 
3847     if (radio_index != 0)
3848         radio_index -= 3;
3849 
3850     combobox_index = gtk_combo_box_get_active(GTK_COMBO_BOX(player->priv->combobox_subtitles));
3851     if (radio_index != combobox_index)
3852         gtk_combo_box_set_active(GTK_COMBO_BOX(player->priv->combobox_subtitles), radio_index);
3853 }
3854 
3855 void parole_player_combo_box_audiotrack_changed_cb(GtkWidget *widget, ParolePlayer *player) {
3856     gint audio_index = gtk_combo_box_get_active(GTK_COMBO_BOX(player->priv->combobox_audiotrack));
3857     if (player->priv->update_languages == FALSE)
3858         gst_set_current_audio_track(PAROLE_GST(player->priv->gst), audio_index);
3859     parole_player_set_audiotrack_radio_menu_item_selected(player, audio_index);
3860 }
3861 
3862 static gboolean
3863 get_has_file_extension(gchar* filename, gchar* extension) {
3864     gchar *lowercase = NULL;
3865     gboolean has_ext;
3866 
3867     lowercase = g_utf8_strdown(filename, g_utf8_strlen(filename, -1));
3868 
3869     has_ext = g_str_has_suffix(lowercase, extension);
3870 
3871     g_free(lowercase);
3872 
3873     return has_ext;
3874 }
3875 
3876 void parole_player_combo_box_subtitles_changed_cb(GtkWidget *widget, ParolePlayer *player) {
3877     gchar *uri = parole_gst_get_file_uri(PAROLE_GST(player->priv->gst));
3878     gint sub_index = gtk_combo_box_get_active(GTK_COMBO_BOX(player->priv->combobox_subtitles));
3879 
3880     if (get_has_file_extension(uri, "mkv")) {
3881         gst_set_current_subtitle_track(PAROLE_GST(player->priv->gst), sub_index);
3882     } else {
3883         if (player->priv->update_languages == FALSE)
3884             gst_set_current_subtitle_track(PAROLE_GST(player->priv->gst), sub_index);
3885     }
3886     parole_player_set_subtitle_radio_menu_item_selected(player, sub_index);
3887 }
3888 
3889 void parole_player_terminate(ParolePlayer *player) {
3890     parole_player_delete_event_cb(NULL, NULL, player);
3891 }
3892 
3893 
3894 static gboolean     parole_player_dbus_play(ParolePlayer *player,
3895                                                          GError *error);
3896 
3897 static gboolean     parole_player_dbus_next_track(ParolePlayer *player,
3898                                                          GError *error);
3899 
3900 static gboolean     parole_player_dbus_prev_track(ParolePlayer *player,
3901                                                          GError *error);
3902 
3903 static gboolean     parole_player_dbus_raise_volume(ParolePlayer *player,
3904                                                          GError *error);
3905 
3906 static gboolean     parole_player_dbus_lower_volume(ParolePlayer *player,
3907                                                          GError *error);
3908 
3909 static gboolean     parole_player_dbus_mute(ParolePlayer *player,
3910                                                          GError *error);
3911 
3912 static gboolean     parole_player_dbus_unmute(ParolePlayer *player,
3913                                                          GError *error);
3914 
3915 static gboolean     parole_player_dbus_play_disc(ParolePlayer *player,
3916                                                          gchar *in_uri,
3917                                                          gchar *in_device,
3918                                                          GError **error);
3919 
3920 #include "src/org.parole.media.player.h"
3921 
3922 /*
3923  * DBus server implementation
3924  */
3925 static void
3926 parole_player_dbus_class_init(ParolePlayerClass *klass) {
3927     dbus_g_object_type_install_info(G_TYPE_FROM_CLASS(klass),
3928                                      &dbus_glib_parole_player_object_info);
3929 }
3930 
3931 static void
3932 parole_player_dbus_init(ParolePlayer *player) {
3933     dbus_g_connection_register_g_object(player->priv->bus,
3934                                         PAROLE_DBUS_PATH,
3935                                         G_OBJECT(player));
3936 }
3937 
3938 static gboolean parole_player_dbus_play(ParolePlayer *player, GError *error) {
3939     parole_player_toggle_playpause(player);
3940     return TRUE;
3941 }
3942 
3943 static gboolean parole_player_dbus_next_track(ParolePlayer *player, GError *error) {
3944     parole_player_play_next(player, TRUE);
3945     return TRUE;
3946 }
3947 
3948 static gboolean parole_player_dbus_prev_track(ParolePlayer *player, GError *error) {
3949     parole_player_play_prev(player);
3950     return TRUE;
3951 }
3952 
3953 static gboolean parole_player_dbus_raise_volume(ParolePlayer *player, GError *error) {
3954     parole_player_volume_up(NULL, player);
3955     return TRUE;
3956 }
3957 
3958 static gboolean parole_player_dbus_lower_volume(ParolePlayer *player, GError *error) {
3959     parole_player_volume_down(NULL, player);
3960     return TRUE;
3961 }
3962 
3963 static gboolean parole_player_dbus_mute(ParolePlayer *player, GError *error) {
3964     if (gtk_scale_button_get_value(GTK_SCALE_BUTTON(player->priv->volume)) != 0.0) {
3965         parole_player_volume_mute(NULL, player);
3966     }
3967     return TRUE;
3968 }
3969 
3970 static gboolean parole_player_dbus_unmute(ParolePlayer *player, GError *error) {
3971     if (gtk_scale_button_get_value(GTK_SCALE_BUTTON(player->priv->volume)) == 0.0) {
3972         parole_player_volume_mute(NULL, player);
3973     }
3974     return TRUE;
3975 }
3976 
3977 static gboolean parole_player_dbus_play_disc(ParolePlayer *player,
3978                                              gchar *in_uri,
3979                                              gchar *in_device,
3980                                              GError **error) {
3981     TRACE("uri : %s", in_uri);
3982 
3983     gtk_window_present(GTK_WINDOW(player->priv->window));
3984 
3985     if ( parole_is_uri_disc (in_uri) )
3986         parole_player_disc_selected_cb(NULL, in_uri, in_device, player);
3987 
3988     return TRUE;
3989 }
3990