1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Goo
5  *
6  *  Copyright (C) 2013 Free Software Foundation, Inc.
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, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #include <config.h>
24 #include <stdlib.h>
25 #include <glib/gi18n.h>
26 #include <glib/gprintf.h>
27 #include <gst/gst.h>
28 #include "goo-application.h"
29 #include "goo-application-actions-callbacks.h"
30 #include "goo-application-actions-entries.h"
31 #include "goo-window.h"
32 #include "gtk-utils.h"
33 #include "main.h"
34 #include "preferences.h"
35 
36 
37 /* -- command line arguments -- */
38 
39 static const char     *program_argv0 = NULL;
40 static int             arg_toggle_play = FALSE;
41 static int             arg_stop = FALSE;
42 static int             arg_next = FALSE;
43 static int             arg_prev = FALSE;
44 static int             arg_eject = FALSE;
45 static int             arg_quit = FALSE;
46 static gboolean        arg_version = FALSE;
47 static char           *arg_device = NULL;
48 
49 
50 static const GOptionEntry options[] = {
51 	{ "device", 'd',  0, G_OPTION_ARG_STRING, &arg_device,
52 	  N_("CD device to be used"),
53 	  N_("DEVICE_PATH") },
54 	{ "play", '\0', 0, G_OPTION_ARG_NONE, &arg_auto_play,
55           N_("Play the CD on startup"),
56           0 },
57 	{ "toggle-play", '\0', 0, G_OPTION_ARG_NONE, &arg_toggle_play,
58           N_("Toggle play"),
59           0 },
60         { "stop", '\0', 0, G_OPTION_ARG_NONE, &arg_stop,
61           N_("Stop playing"),
62           0 },
63 	{ "next", '\0', 0, G_OPTION_ARG_NONE, &arg_next,
64           N_("Play the next track"),
65           0 },
66 	{ "previous", '\0', 0, G_OPTION_ARG_NONE, &arg_prev,
67           N_("Play the previous track"),
68           0 },
69 	{ "eject", '\0', 0, G_OPTION_ARG_NONE, &arg_eject,
70           N_("Eject the CD"),
71           0 },
72 	{ "toggle-visibility", '\0', 0, G_OPTION_ARG_NONE, &arg_toggle_visibility,
73           N_("Toggle the main window visibility"),
74           0 },
75 	{ "quit", '\0', 0, G_OPTION_ARG_NONE, &arg_quit,
76           N_("Quit the application"),
77           0 },
78         { "version", 'v', 0, G_OPTION_ARG_NONE, &arg_version,
79     	  N_("Show version"),
80     	  0 },
81 	{ NULL }
82 };
83 
84 
85 /* -- GooApplication --  */
86 
87 
88 struct _GooApplicationPrivate {
89 	GSettings *settings;
90 };
91 
92 
G_DEFINE_TYPE_WITH_CODE(GooApplication,goo_application,GTK_TYPE_APPLICATION,G_ADD_PRIVATE (GooApplication))93 G_DEFINE_TYPE_WITH_CODE (GooApplication, goo_application, GTK_TYPE_APPLICATION,
94 			 G_ADD_PRIVATE (GooApplication))
95 
96 
97 static void
98 goo_application_finalize (GObject *object)
99 {
100 	GooApplication *self = (GooApplication *) object;
101 
102 	g_object_unref (self->priv->settings);
103 	G_OBJECT_CLASS (goo_application_parent_class)->finalize (object);
104 }
105 
106 
107 static gboolean
required_gstreamer_plugins_available(void)108 required_gstreamer_plugins_available (void)
109 {
110 	char *required_plugins[] = { "cdparanoiasrc", "audioconvert", "volume", "giosink" };
111 	int   i;
112 
113 	for (i = 0; i < G_N_ELEMENTS (required_plugins); i++) {
114 		GstElement *element;
115 		gboolean    present;
116 
117 		element = gst_element_factory_make (required_plugins[i], NULL);
118 		present = (element != NULL);
119 		if (element != NULL)
120 			gst_object_unref (GST_OBJECT (element));
121 
122 		if (! present)
123 			return FALSE;
124 	}
125 
126 	return TRUE;
127 }
128 
129 
130 static int
goo_application_command_line_finished(GApplication * application,int status)131 goo_application_command_line_finished (GApplication *application,
132                                        int           status)
133 {
134         if (status == EXIT_SUCCESS)
135                 gdk_notify_startup_complete ();
136 
137         /* reset the arguments */
138 
139 	arg_auto_play = FALSE;
140 	arg_toggle_play = FALSE;
141 	arg_stop = FALSE;
142 	arg_next = FALSE;
143 	arg_prev = FALSE;
144 	arg_eject = FALSE;
145 	arg_toggle_visibility = FALSE;
146 	arg_quit = FALSE;
147 	g_free (arg_device);
148 	arg_device = NULL;
149 
150         return status;
151 }
152 
153 
154 static GOptionContext *
goo_application_create_option_context(void)155 goo_application_create_option_context (void)
156 {
157 	GOptionContext *options_context;
158 	static gsize    initialized = FALSE;
159 
160 	options_context = g_option_context_new (N_("Play CDs and save the tracks to disk as files"));
161 	g_option_context_set_translation_domain (options_context, GETTEXT_PACKAGE);
162 	g_option_context_set_ignore_unknown_options (options_context, TRUE);
163 	g_option_context_add_main_entries (options_context, options, GETTEXT_PACKAGE);
164 
165 	if (g_once_init_enter (&initialized)) {
166 		g_option_context_add_group (options_context, gtk_get_option_group (TRUE));
167 		g_option_context_add_group (options_context, gst_init_get_option_group ());
168 
169 		g_once_init_leave (&initialized, TRUE);
170 	}
171 
172 	return options_context;
173 }
174 
175 
176 static int
goo_application_command_line(GApplication * application,GApplicationCommandLine * command_line)177 goo_application_command_line (GApplication            *application,
178 			      GApplicationCommandLine *command_line)
179 {
180 	char           **argv;
181 	int              argc;
182 	GOptionContext  *options_context;
183 	GError          *error = NULL;
184 	GtkWidget       *window;
185 
186 	argv = g_application_command_line_get_arguments (command_line, &argc);
187 	options_context = goo_application_create_option_context ();
188 	if (! g_option_context_parse (options_context, &argc, &argv, &error)) {
189 		g_critical ("Failed to parse arguments: %s", error->message);
190 		g_error_free (error);
191 		g_option_context_free (options_context);
192 		return goo_application_command_line_finished (application, EXIT_FAILURE);
193 	}
194 	g_option_context_free (options_context);
195 
196 	/* check the gstreamer plugins */
197 
198 	if (! required_gstreamer_plugins_available ()) {
199 		GtkWidget *d;
200 		d = _gtk_error_dialog_new (NULL,
201 					   0,
202 					   NULL,
203 					   _("Cannot start the CD player"),
204 					   "%s",
205 					   _("In order to read CDs you have to install the GStreamer base plugins"));
206 		g_signal_connect_swapped (G_OBJECT (d), "response",
207 					  G_CALLBACK (gtk_widget_destroy),
208 					  d);
209 		gtk_window_set_application (GTK_WINDOW (d), GTK_APPLICATION (application));
210 		gtk_widget_show (d);
211 
212 		return goo_application_command_line_finished (application, EXIT_FAILURE);
213 	}
214 
215 	/* execute the command line */
216 
217 	window = _gtk_application_get_current_window (GTK_APPLICATION (application));
218 	if (window == NULL)
219 		window = goo_window_new (NULL);
220 	gtk_window_present (GTK_WINDOW (window));
221 
222 	if (arg_auto_play) {
223 		goo_window_play (GOO_WINDOW (window));
224 	}
225 	else if (arg_toggle_play) {
226 		goo_window_toggle_play (GOO_WINDOW (window));
227 	}
228 	else if (arg_stop) {
229 		goo_window_stop (GOO_WINDOW (window));
230 	}
231 	else if (arg_next) {
232 		goo_window_next (GOO_WINDOW (window));
233 	}
234 	else if (arg_prev) {
235 		goo_window_prev (GOO_WINDOW (window));
236 	}
237 	else if (arg_eject) {
238 		goo_window_eject (GOO_WINDOW (window));
239 	}
240 	else if (arg_toggle_visibility) {
241 		goo_window_toggle_visibility (GOO_WINDOW (window));
242 	}
243 	else if (arg_quit) {
244 		goo_window_close (GOO_WINDOW (window));
245 	}
246 	else if (arg_device != NULL) {
247 		BraseroDrive *drive;
248 
249 		drive = main_get_drive_for_device (arg_device);
250 		window = main_get_window_from_device (arg_device);
251 		if (window == NULL) {
252 			window = goo_window_new (drive);
253 			gtk_widget_show (window);
254 		}
255 		else
256 			goo_window_set_drive (GOO_WINDOW (window), drive);
257 
258 		g_object_unref (drive);
259 		g_free (arg_device);
260 		arg_device = NULL;
261 	}
262 
263 	return goo_application_command_line_finished (application, EXIT_SUCCESS);
264 }
265 
266 
267 static gboolean
goo_application_local_command_line(GApplication * application,char *** arguments,int * exit_status)268 goo_application_local_command_line (GApplication   *application,
269 				    char         ***arguments,
270 				    int            *exit_status)
271 {
272 	char           **local_argv;
273 	int              local_argc;
274 	GOptionContext  *options_context;
275 	GError          *error = NULL;
276 	gboolean         handled_locally = FALSE;
277 
278 	local_argv = g_strdupv (*arguments);
279 	local_argc = g_strv_length (local_argv);
280 
281 	program_argv0 = g_strdup (local_argv[0]);
282 	*exit_status = EXIT_SUCCESS;
283 
284 	options_context = goo_application_create_option_context ();
285 	if (! g_option_context_parse (options_context, &local_argc, &local_argv, &error)) {
286 		*exit_status = EXIT_FAILURE;
287 		g_critical ("Failed to parse arguments: %s", error->message);
288 		g_clear_error (&error);
289 		handled_locally = TRUE;
290 	}
291 
292 	if (arg_version) {
293 		g_printf ("%s %s, Copyright © 2001-2013 Free Software Foundation, Inc.\n", PACKAGE_NAME, PACKAGE_VERSION);
294 		handled_locally = TRUE;
295 	}
296 
297 	g_option_context_free (options_context);
298 	g_strfreev (local_argv);
299 
300 	return handled_locally;
301 }
302 
303 
304 /* -- goo_application_startup -- */
305 
306 
307 static void
pref_playlist_playall_changed(GSettings * settings,const char * key,gpointer user_data)308 pref_playlist_playall_changed (GSettings  *settings,
309 	  	 	       const char *key,
310 	  	 	       gpointer    user_data)
311 {
312 	GooApplication *application = user_data;
313 
314 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_PLAYALL)),
315 				   g_variant_new_boolean (g_settings_get_boolean (application->priv->settings, PREF_PLAYLIST_PLAYALL)));
316 	update_actions_sensitivity (G_APPLICATION (application));
317 }
318 
319 
320 static void
pref_playlist_repeat_changed(GSettings * settings,const char * key,gpointer user_data)321 pref_playlist_repeat_changed (GSettings  *settings,
322 	  	 	      const char *key,
323 	  	 	      gpointer    user_data)
324 {
325 	GooApplication *application = user_data;
326 
327 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_REPEAT)),
328 				   g_variant_new_boolean (g_settings_get_boolean (application->priv->settings, PREF_PLAYLIST_REPEAT)));
329 	update_actions_sensitivity (G_APPLICATION (application));
330 }
331 
332 
333 static void
pref_playlist_shuffle_changed(GSettings * settings,const char * key,gpointer user_data)334 pref_playlist_shuffle_changed (GSettings  *settings,
335 	  	 	       const char *key,
336 	  	 	       gpointer    user_data)
337 {
338 	GooApplication *application = user_data;
339 
340 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_SHUFFLE)),
341 				   g_variant_new_boolean (g_settings_get_boolean (application->priv->settings, PREF_PLAYLIST_SHUFFLE)));
342 	update_actions_sensitivity (G_APPLICATION (application));
343 }
344 
345 
346 static void
initialize_app_menu(GApplication * application)347 initialize_app_menu (GApplication *application)
348 {
349 	const _GtkAccelerator app_accelerators[] = {
350 		{ "app.help", "F1" },
351 		{ "app.quit", "<Control>q" }
352 	};
353 	GooApplication *self = (GooApplication *) application;
354 
355 	g_action_map_add_action_entries (G_ACTION_MAP (application),
356 					 goo_application_actions,
357 					 G_N_ELEMENTS (goo_application_actions),
358 					 application);
359 
360 	_gtk_application_add_accelerators (GTK_APPLICATION (application), app_accelerators, G_N_ELEMENTS (app_accelerators));
361 
362 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_PLAYALL)),
363 				   g_variant_new_boolean (g_settings_get_boolean (self->priv->settings, PREF_PLAYLIST_PLAYALL)));
364 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_REPEAT)),
365 				   g_variant_new_boolean (g_settings_get_boolean (self->priv->settings, PREF_PLAYLIST_REPEAT)));
366 	g_simple_action_set_state (G_SIMPLE_ACTION (g_action_map_lookup_action (G_ACTION_MAP (application), PREF_PLAYLIST_SHUFFLE)),
367 				   g_variant_new_boolean (g_settings_get_boolean (self->priv->settings, PREF_PLAYLIST_SHUFFLE)));
368 
369 	g_signal_connect (self->priv->settings,
370 			  "changed::" PREF_PLAYLIST_PLAYALL,
371 			  G_CALLBACK (pref_playlist_playall_changed),
372 			  self);
373 	g_signal_connect (self->priv->settings,
374 			  "changed::" PREF_PLAYLIST_SHUFFLE,
375 			  G_CALLBACK (pref_playlist_shuffle_changed),
376 			  self);
377 	g_signal_connect (self->priv->settings,
378 			  "changed::" PREF_PLAYLIST_REPEAT,
379 			  G_CALLBACK (pref_playlist_repeat_changed),
380 			  self);
381 }
382 
383 
384 static void
goo_application_startup(GApplication * application)385 goo_application_startup (GApplication *application)
386 {
387 	G_APPLICATION_CLASS (goo_application_parent_class)->startup (application);
388 
389 	g_set_application_name (_("CD Player"));
390 	gtk_window_set_default_icon_name ("goobox");
391 
392 	initialize_app_menu (application);
393 }
394 
395 
396 static void
goo_application_activate(GApplication * application)397 goo_application_activate (GApplication *application)
398 {
399 	GtkWidget *window;
400 
401 	window = _gtk_application_get_current_window (GTK_APPLICATION (application));
402 	if (window != NULL)
403 		gtk_window_present (GTK_WINDOW (window));
404 }
405 
406 
407 static void
goo_application_class_init(GooApplicationClass * klass)408 goo_application_class_init (GooApplicationClass *klass)
409 {
410 	GObjectClass      *object_class;
411 	GApplicationClass *application_class;
412 
413 	object_class = G_OBJECT_CLASS (klass);
414 	object_class->finalize = goo_application_finalize;
415 
416 	application_class = G_APPLICATION_CLASS (klass);
417 	application_class->command_line = goo_application_command_line;
418 	application_class->local_command_line = goo_application_local_command_line;
419 	application_class->startup = goo_application_startup;
420 	application_class->activate = goo_application_activate;
421 }
422 
423 
424 static void
goo_application_init(GooApplication * self)425 goo_application_init (GooApplication *self)
426 {
427 	self->priv = goo_application_get_instance_private (self);
428 	self->priv->settings = g_settings_new (GOOBOX_SCHEMA_PLAYLIST);
429 }
430 
431 
432 GtkApplication *
goo_application_new(void)433 goo_application_new (void)
434 {
435 	return g_object_new (GOO_TYPE_APPLICATION,
436 	                     "application-id", "org.gnome.Goobox",
437 	                     "flags", G_APPLICATION_HANDLES_COMMAND_LINE,
438 	                     NULL);
439 }
440