1 /*
2 * Copyright (c) 2017-2022 gnome-mpv
3 *
4 * This file is part of Celluloid.
5 *
6 * Celluloid is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Celluloid is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Celluloid. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <glib/gi18n.h>
21 #include <gdk/gdk.h>
22
23 #ifdef GDK_WINDOWING_WAYLAND
24 #include <gdk/wayland/gdkwayland.h>
25 #endif
26
27 #include "celluloid-view.h"
28 #include "celluloid-file-chooser.h"
29 #include "celluloid-open-location-dialog.h"
30 #include "celluloid-preferences-dialog.h"
31 #include "celluloid-shortcuts-window.h"
32 #include "celluloid-authors.h"
33 #include "celluloid-marshal.h"
34 #include "celluloid-menu.h"
35 #include "celluloid-common.h"
36 #include "celluloid-def.h"
37
38 enum
39 {
40 PROP_0,
41 PROP_PLAYLIST_COUNT,
42 PROP_PAUSE,
43 PROP_IDLE_ACTIVE,
44 PROP_VOLUME,
45 PROP_VOLUME_MAX,
46 PROP_DURATION,
47 PROP_PLAYLIST_POS,
48 PROP_TRACK_LIST,
49 PROP_DISC_LIST,
50 PROP_SKIP_ENABLED,
51 PROP_LOOP,
52 PROP_SHUFFLE,
53 PROP_BORDER,
54 PROP_FULLSCREEN,
55 PROP_MEDIA_TITLE,
56 PROP_DISPLAY_FPS,
57 PROP_SEARCHING,
58 N_PROPERTIES
59 };
60
61 struct _CelluloidView
62 {
63 CelluloidMainWindow parent;
64 gboolean disposed;
65 gboolean has_dialog;
66
67 /* Properties */
68 gint playlist_count;
69 gboolean pause;
70 gboolean idle_active;
71 gdouble volume;
72 gdouble volume_max;
73 gdouble duration;
74 gint playlist_pos;
75 GPtrArray *track_list;
76 GPtrArray *disc_list;
77 gboolean skip_enabled;
78 gboolean control_box_enabled;
79 gboolean loop;
80 gboolean shuffle;
81 gboolean border;
82 gboolean fullscreen;
83 gchar *media_title;
84 gdouble display_fps;
85 gboolean searching;
86 };
87
88 struct _CelluloidViewClass
89 {
90 CelluloidMainWindowClass parent_class;
91 };
92
93 G_DEFINE_TYPE(CelluloidView, celluloid_view, CELLULOID_TYPE_MAIN_WINDOW)
94
95 static void
96 constructed(GObject *object);
97
98 static void
99 dispose(GObject *object);
100
101 static void
102 set_property( GObject *object,
103 guint property_id,
104 const GValue *value,
105 GParamSpec *pspec );
106
107 static void
108 get_property( GObject *object,
109 guint property_id,
110 GValue *value,
111 GParamSpec *pspec );
112
113 static void
114 load_css(CelluloidView *view);
115
116 static void
117 load_settings(CelluloidView *view);
118
119 static void
120 save_playlist(CelluloidView *view, GFile *file, GError **error);
121
122 static void
123 update_title(CelluloidView *view);
124
125 static void
126 show_message_dialog( CelluloidView *view,
127 GtkMessageType type,
128 const gchar *title,
129 const gchar *prefix,
130 const gchar *msg );
131
132 static void
133 show_open_track_dialog(CelluloidView *view, TrackType type);
134
135 /* Dialog responses */
136 static void
137 message_dialog_response_handler( GtkDialog *dialog,
138 gint response_id,
139 gpointer data);
140
141 static void
142 open_dialog_response_handler(GtkDialog *dialog, gint response_id, gpointer data);
143
144 static void
145 open_location_dialog_response_handler( GtkDialog *dialog,
146 gint response_id,
147 gpointer data );
148
149 static void
150 open_track_dialog_response_handler( GtkDialog *dialog,
151 gint response_id,
152 gpointer data );
153
154 static void
155 preferences_dialog_response_handler( GtkDialog *dialog,
156 gint response_id,
157 gpointer data );
158
159 static void
160 save_playlist_response_handler( GtkDialog *dialog,
161 gint response_id,
162 gpointer data );
163
164 static void
165 realize_handler(GtkWidget *widget, gpointer data);
166
167 static void
168 resize_handler( CelluloidVideoArea *video_area,
169 gint width,
170 gint height,
171 gpointer data );
172
173 static void
174 render_handler(CelluloidVideoArea *area, gpointer data);
175
176 static gboolean
177 drop_handler( GtkDropTargetAsync *self,
178 GValue *value,
179 gdouble x,
180 gdouble y,
181 gpointer data );
182
183 static gboolean
184 notify_fullscreened_handler(GObject *gobject, GParamSpec *pspec, gpointer data);
185
186 static gboolean
187 close_request_handler(GtkWidget *widget, gpointer data);
188
189 static void
190 playlist_row_activated_handler( CelluloidPlaylistWidget *playlist,
191 gint64 pos,
192 gpointer data );
193
194 static void
195 playlist_row_inserted_handler( CelluloidPlaylistWidget *widget,
196 gint pos,
197 gpointer data );
198
199 static void
200 playlist_row_deleted_handler( CelluloidPlaylistWidget *widget,
201 gint pos,
202 gpointer data );
203
204 static void
205 playlist_row_reordered_handler( CelluloidPlaylistWidget *widget,
206 gint src,
207 gint dest,
208 gpointer data );
209
210 static void
constructed(GObject * object)211 constructed(GObject *object)
212 {
213 G_OBJECT_CLASS(celluloid_view_parent_class)->constructed(object);
214
215 CelluloidView *view =
216 CELLULOID_VIEW(object);
217 CelluloidMainWindow *wnd =
218 CELLULOID_MAIN_WINDOW(view);
219 CelluloidVideoArea *video_area =
220 celluloid_main_window_get_video_area(wnd);
221 CelluloidPlaylistWidget *playlist =
222 celluloid_main_window_get_playlist(wnd);
223 CelluloidControlBox *control_box =
224 celluloid_main_window_get_control_box(wnd);
225
226 g_object_bind_property( view, "duration",
227 control_box, "duration",
228 G_BINDING_DEFAULT );
229 g_object_bind_property( view, "pause",
230 control_box, "pause",
231 G_BINDING_DEFAULT );
232 g_object_bind_property( view, "skip-enabled",
233 control_box, "skip-enabled",
234 G_BINDING_DEFAULT );
235 g_object_bind_property( view, "loop",
236 control_box, "loop",
237 G_BINDING_BIDIRECTIONAL );
238 g_object_bind_property( view, "shuffle",
239 control_box, "shuffle",
240 G_BINDING_BIDIRECTIONAL );
241 g_object_bind_property( view, "volume",
242 control_box, "volume",
243 G_BINDING_BIDIRECTIONAL );
244 g_object_bind_property( view, "volume-max",
245 control_box, "volume-max",
246 G_BINDING_DEFAULT );
247 g_object_bind_property( playlist, "playlist-count",
248 view, "playlist-count",
249 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE );
250 g_object_bind_property( playlist, "searching",
251 view, "searching",
252 G_BINDING_BIDIRECTIONAL );
253
254 celluloid_main_window_load_state(wnd);
255 load_settings(view);
256
257 g_signal_connect( video_area,
258 "resize",
259 G_CALLBACK(resize_handler),
260 view );
261 g_signal_connect( video_area,
262 "render",
263 G_CALLBACK(render_handler),
264 view );
265
266 GType types[] = {G_TYPE_STRING, GDK_TYPE_FILE_LIST};
267
268 GtkDropTarget *video_area_drop_target =
269 gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY);
270
271 gtk_drop_target_set_gtypes
272 (video_area_drop_target, types, G_N_ELEMENTS(types));
273 gtk_widget_add_controller
274 ( GTK_WIDGET(video_area),
275 GTK_EVENT_CONTROLLER(video_area_drop_target) );
276 g_signal_connect( video_area_drop_target,
277 "drop",
278 G_CALLBACK(drop_handler),
279 view );
280
281 GtkDropTarget *playlist_drop_target =
282 gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY);
283 gtk_drop_target_set_gtypes
284 (playlist_drop_target, types, G_N_ELEMENTS(types));
285 gtk_widget_add_controller
286 ( GTK_WIDGET(playlist),
287 GTK_EVENT_CONTROLLER(playlist_drop_target) );
288 g_signal_connect( playlist_drop_target,
289 "drop",
290 G_CALLBACK(drop_handler),
291 view );
292
293 g_signal_connect( wnd,
294 "notify::fullscreened",
295 G_CALLBACK(notify_fullscreened_handler),
296 view );
297 g_signal_connect( wnd,
298 "close-request",
299 G_CALLBACK(close_request_handler),
300 view );
301 g_signal_connect( playlist,
302 "row-activated",
303 G_CALLBACK(playlist_row_activated_handler),
304 view );
305 g_signal_connect( playlist,
306 "row-inserted",
307 G_CALLBACK(playlist_row_inserted_handler),
308 view );
309 g_signal_connect( playlist,
310 "row-deleted",
311 G_CALLBACK(playlist_row_deleted_handler),
312 view );
313 g_signal_connect( playlist,
314 "rows-reordered",
315 G_CALLBACK(playlist_row_reordered_handler),
316 view );
317 }
318
319 static void
dispose(GObject * object)320 dispose(GObject *object)
321 {
322 CelluloidView *view = CELLULOID_VIEW(object);
323
324 if(!view->disposed)
325 {
326 celluloid_main_window_save_state(CELLULOID_MAIN_WINDOW(object));
327 view->disposed = TRUE;
328 }
329
330 G_OBJECT_CLASS(celluloid_view_parent_class)->dispose(object);
331 }
332
333 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)334 set_property( GObject *object,
335 guint property_id,
336 const GValue *value,
337 GParamSpec *pspec )
338 {
339 CelluloidView *self =
340 CELLULOID_VIEW(object);
341 CelluloidMainWindow *wnd =
342 CELLULOID_MAIN_WINDOW(self);
343 CelluloidControlBox* control_box =
344 celluloid_main_window_get_control_box(wnd);
345
346 switch(property_id)
347 {
348 case PROP_PLAYLIST_COUNT:
349 self->playlist_count = g_value_get_int(value);
350
351 update_title(self);
352
353 g_object_set( control_box,
354 "enabled",
355 self->playlist_count > 0,
356 NULL );
357
358 if(self->playlist_count <= 0)
359 {
360 celluloid_view_reset(self);
361 }
362 break;
363
364 case PROP_PAUSE:
365 self->pause = g_value_get_boolean(value);
366 break;
367
368 case PROP_IDLE_ACTIVE:
369 self->idle_active = g_value_get_boolean(value);
370 update_title(self);
371 break;
372
373 case PROP_VOLUME:
374 self->volume = g_value_get_double(value);
375 break;
376
377 case PROP_VOLUME_MAX:
378 self->volume_max = g_value_get_double(value);
379 break;
380
381 case PROP_DURATION:
382 self->duration = g_value_get_double(value);
383 break;
384
385 case PROP_PLAYLIST_POS:
386 self->playlist_pos = g_value_get_int(value);
387
388 CelluloidPlaylistWidget *playlist =
389 celluloid_main_window_get_playlist(wnd);
390
391 celluloid_playlist_widget_set_indicator_pos
392 (playlist, self->playlist_pos);
393 break;
394
395 case PROP_TRACK_LIST:
396 self->track_list = g_value_get_pointer(value);
397 celluloid_main_window_update_track_list(wnd, self->track_list);
398 break;
399
400 case PROP_DISC_LIST:
401 self->disc_list = g_value_get_pointer(value);
402 celluloid_main_window_update_disc_list(wnd, self->disc_list);
403 break;
404
405 case PROP_SKIP_ENABLED:
406 self->skip_enabled = g_value_get_boolean(value);
407 break;
408
409 case PROP_LOOP:
410 self->loop = g_value_get_boolean(value);
411 break;
412
413 case PROP_SHUFFLE:
414 self->shuffle = g_value_get_boolean(value);
415 break;
416
417 case PROP_BORDER:
418 self->border = g_value_get_boolean(value);
419 {
420 GtkWidget *titlebar =
421 gtk_window_get_titlebar(GTK_WINDOW(wnd));
422
423 if(titlebar)
424 {
425 gtk_widget_set_visible(titlebar, self->border);
426 }
427
428 gtk_window_set_decorated(GTK_WINDOW(wnd), self->border);
429 }
430 break;
431
432 case PROP_FULLSCREEN:
433 self->fullscreen = g_value_get_boolean(value);
434 celluloid_main_window_set_fullscreen(wnd, self->fullscreen);
435 break;
436
437 case PROP_MEDIA_TITLE:
438 g_free(self->media_title);
439 self->media_title = g_value_dup_string(value);
440 update_title(self);
441 break;
442
443 case PROP_DISPLAY_FPS:
444 self->display_fps = g_value_get_double(value);
445 break;
446
447 case PROP_SEARCHING:
448 // We do not display playlist in fullscreen mode so refuse to
449 // enter search mode here if we're in fullscreen mode.
450 self->searching = !self->fullscreen &&
451 g_value_get_boolean(value);
452
453 // Show playlist if we're about to enter search mode and it
454 // isn't already visible.
455 if(self->searching && !celluloid_view_get_playlist_visible(self))
456 {
457 celluloid_view_set_playlist_visible(self, TRUE);
458 }
459 break;
460
461 default:
462 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
463 break;
464 }
465 }
466
467 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)468 get_property( GObject *object,
469 guint property_id,
470 GValue *value,
471 GParamSpec *pspec )
472 {
473 CelluloidView *self = CELLULOID_VIEW(object);
474
475 switch(property_id)
476 {
477 case PROP_PLAYLIST_COUNT:
478 g_value_set_int(value, self->playlist_count);
479 break;
480
481 case PROP_PAUSE:
482 g_value_set_boolean(value, self->pause);
483 break;
484
485 case PROP_IDLE_ACTIVE:
486 g_value_set_boolean(value, self->idle_active);
487 break;
488
489 case PROP_VOLUME:
490 g_value_set_double(value, self->volume);
491 break;
492
493 case PROP_VOLUME_MAX:
494 g_value_set_double(value, self->volume_max);
495 break;
496
497 case PROP_DURATION:
498 g_value_set_double(value, self->duration);
499 break;
500
501 case PROP_PLAYLIST_POS:
502 g_value_set_int(value, self->playlist_pos);
503 break;
504
505 case PROP_TRACK_LIST:
506 g_value_set_pointer(value, self->track_list);
507 break;
508
509 case PROP_DISC_LIST:
510 g_value_set_pointer(value, self->disc_list);
511 break;
512
513 case PROP_SKIP_ENABLED:
514 g_value_set_boolean(value, self->skip_enabled);
515 break;
516
517 case PROP_LOOP:
518 g_value_set_boolean(value, self->loop);
519 break;
520
521 case PROP_SHUFFLE:
522 g_value_set_boolean(value, self->shuffle);
523 break;
524
525 case PROP_BORDER:
526 g_value_set_boolean(value, self->border);
527 break;
528
529 case PROP_FULLSCREEN:
530 g_value_set_boolean(value, self->fullscreen);
531 break;
532
533 case PROP_MEDIA_TITLE:
534 g_value_set_static_string(value, self->media_title);
535 break;
536
537 case PROP_DISPLAY_FPS:
538 g_value_set_double(value, self->display_fps);
539 break;
540
541 case PROP_SEARCHING:
542 g_value_set_boolean(value, self->searching);
543 break;
544
545 default:
546 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
547 break;
548 }
549 }
550
551 static void
load_css(CelluloidView * view)552 load_css(CelluloidView *view)
553 {
554 const gchar *style =
555 "celluloid-video-area { background-color: black; }"
556 ".dialog-action-area { margin: -12px 12px 12px 0px; }"
557 ".dialog-action-area > button { margin-left: 12px; }";
558 GtkCssProvider *style_provider =
559 gtk_css_provider_new();
560
561 gtk_css_provider_load_from_data(style_provider, style, -1);
562
563 GdkSurface *surface = gtk_widget_get_surface(view);
564
565 if(surface)
566 {
567 GdkDisplay *display = gdk_surface_get_display(surface);
568
569 gtk_style_context_add_provider_for_display
570 ( display,
571 GTK_STYLE_PROVIDER(style_provider),
572 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION );
573
574 g_object_unref(style_provider);
575 }
576 else
577 {
578 g_warning("Failed to load CSS: Surface not available");
579 }
580 }
581
582 static void
load_settings(CelluloidView * view)583 load_settings(CelluloidView *view)
584 {
585 GSettings *settings =
586 g_settings_new(CONFIG_ROOT);
587 CelluloidMainWindow *wnd =
588 CELLULOID_MAIN_WINDOW(view);
589 CelluloidControlBox *control_box =
590 celluloid_main_window_get_control_box(wnd);
591
592 gboolean csd_enable;
593 gboolean dark_theme_enable;
594
595 csd_enable = g_settings_get_boolean (settings, "csd-enable");
596 dark_theme_enable = g_settings_get_boolean
597 (settings, "dark-theme-enable");
598
599 g_object_set( control_box,
600 "show-fullscreen-button", !csd_enable,
601 "skip-enabled", FALSE,
602 NULL );
603 g_object_set( gtk_settings_get_default(),
604 "gtk-application-prefer-dark-theme", dark_theme_enable,
605 NULL );
606
607 g_object_unref(settings);
608 }
609
610 static void
save_playlist(CelluloidView * view,GFile * file,GError ** error)611 save_playlist(CelluloidView *view, GFile *file, GError **error)
612 {
613 CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(view);
614 CelluloidPlaylistWidget *wgt = celluloid_main_window_get_playlist(wnd);
615 GPtrArray *playlist = celluloid_playlist_widget_get_contents(wgt);
616 gboolean rc = TRUE;
617 GOutputStream *dest_stream = NULL;
618
619 if(file)
620 {
621 GFileOutputStream *file_stream;
622
623 file_stream = g_file_replace( file,
624 NULL,
625 FALSE,
626 G_FILE_CREATE_NONE,
627 NULL,
628 error );
629 dest_stream = G_OUTPUT_STREAM(file_stream);
630 rc = !!dest_stream;
631 }
632
633 for(guint i = 0; rc && i < playlist->len; i++)
634 {
635 gsize written;
636
637 CelluloidPlaylistEntry *entry = g_ptr_array_index(playlist, i);
638
639 rc = g_output_stream_printf( dest_stream,
640 &written,
641 NULL,
642 NULL,
643 "%s\n",
644 entry->filename);
645 }
646
647 if(dest_stream)
648 {
649 g_output_stream_close(dest_stream, NULL, error);
650 }
651
652 g_ptr_array_free(playlist, TRUE);
653 }
654
655 static void
update_title(CelluloidView * view)656 update_title(CelluloidView *view)
657 {
658 const gboolean use_media_title =
659 view->media_title &&
660 !view->idle_active &&
661 view->playlist_count > 0;
662 const gchar *title_source =
663 use_media_title ?
664 view->media_title :
665 g_get_application_name();
666 gchar *title =
667 sanitize_utf8(title_source, TRUE);
668
669 gtk_window_set_title(GTK_WINDOW(view), title);
670 g_free(title);
671 }
672
673 void
show_message_dialog(CelluloidView * view,GtkMessageType type,const gchar * title,const gchar * prefix,const gchar * msg)674 show_message_dialog( CelluloidView *view,
675 GtkMessageType type,
676 const gchar *title,
677 const gchar *prefix,
678 const gchar *msg )
679 {
680 GtkWidget *dialog = NULL;
681
682 if(view->has_dialog)
683 {
684 g_info("Error dialog suppressed because one is already being shown");
685
686 if(prefix)
687 {
688 g_info("Content: *%s* [%s] %s", title, prefix, msg);
689 }
690 else
691 {
692 g_info("Content: *%s* %s", title, msg);
693 }
694 }
695 else if(prefix)
696 {
697 gchar *prefix_escaped = g_markup_printf_escaped("%s", prefix);
698 gchar *msg_escaped = g_markup_printf_escaped("%s", msg);
699
700 dialog = gtk_message_dialog_new_with_markup
701 ( GTK_WINDOW(view),
702 GTK_DIALOG_DESTROY_WITH_PARENT,
703 type,
704 GTK_BUTTONS_OK,
705 "<b>[%s]</b> %s",
706 prefix_escaped,
707 msg_escaped );
708
709 g_free(prefix_escaped);
710 g_free(msg_escaped);
711 }
712 else
713 {
714 dialog = gtk_message_dialog_new
715 ( GTK_WINDOW(view),
716 GTK_DIALOG_DESTROY_WITH_PARENT,
717 type,
718 GTK_BUTTONS_OK,
719 "%s",
720 msg );
721 }
722
723 if(dialog)
724 {
725 GtkMessageDialog *message_dialog =
726 GTK_MESSAGE_DIALOG(dialog);
727 GtkWidget *message_area =
728 gtk_message_dialog_get_message_area(message_dialog);
729 GtkWidget *first_child =
730 gtk_widget_get_first_child(message_area);
731
732 view->has_dialog = TRUE;
733
734 for( GtkWidget *child = first_child;
735 child;
736 child = gtk_widget_get_next_sibling(child) )
737 {
738 if(GTK_IS_LABEL(child))
739 {
740 gtk_label_set_wrap_mode
741 ( GTK_LABEL(child),
742 PANGO_WRAP_WORD_CHAR );
743 }
744 }
745
746 g_signal_connect
747 ( dialog,
748 "response",
749 G_CALLBACK(message_dialog_response_handler),
750 view );
751
752 gtk_window_set_title(GTK_WINDOW(dialog), title);
753 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
754 gtk_widget_show(dialog);
755 }
756 }
757
758 static void
show_open_track_dialog(CelluloidView * view,TrackType type)759 show_open_track_dialog(CelluloidView *view, TrackType type)
760 {
761 const gchar *title = NULL;
762
763 switch(type)
764 {
765 case TRACK_TYPE_AUDIO:
766 title = _("Load Audio Track…");
767 break;
768
769 case TRACK_TYPE_VIDEO:
770 title = _("Load Video Track…");
771 break;
772
773 case TRACK_TYPE_SUBTITLE:
774 title = _("Load Subtitle Track…");
775 break;
776
777 default:
778 g_assert_not_reached();
779 break;
780 }
781
782 CelluloidFileChooser *chooser = celluloid_file_chooser_new
783 ( title,
784 GTK_WINDOW(view),
785 GTK_FILE_CHOOSER_ACTION_OPEN );
786
787 g_object_set_data( G_OBJECT(chooser),
788 "track-type",
789 (gpointer)type );
790
791 g_signal_connect( chooser,
792 "response",
793 G_CALLBACK(open_track_dialog_response_handler),
794 view );
795
796 celluloid_file_chooser_set_default_filters
797 ( chooser,
798 type == TRACK_TYPE_AUDIO,
799 type == TRACK_TYPE_VIDEO,
800 FALSE,
801 type == TRACK_TYPE_SUBTITLE );
802
803 celluloid_file_chooser_show(chooser);
804 }
805
806 static void
message_dialog_response_handler(GtkDialog * dialog,gint response_id,gpointer data)807 message_dialog_response_handler( GtkDialog *dialog,
808 gint response_id,
809 gpointer data)
810 {
811 CELLULOID_VIEW(data)->has_dialog = FALSE;
812
813 gtk_window_destroy(GTK_WINDOW(dialog));
814 }
815
816 static void
open_dialog_response_handler(GtkDialog * dialog,gint response_id,gpointer data)817 open_dialog_response_handler(GtkDialog *dialog, gint response_id, gpointer data)
818 {
819 GPtrArray *args = data;
820 CelluloidView *view = g_ptr_array_index(args, 0);
821 gboolean *append = g_ptr_array_index(args, 1);
822
823 if(response_id == GTK_RESPONSE_ACCEPT)
824 {
825 GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
826 GListModel *files = gtk_file_chooser_get_files(chooser);
827
828 if(files)
829 {
830 g_signal_emit_by_name(view, "file-open", files, *append);
831 g_object_unref(files);
832 }
833 }
834
835 celluloid_file_chooser_destroy(CELLULOID_FILE_CHOOSER(dialog));
836
837 g_free(append);
838 g_ptr_array_free(args, TRUE);
839 }
840
841 static void
open_location_dialog_response_handler(GtkDialog * dialog,gint response_id,gpointer data)842 open_location_dialog_response_handler( GtkDialog *dialog,
843 gint response_id,
844 gpointer data )
845 {
846 if(response_id == GTK_RESPONSE_ACCEPT)
847 {
848 CelluloidOpenLocationDialog *location_dialog =
849 CELLULOID_OPEN_LOCATION_DIALOG(dialog);
850 const gchar *uri =
851 celluloid_open_location_dialog_get_string
852 (location_dialog);
853
854 GPtrArray *args = data;
855 CelluloidView *view = g_ptr_array_index(args, 0);
856 gboolean *append = g_ptr_array_index(args, 1);
857
858 GFile *file = g_file_new_for_uri(uri);
859 GListStore *list = g_list_store_new(G_TYPE_OBJECT);
860
861 g_list_store_append(list, file);
862
863 g_signal_emit_by_name(view, "file-open", list, *append);
864
865 g_object_unref(list);
866 g_free(append);
867 g_ptr_array_free(args, TRUE);
868 }
869
870 gtk_window_close(GTK_WINDOW(dialog));
871 }
872
873 static void
open_track_dialog_response_handler(GtkDialog * dialog,gint response_id,gpointer data)874 open_track_dialog_response_handler( GtkDialog *dialog,
875 gint response_id,
876 gpointer data )
877 {
878 if(response_id == GTK_RESPONSE_ACCEPT)
879 {
880 GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
881 GListModel *files = gtk_file_chooser_get_files(chooser);
882 const gchar *name = NULL;
883
884 TrackType type =
885 (TrackType)
886 g_object_get_data(G_OBJECT(dialog), "track-type");
887
888 switch(type)
889 {
890 case TRACK_TYPE_AUDIO:
891 name = "audio-track-load";
892 break;
893
894 case TRACK_TYPE_VIDEO:
895 name = "video-track-load";
896 break;
897
898 case TRACK_TYPE_SUBTITLE:
899 name = "subtitle-track-load";
900 break;
901
902 default:
903 g_assert_not_reached();
904 break;
905 }
906
907 CelluloidView *view = CELLULOID_VIEW(data);
908 const guint n_items = g_list_model_get_n_items(files);
909
910 for(guint i = 0; i < n_items; i++)
911 {
912 GFile *file = g_list_model_get_item(files, i);
913 gchar *uri = g_file_get_uri(file);
914
915 g_signal_emit_by_name(view, name, uri);
916 g_free(uri);
917 }
918
919 g_object_unref(files);
920 }
921
922 celluloid_file_chooser_destroy(CELLULOID_FILE_CHOOSER(dialog));
923 }
924
925 static void
preferences_dialog_response_handler(GtkDialog * dialog,gint response_id,gpointer data)926 preferences_dialog_response_handler( GtkDialog *dialog,
927 gint response_id,
928 gpointer data )
929 {
930 if(response_id == GTK_RESPONSE_ACCEPT)
931 {
932 CelluloidMainWindow *wnd;
933 GSettings *settings;
934 gboolean csd_enable;
935
936 wnd = CELLULOID_MAIN_WINDOW(data);
937 settings = g_settings_new(CONFIG_ROOT);
938 csd_enable = g_settings_get_boolean(settings, "csd-enable");
939
940 if(celluloid_main_window_get_csd_enabled(wnd) != csd_enable)
941 {
942 show_message_dialog( CELLULOID_VIEW(data),
943 GTK_MESSAGE_INFO,
944 g_get_application_name(),
945 NULL,
946 _("Enabling or disabling "
947 "client-side decorations "
948 "requires restarting to "
949 "take effect.") );
950 }
951
952 gtk_widget_queue_draw(GTK_WIDGET(wnd));
953 g_signal_emit_by_name(data, "preferences-updated");
954
955 g_object_unref(settings);
956 }
957
958 gtk_window_close(GTK_WINDOW(dialog));
959 }
960
961 static void
save_playlist_response_handler(GtkDialog * dialog,gint response_id,gpointer data)962 save_playlist_response_handler( GtkDialog *dialog,
963 gint response_id,
964 gpointer data )
965 {
966 CelluloidView *view = CELLULOID_VIEW(data);
967 GFile *dest_file = NULL;
968 GError *error = NULL;
969
970 if(response_id == GTK_RESPONSE_ACCEPT)
971 {
972 /* There should be only one file selected. */
973 dest_file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(dialog));
974 }
975
976 celluloid_file_chooser_destroy(CELLULOID_FILE_CHOOSER(dialog));
977
978 if(dest_file)
979 {
980 save_playlist(view, dest_file, &error);
981 g_object_unref(dest_file);
982 }
983
984 if(error)
985 {
986 show_message_dialog( view,
987 GTK_MESSAGE_ERROR,
988 _("Error"),
989 NULL,
990 error->message );
991
992 g_error_free(error);
993 }
994 }
995
996 static void
realize_handler(GtkWidget * widget,gpointer data)997 realize_handler(GtkWidget *widget, gpointer data)
998 {
999 load_css(CELLULOID_VIEW(widget));
1000
1001 g_signal_emit_by_name(CELLULOID_VIEW(widget), "ready");
1002 }
1003
1004 static void
resize_handler(CelluloidVideoArea * video_area,gint width,gint height,gpointer data)1005 resize_handler( CelluloidVideoArea *video_area,
1006 gint width,
1007 gint height,
1008 gpointer data )
1009 {
1010 g_signal_emit_by_name(data, "video-area-resize", width, height);
1011 }
1012
1013 static void
render_handler(CelluloidVideoArea * area,gpointer data)1014 render_handler(CelluloidVideoArea *area, gpointer data)
1015 {
1016 g_signal_emit_by_name(data, "render");
1017 }
1018
1019 static gboolean
drop_handler(GtkDropTargetAsync * self,GValue * value,gdouble x,gdouble y,gpointer data)1020 drop_handler( GtkDropTargetAsync *self,
1021 GValue *value,
1022 gdouble x,
1023 gdouble y,
1024 gpointer data )
1025 {
1026 CelluloidView *view = CELLULOID_VIEW(data);
1027 GtkEventController *controller = GTK_EVENT_CONTROLLER(self);
1028 GtkWidget *source = gtk_event_controller_get_widget(controller);
1029 const gboolean append = CELLULOID_IS_PLAYLIST_WIDGET(source);
1030 GListStore *files = NULL;
1031
1032 if(G_VALUE_HOLDS_STRING(value))
1033 {
1034 const gchar *string = g_value_get_string(value);
1035 gchar **uris = g_uri_list_extract_uris(string);
1036
1037 if(uris)
1038 {
1039 files = g_list_store_new(G_TYPE_FILE);
1040
1041 for(gint i = 0; uris[i]; i++)
1042 {
1043 GFile *file = g_file_new_for_uri(uris[i]);
1044
1045 g_list_store_append(files, file);
1046 g_object_unref(file);
1047 }
1048
1049 g_strfreev(uris);
1050 }
1051 }
1052 else if(G_VALUE_HOLDS(value, GDK_TYPE_FILE_LIST))
1053 {
1054 const GSList *slist = g_value_get_boxed(value);
1055
1056 files = g_list_store_new(G_TYPE_FILE);
1057
1058 for(const GSList *cur = slist; cur; cur = cur->next)
1059 {
1060 g_list_store_append(files, G_FILE(cur->data));
1061 }
1062 }
1063
1064 if(files)
1065 {
1066 g_signal_emit_by_name(view, "file-open", files, append);
1067 g_object_unref(files);
1068 }
1069
1070 return TRUE;
1071 }
1072
1073 static gboolean
notify_fullscreened_handler(GObject * gobject,GParamSpec * pspec,gpointer data)1074 notify_fullscreened_handler(GObject *gobject, GParamSpec *pspec, gpointer data)
1075 {
1076 CelluloidView *view = CELLULOID_VIEW(data);
1077
1078 view->fullscreen = gtk_window_is_fullscreen(GTK_WINDOW(view));
1079 g_object_notify(data, "fullscreen");
1080
1081 return FALSE;
1082 }
1083
1084 static gboolean
close_request_handler(GtkWidget * widget,gpointer data)1085 close_request_handler(GtkWidget *widget, gpointer data)
1086 {
1087 if(!celluloid_main_window_get_fullscreen(CELLULOID_MAIN_WINDOW(widget)))
1088 {
1089 celluloid_main_window_save_state(CELLULOID_MAIN_WINDOW(widget));
1090 }
1091
1092 return FALSE;
1093 }
1094
1095 static void
playlist_row_activated_handler(CelluloidPlaylistWidget * playlist,gint64 pos,gpointer data)1096 playlist_row_activated_handler( CelluloidPlaylistWidget *playlist,
1097 gint64 pos,
1098 gpointer data )
1099 {
1100 g_signal_emit_by_name(data, "playlist-item-activated", pos);
1101 }
1102
1103 static void
playlist_row_inserted_handler(CelluloidPlaylistWidget * widget,gint pos,gpointer data)1104 playlist_row_inserted_handler( CelluloidPlaylistWidget *widget,
1105 gint pos,
1106 gpointer data )
1107 {
1108 g_signal_emit_by_name(data, "playlist-item-inserted", pos);
1109 }
1110
1111 static void
playlist_row_deleted_handler(CelluloidPlaylistWidget * widget,gint pos,gpointer data)1112 playlist_row_deleted_handler( CelluloidPlaylistWidget *widget,
1113 gint pos,
1114 gpointer data )
1115 {
1116 if(celluloid_playlist_widget_empty(widget))
1117 {
1118 celluloid_view_reset(data);
1119 }
1120
1121 g_signal_emit_by_name(data, "playlist-item-deleted", pos);
1122 }
1123
1124 static void
playlist_row_reordered_handler(CelluloidPlaylistWidget * widget,gint src,gint dest,gpointer data)1125 playlist_row_reordered_handler( CelluloidPlaylistWidget *widget,
1126 gint src,
1127 gint dest,
1128 gpointer data )
1129 {
1130 g_signal_emit_by_name(data, "playlist-reordered", src, dest);
1131 }
1132
1133 static void
celluloid_view_class_init(CelluloidViewClass * klass)1134 celluloid_view_class_init(CelluloidViewClass *klass)
1135 {
1136 GObjectClass *object_class = G_OBJECT_CLASS(klass);
1137 GParamSpec *pspec = NULL;
1138
1139 object_class->constructed = constructed;
1140 object_class->dispose = dispose;
1141 object_class->set_property = set_property;
1142 object_class->get_property = get_property;
1143
1144 pspec = g_param_spec_int
1145 ( "playlist-count",
1146 "Playlist count",
1147 "The number of items in playlist",
1148 0,
1149 G_MAXINT,
1150 0,
1151 G_PARAM_READWRITE );
1152 g_object_class_install_property(object_class, PROP_PLAYLIST_COUNT, pspec);
1153
1154 pspec = g_param_spec_boolean
1155 ( "pause",
1156 "Pause",
1157 "Whether or not the player is paused",
1158 FALSE,
1159 G_PARAM_READWRITE );
1160 g_object_class_install_property(object_class, PROP_PAUSE, pspec);
1161
1162 pspec = g_param_spec_boolean
1163 ( "idle-active",
1164 "Idle active",
1165 "Whether or not the player is idle",
1166 TRUE,
1167 G_PARAM_READWRITE );
1168 g_object_class_install_property(object_class, PROP_IDLE_ACTIVE, pspec);
1169
1170 pspec = g_param_spec_double
1171 ( "volume",
1172 "Volume",
1173 "The volume the volume button is set to",
1174 0.0,
1175 G_MAXDOUBLE,
1176 0.0,
1177 G_PARAM_READWRITE );
1178 g_object_class_install_property(object_class, PROP_VOLUME, pspec);
1179
1180 pspec = g_param_spec_double
1181 ( "volume-max",
1182 "Volume Max",
1183 "The maximum amplification level",
1184 0.0,
1185 G_MAXDOUBLE,
1186 100.0,
1187 G_PARAM_READWRITE );
1188 g_object_class_install_property(object_class, PROP_VOLUME_MAX, pspec);
1189
1190 pspec = g_param_spec_double
1191 ( "duration",
1192 "Duration",
1193 "The duration the seek bar is using",
1194 0.0,
1195 G_MAXDOUBLE,
1196 0.0,
1197 G_PARAM_READWRITE );
1198 g_object_class_install_property(object_class, PROP_DURATION, pspec);
1199
1200 pspec = g_param_spec_int
1201 ( "playlist-pos",
1202 "Playlist position",
1203 "The position of the current item in playlist",
1204 -1,
1205 G_MAXINT,
1206 -1,
1207 G_PARAM_READWRITE );
1208 g_object_class_install_property(object_class, PROP_PLAYLIST_POS, pspec);
1209
1210 pspec = g_param_spec_pointer
1211 ( "track-list",
1212 "Track list",
1213 "The list of tracks in the playing file",
1214 G_PARAM_READWRITE );
1215 g_object_class_install_property(object_class, PROP_TRACK_LIST, pspec);
1216
1217 pspec = g_param_spec_pointer
1218 ( "disc-list",
1219 "Disc list",
1220 "The list of mounted discs",
1221 G_PARAM_READWRITE );
1222 g_object_class_install_property(object_class, PROP_DISC_LIST, pspec);
1223
1224 pspec = g_param_spec_boolean
1225 ( "skip-enabled",
1226 "Skip enabled",
1227 "Whether or not skip buttons are shown",
1228 FALSE,
1229 G_PARAM_READWRITE );
1230 g_object_class_install_property(object_class, PROP_SKIP_ENABLED, pspec);
1231
1232 pspec = g_param_spec_boolean
1233 ( "loop",
1234 "Loop",
1235 "Whether or not the the loop button is active",
1236 FALSE,
1237 G_PARAM_READWRITE );
1238 g_object_class_install_property(object_class, PROP_LOOP, pspec);
1239
1240 pspec = g_param_spec_boolean
1241 ( "shuffle",
1242 "Shuffle",
1243 "Whether or not the the shuffle button is active",
1244 FALSE,
1245 G_PARAM_READWRITE );
1246 g_object_class_install_property(object_class, PROP_SHUFFLE, pspec);
1247
1248 pspec = g_param_spec_boolean
1249 ( "border",
1250 "Border",
1251 "Whether or not the main window should have decorations",
1252 FALSE,
1253 G_PARAM_READWRITE );
1254 g_object_class_install_property(object_class, PROP_BORDER, pspec);
1255
1256 pspec = g_param_spec_boolean
1257 ( "fullscreen",
1258 "Fullscreen",
1259 "Whether or not the player is current in fullscreen mode",
1260 FALSE,
1261 G_PARAM_READWRITE );
1262 g_object_class_install_property(object_class, PROP_FULLSCREEN, pspec);
1263
1264 pspec = g_param_spec_string
1265 ( "media-title",
1266 "Media Title",
1267 "The title of the media being played",
1268 NULL,
1269 G_PARAM_READWRITE );
1270 g_object_class_install_property(object_class, PROP_MEDIA_TITLE, pspec);
1271
1272 pspec = g_param_spec_boolean
1273 ( "searching",
1274 "Searching",
1275 "Whether or not the user is searching the playlist",
1276 FALSE,
1277 G_PARAM_READWRITE );
1278 g_object_class_install_property(object_class, PROP_SEARCHING, pspec);
1279
1280 g_signal_new( "video-area-resize",
1281 G_TYPE_FROM_CLASS(klass),
1282 G_SIGNAL_RUN_FIRST|G_SIGNAL_DETAILED,
1283 0,
1284 NULL,
1285 NULL,
1286 g_cclosure_gen_marshal_VOID__INT_INT,
1287 G_TYPE_NONE,
1288 2,
1289 G_TYPE_INT,
1290 G_TYPE_INT );
1291
1292 /* Controls-related signals */
1293 g_signal_new( "volume-changed",
1294 G_TYPE_FROM_CLASS(klass),
1295 G_SIGNAL_RUN_FIRST,
1296 0,
1297 NULL,
1298 NULL,
1299 g_cclosure_marshal_VOID__DOUBLE,
1300 G_TYPE_NONE,
1301 1,
1302 G_TYPE_DOUBLE );
1303 g_signal_new( "ready",
1304 G_TYPE_FROM_CLASS(klass),
1305 G_SIGNAL_RUN_FIRST,
1306 0,
1307 NULL,
1308 NULL,
1309 g_cclosure_marshal_VOID__VOID,
1310 G_TYPE_NONE,
1311 0 );
1312 g_signal_new( "render",
1313 G_TYPE_FROM_CLASS(klass),
1314 G_SIGNAL_RUN_FIRST,
1315 0,
1316 NULL,
1317 NULL,
1318 g_cclosure_marshal_VOID__VOID,
1319 G_TYPE_NONE,
1320 0 );
1321 g_signal_new( "preferences-updated",
1322 G_TYPE_FROM_CLASS(klass),
1323 G_SIGNAL_RUN_FIRST,
1324 0,
1325 NULL,
1326 NULL,
1327 g_cclosure_marshal_VOID__VOID,
1328 G_TYPE_NONE,
1329 0 );
1330 g_signal_new( "audio-track-load",
1331 G_TYPE_FROM_CLASS(klass),
1332 G_SIGNAL_RUN_FIRST,
1333 0,
1334 NULL,
1335 NULL,
1336 g_cclosure_marshal_VOID__STRING,
1337 G_TYPE_NONE,
1338 1,
1339 G_TYPE_STRING );
1340 g_signal_new( "video-track-load",
1341 G_TYPE_FROM_CLASS(klass),
1342 G_SIGNAL_RUN_FIRST,
1343 0,
1344 NULL,
1345 NULL,
1346 g_cclosure_marshal_VOID__STRING,
1347 G_TYPE_NONE,
1348 1,
1349 G_TYPE_STRING );
1350 g_signal_new( "subtitle-track-load",
1351 G_TYPE_FROM_CLASS(klass),
1352 G_SIGNAL_RUN_FIRST,
1353 0,
1354 NULL,
1355 NULL,
1356 g_cclosure_marshal_VOID__STRING,
1357 G_TYPE_NONE,
1358 1,
1359 G_TYPE_STRING );
1360 g_signal_new( "file-open",
1361 G_TYPE_FROM_CLASS(klass),
1362 G_SIGNAL_RUN_FIRST,
1363 0,
1364 NULL,
1365 NULL,
1366 g_cclosure_gen_marshal_VOID__POINTER_BOOLEAN,
1367 G_TYPE_NONE,
1368 2,
1369 G_TYPE_POINTER,
1370 G_TYPE_BOOLEAN );
1371 g_signal_new( "playlist-item-activated",
1372 G_TYPE_FROM_CLASS(klass),
1373 G_SIGNAL_RUN_FIRST,
1374 0,
1375 NULL,
1376 NULL,
1377 g_cclosure_marshal_VOID__INT,
1378 G_TYPE_NONE,
1379 1,
1380 G_TYPE_INT );
1381 g_signal_new( "playlist-item-inserted",
1382 G_TYPE_FROM_CLASS(klass),
1383 G_SIGNAL_RUN_FIRST,
1384 0,
1385 NULL,
1386 NULL,
1387 g_cclosure_marshal_VOID__INT,
1388 G_TYPE_NONE,
1389 1,
1390 G_TYPE_INT );
1391 g_signal_new( "playlist-item-deleted",
1392 G_TYPE_FROM_CLASS(klass),
1393 G_SIGNAL_RUN_FIRST,
1394 0,
1395 NULL,
1396 NULL,
1397 g_cclosure_marshal_VOID__INT,
1398 G_TYPE_NONE,
1399 1,
1400 G_TYPE_INT );
1401 g_signal_new( "playlist-reordered",
1402 G_TYPE_FROM_CLASS(klass),
1403 G_SIGNAL_RUN_FIRST,
1404 0,
1405 NULL,
1406 NULL,
1407 g_cclosure_gen_marshal_VOID__INT_INT,
1408 G_TYPE_NONE,
1409 2,
1410 G_TYPE_INT,
1411 G_TYPE_INT );
1412 }
1413
1414 static void
celluloid_view_init(CelluloidView * view)1415 celluloid_view_init(CelluloidView *view)
1416 {
1417 view->disposed = FALSE;
1418 view->has_dialog = FALSE;
1419 view->playlist_count = 0;
1420 view->pause = FALSE;
1421 view->idle_active = FALSE;
1422 view->volume = 0.0;
1423 view->volume_max = 100.0;
1424 view->duration = 0.0;
1425 view->playlist_pos = 0;
1426 view->disc_list = NULL;
1427 view->skip_enabled = FALSE;
1428 view->loop = FALSE;
1429 view->shuffle = FALSE;
1430 view->border = FALSE;
1431 view->fullscreen = FALSE;
1432 view->media_title = NULL;
1433 view->display_fps = 0;
1434 view->searching = FALSE;
1435
1436 g_signal_connect(view, "realize", G_CALLBACK(realize_handler), NULL);
1437 }
1438
1439 CelluloidView *
celluloid_view_new(CelluloidApplication * app,gboolean always_floating)1440 celluloid_view_new(CelluloidApplication *app, gboolean always_floating)
1441 {
1442 GObject *obj = g_object_new( celluloid_view_get_type(),
1443 "application",
1444 app,
1445 "always-use-floating-controls",
1446 always_floating,
1447 NULL );
1448 CelluloidView *view = CELLULOID_VIEW(obj);
1449 GtkApplication *gtk_app = GTK_APPLICATION(app);
1450 GSettings *settings = g_settings_new(CONFIG_ROOT);
1451
1452 if(g_settings_get_boolean(settings, "csd-enable"))
1453 {
1454 celluloid_main_window_enable_csd(CELLULOID_MAIN_WINDOW(view));
1455 }
1456 else
1457 {
1458 GMenu *full_menu = g_menu_new();
1459
1460 celluloid_menu_build_full(full_menu, NULL, NULL);
1461 gtk_application_set_menubar(gtk_app, G_MENU_MODEL(full_menu));
1462 }
1463
1464 g_object_unref(settings);
1465
1466 return view;
1467 }
1468
1469 CelluloidMainWindow *
celluloid_view_get_main_window(CelluloidView * view)1470 celluloid_view_get_main_window(CelluloidView *view)
1471 {
1472 return CELLULOID_MAIN_WINDOW(view);
1473 }
1474
1475 void
celluloid_view_show_open_dialog(CelluloidView * view,gboolean folder,gboolean append)1476 celluloid_view_show_open_dialog( CelluloidView *view,
1477 gboolean folder,
1478 gboolean append )
1479 {
1480 CelluloidFileChooser *chooser;
1481 GPtrArray *args;
1482 const gchar *title;
1483 GtkFileChooserAction action;
1484 gboolean *append_buf;
1485
1486 if(folder)
1487 {
1488 title = append ? _("Add Folder to Playlist") : _("Open Folder");
1489 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1490 }
1491 else
1492 {
1493 title = append ? _("Add File to Playlist") : _("Open File");
1494 action = GTK_FILE_CHOOSER_ACTION_OPEN;
1495 }
1496
1497 chooser = celluloid_file_chooser_new(title, GTK_WINDOW(view), action);
1498 args = g_ptr_array_new();
1499 append_buf = g_malloc(sizeof(gboolean));
1500 *append_buf = append;
1501
1502 g_ptr_array_add(args, view);
1503 g_ptr_array_add(args, append_buf);
1504
1505 g_signal_connect( chooser,
1506 "response",
1507 G_CALLBACK(open_dialog_response_handler),
1508 args );
1509
1510 if(!folder)
1511 {
1512 celluloid_file_chooser_set_default_filters
1513 (chooser, TRUE, TRUE, TRUE, TRUE);
1514 }
1515
1516 celluloid_file_chooser_show(chooser);
1517 }
1518
1519 void
celluloid_view_show_open_location_dialog(CelluloidView * view,gboolean append)1520 celluloid_view_show_open_location_dialog(CelluloidView *view, gboolean append)
1521 {
1522 GtkWidget *dlg;
1523 GPtrArray *args;
1524 gboolean *append_buf;
1525
1526 dlg = celluloid_open_location_dialog_new
1527 ( GTK_WINDOW(view),
1528 append?
1529 _("Add Location to Playlist"):
1530 _("Open Location") );
1531 args = g_ptr_array_sized_new(2);
1532 append_buf = g_malloc(sizeof(gboolean));
1533 *append_buf = append;
1534
1535 g_ptr_array_add(args, view);
1536 g_ptr_array_add(args, append_buf);
1537
1538 g_signal_connect
1539 ( dlg,
1540 "response",
1541 G_CALLBACK(open_location_dialog_response_handler),
1542 args );
1543
1544 gtk_widget_show(dlg);
1545 }
1546
1547 void
celluloid_view_show_open_audio_track_dialog(CelluloidView * view)1548 celluloid_view_show_open_audio_track_dialog(CelluloidView *view)
1549 {
1550 show_open_track_dialog(view, TRACK_TYPE_AUDIO);
1551 }
1552
1553 void
celluloid_view_show_open_video_track_dialog(CelluloidView * view)1554 celluloid_view_show_open_video_track_dialog(CelluloidView *view)
1555 {
1556 show_open_track_dialog(view, TRACK_TYPE_VIDEO);
1557 }
1558
1559 void
celluloid_view_show_open_subtitle_track_dialog(CelluloidView * view)1560 celluloid_view_show_open_subtitle_track_dialog(CelluloidView *view)
1561 {
1562 show_open_track_dialog(view, TRACK_TYPE_SUBTITLE);
1563 }
1564
1565 void
celluloid_view_show_save_playlist_dialog(CelluloidView * view)1566 celluloid_view_show_save_playlist_dialog(CelluloidView *view)
1567 {
1568 CelluloidFileChooser *file_chooser;
1569 GtkFileChooser *gtk_chooser;
1570
1571 file_chooser = celluloid_file_chooser_new
1572 ( _("Save Playlist"),
1573 GTK_WINDOW(view),
1574 GTK_FILE_CHOOSER_ACTION_SAVE );
1575 gtk_chooser = GTK_FILE_CHOOSER(file_chooser);
1576
1577 gtk_file_chooser_set_current_name(gtk_chooser, "playlist.m3u");
1578
1579 g_signal_connect( file_chooser,
1580 "response",
1581 G_CALLBACK(save_playlist_response_handler),
1582 view );
1583
1584 gtk_native_dialog_show(GTK_NATIVE_DIALOG(file_chooser));
1585 }
1586
1587 void
celluloid_view_show_preferences_dialog(CelluloidView * view)1588 celluloid_view_show_preferences_dialog(CelluloidView *view)
1589 {
1590 GtkWidget *dialog = celluloid_preferences_dialog_new(GTK_WINDOW(view));
1591
1592 g_signal_connect_after( dialog,
1593 "response",
1594 G_CALLBACK(preferences_dialog_response_handler),
1595 view );
1596
1597 gtk_widget_show(dialog);
1598 }
1599
1600 void
celluloid_view_show_shortcuts_dialog(CelluloidView * view)1601 celluloid_view_show_shortcuts_dialog(CelluloidView *view)
1602 {
1603 GtkWidget *wnd = celluloid_shortcuts_window_new(GTK_WINDOW(view));
1604
1605 gtk_widget_show(wnd);
1606 }
1607
1608 void
celluloid_view_show_about_dialog(CelluloidView * view)1609 celluloid_view_show_about_dialog(CelluloidView *view)
1610 {
1611 const gchar *const authors[] = AUTHORS;
1612
1613 gtk_show_about_dialog( GTK_WINDOW(view),
1614 "logo-icon-name",
1615 ICON_NAME,
1616 "version",
1617 VERSION,
1618 "comments",
1619 _("A GTK frontend for MPV"),
1620 "website",
1621 "https://github.com/celluloid-player/celluloid",
1622 "license-type",
1623 GTK_LICENSE_GPL_3_0,
1624 "copyright",
1625 "\u00A9 2014-2021 The Celluloid authors",
1626 "authors",
1627 authors,
1628 "translator-credits",
1629 _("translator-credits"),
1630 NULL );
1631 }
1632
1633 void
celluloid_view_show_message_dialog(CelluloidView * view,GtkMessageType type,const gchar * title,const gchar * prefix,const gchar * msg)1634 celluloid_view_show_message_dialog( CelluloidView *view,
1635 GtkMessageType type,
1636 const gchar *title,
1637 const gchar *prefix,
1638 const gchar *msg )
1639 {
1640 show_message_dialog(view, type, title, prefix, msg);
1641 }
1642
1643 void
celluloid_view_present(CelluloidView * view)1644 celluloid_view_present(CelluloidView *view)
1645 {
1646 gtk_window_present(GTK_WINDOW(view));
1647 }
1648
1649 void
celluloid_view_quit(CelluloidView * view)1650 celluloid_view_quit(CelluloidView *view)
1651 {
1652 gtk_window_close(GTK_WINDOW(view));
1653 }
1654
1655 void
celluloid_view_reset(CelluloidView * view)1656 celluloid_view_reset(CelluloidView *view)
1657 {
1658 celluloid_main_window_reset(CELLULOID_MAIN_WINDOW(view));
1659 }
1660
1661 void
celluloid_view_queue_render(CelluloidView * view)1662 celluloid_view_queue_render(CelluloidView *view)
1663 {
1664 CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(view);
1665 CelluloidVideoArea *area = celluloid_main_window_get_video_area(wnd);
1666
1667 celluloid_video_area_queue_render(area);
1668 }
1669
1670 void
celluloid_view_make_gl_context_current(CelluloidView * view)1671 celluloid_view_make_gl_context_current(CelluloidView *view)
1672 {
1673 CelluloidMainWindow *wnd =
1674 CELLULOID_MAIN_WINDOW(view);
1675 CelluloidVideoArea *video_area =
1676 celluloid_main_window_get_video_area(wnd);
1677 GtkGLArea *gl_area =
1678 celluloid_video_area_get_gl_area(video_area);
1679
1680 gtk_widget_realize(GTK_WIDGET(gl_area));
1681 gtk_gl_area_make_current(gl_area);
1682 }
1683
1684 void
celluloid_view_set_use_opengl_cb(CelluloidView * view,gboolean use_opengl_cb)1685 celluloid_view_set_use_opengl_cb(CelluloidView *view, gboolean use_opengl_cb)
1686 {
1687 CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(view);
1688 CelluloidVideoArea *area = celluloid_main_window_get_video_area(wnd);
1689
1690 celluloid_video_area_set_use_opengl(area, use_opengl_cb);
1691 }
1692
1693 gint
celluloid_view_get_scale_factor(CelluloidView * view)1694 celluloid_view_get_scale_factor(CelluloidView *view)
1695 {
1696 GdkSurface *surface = gtk_widget_get_surface(view);
1697
1698 return gdk_surface_get_scale_factor(surface);
1699 }
1700
1701 void
celluloid_view_get_video_area_geometry(CelluloidView * view,gint * width,gint * height)1702 celluloid_view_get_video_area_geometry( CelluloidView *view,
1703 gint *width,
1704 gint *height )
1705 {
1706 CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(view);
1707 CelluloidVideoArea *area = celluloid_main_window_get_video_area(wnd);
1708 GtkAllocation allocation;
1709
1710 gtk_widget_get_allocation(GTK_WIDGET(area), &allocation);
1711
1712 *width = allocation.width;
1713 *height = allocation.height;
1714 }
1715
1716 void
celluloid_view_move(CelluloidView * view,gboolean flip_x,gboolean flip_y,GValue * x,GValue * y)1717 celluloid_view_move( CelluloidView *view,
1718 gboolean flip_x,
1719 gboolean flip_y,
1720 GValue *x,
1721 GValue *y )
1722 {
1723 #ifdef GDK_WINDOWING_WAYLAND
1724 GdkDisplay *display = gdk_display_get_default();
1725
1726 if (GDK_IS_WAYLAND_DISPLAY(display))
1727 {
1728 g_warning("%s: Not supported on Wayland", __func__);
1729 return;
1730 }
1731 #endif
1732
1733 g_warning("%s: Not implemented", __func__);
1734 }
1735
1736 void
celluloid_view_resize_video_area(CelluloidView * view,gint width,gint height)1737 celluloid_view_resize_video_area(CelluloidView *view, gint width, gint height)
1738 {
1739 celluloid_main_window_resize_video_area
1740 (CELLULOID_MAIN_WINDOW(view), width, height);
1741 }
1742
1743 void
celluloid_view_set_fullscreen(CelluloidView * view,gboolean fullscreen)1744 celluloid_view_set_fullscreen(CelluloidView *view, gboolean fullscreen)
1745 {
1746 g_object_set(view, "fullscreen", fullscreen, NULL);
1747 celluloid_main_window_set_fullscreen
1748 (CELLULOID_MAIN_WINDOW(view), fullscreen);
1749 }
1750
1751 void
celluloid_view_set_time_position(CelluloidView * view,gdouble position)1752 celluloid_view_set_time_position(CelluloidView *view, gdouble position)
1753 {
1754 CelluloidControlBox *control_box;
1755
1756 control_box = celluloid_main_window_get_control_box
1757 (CELLULOID_MAIN_WINDOW(view));
1758 g_object_set(control_box, "time-position", position, NULL);
1759 }
1760
1761 void
celluloid_view_update_playlist(CelluloidView * view,GPtrArray * playlist)1762 celluloid_view_update_playlist(CelluloidView *view, GPtrArray *playlist)
1763 {
1764 CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(view);
1765 CelluloidPlaylistWidget *wgt = celluloid_main_window_get_playlist(wnd);
1766
1767 celluloid_playlist_widget_update_contents(wgt, playlist);
1768 }
1769
1770 void
celluloid_view_set_playlist_pos(CelluloidView * view,gint64 pos)1771 celluloid_view_set_playlist_pos(CelluloidView *view, gint64 pos)
1772 {
1773 g_object_set(view, "playlist-pos", (gint)pos, NULL);
1774 }
1775
1776 void
celluloid_view_set_playlist_visible(CelluloidView * view,gboolean visible)1777 celluloid_view_set_playlist_visible(CelluloidView *view, gboolean visible)
1778 {
1779 celluloid_main_window_set_playlist_visible
1780 (CELLULOID_MAIN_WINDOW(view), visible);
1781 }
1782
1783 gboolean
celluloid_view_get_playlist_visible(CelluloidView * view)1784 celluloid_view_get_playlist_visible(CelluloidView *view)
1785 {
1786 return celluloid_main_window_get_playlist_visible
1787 (CELLULOID_MAIN_WINDOW(view));
1788 }
1789
1790 void
celluloid_view_set_controls_visible(CelluloidView * view,gboolean visible)1791 celluloid_view_set_controls_visible(CelluloidView *view, gboolean visible)
1792 {
1793 celluloid_main_window_set_controls_visible
1794 (CELLULOID_MAIN_WINDOW(view), visible);
1795 }
1796
1797 gboolean
celluloid_view_get_controls_visible(CelluloidView * view)1798 celluloid_view_get_controls_visible(CelluloidView *view)
1799 {
1800 return celluloid_main_window_get_controls_visible
1801 (CELLULOID_MAIN_WINDOW(view));
1802 }
1803
1804 void
celluloid_view_set_main_menu_visible(CelluloidView * view,gboolean visible)1805 celluloid_view_set_main_menu_visible(CelluloidView *view, gboolean visible)
1806 {
1807 gboolean csd =
1808 celluloid_main_window_get_csd_enabled
1809 (CELLULOID_MAIN_WINDOW(view));
1810
1811 if(csd)
1812 {
1813 GtkWidget *header_bar = gtk_window_get_titlebar(GTK_WINDOW(view));
1814
1815 celluloid_header_bar_set_menu_button_popup_visible
1816 (CELLULOID_HEADER_BAR(header_bar), visible);
1817 }
1818 else
1819 {
1820 gtk_application_window_set_show_menubar
1821 (GTK_APPLICATION_WINDOW(view), visible);
1822 }
1823 }
1824
1825 gboolean
celluloid_view_get_main_menu_visible(CelluloidView * view)1826 celluloid_view_get_main_menu_visible(CelluloidView *view)
1827 {
1828 gboolean csd =
1829 celluloid_main_window_get_csd_enabled
1830 (CELLULOID_MAIN_WINDOW(view));
1831
1832 gboolean result = FALSE;
1833
1834 if(csd)
1835 {
1836 GtkWidget *header_bar = gtk_window_get_titlebar(GTK_WINDOW(view));
1837
1838 result =
1839 celluloid_header_bar_get_menu_button_popup_visible
1840 (CELLULOID_HEADER_BAR(header_bar));
1841 }
1842 else
1843 {
1844 result =
1845 gtk_application_window_get_show_menubar
1846 (GTK_APPLICATION_WINDOW(view));
1847 }
1848
1849 return result;
1850 }
1851