1 /*
2  * Copyright (c) 2014-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.h>
21 #include <glib-object.h>
22 #include <gio/gio.h>
23 #include <gdk/gdk.h>
24 
25 #include "celluloid-def.h"
26 #include "celluloid-marshal.h"
27 #include "celluloid-menu.h"
28 #include "celluloid-application.h"
29 #include "celluloid-playlist-widget.h"
30 #include "celluloid-main-window.h"
31 #include "celluloid-header-bar.h"
32 #include "celluloid-control-box.h"
33 #include "celluloid-video-area.h"
34 
35 #define get_private(window) \
36 	((CelluloidMainWindowPrivate *)celluloid_main_window_get_instance_private(CELLULOID_MAIN_WINDOW(window)))
37 
38 typedef struct _CelluloidMainWindowPrivate CelluloidMainWindowPrivate;
39 
40 enum
41 {
42 	PROP_0,
43 	PROP_ALWAYS_FLOATING,
44 	N_PROPERTIES
45 };
46 
47 struct _CelluloidMainWindowPrivate
48 {
49 	GtkApplicationWindow parent;
50 	gint width_offset;
51 	gint height_offset;
52 	gint resize_target[2];
53 	gboolean csd;
54 	gboolean always_floating;
55 	gboolean use_floating_controls;
56 	gboolean fullscreen;
57 	gboolean playlist_visible;
58 	gboolean playlist_first_toggle;
59 	gboolean pre_fs_playlist_visible;
60 	gint playlist_width;
61 	guint resize_tag;
62 	const GPtrArray *track_list;
63 	const GPtrArray *disc_list;
64 	GtkWidget *header_bar;
65 	GtkWidget *main_box;
66 	GtkWidget *video_area_paned;
67 	GtkWidget *video_area;
68 	GtkWidget *control_box;
69 	GtkWidget *playlist;
70 };
71 
72 static void
73 constructed(GObject *object);
74 
75 static void
76 dispose(GObject *object);
77 
78 static void
79 set_property(	GObject *object,
80 		guint property_id,
81 		const GValue *value,
82 		GParamSpec *pspec );
83 
84 static void
85 get_property(	GObject *object,
86 		guint property_id,
87 		GValue *value,
88 		GParamSpec *pspec );
89 
90 static void
91 seek_handler(GtkWidget *widget, gdouble value, gpointer data);
92 
93 static void
94 button_clicked_handler(	CelluloidControlBox *control_box,
95 			const gchar *button,
96 			gpointer data );
97 
98 static void
99 notify(GObject *object, GParamSpec *pspec);
100 
101 static void
102 resize_video_area_finalize(	GtkWidget *widget,
103 				gint width,
104 				gint height,
105 				gpointer data );
106 
107 static gboolean
108 resize_to_target(gpointer data);
109 
G_DEFINE_TYPE_WITH_PRIVATE(CelluloidMainWindow,celluloid_main_window,GTK_TYPE_APPLICATION_WINDOW)110 G_DEFINE_TYPE_WITH_PRIVATE(CelluloidMainWindow, celluloid_main_window, GTK_TYPE_APPLICATION_WINDOW)
111 
112 static void
113 constructed(GObject *object)
114 {
115 	CelluloidMainWindowPrivate *priv = get_private(object);
116 
117 	priv->playlist = celluloid_playlist_widget_new();
118 
119 	gtk_widget_hide(priv->playlist);
120 	gtk_widget_hide(priv->control_box);
121 
122 	gtk_paned_set_start_child
123 		(GTK_PANED(priv->video_area_paned), priv->video_area);
124 	gtk_paned_set_end_child
125 		(GTK_PANED(priv->video_area_paned), priv->playlist);
126 	gtk_paned_set_shrink_end_child
127 		(GTK_PANED(priv->video_area_paned), FALSE);
128 
129 	gtk_application_window_set_show_menubar
130 		(GTK_APPLICATION_WINDOW(object), !priv->csd);
131 
132 	G_OBJECT_CLASS(celluloid_main_window_parent_class)->constructed(object);
133 }
134 
135 static void
dispose(GObject * object)136 dispose(GObject *object)
137 {
138 	g_source_clear(&get_private(object)->resize_tag);
139 
140 	G_OBJECT_CLASS(celluloid_main_window_parent_class)->dispose(object);
141 }
142 
143 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)144 set_property(	GObject *object,
145 		guint property_id,
146 		const GValue *value,
147 		GParamSpec *pspec )
148 {
149 	CelluloidMainWindowPrivate *priv = get_private(object);
150 
151 	if(property_id == PROP_ALWAYS_FLOATING)
152 	{
153 		priv->always_floating = g_value_get_boolean(value);
154 	}
155 	else
156 	{
157 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
158 	}
159 }
160 
161 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)162 get_property(	GObject *object,
163 		guint property_id,
164 		GValue *value,
165 		GParamSpec *pspec )
166 {
167 	CelluloidMainWindowPrivate *priv = get_private(object);
168 
169 	if(property_id == PROP_ALWAYS_FLOATING)
170 	{
171 		g_value_set_boolean(value, priv->always_floating);
172 	}
173 	else
174 	{
175 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
176 	}
177 }
178 
179 static void
seek_handler(GtkWidget * widget,gdouble value,gpointer data)180 seek_handler(GtkWidget *widget, gdouble value, gpointer data)
181 {
182 	g_signal_emit_by_name(data, "seek", value);
183 }
184 
185 static void
button_clicked_handler(CelluloidControlBox * control_box,const gchar * button,gpointer data)186 button_clicked_handler(	CelluloidControlBox *control_box,
187 			const gchar *button,
188 			gpointer data )
189 {
190 	gchar *name = g_strconcat("button-clicked::", button, NULL);
191 
192 	g_signal_emit_by_name(data, name);
193 	g_free(name);
194 }
195 
196 static void
notify(GObject * object,GParamSpec * pspec)197 notify(GObject *object, GParamSpec *pspec)
198 {
199 	if(g_strcmp0(pspec->name, "always-use-floating-controls") == 0)
200 	{
201 		CelluloidMainWindow *wnd = CELLULOID_MAIN_WINDOW(object);
202 		CelluloidMainWindowPrivate *priv = get_private(wnd);
203 		gboolean floating = priv->always_floating || priv->fullscreen;
204 
205 		celluloid_main_window_set_use_floating_controls(wnd, floating);
206 	}
207 }
208 
209 static void
resize_video_area_finalize(GtkWidget * widget,gint width,gint height,gpointer data)210 resize_video_area_finalize(	GtkWidget *widget,
211 				gint width,
212 				gint height,
213 				gpointer data )
214 {
215 	CelluloidMainWindow *wnd = data;
216 	CelluloidMainWindowPrivate *priv = get_private(wnd);
217 	GdkSurface *surface = gtk_widget_get_surface(data);
218 	GdkDisplay *display = gdk_display_get_default();
219 	GdkMonitor *monitor = gdk_display_get_monitor_at_surface(display, surface);
220 	GdkRectangle monitor_geom = {0};
221 	gint target_width = priv->resize_target[0];
222 	gint target_height = priv->resize_target[1];
223 
224 	g_signal_handlers_disconnect_by_func
225 		(widget, resize_video_area_finalize, data);
226 
227 	gdk_monitor_get_geometry(monitor, &monitor_geom);
228 
229 	/* Adjust resize offset */
230 	if((width != target_width || height != target_height)
231 	&& (	target_width < monitor_geom.width &&
232 		target_height < monitor_geom.height )
233 	&& !gtk_window_is_maximized(GTK_WINDOW(wnd))
234 	&& !priv->fullscreen)
235 	{
236 		priv->width_offset += target_width-width;
237 		priv->height_offset += target_height-height;
238 
239 		g_source_clear(&priv->resize_tag);
240 		priv->resize_tag = g_idle_add_full(	G_PRIORITY_HIGH_IDLE,
241 							resize_to_target,
242 							wnd,
243 							NULL );
244 	}
245 }
246 
247 static gboolean
resize_to_target(gpointer data)248 resize_to_target(gpointer data)
249 {
250 	CelluloidMainWindow *wnd = data;
251 	CelluloidMainWindowPrivate *priv = get_private(data);
252 	gint target_width = priv->resize_target[0];
253 	gint target_height = priv->resize_target[1];
254 
255 	g_source_clear(&priv->resize_tag);
256 
257 	gtk_window_set_default_size(	GTK_WINDOW(wnd),
258 					target_width + priv->width_offset,
259 					target_height + priv->height_offset );
260 
261 	/* Prevent graphical glitches that appear when calling
262 	 * celluloid_main_window_resize_video_area() with the current size as
263 	 * the target size.
264 	 */
265 	celluloid_playlist_widget_queue_draw
266 		(CELLULOID_PLAYLIST_WIDGET(priv->playlist));
267 
268 	return FALSE;
269 }
270 
271 static void
celluloid_main_window_class_init(CelluloidMainWindowClass * klass)272 celluloid_main_window_class_init(CelluloidMainWindowClass *klass)
273 {
274 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
275 	GParamSpec *pspec = NULL;
276 
277 	obj_class->constructed = constructed;
278 	obj_class->dispose = dispose;
279 	obj_class->set_property = set_property;
280 	obj_class->get_property = get_property;
281 	obj_class->notify = notify;
282 
283 	pspec = g_param_spec_boolean
284 		(	"always-use-floating-controls",
285 			"Always use floating controls",
286 			"Whether or not to use floating controls in windowed mode",
287 			FALSE,
288 			G_PARAM_READWRITE );
289 	g_object_class_install_property(obj_class, PROP_ALWAYS_FLOATING, pspec);
290 
291 	g_signal_new(	"button-clicked",
292 			G_TYPE_FROM_CLASS(klass),
293 			G_SIGNAL_RUN_FIRST|G_SIGNAL_DETAILED,
294 			0,
295 			NULL,
296 			NULL,
297 			g_cclosure_marshal_VOID__VOID,
298 			G_TYPE_NONE,
299 			0 );
300 	g_signal_new(	"seek",
301 			G_TYPE_FROM_CLASS(klass),
302 			G_SIGNAL_RUN_FIRST,
303 			0,
304 			NULL,
305 			NULL,
306 			g_cclosure_marshal_VOID__DOUBLE,
307 			G_TYPE_NONE,
308 			1,
309 			G_TYPE_DOUBLE );
310 }
311 
312 static void
celluloid_main_window_init(CelluloidMainWindow * wnd)313 celluloid_main_window_init(CelluloidMainWindow *wnd)
314 {
315 	CelluloidMainWindowPrivate *priv = get_private(wnd);
316 	CelluloidControlBox *video_area_control_box = NULL;
317 	CelluloidHeaderBar *video_area_header_bar = NULL;
318 	GSettings *settings = g_settings_new(CONFIG_WIN_STATE);
319 
320 	priv->csd = FALSE;
321 	priv->always_floating = FALSE;
322 	priv->use_floating_controls = FALSE;
323 	priv->fullscreen = FALSE;
324 	priv->playlist_visible = FALSE;
325 	priv->pre_fs_playlist_visible = FALSE;
326 	priv->playlist_width = PLAYLIST_DEFAULT_WIDTH;
327 	priv->resize_tag = 0;
328 	priv->track_list = NULL;
329 	priv->disc_list = NULL;
330 	priv->header_bar = celluloid_header_bar_new();
331 	priv->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
332 	priv->video_area_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
333 	priv->video_area = celluloid_video_area_new();
334 	priv->control_box = celluloid_control_box_new();
335 
336 	priv->playlist_first_toggle = TRUE;
337 	priv->width_offset = 0;
338 	priv->height_offset = 0;
339 
340 	video_area_control_box =
341 		celluloid_video_area_get_control_box
342 		(CELLULOID_VIDEO_AREA(priv->video_area));
343 	video_area_header_bar =
344 		celluloid_video_area_get_header_bar
345 		(CELLULOID_VIDEO_AREA(priv->video_area));
346 
347 	g_settings_bind(	settings, "loop-playlist",
348 				priv->control_box, "loop",
349 				G_SETTINGS_BIND_DEFAULT );
350 
351 	g_object_bind_property(	priv->header_bar, "open-button-active",
352 				video_area_header_bar, "open-button-active",
353 				G_BINDING_DEFAULT );
354 	g_object_bind_property(	priv->header_bar, "menu-button-active",
355 				video_area_header_bar, "menu-button-active",
356 				G_BINDING_DEFAULT );
357 
358 	g_object_bind_property(	priv->control_box, "duration",
359 				video_area_control_box, "duration",
360 				G_BINDING_DEFAULT );
361 	g_object_bind_property(	priv->control_box, "pause",
362 				video_area_control_box, "pause",
363 				G_BINDING_DEFAULT );
364 	g_object_bind_property(	priv->control_box, "skip-enabled",
365 				video_area_control_box, "skip-enabled",
366 				G_BINDING_DEFAULT );
367 	g_object_bind_property(	priv->control_box, "show-fullscreen-button",
368 				video_area_control_box, "show-fullscreen-button",
369 				G_BINDING_DEFAULT );
370 	g_object_bind_property(	priv->control_box, "time-position",
371 				video_area_control_box, "time-position",
372 				G_BINDING_DEFAULT );
373 	g_object_bind_property(	priv->control_box, "loop",
374 				video_area_control_box, "loop",
375 				G_BINDING_BIDIRECTIONAL );
376 	g_object_bind_property(	priv->control_box, "volume",
377 				video_area_control_box, "volume",
378 				G_BINDING_BIDIRECTIONAL );
379 	g_object_bind_property(	priv->control_box, "volume-popup-visible",
380 				video_area_control_box, "volume-popup-visible",
381 				G_BINDING_BIDIRECTIONAL );
382 
383 	g_signal_connect(	priv->control_box,
384 				"seek",
385 				G_CALLBACK(seek_handler),
386 				wnd );
387 	g_signal_connect(	priv->control_box,
388 				"button-clicked",
389 				G_CALLBACK(button_clicked_handler),
390 				wnd );
391 	g_signal_connect(	video_area_control_box,
392 				"seek",
393 				G_CALLBACK(seek_handler),
394 				wnd );
395 	g_signal_connect(	video_area_control_box,
396 				"button-clicked",
397 				G_CALLBACK(button_clicked_handler),
398 				wnd );
399 
400 	gtk_window_set_title(GTK_WINDOW(wnd), g_get_application_name());
401 
402 	gtk_paned_set_position(	GTK_PANED(priv->video_area_paned),
403 				MAIN_WINDOW_DEFAULT_WIDTH
404 				-PLAYLIST_DEFAULT_WIDTH );
405 	gtk_paned_set_resize_end_child(GTK_PANED(priv->video_area_paned), FALSE);
406 
407 	gtk_window_set_default_size(	GTK_WINDOW(wnd),
408 					MAIN_WINDOW_DEFAULT_WIDTH,
409 					MAIN_WINDOW_DEFAULT_HEIGHT );
410 
411 	gtk_widget_set_hexpand(priv->header_bar, TRUE);
412 	gtk_widget_set_hexpand(priv->control_box, TRUE);
413 	gtk_widget_set_vexpand(priv->video_area_paned, TRUE);
414 
415 	gtk_box_append(GTK_BOX(priv->main_box), priv->video_area_paned);
416 	gtk_box_append(GTK_BOX(priv->main_box), priv->control_box);
417 	gtk_window_set_child(GTK_WINDOW(wnd), priv->main_box);
418 }
419 
420 GtkWidget *
celluloid_main_window_new(GtkApplication * app,gboolean always_floating)421 celluloid_main_window_new(	GtkApplication *app,
422 				gboolean always_floating )
423 {
424 	return GTK_WIDGET(g_object_new(	celluloid_main_window_get_type(),
425 					"application",
426 					app,
427 					"always-use-floating-controls",
428 					always_floating,
429 					NULL ));
430 }
431 
432 CelluloidPlaylistWidget *
celluloid_main_window_get_playlist(CelluloidMainWindow * wnd)433 celluloid_main_window_get_playlist(CelluloidMainWindow *wnd)
434 {
435 	return CELLULOID_PLAYLIST_WIDGET(get_private(wnd)->playlist);
436 }
437 
438 CelluloidControlBox *
celluloid_main_window_get_control_box(CelluloidMainWindow * wnd)439 celluloid_main_window_get_control_box(CelluloidMainWindow *wnd)
440 {
441 	return CELLULOID_CONTROL_BOX(get_private(wnd)->control_box);
442 }
443 
444 CelluloidVideoArea *
celluloid_main_window_get_video_area(CelluloidMainWindow * wnd)445 celluloid_main_window_get_video_area(CelluloidMainWindow *wnd)
446 {
447 	return CELLULOID_VIDEO_AREA(get_private(wnd)->video_area);
448 }
449 
450 void
celluloid_main_window_set_use_floating_controls(CelluloidMainWindow * wnd,gboolean floating)451 celluloid_main_window_set_use_floating_controls(	CelluloidMainWindow *wnd,
452 							gboolean floating )
453 {
454 	CelluloidMainWindowPrivate *priv = get_private(wnd);
455 
456 	if(floating != priv->use_floating_controls)
457 	{
458 		GSettings *settings = g_settings_new(CONFIG_WIN_STATE);
459 		gboolean controls_visible =	g_settings_get_boolean
460 						(settings, "show-controls");
461 
462 		gtk_widget_set_visible
463 			(priv->control_box, controls_visible && !floating);
464 		celluloid_video_area_set_control_box_visible
465 			(CELLULOID_VIDEO_AREA(priv->video_area), floating);
466 
467 		priv->use_floating_controls = floating;
468 
469 		g_clear_object(&settings);
470 	}
471 }
472 
473 gboolean
celluloid_main_window_get_use_floating_controls(CelluloidMainWindow * wnd)474 celluloid_main_window_get_use_floating_controls(CelluloidMainWindow *wnd)
475 {
476 	return get_private(wnd)->use_floating_controls;
477 }
478 
479 void
celluloid_main_window_set_fullscreen(CelluloidMainWindow * wnd,gboolean fullscreen)480 celluloid_main_window_set_fullscreen(CelluloidMainWindow *wnd, gboolean fullscreen)
481 {
482 	CelluloidMainWindowPrivate *priv = get_private(wnd);
483 
484 	if(fullscreen != priv->fullscreen)
485 	{
486 		CelluloidVideoArea *video_area =
487 			CELLULOID_VIDEO_AREA(priv->video_area);
488 		GSettings *settings =
489 			g_settings_new(CONFIG_WIN_STATE);
490 		gboolean floating =
491 			priv->always_floating || fullscreen;
492 		gboolean playlist_visible =
493 			!fullscreen && priv->pre_fs_playlist_visible;
494 		gboolean show_controls = g_settings_get_boolean
495 			(settings, "show-controls");
496 
497 		if(fullscreen)
498 		{
499 			gtk_window_fullscreen(GTK_WINDOW(wnd));
500 			gtk_window_present(GTK_WINDOW(wnd));
501 
502 			priv->pre_fs_playlist_visible = priv->playlist_visible;
503 		}
504 		else
505 		{
506 			gtk_window_unfullscreen(GTK_WINDOW(wnd));
507 
508 			priv->playlist_visible = priv->pre_fs_playlist_visible;
509 		}
510 
511 		if(!celluloid_main_window_get_csd_enabled(wnd))
512 		{
513 			gtk_application_window_set_show_menubar
514 				(GTK_APPLICATION_WINDOW(wnd), !fullscreen);
515 		}
516 
517 		celluloid_video_area_set_fullscreen_state
518 			(video_area, fullscreen);
519 		celluloid_main_window_set_use_floating_controls
520 			(wnd, floating && show_controls);
521 		gtk_widget_set_visible
522 			(priv->playlist, playlist_visible);
523 
524 		priv->fullscreen = fullscreen;
525 
526 		g_object_unref(settings);
527 	}
528 }
529 
530 gboolean
celluloid_main_window_get_fullscreen(CelluloidMainWindow * wnd)531 celluloid_main_window_get_fullscreen(CelluloidMainWindow *wnd)
532 {
533 	return get_private(wnd)->fullscreen;
534 }
535 
536 void
celluloid_main_window_toggle_fullscreen(CelluloidMainWindow * wnd)537 celluloid_main_window_toggle_fullscreen(CelluloidMainWindow *wnd)
538 {
539 	celluloid_main_window_set_fullscreen(wnd, !get_private(wnd)->fullscreen);
540 }
541 
542 void
celluloid_main_window_reset(CelluloidMainWindow * wnd)543 celluloid_main_window_reset(CelluloidMainWindow *wnd)
544 {
545 	gtk_window_set_title
546 		(GTK_WINDOW(wnd), g_get_application_name());
547 	celluloid_control_box_reset
548 		(CELLULOID_CONTROL_BOX(get_private(wnd)->control_box));
549 }
550 
551 void
celluloid_main_window_save_state(CelluloidMainWindow * wnd)552 celluloid_main_window_save_state(CelluloidMainWindow *wnd)
553 {
554 	GSettings *settings;
555 	CelluloidMainWindowPrivate *priv;
556 	gint width;
557 	gint height;
558 	gboolean maximized;
559 	gint handle_pos;
560 	gdouble volume;
561 
562 	settings = g_settings_new(CONFIG_WIN_STATE);
563 	priv = get_private(wnd);
564 	maximized = gtk_window_is_maximized(GTK_WINDOW(wnd));
565 	handle_pos = gtk_paned_get_position(GTK_PANED(priv->video_area_paned));
566 
567 	g_object_get(priv->control_box, "volume", &volume, NULL);
568 	gtk_window_get_default_size(GTK_WINDOW(wnd), &width, &height);
569 
570 	// Controls visibility does not need to be saved here since
571 	// celluloid_main_window_set_controls_visible() already updates the
572 	// associated GSettings key when it is called.
573 	g_settings_set_boolean(settings, "maximized", maximized);
574 	g_settings_set_double(settings, "volume", volume/100.0);
575 	g_settings_set_boolean(settings, "show-playlist", priv->playlist_visible);
576 
577 	if(!maximized)
578 	{
579 		g_settings_set_int(settings, "width", width);
580 		g_settings_set_int(settings, "height", height);
581 	}
582 
583 	if(celluloid_main_window_get_playlist_visible(wnd))
584 	{
585 		g_settings_set_int(	settings,
586 					"playlist-width",
587 					width-handle_pos );
588 	}
589 	else
590 	{
591 		g_settings_set_int(	settings,
592 					"playlist-width",
593 					priv->playlist_width );
594 	}
595 
596 	g_clear_object(&settings);
597 }
598 
599 void
celluloid_main_window_load_state(CelluloidMainWindow * wnd)600 celluloid_main_window_load_state(CelluloidMainWindow *wnd)
601 {
602 	if(!gtk_widget_get_realized(GTK_WIDGET(wnd)))
603 	{
604 		GSettings *settings = g_settings_new(CONFIG_WIN_STATE);
605 		CelluloidMainWindowPrivate *priv = get_private(wnd);
606 		gint width = g_settings_get_int(settings, "width");
607 		gint height = g_settings_get_int(settings, "height");
608 		gboolean maximized = g_settings_get_boolean(settings, "maximized");
609 		gint handle_pos;
610 		gboolean controls_visible;
611 		gdouble volume;
612 
613 		priv->playlist_width
614 			= g_settings_get_int(settings, "playlist-width");
615 		priv->playlist_visible
616 			= g_settings_get_boolean(settings, "show-playlist");
617 		controls_visible
618 			= g_settings_get_boolean(settings, "show-controls");
619 		volume = g_settings_get_double(settings, "volume");
620 		handle_pos = width-(priv->playlist_visible?priv->playlist_width:0);
621 
622 		g_object_set(priv->control_box, "volume", volume, NULL);
623 
624 		gtk_widget_set_visible(priv->control_box, controls_visible);
625 		gtk_widget_set_visible(priv->playlist, priv->playlist_visible);
626 		gtk_window_set_default_size(GTK_WINDOW(wnd), width, height);
627 		gtk_paned_set_position
628 			(GTK_PANED(priv->video_area_paned), handle_pos);
629 
630 		if(maximized)
631 		{
632 			gtk_window_maximize(GTK_WINDOW(wnd));
633 		}
634 
635 		g_clear_object(&settings);
636 	}
637 	else
638 	{
639 		g_critical(	"Attempted to call "
640 				"celluloid_main_window_load_state() "
641 				"on realized window" );
642 	}
643 }
644 
645 void
celluloid_main_window_update_track_list(CelluloidMainWindow * wnd,const GPtrArray * track_list)646 celluloid_main_window_update_track_list(	CelluloidMainWindow *wnd,
647 						const GPtrArray *track_list )
648 {
649 	CelluloidMainWindowPrivate *priv = get_private(wnd);
650 
651 	priv->track_list = track_list;
652 
653 	celluloid_video_area_update_track_list
654 		(CELLULOID_VIDEO_AREA(priv->video_area), track_list);
655 
656 	if(celluloid_main_window_get_csd_enabled(wnd))
657 	{
658 		celluloid_header_bar_update_track_list
659 			(CELLULOID_HEADER_BAR(priv->header_bar), track_list);
660 	}
661 	else
662 	{
663 		GtkApplication *app;
664 		GMenu *menu;
665 
666 		app = gtk_window_get_application(GTK_WINDOW(wnd));
667 		menu = G_MENU(gtk_application_get_menubar(app));
668 
669 		if(menu)
670 		{
671 			g_menu_remove_all(menu);
672 
673 			celluloid_menu_build_full
674 				(menu, track_list, priv->disc_list);
675 		}
676 	}
677 }
678 
679 void
celluloid_main_window_update_disc_list(CelluloidMainWindow * wnd,const GPtrArray * disc_list)680 celluloid_main_window_update_disc_list(	CelluloidMainWindow *wnd,
681 					const GPtrArray *disc_list )
682 {
683 	CelluloidMainWindowPrivate *priv = get_private(wnd);
684 
685 	priv->disc_list = disc_list;
686 
687 	if(celluloid_main_window_get_csd_enabled(wnd))
688 	{
689 		celluloid_header_bar_update_disc_list
690 			(CELLULOID_HEADER_BAR(priv->header_bar), disc_list);
691 		celluloid_video_area_update_disc_list
692 			(CELLULOID_VIDEO_AREA(priv->video_area), disc_list);
693 	}
694 	else
695 	{
696 		GtkApplication *app;
697 		GMenu *menu;
698 
699 		app = gtk_window_get_application(GTK_WINDOW(wnd));
700 		menu = G_MENU(gtk_application_get_menubar(app));
701 
702 		if(menu)
703 		{
704 			g_menu_remove_all(menu);
705 
706 			celluloid_menu_build_full
707 				(menu, priv->track_list, disc_list);
708 		}
709 	}
710 }
711 
712 void
celluloid_main_window_resize_video_area(CelluloidMainWindow * wnd,gint width,gint height)713 celluloid_main_window_resize_video_area(	CelluloidMainWindow *wnd,
714 						gint width,
715 						gint height )
716 {
717 	/* As of GNOME 3.36, attempting to resize the window while it is
718 	 * maximized will cause the UI to stop rendering. Resizing while
719 	 * fullscreen is unaffected, but it doesn't make sense to resize there
720 	 * either.
721 	 */
722 	if(	!get_private(wnd)->fullscreen &&
723 		!gtk_window_is_maximized(GTK_WINDOW(wnd)) )
724 	{
725 		CelluloidMainWindowPrivate *priv = get_private(wnd);
726 
727 		g_signal_connect(	priv->video_area,
728 					"resize",
729 					G_CALLBACK(resize_video_area_finalize),
730 					wnd );
731 
732 		priv->resize_target[0] = width;
733 		priv->resize_target[1] = height;
734 		resize_to_target(wnd);
735 
736 		/* The size may not change, so this is needed to ensure that
737 		 * resize_video_area_finalize() will be called so that the event handler
738 		 * will be disconnected.
739 		 */
740 		gtk_widget_queue_allocate(priv->video_area);
741 	}
742 }
743 
744 void
celluloid_main_window_enable_csd(CelluloidMainWindow * wnd)745 celluloid_main_window_enable_csd(CelluloidMainWindow *wnd)
746 {
747 	CelluloidMainWindowPrivate *priv = get_private(wnd);
748 
749 	priv->csd = TRUE;
750 
751 	gtk_window_set_titlebar(GTK_WINDOW(wnd), priv->header_bar);
752 	gtk_window_set_title(GTK_WINDOW(wnd), g_get_application_name());
753 }
754 
755 gboolean
celluloid_main_window_get_csd_enabled(CelluloidMainWindow * wnd)756 celluloid_main_window_get_csd_enabled(CelluloidMainWindow *wnd)
757 {
758 	return get_private(wnd)->csd;
759 }
760 
761 void
celluloid_main_window_set_playlist_visible(CelluloidMainWindow * wnd,gboolean visible)762 celluloid_main_window_set_playlist_visible(	CelluloidMainWindow *wnd,
763 						gboolean visible )
764 {
765 	CelluloidMainWindowPrivate *priv = get_private(wnd);
766 
767 	if(visible != priv->playlist_visible && !priv->fullscreen)
768 	{
769 		gboolean resize;
770 		gint handle_pos;
771 		gint width;
772 		gint height;
773 
774 		resize = gtk_window_get_resizable(GTK_WINDOW(wnd));
775 		handle_pos =	gtk_paned_get_position
776 				(GTK_PANED(priv->video_area_paned));
777 
778 		gtk_window_get_default_size(GTK_WINDOW(wnd), &width, &height);
779 
780 		if(priv->playlist_first_toggle && visible)
781 		{
782 			gint new_pos = width - (resize ? 0 : priv->playlist_width);
783 
784 			gtk_paned_set_position
785 				(GTK_PANED(priv->video_area_paned), new_pos);
786 		}
787 		else if(!visible)
788 		{
789 			priv->playlist_width = width - handle_pos;
790 		}
791 
792 		priv->playlist_visible = visible;
793 		gtk_widget_set_visible(priv->playlist, visible);
794 
795 		if(resize)
796 		{
797 			gint new_width;
798 
799 			new_width =	visible?
800 					width + priv->playlist_width:
801 					handle_pos;
802 
803 			gtk_window_set_default_size
804 				(GTK_WINDOW(wnd), new_width, height);
805 		}
806 
807 		priv->playlist_first_toggle = FALSE;
808 	}
809 }
810 
811 gboolean
celluloid_main_window_get_playlist_visible(CelluloidMainWindow * wnd)812 celluloid_main_window_get_playlist_visible(CelluloidMainWindow *wnd)
813 {
814 	return gtk_widget_get_visible(GTK_WIDGET(get_private(wnd)->playlist));
815 }
816 
817 void
celluloid_main_window_set_controls_visible(CelluloidMainWindow * wnd,gboolean visible)818 celluloid_main_window_set_controls_visible(	CelluloidMainWindow *wnd,
819 						gboolean visible )
820 {
821 	GSettings *settings = g_settings_new(CONFIG_WIN_STATE);
822 	CelluloidMainWindowPrivate *priv = get_private(wnd);
823 	const gboolean floating = priv->use_floating_controls;
824 	const gboolean fullscreen = priv->fullscreen;
825 
826 	gtk_widget_set_visible
827 		(GTK_WIDGET(priv->control_box), visible && !fullscreen && !floating);
828 	celluloid_video_area_set_control_box_visible
829 		(CELLULOID_VIDEO_AREA(priv->video_area), visible && fullscreen);
830 	g_settings_set_boolean
831 		(settings, "show-controls", visible);
832 
833 	g_clear_object(&settings);
834 }
835 
836 gboolean
celluloid_main_window_get_controls_visible(CelluloidMainWindow * wnd)837 celluloid_main_window_get_controls_visible(CelluloidMainWindow *wnd)
838 {
839 	CelluloidMainWindowPrivate *priv = get_private(wnd);
840 	CelluloidVideoArea *video_area = CELLULOID_VIDEO_AREA(priv->video_area);
841 
842 	return	priv->fullscreen ?
843 		celluloid_video_area_get_control_box_visible(video_area) :
844 		gtk_widget_get_visible(GTK_WIDGET(priv->control_box));
845 }
846