1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Goo
5  *
6  *  Copyright (C) 2004-2009 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 #include <config.h>
23 #include <math.h>
24 #include <string.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gtk/gtk.h>
28 #include <gst/gst.h>
29 #include "gio-utils.h"
30 #include "dlg-cover-chooser.h"
31 #include "goo-marshal.h"
32 #include "goo-player.h"
33 #include "goo-player-info.h"
34 #include "goo-player-progress.h"
35 #include "goo-window.h"
36 #include "goo-window-actions-entries.h"
37 #include "gth-user-dir.h"
38 #include "gtk-utils.h"
39 #include "gtk-file-chooser-preview.h"
40 #include "glib-utils.h"
41 #include "main.h"
42 #include "preferences.h"
43 #include "typedefs.h"
44 
45 #define FILES_TO_PROCESS_AT_ONCE 500
46 #define PLAYER_CHECK_RATE 100
47 #define COVER_SIZE 80
48 #define IDLE_TIMEOUT 200
49 #define FALLBACK_ICON_SIZE 16
50 #define CONFIG_KEY_AUTOFETCH_GROUP "AutoFetch"
51 #define ACTIONS_MENU_BUTTON_POSITION 6
52 #define TRAY_TOOLTIP_DELAY 500
53 #define AUTOPLAY_DELAY 250
54 #define MAX_WINDOW_HEIGHT_PERCENTAGE 0.80
55 #define MESSAGE_BAR_RESPONSE_PROPERTIES 1
56 
57 struct _GooWindowPrivate {
58 	GtkWidget         *list_view;
59 	GtkListStore      *list_store;
60 	GtkWidget         *list_scrolled_window;
61 
62 	GtkTreeViewColumn *author_column;
63 	WindowSortMethod   sort_method;
64 	GtkSortType        sort_type;
65 
66 	GtkWidget         *file_popup_menu;
67 	GtkWidget         *cover_popup_menu;
68 
69 	GtkWidget         *message_bar;
70 	GtkWidget         *message_label;
71 	GtkWidget         *message_bar_properties_button;
72 	GtkWidget         *info;
73 	GtkWidget         *play_button;
74 	GtkWidget         *progress;
75 
76 	guint              first_time_event;
77 	guint              next_timeout_handle;
78 	gint               activity_ref;              /* when > 0 some activity
79                                                        * is present. */
80 	GtkAccelGroup     *accel_group;
81 	GSettings         *settings_general;
82 	GSettings         *settings_ui;
83 	GSettings         *settings_playlist;
84 	GSettings         *settings_encoder;
85 
86 	guint              update_timeout_handle;     /* update list timeout
87 						       * handle. */
88 
89 	GooPlayer         *player;
90 	AlbumInfo         *album;
91 	TrackInfo         *current_track;
92 	GList             *playlist;                  /* int list. */
93 
94 	gboolean           exiting;
95 	guint              check_id;
96 	GList             *url_list;
97 	GtkWidget         *preview;
98 	int                pos_x, pos_y;
99 	gboolean           hibernate;
100 	gboolean           resizable_playlist;
101 
102 	GDBusProxy        *media_keys_proxy;
103 	gulong             focus_in_event;
104 	gulong             media_key_event;
105 	gulong             notify_event;
106 };
107 
108 enum {
109 	UPDATE_COVER,
110         LAST_SIGNAL
111 };
112 
113 static guint goo_window_signals[LAST_SIGNAL] = { 0 };
114 static int icon_size = 0;
115 
116 
117 enum {
118 	COLUMN_TRACK_INFO,
119 	COLUMN_NUMBER,
120 	COLUMN_ICON,
121 	COLUMN_TIME,
122 	COLUMN_TITLE,
123 	COLUMN_ARTIST,
124 	NUMBER_OF_COLUMNS
125 };
126 
127 
G_DEFINE_TYPE_WITH_CODE(GooWindow,goo_window,GTK_TYPE_APPLICATION_WINDOW,G_ADD_PRIVATE (GooWindow))128 G_DEFINE_TYPE_WITH_CODE (GooWindow, goo_window, GTK_TYPE_APPLICATION_WINDOW,
129 			 G_ADD_PRIVATE (GooWindow))
130 
131 
132 static void
133 window_update_sensitivity (GooWindow *window)
134 {
135 	int            n_selected;
136 	gboolean       one_file_selected;
137 	GooPlayerState state;
138 	gboolean       error;
139 	gboolean       audio_cd;
140 	gboolean       playing;
141 	gboolean       paused;
142 
143 	n_selected        = _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)));
144 	one_file_selected = n_selected == 1;
145 	state             = goo_player_get_state (window->priv->player);
146 	error             = ! goo_player_is_audio_cd (window->priv->player) || window->priv->hibernate;
147 	playing           = state == GOO_PLAYER_STATE_PLAYING;
148 	paused            = state == GOO_PLAYER_STATE_PAUSED;
149 	audio_cd          = (! error) && (goo_player_get_discid (window->priv->player) != NULL);
150 
151 	gtk_widget_set_sensitive (window->priv->list_view, audio_cd);
152 
153 	_g_action_map_enable_action (G_ACTION_MAP (window), "play", audio_cd && ! playing);
154 	_g_action_map_enable_action (G_ACTION_MAP (window), "play-selected", audio_cd && one_file_selected);
155 	_g_action_map_enable_action (G_ACTION_MAP (window), "pause", audio_cd && playing);
156 	_g_action_map_enable_action (G_ACTION_MAP (window), "stop", audio_cd && (playing || paused));
157 	_g_action_map_enable_action (G_ACTION_MAP (window), "toggle-play", audio_cd);
158 	_g_action_map_enable_action (G_ACTION_MAP (window), "next-track", audio_cd);
159 	_g_action_map_enable_action (G_ACTION_MAP (window), "previous-track", audio_cd);
160 	_g_action_map_enable_action (G_ACTION_MAP (window), "extract", audio_cd && (window->priv->album->n_tracks > 0));
161 	_g_action_map_enable_action (G_ACTION_MAP (window), "copy-disc", audio_cd && (window->priv->album->n_tracks > 0));
162 	_g_action_map_enable_action (G_ACTION_MAP (window), "properties", audio_cd);
163 	_g_action_map_enable_action (G_ACTION_MAP (window), "pick-cover-from-disk", audio_cd);
164 	_g_action_map_enable_action (G_ACTION_MAP (window), "remove-cover", audio_cd);
165 	_g_action_map_enable_action (G_ACTION_MAP (window), "search-cover", audio_cd);
166 	_g_action_map_enable_action (G_ACTION_MAP (window), "eject", ! window->priv->hibernate);
167 }
168 
169 
170 static int
get_icon_size_from_settings(GtkIconSize gtk_icon_size)171 get_icon_size_from_settings (GtkIconSize gtk_icon_size)
172 {
173 	int width;
174 	int height;
175 	int icon_size;
176 
177 	if (gtk_icon_size_lookup (gtk_icon_size, &width, &height))
178 		icon_size = MAX (width, height);
179 	else
180 		icon_size = FALLBACK_ICON_SIZE;
181 
182 	return icon_size;
183 }
184 
185 
186 static GdkPixbuf *
create_void_icon(GooWindow * window)187 create_void_icon (GooWindow *window)
188 {
189 	int        icon_size;
190 	GdkPixbuf *icon;
191 
192 	icon_size = get_icon_size_from_settings (GTK_ICON_SIZE_MENU);
193 	icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, icon_size, icon_size);
194 	gdk_pixbuf_fill (icon, 0x00000000);
195 
196 	return icon;
197 }
198 
199 
200 static gboolean
get_iter_from_track_number(GooWindow * window,int track_number,GtkTreeIter * iter)201 get_iter_from_track_number (GooWindow   *window,
202 			    int          track_number,
203 			    GtkTreeIter *iter)
204 {
205 
206 	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
207 
208 	if (! gtk_tree_model_get_iter_first (model, iter))
209 		return FALSE;
210 
211 	do {
212 		TrackInfo *track;
213 
214 		gtk_tree_model_get (model, iter, COLUMN_TRACK_INFO, &track, -1);
215 		if (track->number == track_number) {
216 			track_info_unref (track);
217 			return TRUE;
218 		}
219 		track_info_unref (track);
220 	}
221 	while (gtk_tree_model_iter_next (model, iter));
222 
223 	return FALSE;
224 }
225 
226 
227 static void
set_track_icon(GooWindow * window,int track_number,const char * icon_name)228 set_track_icon (GooWindow  *window,
229 	        int         track_number,
230 	        const char *icon_name)
231 {
232 	GtkTreeIter  iter;
233 	GdkPixbuf   *icon;
234 
235 	if (! get_iter_from_track_number (window, track_number, &iter))
236 		return;
237 
238 	if (icon_name != NULL)
239 		icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (window))),
240 						 icon_name,
241 						 get_icon_size_from_settings (GTK_ICON_SIZE_MENU),
242 						 0,
243 						 NULL);
244 	else
245 		icon = create_void_icon (window);
246 	gtk_list_store_set (window->priv->list_store, &iter,
247 			    COLUMN_ICON, icon,
248 			    -1);
249 	g_object_unref (icon);
250 }
251 
252 
253 static void
set_current_track_icon(GooWindow * window,const char * icon_name)254 set_current_track_icon (GooWindow  *window,
255 		        const char *icon_name)
256 {
257 	if (window->priv->current_track != NULL)
258 		set_track_icon (window, window->priv->current_track->number, icon_name);
259 }
260 
261 
262 void
goo_window_update(GooWindow * window)263 goo_window_update (GooWindow *window)
264 {
265 	goo_window_stop (window);
266 	goo_player_update (window->priv->player);
267 }
268 
269 
270 static void
goo_window_update_list(GooWindow * window)271 goo_window_update_list (GooWindow *window)
272 {
273 	GdkPixbuf *icon;
274 	GList     *scan;
275 
276 	if (gtk_widget_get_realized (window->priv->list_view))
277 		gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->list_view), 0, 0);
278 
279 	/**/
280 
281 	track_info_unref (window->priv->current_track);
282 	window->priv->current_track = NULL;
283 
284 	if (window->priv->playlist != NULL)
285 		g_list_free (window->priv->playlist);
286 	window->priv->playlist = NULL;
287 
288 	/**/
289 
290 	gtk_widget_set_visible (window->priv->list_scrolled_window, (window->priv->album->tracks != NULL));
291 
292 	/**/
293 
294 	icon = create_void_icon (window);
295 	gtk_list_store_clear (window->priv->list_store);
296 	for (scan = window->priv->album->tracks; scan; scan = scan->next) {
297 		TrackInfo   *track = scan->data;
298 		GtkTreeIter  iter;
299 		char        *time_s;
300 
301 		gtk_list_store_prepend (window->priv->list_store, &iter);
302 
303 		time_s = _g_format_duration_for_display (track->length * 1000);
304 		gtk_list_store_set (window->priv->list_store, &iter,
305 				    COLUMN_TRACK_INFO, track,
306 				    COLUMN_NUMBER, track->number + 1,
307 				    COLUMN_ICON, icon,
308 				    COLUMN_TIME, time_s,
309 				    COLUMN_TITLE, track->title,
310 				    COLUMN_ARTIST, track->artist,
311 				    -1);
312 		g_free (time_s);
313 	}
314 
315 	window_update_sensitivity (window);
316 
317 	{
318 		GdkGeometry hints;
319 
320 		hints.max_height = -1;
321 		hints.max_width = G_MAXINT;
322 		gtk_window_set_geometry_hints (GTK_WINDOW (window),
323 					       GTK_WIDGET (window),
324 					       &hints,
325 					       GDK_HINT_MAX_SIZE);
326 
327 		window->priv->resizable_playlist = FALSE;
328 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window->priv->list_scrolled_window),
329 						GTK_POLICY_NEVER,
330 						GTK_POLICY_NEVER);
331 	}
332 
333 	g_object_unref (icon);
334 }
335 
336 
337 /**/
338 
339 
340 static void
goo_window_update_titles(GooWindow * window)341 goo_window_update_titles (GooWindow *window)
342 {
343 	GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
344 	GtkTreeIter   iter;
345 
346 	if (! gtk_tree_model_get_iter_first (model, &iter))
347 		return;
348 
349 	do {
350 		TrackInfo *track;
351 		TrackInfo *new_track;
352 
353 		gtk_tree_model_get (model, &iter, COLUMN_TRACK_INFO, &track, -1);
354 		new_track = album_info_get_track (window->priv->album, track->number);
355 		track_info_unref (track);
356 
357 		if (new_track == NULL)
358 			continue;
359 
360 		gtk_list_store_set (window->priv->list_store, &iter,
361 				    COLUMN_TRACK_INFO, new_track,
362 				    COLUMN_TITLE, new_track->title,
363 				    COLUMN_ARTIST, new_track->artist,
364 				    -1);
365 
366 		/* Update the current track info. */
367 
368 		if ((window->priv->current_track != NULL)
369 		    && (new_track->number == window->priv->current_track->number))
370 		{
371 			track_info_unref (window->priv->current_track);
372 			track_info_ref (new_track);
373 			window->priv->current_track = new_track;
374 		}
375 		track_info_unref (new_track);
376 	}
377 	while (gtk_tree_model_iter_next (model, &iter));
378 }
379 
380 
381 static void
goo_window_finalize(GObject * object)382 goo_window_finalize (GObject *object)
383 {
384 	GooWindow *window = GOO_WINDOW (object);
385 
386 	debug (DEBUG_INFO, "[FINALIZE]\n");
387 
388 	if (window->priv != NULL) {
389 		if (window->priv->media_keys_proxy != NULL) {
390 			g_dbus_proxy_call_sync (window->priv->media_keys_proxy,
391 						"ReleaseMediaPlayerKeys",
392 						g_variant_new ("(s)", g_get_application_name ()),
393 						G_DBUS_CALL_FLAGS_NONE,
394 						-1,
395 						NULL,
396 						NULL);
397 			g_object_unref (window->priv->media_keys_proxy);
398 		}
399 
400 		/* Save preferences */
401 
402 		g_settings_set_int (window->priv->settings_general, PREF_GENERAL_VOLUME, goo_player_get_audio_volume (window->priv->player));
403 
404 		/**/
405 
406 		_g_object_unref (window->priv->accel_group);
407 		_g_object_unref (window->priv->settings_ui);
408 		_g_object_unref (window->priv->settings_general);
409 		_g_object_unref (window->priv->settings_playlist);
410 		_g_object_unref (window->priv->settings_encoder);
411 
412 		g_object_unref (window->priv->list_store);
413 
414 		if (window->priv->notify_event != 0) {
415 			g_source_remove (window->priv->notify_event);
416 			window->priv->notify_event = 0;
417 		}
418 
419 		if (window->priv->next_timeout_handle != 0) {
420 			g_source_remove (window->priv->next_timeout_handle);
421 			window->priv->next_timeout_handle = 0;
422 		}
423 
424 		if (window->priv->update_timeout_handle != 0) {
425 			g_source_remove (window->priv->update_timeout_handle);
426 			window->priv->update_timeout_handle = 0;
427 		}
428 
429 		if (window->priv->playlist != NULL)
430 			g_list_free (window->priv->playlist);
431 
432 		g_signal_handlers_disconnect_by_data (window->priv->player, window);
433 		g_object_unref (window->priv->player);
434 
435 		track_info_unref (window->priv->current_track);
436 		album_info_unref (window->priv->album);
437 
438 		_g_string_list_free (window->priv->url_list);
439 		window->priv->url_list = NULL;
440 
441 		window->priv = NULL;
442 	}
443 
444 	G_OBJECT_CLASS (goo_window_parent_class)->finalize (object);
445 }
446 
447 
448 static void
add_columns(GooWindow * window,GtkTreeView * treeview)449 add_columns (GooWindow   *window,
450 	     GtkTreeView *treeview)
451 {
452 	GtkCellRenderer   *renderer;
453 	GtkTreeViewColumn *column;
454 	GValue             value = { 0, };
455 
456 	/* The Number column. */
457 
458 	column = gtk_tree_view_column_new ();
459 	/*gtk_tree_view_column_set_title (column, _("#"));*/
460 
461 	renderer = gtk_cell_renderer_text_new ();
462 	gtk_tree_view_column_pack_start (column,
463 					 renderer,
464 					 TRUE);
465 	gtk_tree_view_column_set_attributes (column, renderer,
466                                              "text", COLUMN_NUMBER,
467                                              NULL);
468 
469 	renderer = gtk_cell_renderer_pixbuf_new ();
470 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
471 	gtk_tree_view_column_set_attributes (column, renderer,
472                                              "pixbuf", COLUMN_ICON,
473                                              NULL);
474 
475 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
476 	gtk_tree_view_column_set_resizable (column, TRUE);
477 
478 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_NUMBER);
479 	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
480 
481 	/* Title */
482 
483 	column = gtk_tree_view_column_new ();
484 	/*gtk_tree_view_column_set_title (column, _("Title"));*/
485 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
486 	gtk_tree_view_column_set_resizable (column, TRUE);
487 	gtk_tree_view_column_set_expand (column, TRUE);
488 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_TITLE);
489 
490 	renderer = gtk_cell_renderer_text_new ();
491 
492 	g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE);
493 	g_value_set_enum (&value, PANGO_ELLIPSIZE_END);
494 	g_object_set_property (G_OBJECT (renderer), "ellipsize", &value);
495 	g_value_unset (&value);
496 
497 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
498         gtk_tree_view_column_set_attributes (column, renderer,
499                                              "text", COLUMN_TITLE,
500                                              NULL);
501 
502 	gtk_tree_view_append_column (treeview, column);
503 
504 	/* Author */
505 
506 	window->priv->author_column = column = gtk_tree_view_column_new ();
507 	/*gtk_tree_view_column_set_title (column, _("Artist"));*/
508 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
509 	gtk_tree_view_column_set_resizable (column, FALSE);
510 	gtk_tree_view_column_set_expand (column, TRUE);
511 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_ARTIST);
512 
513 	renderer = gtk_cell_renderer_text_new ();
514 
515 	g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE);
516 	g_value_set_enum (&value, PANGO_ELLIPSIZE_END);
517 	g_object_set_property (G_OBJECT (renderer), "ellipsize", &value);
518 	g_value_unset (&value);
519 
520 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
521         gtk_tree_view_column_set_attributes (column, renderer,
522                                              "text", COLUMN_ARTIST,
523                                              NULL);
524 
525 	gtk_tree_view_append_column (treeview, column);
526 	gtk_tree_view_column_set_visible (column, FALSE);
527 
528 	/* Time */
529 
530 	column = gtk_tree_view_column_new ();
531 	/*gtk_tree_view_column_set_title (column, _("Length"));*/
532 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
533 	gtk_tree_view_column_set_resizable (column, FALSE);
534 	gtk_tree_view_column_set_sort_column_id (column, COLUMN_TIME);
535 
536 	renderer = gtk_cell_renderer_text_new ();
537 
538 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
539         gtk_tree_view_column_set_attributes (column, renderer,
540                                              "text", COLUMN_TIME,
541                                              NULL);
542 
543 	gtk_tree_view_append_column (treeview, column);
544 }
545 
546 
547 static void
print_playlist(GooWindow * window)548 print_playlist (GooWindow *window)
549 {
550 #ifdef DEBUG
551 	GList *scan;
552 
553 	debug (DEBUG_INFO, "PLAYLIST: ");
554 	for (scan = window->priv->playlist; scan; scan = scan->next)
555 		debug (DEBUG_INFO, "%d, ", GPOINTER_TO_INT (scan->data));
556 	debug (DEBUG_INFO, "\n");
557 #endif
558 }
559 
560 
561 static int
get_track_number_from_position(GooWindow * window,int pos)562 get_track_number_from_position (GooWindow   *window,
563 				int          pos)
564 {
565 
566 	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
567 	GtkTreeIter           iter;
568 	int                   i = 0;
569 
570 	if (! gtk_tree_model_get_iter_first (model, &iter))
571 		return -1;
572 
573 	do {
574 		TrackInfo *track;
575 		int       n;
576 		gtk_tree_model_get (model, &iter, COLUMN_TRACK_INFO, &track, -1);
577 		n = track->number;
578 		track_info_unref (track);
579 		if (i == pos)
580 			return n;
581 		i++;
582 	}
583 	while (gtk_tree_model_iter_next (model, &iter));
584 
585 	return -1;
586 }
587 
588 
589 static int
get_position_from_track_number(GooWindow * window,int track_number)590 get_position_from_track_number (GooWindow   *window,
591 				int          track_number)
592 {
593 
594 	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
595 	GtkTreeIter           iter;
596 	int                   pos = 0;
597 
598 	if (! gtk_tree_model_get_iter_first (model, &iter))
599 		return -1;
600 
601 	do {
602 		TrackInfo *track;
603 		int       n;
604 		gtk_tree_model_get (model, &iter, COLUMN_TRACK_INFO, &track, -1);
605 		n = track->number;
606 		track_info_unref (track);
607 		if (n == track_number)
608 			return pos;
609 		pos++;
610 	} while (gtk_tree_model_iter_next (model, &iter));
611 
612 	return -1;
613 }
614 
615 
616 static void
add_selected_track(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)617 add_selected_track (GtkTreeModel *model,
618 		    GtkTreePath  *path,
619 		    GtkTreeIter  *iter,
620 		    gpointer      data)
621 {
622 	GList    **list = data;
623 	TrackInfo  *track;
624 
625 	gtk_tree_model_get (model, iter, COLUMN_TRACK_INFO, &track, -1);
626 	*list = g_list_prepend (*list, track);
627 }
628 
629 
630 static gboolean
set_selected_track_if_unique(GooWindow * window,int * n_track)631 set_selected_track_if_unique (GooWindow *window,
632 			      int       *n_track)
633 {
634 	GtkTreeSelection *list_selection;
635 	GList            *tracks;
636 	gboolean          result;
637 
638 	if (window->priv->album->tracks == NULL)
639 		return FALSE;
640 
641 	list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
642 	if (list_selection == NULL)
643 		return FALSE;
644 
645 	tracks = NULL;
646 	gtk_tree_selection_selected_foreach (list_selection, add_selected_track, &tracks);
647 
648 	if ((tracks != NULL) && (tracks->next == NULL)) {
649 		TrackInfo *track;
650 
651 		track = tracks->data;
652 		*n_track = track->number;
653 		result = TRUE;
654 	}
655 	else
656 		result = FALSE;
657 
658 	track_list_free (tracks);
659 
660 	return result;
661 }
662 
663 
664 static void
create_playlist(GooWindow * window,gboolean play_all,gboolean shuffle)665 create_playlist (GooWindow *window,
666 		 gboolean   play_all,
667 		 gboolean   shuffle)
668 {
669 
670 	GList *playlist;
671 	int    selected_track_number;
672 	int    pos;
673 
674 	debug (DEBUG_INFO, "PLAY ALL: %d\n", play_all);
675 	debug (DEBUG_INFO, "SHUFFLE: %d\n", shuffle);
676 
677 	if (window->priv->playlist != NULL)
678 		g_list_free (window->priv->playlist);
679 	window->priv->playlist = NULL;
680 
681 	playlist = NULL;
682 
683 	if (window->priv->current_track != NULL) {
684 
685 		/* ignore the currently playing track */
686 
687 		selected_track_number = window->priv->current_track->number;
688 		pos = get_position_from_track_number (window, window->priv->current_track->number);
689 	}
690 	else if (set_selected_track_if_unique (window, &selected_track_number)) {
691 
692 		/* put the selected track first */
693 
694 		playlist = g_list_prepend (playlist, GINT_TO_POINTER (selected_track_number));
695 		pos = get_position_from_track_number (window, selected_track_number);
696 	}
697 	else {
698 		selected_track_number = -1;
699 		pos = 0;
700 	}
701 
702 	if (play_all) {
703 		int i;
704 
705 		for (i = 0;
706 		     i < window->priv->album->n_tracks;
707 		     i++, pos = (pos + 1) % window->priv->album->n_tracks)
708 		{
709 			int track_number;
710 
711 			track_number = get_track_number_from_position (window, pos);
712 			if (track_number == selected_track_number)
713 				/* skip the selected track */
714 				continue;
715 
716 			playlist = g_list_prepend (playlist, GINT_TO_POINTER (track_number));
717 		}
718 
719 		playlist = g_list_reverse (playlist);
720 
721 		if (shuffle) {
722 			GRand *grand = g_rand_new ();
723 			GList *random_list = NULL;
724 			int    len = g_list_length (playlist);
725 
726 			while (playlist != NULL) {
727 				GList *item;
728 
729 				pos = g_rand_int_range (grand, 0, len--);
730 				item = g_list_nth (playlist, pos);
731 				playlist = g_list_remove_link (playlist, item);
732 				random_list = g_list_concat (random_list, item);
733 			}
734 			playlist = random_list;
735 
736 			g_rand_free (grand);
737 		}
738 	}
739 
740 	window->priv->playlist = playlist;
741 
742 	print_playlist (window);
743 }
744 
745 
746 static void
update_playlist_after_started_next(GooWindow * window)747 update_playlist_after_started_next (GooWindow *window)
748 {
749 	GList *current;
750 
751 	if (window->priv->playlist == NULL)
752 		return;
753 
754 	current = window->priv->playlist;
755 	window->priv->playlist = g_list_remove_link (window->priv->playlist, current);
756 
757 	g_list_free (current);
758 }
759 
760 
761 static void
update_next_track_to_play(GooWindow * window)762 update_next_track_to_play (GooWindow *window)
763 {
764 	GList *track;
765 	int    next_track_to_play;
766 
767 	for (track = window->priv->playlist; track; track = track->next)
768 		if (GPOINTER_TO_INT (track->data) == goo_player_get_current_track (window->priv->player))
769 			break;
770 
771 	if (track != NULL) {
772 		if (track->next != NULL)
773 			next_track_to_play = GPOINTER_TO_INT (track->next->data);
774 		else
775 			next_track_to_play = -1;
776 	}
777 	else if (window->priv->playlist != NULL)
778 		next_track_to_play = GPOINTER_TO_INT (window->priv->playlist->data);
779 	else
780 		next_track_to_play = -1;
781 
782 	goo_player_set_next_track (window->priv->player, next_track_to_play);
783 }
784 
785 
786 static void
play_track(GooWindow * window,int track_to_play)787 play_track (GooWindow *window,
788 	    int        track_to_play)
789 {
790 	goo_player_seek_track (window->priv->player, track_to_play);
791 	update_next_track_to_play (window);
792 }
793 
794 
795 static gboolean
play_next_track_in_playlist(GooWindow * window)796 play_next_track_in_playlist (GooWindow *window)
797 {
798 	gboolean  play_all;
799 	gboolean  shuffle;
800 	gboolean  repeat;
801 	GList    *next = NULL;
802 
803 	play_all = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_PLAYALL);
804 	shuffle = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_SHUFFLE);
805 	repeat = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_REPEAT);
806 
807 	next = window->priv->playlist;
808 
809 	if ((next == NULL) && repeat) {
810 		track_info_unref (window->priv->current_track);
811 		window->priv->current_track = NULL;
812 		create_playlist (window, play_all, shuffle);
813 		next = window->priv->playlist;
814 	}
815 
816 	if (next != NULL) {
817 		int pos;
818 
819 		pos = GPOINTER_TO_INT (next->data);
820 		window->priv->playlist = g_list_remove_link (window->priv->playlist, next);
821 		g_list_free (next);
822 
823 		play_track (window, pos);
824 	}
825 	else
826 		goo_window_stop (window);
827 
828 	return (next != NULL);
829 }
830 
831 
832 static void
pref_playlist_playall_changed(GSettings * settings,const char * key,gpointer user_data)833 pref_playlist_playall_changed (GSettings  *settings,
834 	  	 	       const char *key,
835 	  	 	       gpointer    user_data)
836 {
837 	GooWindow *window = user_data;
838 	gboolean   play_all;
839 	gboolean   shuffle;
840 
841 	g_return_if_fail (window != NULL);
842 
843 	play_all = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_PLAYALL);
844 	shuffle = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_SHUFFLE);
845 	create_playlist (window, play_all, shuffle);
846 	update_next_track_to_play (window);
847 
848 	window_update_sensitivity (window);
849 }
850 
851 
852 static void
pref_playlist_shuffle_changed(GSettings * settings,const char * key,gpointer user_data)853 pref_playlist_shuffle_changed (GSettings  *settings,
854 	  	 	       const char *key,
855 	  	 	       gpointer    user_data)
856 {
857 	GooWindow *window = user_data;
858 	gboolean   play_all;
859 	gboolean   shuffle;
860 
861 	g_return_if_fail (window != NULL);
862 
863 	play_all = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_PLAYALL);
864 	shuffle = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_SHUFFLE);
865 	create_playlist (window, play_all, shuffle);
866 	update_next_track_to_play (window);
867 }
868 
869 
870 static void
save_window_size(GooWindow * window)871 save_window_size (GooWindow *window)
872 {
873 	int width, height;
874 
875 	gtk_window_get_size (GTK_WINDOW (window), &width, &height);
876 	g_settings_set_int (window->priv->settings_ui, PREF_UI_WINDOW_WIDTH, width);
877 	g_settings_set_int (window->priv->settings_ui, PREF_UI_WINDOW_HEIGHT, height);
878 }
879 
880 
881 static void
goo_window_unrealize(GtkWidget * widget)882 goo_window_unrealize (GtkWidget *widget)
883 {
884 	GooWindow *window;
885 
886 	window = GOO_WINDOW (widget);
887 
888 	/* save ui preferences. */
889 
890 	if (gtk_widget_get_visible (window->priv->list_scrolled_window))
891 		save_window_size (window);
892 
893 	g_settings_set_enum (window->priv->settings_playlist, PREF_PLAYLIST_SORT_METHOD, window->priv->sort_method);
894 	g_settings_set_enum (window->priv->settings_playlist, PREF_PLAYLIST_SORT_TYPE, window->priv->sort_type);
895 
896 	GTK_WIDGET_CLASS (goo_window_parent_class)->unrealize (widget);
897 }
898 
899 
900 static gboolean
first_time_idle(gpointer callback_data)901 first_time_idle (gpointer callback_data)
902 {
903 	GooWindow *window = callback_data;
904 
905 	g_source_remove (window->priv->first_time_event);
906 	window->priv->first_time_event = 0;
907 	goo_player_update (window->priv->player);
908 
909 	return FALSE;
910 }
911 
912 
913 static void
goo_window_show(GtkWidget * widget)914 goo_window_show (GtkWidget *widget)
915 {
916 	GooWindow *window = GOO_WINDOW (widget);
917 
918 	if (! arg_toggle_visibility)
919 		GTK_WIDGET_CLASS (goo_window_parent_class)->show (widget);
920 	else
921 		arg_toggle_visibility = FALSE;
922 
923 	if (window->priv->first_time_event == 0)
924 		window->priv->first_time_event = g_timeout_add (IDLE_TIMEOUT, first_time_idle, window);
925 }
926 
927 
928 static void
goo_window_class_init(GooWindowClass * class)929 goo_window_class_init (GooWindowClass *class)
930 {
931 	GObjectClass   *gobject_class;
932 	GtkWidgetClass *widget_class;
933 
934 	gobject_class = (GObjectClass*) class;
935 	gobject_class->finalize = goo_window_finalize;
936 
937 	widget_class = (GtkWidgetClass*) class;
938 	widget_class->unrealize = goo_window_unrealize;
939 	widget_class->show = goo_window_show;
940 
941 	goo_window_signals[UPDATE_COVER] =
942                 g_signal_new ("update-cover",
943 			      G_TYPE_FROM_CLASS (class),
944 			      G_SIGNAL_RUN_LAST,
945 			      G_STRUCT_OFFSET (GooWindowClass, update_cover),
946 			      NULL, NULL,
947 			      goo_marshal_VOID__VOID,
948 			      G_TYPE_NONE,
949 			      0);
950 }
951 
952 
953 static gboolean
window_delete_event_cb(GtkWidget * caller,GdkEvent * event,GooWindow * window)954 window_delete_event_cb (GtkWidget  *caller,
955 			GdkEvent   *event,
956 			GooWindow  *window)
957 {
958 	if (goo_player_get_state (window->priv->player) == GOO_PLAYER_STATE_PLAYING)
959 		gtk_window_iconify (GTK_WINDOW (window));
960 	else
961 		goo_window_close (GOO_WINDOW (window));
962 
963 	return TRUE;
964 }
965 
966 
967 static int
sort_by_number(int i1,int i2)968 sort_by_number (int i1,
969 		int i2)
970 {
971 	if (i1 == i2)
972 		return 0;
973 	else if (i1 > i2)
974 		return 1;
975 	else
976 		return -1;
977 }
978 
979 
980 static int
sort_by_name(const char * s1,const char * s2)981 sort_by_name (const char *s1,
982               const char *s2)
983 {
984 	if ((s1 == NULL) && (s2 == NULL))
985 		return 0;
986 	else if (s1 == NULL)
987 		return 1;
988 	else if (s2 == NULL)
989 		return -1;
990 	else
991 		return g_utf8_collate (s1, s2);
992 }
993 
994 
995 static int
title_column_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)996 title_column_sort_func (GtkTreeModel *model,
997 			GtkTreeIter  *a,
998 			GtkTreeIter  *b,
999 			gpointer      user_data)
1000 {
1001 	TrackInfo *track1 = NULL, *track2 = NULL;
1002 	int       retval = -1;
1003 
1004         gtk_tree_model_get (model, a, COLUMN_TRACK_INFO, &track1, -1);
1005         gtk_tree_model_get (model, b, COLUMN_TRACK_INFO, &track2, -1);
1006 
1007 	if ((track1 == NULL) && (track2 == NULL))
1008 		retval = 0;
1009 	else if (track1 == NULL)
1010 		retval = 1;
1011 	else if (track2 == NULL)
1012 		retval = -1;
1013 	else
1014 		retval = sort_by_name (track1->title, track2->title);
1015 
1016 	track_info_unref (track1);
1017 	track_info_unref (track2);
1018 
1019 	return retval;
1020 }
1021 
1022 
1023 static int
artist_column_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)1024 artist_column_sort_func (GtkTreeModel *model,
1025 			 GtkTreeIter  *a,
1026 			 GtkTreeIter  *b,
1027 			 gpointer      user_data)
1028 {
1029 	TrackInfo *track1 = NULL, *track2 = NULL;
1030 	int       retval = -1;
1031 
1032         gtk_tree_model_get (model, a, COLUMN_TRACK_INFO, &track1, -1);
1033         gtk_tree_model_get (model, b, COLUMN_TRACK_INFO, &track2, -1);
1034 
1035 	if ((track1 == NULL) && (track2 == NULL))
1036 		retval = 0;
1037 	else if (track1 == NULL)
1038 		retval = 1;
1039 	else if (track2 == NULL)
1040 		retval = -1;
1041 	else {
1042 		int result = sort_by_name (track1->artist, track2->artist);
1043 		if (result == 0)
1044 			retval = sort_by_number (track1->number, track2->number);
1045 		else
1046 			retval = result;
1047 	}
1048 
1049 	track_info_unref (track1);
1050 	track_info_unref (track2);
1051 
1052 	return retval;
1053 }
1054 
1055 
1056 static int
time_column_sort_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer user_data)1057 time_column_sort_func (GtkTreeModel *model,
1058 		       GtkTreeIter  *a,
1059 		       GtkTreeIter  *b,
1060 		       gpointer      user_data)
1061 {
1062 	TrackInfo *track1 = NULL, *track2 = NULL;
1063 	int       retval = -1;
1064 
1065         gtk_tree_model_get (model, a, COLUMN_TRACK_INFO, &track1, -1);
1066         gtk_tree_model_get (model, b, COLUMN_TRACK_INFO, &track2, -1);
1067 
1068 	if ((track1 == NULL) && (track2 == NULL))
1069 		retval = 0;
1070 	else if (track1 == NULL)
1071 		retval = 1;
1072 	else if (track2 == NULL)
1073 		retval = -1;
1074 	else
1075 		retval = sort_by_number (track1->length, track2->length);
1076 
1077 	track_info_unref (track1);
1078 	track_info_unref (track2);
1079 
1080 	return retval;
1081 }
1082 
1083 
1084 static char *
get_action_name(GooPlayerAction action)1085 get_action_name (GooPlayerAction action)
1086 {
1087 	char *name;
1088 
1089 	switch (action) {
1090 	case GOO_PLAYER_ACTION_NONE:
1091 		name = "NONE";
1092 		break;
1093 	case GOO_PLAYER_ACTION_LIST:
1094 		name = "LIST";
1095 		break;
1096 	case GOO_PLAYER_ACTION_SEEK_SONG:
1097 		name = "SEEK_SONG";
1098 		break;
1099 	case GOO_PLAYER_ACTION_SEEK:
1100 		name = "SEEK";
1101 		break;
1102 	case GOO_PLAYER_ACTION_PLAY:
1103 		name = "PLAY";
1104 		break;
1105 	case GOO_PLAYER_ACTION_PAUSE:
1106 		name = "PAUSE";
1107 		break;
1108 	case GOO_PLAYER_ACTION_STOP:
1109 		name = "STOP";
1110 		break;
1111 	case GOO_PLAYER_ACTION_MEDIUM_ADDED:
1112 		name = "MEDIUM ADDED";
1113 		break;
1114 	case GOO_PLAYER_ACTION_MEDIUM_REMOVED:
1115 		name = "MEDIUM REMOVED";
1116 		break;
1117 	case GOO_PLAYER_ACTION_UPDATE:
1118 		name = "UPDATE";
1119 		break;
1120 	case GOO_PLAYER_ACTION_METADATA:
1121 		name = "METADATA";
1122 		break;
1123 	case GOO_PLAYER_ACTION_STARTED_NEXT:
1124 		name = "STARTED_NEXT";
1125 		break;
1126 	default:
1127 		name = "???";
1128 		break;
1129 	}
1130 
1131 	return name;
1132 }
1133 
1134 
1135 static gboolean
notify_current_state_cb(gpointer user_data)1136 notify_current_state_cb (gpointer user_data)
1137 {
1138 	GooWindow      *window = user_data;
1139 	GString        *info;
1140 	GooPlayerState  state;
1141 
1142 	if (window->priv->notify_event != 0) {
1143 		g_source_remove (window->priv->notify_event);
1144 		window->priv->notify_event = 0;
1145 	}
1146 
1147 	state = goo_player_get_state (window->priv->player);
1148 
1149 	if ((state == GOO_PLAYER_STATE_ERROR) || (state == GOO_PLAYER_STATE_NO_DISC)) {
1150 		system_notify (window, "no-disc", _("No disc"), NULL);
1151 		return FALSE;
1152 	}
1153 
1154 	if (state == GOO_PLAYER_STATE_DATA_DISC) {
1155 		system_notify (window, "data-disc", _("Data disc"), NULL);
1156 		return FALSE;
1157 	}
1158 
1159 	if (window->priv->album == NULL) {
1160 		system_notify (window, "no-album", "", NULL);
1161 		return FALSE;
1162 	}
1163 
1164 	if (window->priv->current_track == NULL) {
1165 		if ((window->priv->album != NULL) && (window->priv->album->title != NULL))
1166 			system_notify (window, "new-album", window->priv->album->title , NULL);
1167 		else
1168 			system_notify (window, "audio-cd", _("Audio CD"), NULL);
1169 		return FALSE;
1170 	}
1171 
1172 	info = g_string_new ("");
1173 
1174 	if (window->priv->album->artist != NULL) {
1175 		char *e_artist = g_markup_escape_text (window->priv->album->artist, -1);
1176 
1177 		g_string_append_printf (info, "%s", e_artist);
1178 		g_free (e_artist);
1179 	}
1180 
1181 	if (window->priv->album->title != NULL) {
1182 		g_string_append (info, " - ");
1183 		g_string_append_printf (info, "%s", window->priv->album->title);
1184 	}
1185 
1186 	if (goo_player_get_state (goo_window_get_player (window)) == GOO_PLAYER_STATE_PLAYING)
1187 		system_notify (window,
1188 			       "new-track",
1189 			       window->priv->current_track->title,
1190 			       (strlen (info->str) > 0) ? info->str : NULL);
1191 
1192 	g_string_free (info, TRUE);
1193 
1194 	return FALSE;
1195 }
1196 
1197 
1198 static void
notify_current_state(GooWindow * window,GooPlayerAction action)1199 notify_current_state (GooWindow       *window,
1200 		      GooPlayerAction  action)
1201 {
1202 	if (window->priv->notify_event != 0)
1203 		g_source_remove (window->priv->notify_event);
1204 	window->priv->notify_event = g_idle_add (notify_current_state_cb, window);
1205 }
1206 
1207 
1208 static void
goo_window_show_message_bar_action(GooWindow * window,const char * message)1209 goo_window_show_message_bar_action (GooWindow  *window,
1210 				    const char *message)
1211 {
1212 	gtk_label_set_text (GTK_LABEL (window->priv->message_label), message);
1213 	gtk_info_bar_set_message_type (GTK_INFO_BAR (window->priv->message_bar), GTK_MESSAGE_INFO);
1214 	gtk_widget_hide (window->priv->message_bar_properties_button);
1215 	gtk_widget_show (window->priv->message_bar);
1216 }
1217 
1218 
1219 static void
goo_window_show_message_bar_result(GooWindow * window,const char * message,GtkMessageType message_type)1220 goo_window_show_message_bar_result (GooWindow      *window,
1221 				    const char     *message,
1222 				    GtkMessageType  message_type)
1223 {
1224 	gtk_label_set_text (GTK_LABEL (window->priv->message_label), message);
1225 	gtk_info_bar_set_message_type (GTK_INFO_BAR (window->priv->message_bar), message_type);
1226 	gtk_widget_hide (window->priv->message_bar_properties_button);
1227 	gtk_widget_show (window->priv->message_bar);
1228 }
1229 
1230 
1231 static void
goo_window_hide_message_bar(GooWindow * window)1232 goo_window_hide_message_bar (GooWindow *window)
1233 {
1234 	gtk_widget_hide (window->priv->message_bar);
1235 }
1236 
1237 
1238 static void
player_start_cb(GooPlayer * player,GooPlayerAction action,GooWindow * window)1239 player_start_cb (GooPlayer       *player,
1240 		 GooPlayerAction  action,
1241 		 GooWindow       *window)
1242 {
1243 	debug (DEBUG_INFO, "START [%s]\n", get_action_name (action));
1244 
1245 	switch (action) {
1246 	case GOO_PLAYER_ACTION_PLAY:
1247 		notify_current_state (window, action);
1248 		break;
1249 
1250 	case GOO_PLAYER_ACTION_METADATA:
1251 		goo_window_show_message_bar_action (window, _("Searching disc information…"));
1252 		break;
1253 
1254 	default:
1255 		break;
1256 	}
1257 }
1258 
1259 
1260 static void
goo_window_select_current_track(GooWindow * window)1261 goo_window_select_current_track (GooWindow *window)
1262 {
1263 	GtkTreeSelection *selection;
1264 	GtkTreePath      *path;
1265 	int               pos;
1266 
1267 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
1268 	gtk_tree_selection_unselect_all (selection);
1269 
1270 	pos = get_position_from_track_number (window, window->priv->current_track->number);
1271 	if (pos == -1)
1272 		return;
1273 
1274 	path = gtk_tree_path_new_from_indices (pos, -1);
1275 	gtk_tree_selection_select_path (selection, path);
1276 	gtk_tree_path_free (path);
1277 }
1278 
1279 
1280 static gboolean
next_time_idle(gpointer callback_data)1281 next_time_idle (gpointer callback_data)
1282 {
1283 	GooWindow *window = callback_data;
1284 
1285 	if (window->priv->next_timeout_handle != 0) {
1286 		g_source_remove (window->priv->next_timeout_handle);
1287 		window->priv->next_timeout_handle = 0;
1288 	}
1289 
1290 	play_next_track_in_playlist (window);
1291 
1292 	return FALSE;
1293 }
1294 
1295 
1296 static void
goo_window_set_current_track(GooWindow * window,int n)1297 goo_window_set_current_track (GooWindow *window,
1298 			      int        n)
1299 {
1300 	if (window->priv->hibernate)
1301 		return;
1302 
1303 	if (window->priv->current_track != NULL) {
1304 		set_current_track_icon (window, NULL);
1305 		track_info_unref (window->priv->current_track);
1306 		window->priv->current_track = NULL;
1307 	}
1308 
1309 	window->priv->current_track = album_info_get_track (window->priv->album, n);
1310 }
1311 
1312 
1313 static void
window_update_title(GooWindow * window)1314 window_update_title (GooWindow *window)
1315 {
1316 	goo_player_progress_set_title (GOO_PLAYER_PROGRESS (window->priv->progress), _("CD Player"));
1317 
1318 #if 0
1319 	GooPlayerState  state;
1320 	GString        *title;
1321 
1322 	title = g_string_new ("");
1323 
1324 	state = goo_player_get_state (window->priv->player);
1325 	switch (state) {
1326 	case GOO_PLAYER_STATE_NO_DISC:
1327 	case GOO_PLAYER_STATE_DATA_DISC:
1328 	case GOO_PLAYER_STATE_ERROR:
1329 		g_string_append (title, _("CD Player"));
1330 		break;
1331 
1332 	default:
1333 		if ((window->priv->album != NULL) && (window->priv->album->title != NULL))
1334 			g_string_append (title, window->priv->album->title);
1335 		else
1336 			g_string_append (title, _("Audio CD"));
1337 		break;
1338 	}
1339 
1340 	goo_player_progress_set_title (GOO_PLAYER_PROGRESS (window->priv->progress), title->str);
1341 
1342 	g_string_free (title, TRUE);
1343 #endif
1344 }
1345 
1346 
1347 char *
goo_window_get_cover_filename(GooWindow * window)1348 goo_window_get_cover_filename (GooWindow *window)
1349 {
1350 	const char *discid;
1351 	char       *filename;
1352 	char       *path;
1353 
1354 	discid = goo_player_get_discid (window->priv->player);
1355 	if (discid == NULL)
1356 		return NULL;
1357 
1358 	filename = g_strconcat (discid, ".png", NULL);
1359 	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
1360 	path = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
1361 
1362 	g_free (filename);
1363 
1364 	return path;
1365 }
1366 
1367 
1368 void
goo_window_update_cover(GooWindow * window)1369 goo_window_update_cover (GooWindow *window)
1370 {
1371 	g_signal_emit (G_OBJECT (window),
1372 		       goo_window_signals[UPDATE_COVER],
1373 		       0,
1374 		       NULL);
1375 }
1376 
1377 
1378 static void
save_config_file(GKeyFile * kv_file,char * config_filename)1379 save_config_file (GKeyFile *kv_file,
1380 		  char     *config_filename)
1381 {
1382 	GFile *file;
1383 	char  *buffer;
1384 	gsize  buffer_size;
1385 
1386 	file = g_file_new_for_path (config_filename);
1387 	buffer = g_key_file_to_data (kv_file,
1388 				     &buffer_size,
1389 				     NULL);
1390 	if (buffer != NULL)
1391 		g_write_file (file,
1392 			      FALSE,
1393 			      G_FILE_CREATE_NONE,
1394 			      buffer,
1395 			      buffer_size,
1396 			      NULL,
1397 			      NULL);
1398 
1399 	g_free (buffer);
1400 	g_object_unref (file);
1401 }
1402 
1403 
1404 void
goo_window_set_current_cd_autofetch(GooWindow * window,gboolean autofetch)1405 goo_window_set_current_cd_autofetch (GooWindow *window,
1406 				     gboolean   autofetch)
1407 {
1408 	GKeyFile   *kv_file;
1409 	char       *config_filename;
1410 	const char *discid;
1411 
1412 	kv_file = g_key_file_new ();
1413 
1414 	config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
1415 	g_key_file_load_from_file (kv_file,
1416 				   config_filename,
1417 				   G_KEY_FILE_NONE,
1418 				   NULL);
1419 	discid = goo_player_get_discid (window->priv->player);
1420 	g_key_file_set_boolean (kv_file,
1421 				CONFIG_KEY_AUTOFETCH_GROUP,
1422 				discid,
1423 				autofetch);
1424 
1425 	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
1426 	save_config_file (kv_file, config_filename);
1427 
1428 	g_free (config_filename);
1429 	g_key_file_free (kv_file);
1430 }
1431 
1432 
1433 gboolean
goo_window_get_current_cd_autofetch(GooWindow * window)1434 goo_window_get_current_cd_autofetch (GooWindow *window)
1435 {
1436 	gboolean  autofetch = TRUE;
1437 	GKeyFile *kv_file;
1438 	char     *config_filename;
1439 
1440 	kv_file = g_key_file_new ();
1441 	config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
1442 	if (g_key_file_load_from_file (kv_file,
1443 				       config_filename,
1444 				       G_KEY_FILE_NONE,
1445 				       NULL))
1446 	{
1447 		const char *discid;
1448 		GError     *error = NULL;
1449 
1450 		discid = goo_player_get_discid (window->priv->player);
1451 		autofetch = g_key_file_get_boolean (kv_file,
1452 					            CONFIG_KEY_AUTOFETCH_GROUP,
1453 					            discid,
1454 					            &error);
1455 		if (error != NULL) {
1456 			autofetch = TRUE;
1457 			g_error_free (error);
1458 		}
1459 	}
1460 
1461 	g_free (config_filename);
1462 	g_key_file_free (kv_file);
1463 
1464 	return autofetch;
1465 }
1466 
1467 
1468 static void
auto_fetch_cover_image(GooWindow * window)1469 auto_fetch_cover_image (GooWindow *window)
1470 {
1471 	char *filename;
1472 
1473 	if (window->priv->hibernate)
1474 		return;
1475 
1476 	if (! goo_window_get_current_cd_autofetch (window))
1477 		return;
1478 	goo_window_set_current_cd_autofetch (window, FALSE);
1479 
1480 	filename = goo_window_get_cover_filename (window);
1481 	if (filename == NULL)
1482 		return;
1483 
1484 	if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
1485 		g_free (filename);
1486 		return;
1487 	}
1488 	g_free (filename);
1489 
1490 	fetch_cover_image_from_album_info (window, window->priv->album, FETCH_COVER_STAGE_0, NULL);
1491 }
1492 
1493 
1494 static gboolean
autoplay_cb(gpointer data)1495 autoplay_cb (gpointer data)
1496 {
1497 	GooWindow *window = data;
1498 
1499 	if (window->priv->album->n_tracks > 0)
1500 		goo_window_play (window);
1501 
1502 	return FALSE;
1503 }
1504 
1505 
1506 static void
goo_window_update_album(GooWindow * window)1507 goo_window_update_album (GooWindow *window)
1508 {
1509 	album_info_unref (window->priv->album);
1510 	window->priv->album = album_info_copy (goo_player_get_album (window->priv->player));
1511 
1512 	gtk_tree_view_column_set_visible (window->priv->author_column,
1513 					  window->priv->album->various_artist);
1514 }
1515 
1516 
1517 static void
window_update_play_button_state(GooWindow * window)1518 window_update_play_button_state (GooWindow *window)
1519 {
1520 	gboolean playing = (goo_player_get_state (window->priv->player) == GOO_PLAYER_STATE_PLAYING);
1521 	gtk_button_set_image (GTK_BUTTON (window->priv->play_button),
1522 			      gtk_image_new_from_icon_name (playing ? GOO_ICON_NAME_PAUSE : GOO_ICON_NAME_PLAY, GTK_ICON_SIZE_MENU));
1523 }
1524 
1525 
1526 static void
player_done_cb(GooPlayer * player,GooPlayerAction action,GError * error,GooWindow * window)1527 player_done_cb (GooPlayer       *player,
1528 		GooPlayerAction  action,
1529 		GError          *error,
1530 		GooWindow       *window)
1531 {
1532 	debug (DEBUG_INFO, "DONE [%s]\n", get_action_name (action));
1533 
1534 	switch (action) {
1535 	case GOO_PLAYER_ACTION_LIST:
1536 		goo_window_update_album (window);
1537 		goo_window_update_list (window);
1538 		goo_window_update_cover (window);
1539 		window_update_title (window);
1540 		set_current_track_icon (window, NULL);
1541 		if (arg_auto_play || g_settings_get_boolean (window->priv->settings_general, PREF_GENERAL_AUTOPLAY)) {
1542 			arg_auto_play = FALSE;
1543 			g_timeout_add (AUTOPLAY_DELAY, autoplay_cb, window);
1544 		}
1545 		break;
1546 
1547 	case GOO_PLAYER_ACTION_METADATA:
1548 		goo_window_update_album (window);
1549 		goo_window_update_titles (window);
1550 		window_update_title (window);
1551 		if (window->priv->album->title == NULL) {
1552 			goo_window_show_message_bar_result (window, _("No information found for this disc"), GTK_MESSAGE_WARNING);
1553 			gtk_widget_show (window->priv->message_bar_properties_button);
1554 		}
1555 		else
1556 			goo_window_hide_message_bar (window);
1557 		notify_current_state (window, action);
1558 		auto_fetch_cover_image (window);
1559 		break;
1560 
1561 	case GOO_PLAYER_ACTION_SEEK_SONG:
1562 		goo_window_set_current_track (window, goo_player_get_current_track (window->priv->player));
1563 		goo_window_select_current_track (window);
1564 		set_current_track_icon (window, GOO_ICON_NAME_PLAY);
1565 		break;
1566 
1567 	case GOO_PLAYER_ACTION_PLAY:
1568 	case GOO_PLAYER_ACTION_STOP:
1569 	case GOO_PLAYER_ACTION_MEDIUM_REMOVED:
1570 		if (action == GOO_PLAYER_ACTION_PLAY) {
1571 			set_current_track_icon (window, GOO_ICON_NAME_PLAY);
1572 			window->priv->next_timeout_handle = g_idle_add (next_time_idle, window);
1573 		}
1574 		else if (action == GOO_PLAYER_ACTION_STOP)
1575 			set_current_track_icon (window, GOO_ICON_NAME_STOP);
1576 		notify_current_state (window, action);
1577 		window_update_play_button_state (window);
1578 		break;
1579 
1580 	case GOO_PLAYER_ACTION_PAUSE:
1581 		set_current_track_icon (window, GOO_ICON_NAME_PAUSE);
1582 		notify_current_state (window, action);
1583 		window_update_play_button_state (window);
1584 		break;
1585 
1586 	case GOO_PLAYER_ACTION_STARTED_NEXT:
1587 		notify_current_state (window, GOO_PLAYER_ACTION_PLAY);
1588 		update_playlist_after_started_next (window);
1589 		update_next_track_to_play (window);
1590 		break;
1591 
1592 	default:
1593 		break;
1594 	}
1595 }
1596 
1597 
1598 static void
player_state_changed_cb(GooPlayer * player,GooWindow * window)1599 player_state_changed_cb (GooPlayer *player,
1600 			 GooWindow *window)
1601 {
1602 	window_update_sensitivity (window);
1603 	window_update_title (window);
1604 	window_update_play_button_state (window);
1605 }
1606 
1607 
1608 static void
row_activated_cb(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)1609 row_activated_cb (GtkTreeView       *tree_view,
1610 		  GtkTreePath       *path,
1611 		  GtkTreeViewColumn *column,
1612 		  gpointer           data)
1613 {
1614 	GooWindow   *window = data;
1615 	GtkTreeIter  iter;
1616 	TrackInfo    *track;
1617 
1618 	if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store),
1619 				       &iter,
1620 				       path))
1621 		return;
1622 
1623 	gtk_tree_model_get (GTK_TREE_MODEL (window->priv->list_store), &iter,
1624 			    COLUMN_TRACK_INFO, &track,
1625 			    -1);
1626 
1627 	goo_window_stop (window);
1628 	goo_window_set_current_track (window, track->number);
1629 	goo_window_play (window);
1630 
1631 	track_info_unref (track);
1632 }
1633 
1634 
1635 static void
selection_changed_cb(GtkTreeSelection * selection,gpointer user_data)1636 selection_changed_cb (GtkTreeSelection *selection,
1637 		      gpointer          user_data)
1638 {
1639 	GooWindow *window = user_data;
1640 
1641 	window_update_sensitivity (window);
1642 	window_update_title (window);
1643 }
1644 
1645 
1646 static int
file_button_press_cb(GtkWidget * widget,GdkEventButton * event,gpointer data)1647 file_button_press_cb (GtkWidget      *widget,
1648 		      GdkEventButton *event,
1649 		      gpointer        data)
1650 {
1651 	GooWindow             *window = data;
1652 	GtkTreeSelection      *selection;
1653 
1654 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
1655 	if (selection == NULL)
1656 		return FALSE;
1657 
1658 	if (event->type != GDK_BUTTON_PRESS)
1659 		return FALSE;
1660 
1661 	if (event->button == 3) {
1662 		GtkTreePath *path;
1663 		GtkTreeIter iter;
1664 
1665 		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
1666 						   event->x, event->y,
1667 						   &path, NULL, NULL, NULL))
1668 		{
1669 			if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), &iter, path)) {
1670 				gtk_tree_path_free (path);
1671 				return FALSE;
1672 			}
1673 			gtk_tree_path_free (path);
1674 
1675 			if (! gtk_tree_selection_iter_is_selected (selection, &iter)) {
1676 				gtk_tree_selection_unselect_all (selection);
1677 				gtk_tree_selection_select_iter (selection, &iter);
1678 			}
1679 		}
1680 		else
1681 			gtk_tree_selection_unselect_all (selection);
1682 
1683 		gtk_menu_popup_at_pointer (GTK_MENU (window->priv->file_popup_menu), (GdkEvent *) event);
1684 		return TRUE;
1685 	}
1686 	else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) {
1687 		GtkTreePath *path;
1688 
1689 		if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
1690 						     event->x, event->y,
1691 						     &path, NULL, NULL, NULL))
1692 		{
1693 			gtk_tree_selection_unselect_all (selection);
1694 		}
1695 	}
1696 
1697 	return FALSE;
1698 }
1699 
1700 
1701 static int
get_column_from_sort_method(WindowSortMethod sort_method)1702 get_column_from_sort_method (WindowSortMethod sort_method)
1703 {
1704 	switch (sort_method) {
1705 	case WINDOW_SORT_BY_NUMBER: return COLUMN_NUMBER;
1706 	case WINDOW_SORT_BY_TIME: return COLUMN_TIME;
1707 	case WINDOW_SORT_BY_TITLE: return COLUMN_TITLE;
1708 	default:
1709 		break;
1710 	}
1711 
1712 	return COLUMN_NUMBER;
1713 }
1714 
1715 
1716 static int
get_sort_method_from_column(int column_id)1717 get_sort_method_from_column (int column_id)
1718 {
1719 	switch (column_id) {
1720 	case COLUMN_NUMBER: return WINDOW_SORT_BY_NUMBER;
1721 	case COLUMN_TIME: return WINDOW_SORT_BY_TIME;
1722 	case COLUMN_TITLE: return WINDOW_SORT_BY_TITLE;
1723 	default:
1724 		break;
1725 	}
1726 
1727 	return WINDOW_SORT_BY_NUMBER;
1728 }
1729 
1730 
1731 static void
sort_column_changed_cb(GtkTreeSortable * sortable,gpointer user_data)1732 sort_column_changed_cb (GtkTreeSortable *sortable,
1733 			gpointer         user_data)
1734 {
1735 	GooWindow      *window = user_data;
1736 	GtkSortType     order;
1737 	int             column_id;
1738 	GooPlayerState  state;
1739 
1740 	if (! gtk_tree_sortable_get_sort_column_id (sortable,
1741 						    &column_id,
1742 						    &order))
1743 	{
1744 		return;
1745 	}
1746 
1747 	window->priv->sort_method = get_sort_method_from_column (column_id);
1748 	window->priv->sort_type = order;
1749 
1750 	/* Recreate the playlist if not already playing. */
1751 
1752 	state = goo_player_get_state (window->priv->player);
1753 	if ((state != GOO_PLAYER_STATE_PLAYING) &&  (state != GOO_PLAYER_STATE_PAUSED))
1754 		create_playlist (window,
1755 				 g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_PLAYALL),
1756 				 g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_SHUFFLE));
1757 }
1758 
1759 
1760 static void
update_ui_from_expander_state(GooWindow * window)1761 update_ui_from_expander_state (GooWindow *window)
1762 {
1763 	GdkGeometry hints;
1764 
1765 	if (gtk_widget_get_realized (GTK_WIDGET (window)))
1766 		gtk_window_resize (GTK_WINDOW (window),
1767 				   g_settings_get_int (window->priv->settings_ui, PREF_UI_WINDOW_WIDTH),
1768 				   g_settings_get_int (window->priv->settings_ui, PREF_UI_WINDOW_HEIGHT));
1769 
1770 	hints.max_height = window->priv->resizable_playlist ? G_MAXINT : -1;
1771 	hints.max_width = G_MAXINT;
1772 	gtk_window_set_geometry_hints (GTK_WINDOW (window),
1773 				       GTK_WIDGET (window),
1774 				       &hints,
1775 				       GDK_HINT_MAX_SIZE);
1776 }
1777 
1778 
1779 static void
player_info_cover_clicked_cb(GooPlayerInfo * info,GooWindow * window)1780 player_info_cover_clicked_cb (GooPlayerInfo *info,
1781 			      GooWindow     *window)
1782 {
1783 	debug (DEBUG_INFO, "[Window] cover clicked\n");
1784 	gtk_menu_popup_at_widget (GTK_MENU (window->priv->cover_popup_menu),
1785 				  GTK_WIDGET (info),
1786 				  GDK_GRAVITY_SOUTH_WEST,
1787 				  GDK_GRAVITY_NORTH_WEST,
1788 				  NULL);
1789 }
1790 
1791 
1792 static void
player_info_update_status_cb(GooPlayerInfo * info,GooWindow * window)1793 player_info_update_status_cb (GooPlayerInfo *info,
1794 			      GooWindow     *window)
1795 {
1796 	BraseroDrive *drive;
1797 
1798 	debug (DEBUG_INFO, "[Window] update status\n");
1799 
1800 	drive = goo_player_get_drive (window->priv->player);
1801 	if (drive != NULL)
1802 		brasero_drive_reprobe (drive);
1803 }
1804 
1805 
1806 static void
window_sync_ui_with_preferences(GooWindow * window)1807 window_sync_ui_with_preferences (GooWindow *window)
1808 {
1809 	update_ui_from_expander_state (window);
1810 }
1811 
1812 
1813 static gboolean
window_key_press_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)1814 window_key_press_cb (GtkWidget   *widget,
1815 		     GdkEventKey *event,
1816 		     gpointer     data)
1817 {
1818 	GooWindow *window = data;
1819 	gboolean   retval = FALSE;
1820 	int        new_track = -1;
1821 
1822 	if (window->priv->album->n_tracks == 0)
1823 		return FALSE;
1824 
1825 	switch (event->keyval) {
1826 	case GDK_KEY_1:
1827 	case GDK_KEY_2:
1828 	case GDK_KEY_3:
1829 	case GDK_KEY_4:
1830 	case GDK_KEY_5:
1831 	case GDK_KEY_6:
1832 	case GDK_KEY_7:
1833 	case GDK_KEY_8:
1834 	case GDK_KEY_9:
1835 		new_track = event->keyval - GDK_KEY_1;
1836 		retval = TRUE;
1837 		break;
1838 	case GDK_KEY_0:
1839 		new_track = 10 - 1;
1840 		retval = TRUE;
1841 		break;
1842 	default:
1843 		break;
1844 	}
1845 
1846 	if ((new_track >= 0) && (new_track <= window->priv->album->n_tracks - 1)) {
1847 		goo_window_stop (window);
1848 		goo_window_set_current_track (window, new_track);
1849 		goo_window_play (window);
1850 	}
1851 
1852 
1853 	return retval;
1854 }
1855 
1856 
1857 /* -- fr_window_add_accelerators -- */
1858 
1859 
1860 static GtkAccelGroup *
goo_window_get_accel_group(GooWindow * window)1861 goo_window_get_accel_group (GooWindow *window)
1862 {
1863 	if (window->priv->accel_group == NULL) {
1864 		window->priv->accel_group = gtk_accel_group_new ();
1865 		gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accel_group);
1866 	}
1867 
1868 	return window->priv->accel_group;
1869 }
1870 
1871 
1872 static void
goo_window_add_accelerators(GooWindow * window,const _GtkAccelerator * accelerators,int n_accelerators)1873 goo_window_add_accelerators (GooWindow                *window,
1874 			     const _GtkAccelerator    *accelerators,
1875 			     int                       n_accelerators)
1876 {
1877 	GtkAccelGroup *accel_group;
1878 	int            i;
1879 
1880 	accel_group = goo_window_get_accel_group (window);
1881 	for (i = 0; i < n_accelerators; i++) {
1882 		const _GtkAccelerator *acc = accelerators + i;
1883 
1884 		_gtk_window_add_accelerator_for_action (GTK_WINDOW (window),
1885 						        accel_group,
1886 						        acc->action_name,
1887 						        acc->accelerator,
1888 						        NULL);
1889 	}
1890 }
1891 
1892 
1893 static void
window_size_allocate_cb(GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)1894 window_size_allocate_cb (GtkWidget    *widget,
1895 	                 GdkRectangle *allocation,
1896 	                 gpointer      user_data)
1897 {
1898 	GooWindow    *window = user_data;
1899 	GdkRectangle  geometry;
1900 	int           max_window_height;
1901 
1902 	if (window->priv->resizable_playlist)
1903 		return;
1904 
1905 	if (!_gtk_widget_get_monitor_geometry (GTK_WIDGET (window), &geometry))
1906 		return;
1907 
1908 	max_window_height = (float) geometry.height * MAX_WINDOW_HEIGHT_PERCENTAGE;
1909 	if (allocation->height > max_window_height) {
1910 		GdkGeometry hints;
1911 
1912 		hints.max_height = G_MAXINT;
1913 		hints.max_width = G_MAXINT;
1914 		gtk_window_set_geometry_hints (GTK_WINDOW (window),
1915 					       GTK_WIDGET (window),
1916 					       &hints,
1917 					       GDK_HINT_MAX_SIZE);
1918 
1919 		window->priv->resizable_playlist = TRUE;
1920 
1921 		gtk_window_resize (GTK_WINDOW (window),
1922 				   g_settings_get_int (window->priv->settings_ui, PREF_UI_WINDOW_WIDTH),
1923 				   max_window_height);
1924 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window->priv->list_scrolled_window),
1925 						GTK_POLICY_NEVER,
1926 						GTK_POLICY_AUTOMATIC);
1927 	}
1928 }
1929 
1930 
1931 static void
_gtk_menu_button_set_style_for_header_bar(GtkWidget * button)1932 _gtk_menu_button_set_style_for_header_bar (GtkWidget *button)
1933 {
1934 	GtkStyleContext *context;
1935 
1936 	gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
1937 	context = gtk_widget_get_style_context (button);
1938 	gtk_style_context_add_class (context, "image-button");
1939 	gtk_style_context_remove_class (context, "text-button");
1940 }
1941 
1942 
1943 static GtkWidget *
_gtk_image_button_new_for_header_bar(const char * icon_name)1944 _gtk_image_button_new_for_header_bar (const char *icon_name)
1945 	{
1946 	GtkWidget *button;
1947 
1948 	button = gtk_button_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
1949 	_gtk_menu_button_set_style_for_header_bar (button);
1950 
1951 	return button;
1952 }
1953 
1954 
1955 static void
goo_window_init(GooWindow * window)1956 goo_window_init (GooWindow *window)
1957 {
1958 	window->priv = goo_window_get_instance_private (window);
1959 	window->priv->exiting = FALSE;
1960 	window->priv->check_id = 0;
1961 	window->priv->url_list = NULL;
1962 	window->priv->hibernate = FALSE;
1963 	window->priv->album = album_info_new ();
1964 	window->priv->resizable_playlist = FALSE;
1965 	window->priv->accel_group = gtk_accel_group_new ();
1966 
1967 	gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accel_group);
1968 
1969 
1970 	g_action_map_add_action_entries (G_ACTION_MAP (window),
1971 					 goo_window_actions,
1972 					 G_N_ELEMENTS (goo_window_actions),
1973 					 window);
1974 	goo_window_add_accelerators (window,
1975 				     goo_window_accelerators,
1976 				     G_N_ELEMENTS (goo_window_accelerators));
1977 
1978 	gtk_window_set_application (GTK_WINDOW (window), Main_Application);
1979 
1980 	g_signal_connect (window,
1981 			  "size-allocate",
1982 			  G_CALLBACK (window_size_allocate_cb),
1983 			  window);
1984 }
1985 
1986 
1987 static void
media_player_key_pressed_cb(GDBusProxy * proxy,char * sender_name,char * signal_name,GVariant * parameters,gpointer user_data)1988 media_player_key_pressed_cb (GDBusProxy *proxy,
1989 			     char       *sender_name,
1990 			     char       *signal_name,
1991 			     GVariant   *parameters,
1992 			     gpointer    user_data)
1993 {
1994 	GooWindow *window = user_data;
1995 	char      *application;
1996 	char      *key;
1997 
1998 	if (g_strcmp0 (signal_name, "MediaPlayerKeyPressed") != 0)
1999 		return;
2000 
2001 	g_variant_get (parameters, "(ss)", &application, &key);
2002 
2003 	if (g_strcmp0 (application, PACKAGE_NAME) == 0) {
2004 		if (g_strcmp0 (key, "Play") == 0)
2005 			goo_window_toggle_play (window);
2006 		else if (g_strcmp0 (key, "Previous") == 0)
2007 			goo_window_prev (window);
2008 		else if (g_strcmp0 (key, "Next") == 0)
2009 			goo_window_next (window);
2010 		else if (g_strcmp0 (key, "Stop") == 0)
2011 			goo_window_stop (window);
2012 	}
2013 
2014 	g_free (application);
2015 	g_free (key);
2016 }
2017 
2018 
2019 static gboolean
window_focus_in_event_cb(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)2020 window_focus_in_event_cb (GtkWidget     *widget,
2021 			  GdkEventFocus *event,
2022 			  gpointer       user_data)
2023 {
2024 	GooWindow *window = user_data;
2025 
2026 	g_dbus_proxy_call_sync (window->priv->media_keys_proxy,
2027 				"GrabMediaPlayerKeys",
2028 				g_variant_new ("(su)", PACKAGE_NAME, 0),
2029 				G_DBUS_CALL_FLAGS_NONE,
2030 				-1,
2031 				NULL,
2032 				NULL);
2033 
2034 	return FALSE;
2035 }
2036 
2037 
2038 static void
_goo_window_enable_media_keys(GooWindow * window)2039 _goo_window_enable_media_keys (GooWindow *window)
2040 {
2041 	GDBusConnection *connection;
2042 
2043 	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
2044 	if (connection == NULL)
2045 		return;
2046 
2047 	window->priv->media_keys_proxy = g_dbus_proxy_new_sync (connection,
2048 								G_DBUS_PROXY_FLAGS_NONE,
2049 								NULL,
2050 								"org.gnome.SettingsDaemon",
2051 								"/org/gnome/SettingsDaemon/MediaKeys",
2052 								"org.gnome.SettingsDaemon.MediaKeys",
2053 								NULL,
2054 								NULL);
2055 	if (window->priv->media_keys_proxy == NULL)
2056 		window->priv->media_keys_proxy = g_dbus_proxy_new_sync (connection,
2057 									G_DBUS_PROXY_FLAGS_NONE,
2058 									NULL,
2059 									"org.gnome.SettingsDaemon",
2060 									"/org/gnome/SettingsDaemon",
2061 									"org.gnome.SettingsDaemon",
2062 									NULL,
2063 									NULL);
2064 	g_object_unref (connection);
2065 
2066 	if (window->priv->media_keys_proxy == NULL)
2067 		return;
2068 
2069 	g_dbus_proxy_call_sync (window->priv->media_keys_proxy,
2070 				"GrabMediaPlayerKeys",
2071 				g_variant_new ("(su)", PACKAGE_NAME, 0),
2072 				G_DBUS_CALL_FLAGS_NONE,
2073 				-1,
2074 				NULL,
2075 				NULL);
2076 
2077 	window->priv->media_key_event = g_signal_connect (window->priv->media_keys_proxy,
2078 							  "g-signal",
2079 							  G_CALLBACK (media_player_key_pressed_cb),
2080 							  window);
2081 	window->priv->focus_in_event = g_signal_connect (window,
2082 							 "focus-in-event",
2083 							 G_CALLBACK (window_focus_in_event_cb),
2084 							 window);
2085 }
2086 
2087 
2088 static void
message_bar_response_cb(GtkInfoBar * info_bar,int response_id,gpointer user_data)2089 message_bar_response_cb (GtkInfoBar *info_bar,
2090                 	 int         response_id,
2091                 	 gpointer    user_data)
2092 {
2093 	GooWindow *window = user_data;
2094 
2095 	switch (response_id) {
2096 	case GTK_RESPONSE_CLOSE:
2097 		goo_window_hide_message_bar (window);
2098 		break;
2099 	case MESSAGE_BAR_RESPONSE_PROPERTIES:
2100 		goo_window_activate_properties (NULL, NULL, window);
2101 		goo_window_hide_message_bar (window);
2102 		break;
2103 	}
2104 }
2105 
2106 
2107 static void
progress_skip_to_cb(GooPlayerProgress * progress,int seconds,GooWindow * window)2108 progress_skip_to_cb (GooPlayerProgress *progress,
2109 		     int                seconds,
2110 		     GooWindow         *window)
2111 {
2112 	debug (DEBUG_INFO, "[Window] skip to %d\n", seconds);
2113 	goo_player_skip_to (window->priv->player, (guint) seconds);
2114 }
2115 
2116 
2117 static void
volume_button_value_changed_cb(GtkScaleButton * button,gdouble value,gpointer user_data)2118 volume_button_value_changed_cb (GtkScaleButton *button,
2119 				gdouble         value,
2120 				gpointer        user_data)
2121 {
2122 	GooWindow *window = user_data;
2123 	goo_player_set_audio_volume (window->priv->player, value);
2124 }
2125 
2126 
2127 static void
goo_window_construct(GooWindow * window,BraseroDrive * drive)2128 goo_window_construct (GooWindow    *window,
2129 		      BraseroDrive *drive)
2130 {
2131 
2132 	GtkWidget        *scrolled_window;
2133 	GtkWidget        *vbox;
2134 	GtkWidget        *hbox;
2135 	GtkTreeSelection *selection;
2136 	GtkWidget        *headerbar;
2137 
2138 	gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (window)), "goobox-main-window");
2139 	gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (window)), GTK_STYLE_CLASS_VIEW);
2140 
2141 	g_signal_connect (G_OBJECT (window),
2142 			  "delete_event",
2143 			  G_CALLBACK (window_delete_event_cb),
2144 			  window);
2145 	g_signal_connect (G_OBJECT (window),
2146 			  "key_press_event",
2147 			  G_CALLBACK (window_key_press_cb),
2148 			  window);
2149 
2150 	if (icon_size == 0)
2151 		icon_size = get_icon_size_from_settings (GTK_ICON_SIZE_LARGE_TOOLBAR);
2152 
2153 	/* Create the settings objects */
2154 
2155 	window->priv->settings_general = g_settings_new (GOOBOX_SCHEMA_GENERAL);
2156 	window->priv->settings_ui = g_settings_new (GOOBOX_SCHEMA_UI);
2157 	window->priv->settings_playlist = g_settings_new (GOOBOX_SCHEMA_PLAYLIST);
2158 	window->priv->settings_encoder = g_settings_new (GOOBOX_SCHEMA_ENCODER);
2159 
2160 	/* Create the data */
2161 
2162 	window->priv->player = goo_player_new (drive);
2163 	goo_player_set_audio_volume (window->priv->player, g_settings_get_int (window->priv->settings_general, PREF_GENERAL_VOLUME));
2164 
2165 	g_signal_connect (window->priv->player,
2166 			  "start",
2167 			  G_CALLBACK (player_start_cb),
2168 			  window);
2169 	g_signal_connect (window->priv->player,
2170 			  "done",
2171 			  G_CALLBACK (player_done_cb),
2172 			  window);
2173 	g_signal_connect (window->priv->player,
2174 			  "state_changed",
2175 			  G_CALLBACK (player_state_changed_cb),
2176 			  window);
2177 
2178 	window->priv->playlist = NULL;
2179 
2180 	/* Create the widgets. */
2181 
2182 	/* * File list. */
2183 
2184 	window->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
2185 						       GOO_TYPE_TRACK_INFO,
2186 						       G_TYPE_INT,
2187 						       GDK_TYPE_PIXBUF,
2188 						       G_TYPE_STRING,
2189 						       G_TYPE_STRING,
2190 						       G_TYPE_STRING);
2191 	window->priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->list_store));
2192 
2193 	add_columns (window, GTK_TREE_VIEW (window->priv->list_view));
2194 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (window->priv->list_view), TRUE);
2195 	gtk_tree_view_set_search_column (GTK_TREE_VIEW (window->priv->list_view), COLUMN_TITLE);
2196 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (window->priv->list_view), FALSE);
2197 
2198 	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
2199 					 COLUMN_TITLE, title_column_sort_func,
2200 					 NULL, NULL);
2201 	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
2202 					 COLUMN_ARTIST, artist_column_sort_func,
2203 					 NULL, NULL);
2204 	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
2205 					 COLUMN_TIME, time_column_sort_func,
2206 					 NULL, NULL);
2207 
2208 	window->priv->sort_method = g_settings_get_enum (window->priv->settings_playlist, PREF_PLAYLIST_SORT_METHOD);
2209 	window->priv->sort_type = g_settings_get_enum (window->priv->settings_playlist, PREF_PLAYLIST_SORT_TYPE);
2210 
2211 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), get_column_from_sort_method (window->priv->sort_method), window->priv->sort_type);
2212 
2213 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
2214 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
2215 
2216 	g_signal_connect (G_OBJECT (window->priv->list_view),
2217                           "row_activated",
2218                           G_CALLBACK (row_activated_cb),
2219                           window);
2220 	g_signal_connect (selection,
2221                           "changed",
2222                           G_CALLBACK (selection_changed_cb),
2223                           window);
2224 	g_signal_connect (G_OBJECT (window->priv->list_view),
2225 			  "button_press_event",
2226 			  G_CALLBACK (file_button_press_cb),
2227 			  window);
2228 
2229 	g_signal_connect (G_OBJECT (window->priv->list_store),
2230 			  "sort_column_changed",
2231 			  G_CALLBACK (sort_column_changed_cb),
2232 			  window);
2233 
2234 	window->priv->list_scrolled_window = scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2235 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
2236 					GTK_POLICY_NEVER,
2237 					GTK_POLICY_NEVER);
2238 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
2239 	gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 15);
2240 	gtk_widget_set_hexpand (scrolled_window, TRUE);
2241 	gtk_container_add (GTK_CONTAINER (scrolled_window), window->priv->list_view);
2242 
2243         /* popup menus */
2244         {
2245 		GtkBuilder *builder;
2246 
2247 		builder = _gtk_builder_new_from_resource ("menus.ui");
2248 
2249 		window->priv->file_popup_menu = gtk_menu_new_from_model (G_MENU_MODEL (gtk_builder_get_object (builder, "track-list-popup")));
2250 		gtk_menu_attach_to_widget (GTK_MENU (window->priv->file_popup_menu), GTK_WIDGET (window), NULL);
2251 
2252 		window->priv->cover_popup_menu = gtk_menu_new_from_model (G_MENU_MODEL (gtk_builder_get_object (builder, "cover-popup")));
2253 		gtk_menu_attach_to_widget (GTK_MENU (window->priv->cover_popup_menu), GTK_WIDGET (window), NULL);
2254 
2255 		g_object_unref (builder);
2256         }
2257 
2258 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2259 
2260 	/* message bar */
2261 
2262 	window->priv->message_bar = gtk_info_bar_new ();
2263 	gtk_widget_set_no_show_all (window->priv->message_bar, TRUE);
2264 	window->priv->message_label = gtk_label_new ("");
2265 	gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (window->priv->message_bar))), window->priv->message_label);
2266 	window->priv->message_bar_properties_button = gtk_button_new_with_mnemonic (_("_Properties"));
2267 	gtk_info_bar_add_action_widget (GTK_INFO_BAR (window->priv->message_bar), window->priv->message_bar_properties_button, MESSAGE_BAR_RESPONSE_PROPERTIES);
2268 	gtk_info_bar_add_button (GTK_INFO_BAR (window->priv->message_bar), _GTK_LABEL_CLOSE, GTK_RESPONSE_CLOSE);
2269 	g_signal_connect (window->priv->message_bar,
2270 			  "response",
2271 	                  G_CALLBACK (message_bar_response_cb),
2272 	                  window);
2273 	gtk_widget_show (window->priv->message_label);
2274 	gtk_box_pack_start (GTK_BOX (vbox), window->priv->message_bar, FALSE, FALSE, 0);
2275 
2276 	/**/
2277 
2278 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2279 	gtk_widget_set_vexpand (hbox, FALSE);
2280 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 10);
2281 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
2282 
2283 	window->priv->info = goo_player_info_new (window);
2284 	gtk_container_set_border_width (GTK_CONTAINER (window->priv->info), 0);
2285 	g_signal_connect (window->priv->info,
2286 			  "cover_clicked",
2287 			  G_CALLBACK (player_info_cover_clicked_cb),
2288 			  window);
2289 	g_signal_connect (window->priv->info,
2290 			  "update-status",
2291 			  G_CALLBACK (player_info_update_status_cb),
2292 			  window);
2293 	gtk_box_pack_start (GTK_BOX (hbox), window->priv->info, TRUE, TRUE, 0);
2294 
2295 	/**/
2296 
2297 	gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
2298 
2299 	/**/
2300 
2301 	gtk_widget_show_all (vbox);
2302 	gtk_widget_hide (window->priv->list_scrolled_window);
2303 
2304 	gtk_container_add (GTK_CONTAINER (window), vbox);
2305 
2306 	gtk_widget_grab_focus (window->priv->list_view);
2307 
2308 	window_sync_ui_with_preferences (window);
2309 
2310 	gtk_window_set_default_size (GTK_WINDOW (window),
2311 				     g_settings_get_int (window->priv->settings_ui, PREF_UI_WINDOW_WIDTH),
2312 				     g_settings_get_int (window->priv->settings_ui, PREF_UI_WINDOW_HEIGHT));
2313 
2314 	headerbar = gtk_header_bar_new ();
2315 	gtk_widget_show (headerbar);
2316 	gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
2317 
2318 	/* play buttons */
2319 
2320 	{
2321 		GtkWidget *button;
2322 		GtkWidget *button_box;
2323 
2324 		window->priv->play_button = button = _gtk_image_button_new_for_header_bar (GOO_ICON_NAME_PLAY);
2325 		gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.toggle-play");
2326 		gtk_widget_show_all (button);
2327 		gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
2328 
2329 		button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2330 		gtk_style_context_add_class (gtk_widget_get_style_context (button_box), GTK_STYLE_CLASS_LINKED);
2331 		gtk_widget_show (button_box);
2332 		gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button_box);
2333 
2334 		button = _gtk_image_button_new_for_header_bar (GOO_ICON_NAME_PREV);
2335 		gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.previous-track");
2336 		gtk_widget_show_all (button);
2337 		gtk_box_pack_start (GTK_BOX (button_box), button, FALSE, FALSE, 0);
2338 
2339 		button = _gtk_image_button_new_for_header_bar (GOO_ICON_NAME_NEXT);
2340 		gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.next-track");
2341 		gtk_widget_show_all (button);
2342 		gtk_box_pack_start (GTK_BOX (button_box), button, FALSE, FALSE, 0);
2343 
2344 		button = gtk_volume_button_new ();
2345 		gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL);
2346 		gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
2347 		gtk_scale_button_set_adjustment (GTK_SCALE_BUTTON (button),
2348 						 gtk_adjustment_new (0.0,
2349 								     0.0,
2350 								     100.0,
2351 								     1.0,
2352 								     10.0,
2353 								     0.0));
2354 		gtk_scale_button_set_value (GTK_SCALE_BUTTON (button), goo_player_get_audio_volume (window->priv->player));
2355 		gtk_widget_show (button);
2356 		gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), button);
2357 
2358 		g_signal_connect (button,
2359 				  "value-changed",
2360 				  G_CALLBACK (volume_button_value_changed_cb),
2361 				  window);
2362 	}
2363 
2364 	/* gears menu button */
2365 
2366 	{
2367 		GtkBuilder *builder;
2368 		GMenuModel *menu;
2369 		GtkWidget  *button;
2370 
2371 		builder = _gtk_builder_new_from_resource ("gears-menu.ui");
2372 		menu = G_MENU_MODEL (gtk_builder_get_object (builder, "gears-menu"));
2373 		button = gtk_menu_button_new ();
2374 		_gtk_menu_button_set_style_for_header_bar (button);
2375 		gtk_menu_button_set_direction (GTK_MENU_BUTTON (button), GTK_ARROW_NONE);
2376 		gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (button), menu);
2377 		gtk_menu_button_set_use_popover (GTK_MENU_BUTTON (button), FALSE);
2378 		gtk_widget_show_all (button);
2379 		gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), button);
2380 
2381 		_gtk_window_add_accelerators_from_menu ((GTK_WINDOW (window)), menu);
2382 
2383 		g_object_unref (builder);
2384 	}
2385 
2386 	/* extract button */
2387 
2388 	{
2389 		GtkWidget *button;
2390 
2391 		button = _gtk_image_button_new_for_header_bar (GOO_ICON_NAME_EXTRACT);
2392 		gtk_widget_set_tooltip_text (button, _("Extract Tracks"));
2393 		gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.extract");
2394 		gtk_widget_show_all (button);
2395 		gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), button);
2396 	}
2397 
2398 	/* custom title */
2399 
2400 	window->priv->progress = goo_player_progress_new (window->priv->player);
2401 	gtk_widget_show (window->priv->progress);
2402 	gtk_header_bar_set_custom_title (GTK_HEADER_BAR (headerbar), window->priv->progress);
2403 
2404 	g_signal_connect (window->priv->progress,
2405 			  "skip-to",
2406 			  G_CALLBACK (progress_skip_to_cb),
2407 			  window);
2408 
2409 	gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
2410 
2411 	goo_player_progress_set_title (GOO_PLAYER_PROGRESS (window->priv->progress), _("CD Player"));
2412 
2413 	/* Add notification callbacks. */
2414 
2415 	g_signal_connect (window->priv->settings_playlist,
2416 			  "changed::" PREF_PLAYLIST_PLAYALL,
2417 			  G_CALLBACK (pref_playlist_playall_changed),
2418 			  window);
2419 	g_signal_connect (window->priv->settings_playlist,
2420 			  "changed::" PREF_PLAYLIST_SHUFFLE,
2421 			  G_CALLBACK (pref_playlist_shuffle_changed),
2422 			  window);
2423 
2424 	/* Media keys*/
2425 
2426 	_goo_window_enable_media_keys (window);
2427 }
2428 
2429 
2430 GtkWidget *
goo_window_new(BraseroDrive * drive)2431 goo_window_new (BraseroDrive *drive)
2432 {
2433 	GooWindow *window;
2434 
2435 	if (drive == NULL) {
2436 		GSettings *settings;
2437 		char      *default_device;
2438 
2439 		settings = g_settings_new (GOOBOX_SCHEMA_GENERAL);
2440 		default_device = g_settings_get_string (settings, PREF_GENERAL_DEVICE);
2441 		g_object_unref (settings);
2442 
2443 		if (default_device != NULL)
2444 			drive = main_get_drive_for_device (default_device);
2445 		if (drive == NULL)
2446 			drive = main_get_most_likely_drive ();
2447 
2448 		g_free (default_device);
2449 	}
2450 
2451 	window = (GooWindow*) g_object_new (GOO_TYPE_WINDOW, NULL);
2452 	goo_window_construct (window, drive);
2453 
2454 	return (GtkWidget *) window;
2455 }
2456 
2457 
2458 /* -- goo_window_close -- */
2459 
2460 
2461 static gboolean
check_state_for_closing_cb(gpointer data)2462 check_state_for_closing_cb (gpointer data)
2463 {
2464 	GooWindow *self = data;
2465 
2466 	g_source_remove (self->priv->check_id);
2467 	self->priv->check_id = 0;
2468 
2469 	if (! goo_player_get_is_busy (self->priv->player)) {
2470 		gtk_widget_destroy (GTK_WIDGET (self));
2471 		return FALSE;
2472 	}
2473 
2474 	self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
2475 					      check_state_for_closing_cb,
2476 					      self);
2477 
2478 	return FALSE;
2479 }
2480 
2481 
2482 void
goo_window_close(GooWindow * base)2483 goo_window_close (GooWindow *base)
2484 {
2485 	GooWindow *self = GOO_WINDOW (base);
2486 
2487 	self->priv->exiting = TRUE;
2488 	if (goo_player_get_is_busy (self->priv->player)) {
2489 		gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
2490 
2491 		if (self->priv->check_id != 0)
2492 			g_source_remove (self->priv->check_id);
2493 		self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
2494 						      check_state_for_closing_cb,
2495 						      self);
2496 	}
2497 	else
2498 		gtk_widget_destroy (GTK_WIDGET (self));
2499 }
2500 
2501 
2502 void
goo_window_play(GooWindow * window)2503 goo_window_play (GooWindow *window)
2504 {
2505 	if (window->priv->hibernate)
2506 		return;
2507 
2508 	if (! goo_player_is_audio_cd (window->priv->player))
2509 		return;
2510 
2511 	if (goo_player_get_state (window->priv->player) == GOO_PLAYER_STATE_PLAYING)
2512 		return;
2513 
2514 	if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PAUSED) {
2515 		gboolean  play_all;
2516 		gboolean  shuffle;
2517 
2518 		play_all = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_PLAYALL);
2519 		shuffle  = g_settings_get_boolean (window->priv->settings_playlist, PREF_PLAYLIST_SHUFFLE);
2520 		create_playlist (window, play_all, shuffle);
2521 
2522 		if (window->priv->current_track != NULL)
2523 			play_track (window, window->priv->current_track->number);
2524 		else if (window->priv->playlist != NULL)
2525 			play_next_track_in_playlist (window);
2526 		else
2527 			play_track (window, 0);
2528 	}
2529 	else {
2530 		set_current_track_icon (window, GOO_ICON_NAME_PLAY);
2531 		goo_player_play (window->priv->player);
2532 	}
2533 }
2534 
2535 
2536 void
goo_window_play_selected(GooWindow * window)2537 goo_window_play_selected (GooWindow *window)
2538 {
2539 	int track_n;
2540 
2541 	if (set_selected_track_if_unique (window, &track_n)) {
2542 		goo_window_stop (window);
2543 		goo_window_set_current_track (window, track_n);
2544 		goo_window_play (window);
2545 	}
2546 }
2547 
2548 
2549 void
goo_window_stop(GooWindow * window)2550 goo_window_stop (GooWindow *window)
2551 {
2552 	if (window->priv->hibernate)
2553 		return;
2554 	if (! goo_player_is_audio_cd (window->priv->player))
2555 		return;
2556 	goo_player_stop (window->priv->player);
2557 }
2558 
2559 
2560 void
goo_window_pause(GooWindow * window)2561 goo_window_pause (GooWindow *window)
2562 {
2563 	if (window->priv->hibernate)
2564 		return;
2565 	goo_player_pause (window->priv->player);
2566 }
2567 
2568 
2569 void
goo_window_toggle_play(GooWindow * window)2570 goo_window_toggle_play (GooWindow *window)
2571 {
2572 	if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PLAYING)
2573 		goo_window_play (window);
2574 	else
2575 		goo_window_pause (window);
2576 }
2577 
2578 
2579 void
goo_window_next(GooWindow * window)2580 goo_window_next (GooWindow *window)
2581 {
2582 	if (window->priv->album->n_tracks == 0)
2583 		return;
2584 
2585 	if (! goo_player_is_audio_cd (window->priv->player))
2586 		return;
2587 
2588 	if (play_next_track_in_playlist (window))
2589 		return;
2590 
2591 	if (window->priv->current_track != NULL) {
2592 		int current_track = window->priv->current_track->number;
2593 		int current_pos;
2594 		int new_pos, new_track;
2595 
2596 		goo_window_stop (window);
2597 
2598 		current_pos = get_position_from_track_number (window, current_track);
2599 		new_pos = MIN (current_pos + 1, window->priv->album->n_tracks - 1);
2600 		new_track = get_track_number_from_position (window, new_pos);
2601 		goo_window_set_current_track (window, new_track);
2602 		goo_window_play (window);
2603 	}
2604 	else
2605 		goo_window_play (window);
2606 }
2607 
2608 
2609 void
goo_window_prev(GooWindow * window)2610 goo_window_prev (GooWindow *window)
2611 {
2612 	int new_pos;
2613 
2614 	if (! goo_player_is_audio_cd (window->priv->player))
2615 		return;
2616 
2617 	if (window->priv->album->n_tracks == 0)
2618 		return;
2619 
2620 	if (window->priv->current_track != NULL) {
2621 		int current_track, current_pos;
2622 
2623 		goo_window_stop (window);
2624 
2625 		current_track = window->priv->current_track->number;
2626 		current_pos = get_position_from_track_number (window, current_track);
2627 		new_pos = MAX (current_pos - 1, 0);
2628 		goo_window_set_current_track (window, get_track_number_from_position (window, new_pos));
2629 		goo_window_play (window);
2630 	}
2631 	else
2632 		goo_window_play (window);
2633 
2634 }
2635 
2636 
2637 void
goo_window_eject(GooWindow * window)2638 goo_window_eject (GooWindow *window)
2639 {
2640 	if (window->priv->hibernate)
2641 		return;
2642 	goo_player_eject (window->priv->player);
2643 }
2644 
2645 
2646 void
goo_window_set_drive(GooWindow * window,BraseroDrive * drive)2647 goo_window_set_drive (GooWindow    *window,
2648 		      BraseroDrive *drive)
2649 {
2650 	BraseroDrive *current_drive;
2651 
2652 	current_drive = goo_player_get_drive (window->priv->player);
2653 	if ((drive == NULL) && (current_drive == NULL))
2654 		return;
2655 	if ((drive != NULL) && (current_drive != NULL) && (g_strcmp0 (brasero_drive_get_device (drive), brasero_drive_get_device (current_drive)) == 0))
2656 		return;
2657 
2658 	goo_player_set_drive (window->priv->player, drive);
2659 	goo_player_update (window->priv->player);
2660 }
2661 
2662 
2663 AlbumInfo *
goo_window_get_album(GooWindow * window)2664 goo_window_get_album (GooWindow *window)
2665 {
2666 	return window->priv->album;
2667 }
2668 
2669 
2670 GList *
goo_window_get_tracks(GooWindow * window,gboolean selection)2671 goo_window_get_tracks (GooWindow *window,
2672 		       gboolean   selection)
2673 {
2674 	GtkTreeSelection     *list_selection;
2675 	GList                *tracks;
2676 
2677 	if (window->priv->album->tracks == NULL)
2678 		return NULL;
2679 
2680 	if (! selection)
2681 		return track_list_dup (window->priv->album->tracks);
2682 
2683 	/* return selected track list */
2684 
2685 	list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
2686 	if (list_selection == NULL)
2687 		return NULL;
2688 
2689 	tracks = NULL;
2690 	gtk_tree_selection_selected_foreach (list_selection, add_selected_track, &tracks);
2691 
2692 	return g_list_reverse (tracks);
2693 }
2694 
2695 
2696 GooPlayer *
goo_window_get_player(GooWindow * window)2697 goo_window_get_player (GooWindow *window)
2698 {
2699 	return window->priv->player;
2700 }
2701 
2702 
2703 GtkWidget *
goo_window_get_player_info(GooWindow * window)2704 goo_window_get_player_info (GooWindow *window)
2705 {
2706 	return window->priv->info;
2707 }
2708 
2709 
2710 #define MIN_COVER_SIZE 10
2711 
2712 
2713 gboolean
goo_window_set_cover_image_from_pixbuf(GooWindow * window,GdkPixbuf * image)2714 goo_window_set_cover_image_from_pixbuf (GooWindow *window,
2715 					GdkPixbuf *image)
2716 {
2717 	GError    *error = NULL;
2718 	GdkPixbuf *frame;
2719 	char      *cover_filename;
2720 	gboolean   image_saved;
2721 
2722 	if (image == NULL)
2723 		return FALSE;
2724 
2725 	if ((gdk_pixbuf_get_width (image) < MIN_COVER_SIZE) || (gdk_pixbuf_get_height (image) < MIN_COVER_SIZE))
2726 		return FALSE;
2727 
2728 	frame = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (image),
2729 				gdk_pixbuf_get_has_alpha (image),
2730 				gdk_pixbuf_get_bits_per_sample (image),
2731 				gdk_pixbuf_get_width (image) + 2,
2732 				gdk_pixbuf_get_height (image) + 2);
2733 	gdk_pixbuf_fill (frame, 0x00000000);
2734 	gdk_pixbuf_copy_area (image,
2735 			      0, 0,
2736 			      gdk_pixbuf_get_width (image),
2737 			      gdk_pixbuf_get_height (image),
2738 			      frame,
2739 			      1, 1);
2740 
2741 	cover_filename = goo_window_get_cover_filename (window);
2742 	debug (DEBUG_INFO, "SAVE IMAGE %s\n", cover_filename);
2743 
2744 	image_saved = gdk_pixbuf_save (frame, cover_filename, "png", &error, NULL);
2745 	if (image_saved) {
2746 		goo_window_set_current_cd_autofetch (window, FALSE);
2747 		goo_window_update_cover (window);
2748 	}
2749 	else
2750 		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
2751 						   _("Could not save cover image"),
2752 						   &error);
2753 
2754 	g_free (cover_filename);
2755 	g_object_unref (frame);
2756 
2757 	return image_saved;
2758 }
2759 
2760 
2761 gboolean
goo_window_set_cover_image(GooWindow * window,const char * filename)2762 goo_window_set_cover_image (GooWindow  *window,
2763 			    const char *filename)
2764 {
2765 	GdkPixbuf *image;
2766 	GError    *error = NULL;
2767 	gboolean   result;
2768 
2769 	if (window->priv->hibernate)
2770 		return FALSE;
2771 
2772 	image = gdk_pixbuf_new_from_file (filename, &error);
2773 	if (image == NULL) {
2774 		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
2775 						   _("Could not load image"),
2776 						   &error);
2777 		return FALSE;
2778 	}
2779 
2780 	result = goo_window_set_cover_image_from_pixbuf (window, image);
2781 
2782 	g_object_unref (image);
2783 
2784 	return result;
2785 }
2786 
2787 
2788 gboolean
goo_window_set_cover_image_from_data(GooWindow * window,void * buffer,gsize count)2789 goo_window_set_cover_image_from_data (GooWindow *window,
2790 				      void      *buffer,
2791 				      gsize      count)
2792 {
2793 	GInputStream *stream;
2794 	GdkPixbuf    *image;
2795 	GError       *error = NULL;
2796 	gboolean      result;
2797 
2798 	if (window->priv->hibernate)
2799 		return FALSE;
2800 
2801 	stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
2802 	image = gdk_pixbuf_new_from_stream (stream, NULL, &error);
2803 
2804 	if (image == NULL) {
2805 		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
2806 						   _("Could not load image"),
2807 						   &error);
2808 		g_object_unref (stream);
2809 		return FALSE;
2810 	}
2811 
2812 	result = goo_window_set_cover_image_from_pixbuf (window, image);
2813 
2814 	g_object_unref (image);
2815 	g_object_unref (stream);
2816 
2817 	return result;
2818 }
2819 
2820 
2821 static void
open_update_preview_cb(GtkFileChooser * file_sel,gpointer user_data)2822 open_update_preview_cb (GtkFileChooser *file_sel,
2823 			gpointer        user_data)
2824 {
2825 	GooWindow *window = user_data;
2826 	char      *uri;
2827 
2828 	uri = gtk_file_chooser_get_preview_uri (file_sel);
2829 	debug (DEBUG_INFO, "PREVIEW: %s", uri);
2830 	gtk_file_chooser_preview_set_uri (GTK_FILE_CHOOSER_PREVIEW (window->priv->preview), uri);
2831 	g_free (uri);
2832 }
2833 
2834 
2835 static void
open_response_cb(GtkDialog * file_sel,int button_number,gpointer user_data)2836 open_response_cb (GtkDialog  *file_sel,
2837 		  int         button_number,
2838 		  gpointer    user_data)
2839 {
2840 	GooWindow *window = user_data;
2841 	char      *folder;
2842 	char      *filename;
2843 
2844 	if (button_number != GTK_RESPONSE_ACCEPT) {
2845 		gtk_widget_destroy (GTK_WIDGET (file_sel));
2846 		return;
2847 	}
2848 
2849 	/* Save the folder */
2850 
2851 	folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (file_sel));
2852 	g_settings_set_string (window->priv->settings_general, PREF_GENERAL_COVER_PATH, folder);
2853 	g_free (folder);
2854 
2855 	/* Load the image. */
2856 
2857 	filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_sel));
2858 	goo_window_set_cover_image (window, filename);
2859 	g_free (filename);
2860 
2861 	gtk_widget_destroy (GTK_WIDGET (file_sel));
2862 }
2863 
2864 
2865 void
goo_window_pick_cover_from_disk(GooWindow * window)2866 goo_window_pick_cover_from_disk (GooWindow *window)
2867 {
2868 	GtkWidget     *file_sel;
2869 	GtkFileFilter *filter;
2870 	char          *path;
2871 
2872 	if (window->priv->hibernate)
2873 		return;
2874 
2875 	goo_window_set_current_cd_autofetch (window, FALSE);
2876 
2877 	file_sel = gtk_file_chooser_dialog_new (_("Choose Disc Cover Image"),
2878 						GTK_WINDOW (window),
2879 						GTK_FILE_CHOOSER_ACTION_OPEN,
2880 						_GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL,
2881 						_GTK_LABEL_OPEN, GTK_RESPONSE_ACCEPT,
2882 						NULL);
2883 	gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
2884 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), TRUE);
2885 
2886 	window->priv->preview = gtk_file_chooser_preview_new ();
2887 	gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (file_sel), window->priv->preview);
2888 	gtk_file_chooser_set_use_preview_label (GTK_FILE_CHOOSER (file_sel), FALSE);
2889 	gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (file_sel), TRUE);
2890 
2891 	path = g_settings_get_string (window->priv->settings_general, PREF_GENERAL_COVER_PATH);
2892 	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_sel), path);
2893 	g_free (path);
2894 
2895 	filter = gtk_file_filter_new ();
2896 	gtk_file_filter_set_name (filter, _("Images"));
2897 	gtk_file_filter_add_mime_type (filter, "image/jpeg");
2898 	gtk_file_filter_add_mime_type (filter, "image/png");
2899 	gtk_file_filter_add_mime_type (filter, "image/gif");
2900 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter);
2901 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_sel), filter);
2902 
2903 	filter = gtk_file_filter_new ();
2904 	gtk_file_filter_set_name (filter, _("All files"));
2905 	gtk_file_filter_add_pattern (filter, "*");
2906 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter);
2907 
2908 	g_signal_connect (G_OBJECT (file_sel),
2909 			  "response",
2910 			  G_CALLBACK (open_response_cb),
2911 			  window);
2912 	g_signal_connect (G_OBJECT (file_sel),
2913 			  "update-preview",
2914 			  G_CALLBACK (open_update_preview_cb),
2915 			  window);
2916 	g_signal_connect_swapped (GTK_DIALOG (file_sel),
2917 				  "close",
2918 				  G_CALLBACK (gtk_widget_destroy),
2919 				  GTK_WIDGET (file_sel));
2920 
2921 	gtk_widget_show_all (GTK_WIDGET (file_sel));
2922 }
2923 
2924 
2925 void
goo_window_search_cover_on_internet(GooWindow * window)2926 goo_window_search_cover_on_internet (GooWindow *window)
2927 {
2928 	if (window->priv->hibernate)
2929 		return;
2930 
2931 	goo_window_set_current_cd_autofetch (window, FALSE);
2932 
2933 	debug (DEBUG_INFO, "SEARCH ON INTERNET\n");
2934 
2935 	if ((window->priv->album->title == NULL) || (window->priv->album->artist == NULL)) {
2936 		GtkWidget *d;
2937 
2938 		d = _gtk_error_dialog_new (GTK_WINDOW (window),
2939 					   GTK_DIALOG_MODAL,
2940 					   NULL,
2941 					   _("Could not search for a cover on Internet"),
2942 					   "%s",
2943 					   _("You have to enter the artist and album names in order to find the album cover."));
2944 		g_signal_connect (G_OBJECT (d), "response",
2945 				  G_CALLBACK (gtk_widget_destroy),
2946 				  NULL);
2947 		gtk_widget_show (d);
2948 		return;
2949 	}
2950 
2951 	dlg_cover_chooser (window, window->priv->album->title, window->priv->album->artist);
2952 }
2953 
2954 
2955 void
goo_window_remove_cover(GooWindow * window)2956 goo_window_remove_cover (GooWindow *window)
2957 {
2958 	char  *cover_filename;
2959 	GFile *file;
2960 
2961 	if (window->priv->hibernate)
2962 		return;
2963 
2964 	goo_window_set_current_cd_autofetch (window, FALSE);
2965 
2966 	cover_filename = goo_window_get_cover_filename (window);
2967 	if (cover_filename == NULL)
2968 		return;
2969 
2970 	file = g_file_new_for_path (cover_filename);
2971 	g_file_delete (file, NULL, NULL);
2972 
2973 	g_free (cover_filename);
2974 
2975 	goo_window_update_cover (window);
2976 }
2977 
2978 
2979 void
goo_window_toggle_visibility(GooWindow * window)2980 goo_window_toggle_visibility (GooWindow *window)
2981 {
2982 	if (gtk_widget_get_visible (GTK_WIDGET (window))) {
2983 		gtk_window_get_position (GTK_WINDOW (window),
2984 					 &window->priv->pos_x,
2985 					 &window->priv->pos_y);
2986 		gtk_widget_hide (GTK_WIDGET (window));
2987 	}
2988 	else {
2989 		gtk_window_move (GTK_WINDOW (window),
2990 				 window->priv->pos_x,
2991 				 window->priv->pos_y);
2992 		gtk_window_present (GTK_WINDOW (window));
2993 	}
2994 }
2995 
2996 
2997 void
goo_window_set_hibernate(GooWindow * window,gboolean hibernate)2998 goo_window_set_hibernate (GooWindow *window,
2999 			  gboolean   hibernate)
3000 {
3001 	window->priv->hibernate = hibernate;
3002 	goo_player_hibernate (window->priv->player, hibernate);
3003 	if (! hibernate)
3004 		goo_window_update (window);
3005 }
3006