1 /*
2  * Copyright (C) 2007 Felipe Weckx <felipe.weckx@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include "gbemol-utils.h"
21 #include "gbemol-mpd.h"
22 #include "gbemol-net.h"
23 #include "gbemol-amazon.h"
24 
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 
31 #ifdef ENABLE_ID3_APIC
32 /* ID3 Picture Tag */
33 #include <id3.h>
34 #endif
35 
36 #include <gtk/gtk.h>
37 #include <glib/gstdio.h>
38 
39 /* Got from brasero (http://perso.wanadoo.fr/bonfire) */
40 GtkWidget *
gbemol_utils_make_button(char * text,char * stock)41 gbemol_utils_make_button (char *text, char *stock)
42 {
43 	GtkWidget *button;
44 	GtkWidget *box, *lbl;
45 
46 	box = gtk_hbox_new (FALSE, 6);
47 	gtk_box_pack_start (GTK_BOX (box),
48 			    gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON),
49 			    FALSE,
50 			    FALSE,
51 			    0);
52 
53 	button = gtk_button_new ();
54 	if (text)
55 	{
56 		lbl = gtk_label_new_with_mnemonic (text);
57 		gtk_box_pack_start (GTK_BOX (box),
58 				    lbl,
59 				    FALSE,
60 				    FALSE,
61 				    0);
62 		gtk_label_set_mnemonic_widget (GTK_LABEL (lbl), button);
63 	}
64 
65 	gtk_container_add (GTK_CONTAINER (button), box);
66 	gtk_button_set_alignment (GTK_BUTTON (button), 0.5, 0.5);
67 
68 	return button;
69 }
70 
71 /* Same as above, but returns the label as well */
72 GtkWidget *
gbemol_utils_make_button_and_label(char * text,char * stock,GtkWidget ** lbl)73 gbemol_utils_make_button_and_label (char *text, char *stock, GtkWidget** lbl)
74 {
75 	GtkWidget *button;
76 	GtkWidget *box;
77 
78 	box = gtk_hbox_new (FALSE, 6);
79 	gtk_box_pack_start (GTK_BOX (box),
80 			    gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON),
81 			    FALSE,
82 			    FALSE,
83 			    0);
84 
85 	button = gtk_button_new ();
86 	if (text)
87 	{
88 		*lbl = gtk_label_new_with_mnemonic (text);
89 		gtk_box_pack_start (GTK_BOX (box),
90 				    *lbl,
91 				    FALSE,
92 				    FALSE,
93 				    0);
94 		gtk_label_set_mnemonic_widget (GTK_LABEL (*lbl), button);
95 	}
96 
97 	gtk_container_add (GTK_CONTAINER (button), box);
98 	gtk_button_set_alignment (GTK_BUTTON (button), 0.5, 0.5);
99 
100 	return button;
101 }
102 
gbemol_utils_char_list_free(GList * l)103 void gbemol_utils_char_list_free (GList *l)
104 {
105 	if (l)
106 	{
107 		g_list_foreach (l, (GFunc) g_free, NULL);
108 		g_list_free (l);
109 	}
110 }
111 
gbemol_utils_song_list_free(GList * l)112 void gbemol_utils_song_list_free (GList *l)
113 {
114 	if (l)
115 	{
116 		g_list_foreach (l, (GFunc) gbemol_mpd_free_song, NULL);
117 		g_list_free (l);
118 	}
119 }
120 
121 GList*
gbemol_utils_get_selected(GtkTreeView * tvw)122 gbemol_utils_get_selected (GtkTreeView* tvw)
123 {
124 	GtkTreeModel *model;
125 	GtkTreeSelection *select;
126 	GList *rows = NULL;
127 
128 	select = gtk_tree_view_get_selection (tvw);
129 	if (gtk_tree_selection_count_selected_rows (select) > 0)
130 	{
131 		rows = gtk_tree_selection_get_selected_rows (select, &model);
132 		return rows;
133 	}
134 	else
135 		return NULL;
136 }
137 
138 void
gbemol_utils_selected_list_free(GList * l)139 gbemol_utils_selected_list_free (GList* l)
140 {
141         if (l)
142         {
143                 g_list_foreach (l, (GFunc) gtk_tree_path_free, NULL);
144                 g_list_free (l);
145         }
146 }
147 
148 /*
149  * Config file functions
150  */
gbemol_cfg_create_file()151 gboolean gbemol_cfg_create_file ()
152 {
153 	gchar* path;
154 	gint fd;
155 
156 	path = g_strdup_printf ("%s/.gbemol.cfg", g_get_home_dir());
157 
158 	if ((fd = open (path, O_CREAT)) != -1)
159 	{
160 		chmod (path, 0644);
161 		close (fd);
162 		/* Initial configs */
163 		gbemol_cfg_set_int ("win_main", "visible", 1);
164 		return TRUE;
165 	}
166 
167 	return FALSE;
168 }
169 
170 /*
171  * Opens and reads the key file
172  */
gbemol_cfg_load_file()173 GKeyFile* gbemol_cfg_load_file ()
174 {
175 	gchar* path, *path2;
176 	GKeyFile* file;
177 	GError* error = NULL;
178 
179 	path = g_strdup_printf ("%s/gbemol/gbemol.cfg", g_get_user_config_dir());
180 	file = g_key_file_new ();
181 
182 	if (g_file_test (path, G_FILE_TEST_EXISTS))
183 		/* File exists, load it */
184 		g_key_file_load_from_file (file, path, G_KEY_FILE_NONE, &error);
185 	else
186 	{
187 		/* File doesn't exist, check if the directory exists */
188 		path2 = g_strdup_printf ("%s/gbemol", g_get_user_config_dir());
189 		if (!(g_file_test (path2, G_FILE_TEST_EXISTS) && g_file_test (path2, G_FILE_TEST_IS_DIR)))
190 			g_mkdir (path2, 0755);
191 		g_free (path2);
192 	}
193 
194 	g_free (path);
195 
196 	return file;
197 }
198 
gbemol_cfg_save_file(GKeyFile * file)199 gboolean gbemol_cfg_save_file (GKeyFile* file)
200 {
201 	gchar* path, *data;
202 	gsize len;
203 	FILE* cfg;
204 
205 	path = g_strdup_printf ("%s/gbemol/gbemol.cfg", g_get_user_config_dir ());
206 	data = g_key_file_to_data (file, &len, NULL);
207 
208 	cfg = fopen (path, "w");
209 
210 	if (cfg)
211 	{
212 		fprintf (cfg, data);
213 		fclose (cfg);
214 		g_free (path);
215 		g_free (data);
216 		return TRUE;
217 	}
218 
219 	g_free (path);
220 	g_free (data);
221 	g_key_file_free (file);
222 
223 	return FALSE;
224 }
225 
gbemol_cfg_set_string(gchar * group,gchar * key,const gchar * value)226 void gbemol_cfg_set_string (gchar* group, gchar* key, const gchar* value)
227 {
228 	GKeyFile *file;
229 
230 	file = gbemol_cfg_load_file ();
231 	g_key_file_set_string (file, group, key, value);
232 	gbemol_cfg_save_file (file);
233 }
234 
gbemol_cfg_get_string(gchar * group,gchar * key)235 gchar* gbemol_cfg_get_string (gchar* group, gchar* key)
236 {
237 	GKeyFile *file;
238 	gchar* str;
239 
240 	file = gbemol_cfg_load_file ();
241 	str = g_key_file_get_string (file, group, key, NULL);
242 	g_key_file_free (file);
243 
244 	return str;
245 }
246 
gbemol_cfg_set_int(gchar * group,gchar * key,gint value)247 void gbemol_cfg_set_int (gchar* group, gchar* key, gint value)
248 {
249 	GKeyFile *file;
250 
251 	file = gbemol_cfg_load_file ();
252 	g_key_file_set_integer (file, group, key, value);
253 	gbemol_cfg_save_file (file);
254 	g_key_file_free (file);
255 
256 }
257 
gbemol_cfg_get_int(gchar * group,gchar * key)258 gint gbemol_cfg_get_int (gchar* group, gchar* key)
259 {
260 	GKeyFile *file;
261 	gint value;
262 
263 	file = gbemol_cfg_load_file ();
264 	value = g_key_file_get_integer (file, group, key, NULL);
265 	g_key_file_free (file);
266 
267 	return value;
268 }
269 
gbemol_cfg_set_bool(gchar * group,gchar * key,gboolean value)270 void gbemol_cfg_set_bool(gchar* group, gchar* key, gboolean value)
271 {
272 	GKeyFile *file;
273 
274 	file = gbemol_cfg_load_file ();
275 	g_key_file_set_boolean(file, group, key, value);
276 	gbemol_cfg_save_file (file);
277 	g_key_file_free (file);
278 
279 }
280 
gbemol_cfg_get_bool(gchar * group,gchar * key)281 gboolean gbemol_cfg_get_bool(gchar* group, gchar* key)
282 {
283 	GKeyFile *file;
284 	gboolean value;
285 
286 	file = gbemol_cfg_load_file ();
287 	value = g_key_file_get_boolean(file, group, key, NULL);
288 	g_key_file_free (file);
289 
290 	return value;
291 }
292 
gbemol_cfg_get_bool_with_default(gchar * group,gchar * key,gboolean def)293 gboolean gbemol_cfg_get_bool_with_default(gchar* group, gchar* key, gboolean def)
294 {
295 	GKeyFile *file;
296 	gboolean value;
297 	GError* e = NULL;
298 
299 	file = gbemol_cfg_load_file();
300 	value = g_key_file_get_boolean(file, group, key, &e);
301 	if ((e != NULL) && (e->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND))
302 		value = def;
303 	g_key_file_free (file);
304 
305 	return value;
306 }
307 
gbemol_cfg_set_double(gchar * group,gchar * key,gdouble value)308 void gbemol_cfg_set_double (gchar* group, gchar* key, gdouble value)
309 {
310 	GKeyFile *file;
311 
312 	file = gbemol_cfg_load_file ();
313 	g_key_file_set_double (file, group, key, value);
314 	gbemol_cfg_save_file (file);
315 
316 }
317 
gbemol_cfg_get_double(gchar * group,gchar * key)318 gdouble gbemol_cfg_get_double (gchar* group, gchar* key)
319 {
320 	GKeyFile *file;
321 	gdouble value;
322 
323 	file = gbemol_cfg_load_file ();
324 	value = g_key_file_get_double (file, group, key, NULL);
325 	g_key_file_free (file);
326 
327 	return value;
328 }
329 
330 #ifdef ENABLE_ID3_APIC
331 /*
332  * Got from EasyTag (http://easytag.sf.net)
333  */
ID3Tag_Link_1(ID3Tag * id3tag,const char * filename)334 ID3_C_EXPORT size_t ID3Tag_Link_1 (ID3Tag *id3tag, const char *filename)
335 {
336     size_t offset;
337 
338 #   if (0) // Link the file with the both tags may cause damage to unicode strings
339 //#   if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) && (ID3LIB_PATCH >= 1) ) // Same test used in Id3tag_Read_File_Tag to use ID3Tag_HasTagType
340         /* No problem of priority, so we link the file with the both tags
341          * to manage => ID3Tag_HasTagType works correctly */
342         offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1 | ID3TT_ID3V2);
343 #   elif ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) )
344         /* Version 3.8.0pre2 gives priority to tag id3v1 instead of id3v2, so we
345          * try to fix it by linking the file with the id3v2 tag first. This bug
346          * was fixed in the final version of 3.8.0 but we can't know it... */
347         /* First, try to get the ID3v2 tags */
348         offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V2);
349         if (offset == 0)
350         {
351             /* No ID3v2 tags available => try to get the ID3v1 tags */
352             offset = ID3Tag_LinkWithFlags(id3tag,filename,ID3TT_ID3V1);
353         }
354 #   else
355         /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */
356         offset = ID3Tag_Link(id3tag,filename);
357 #   endif
358     //g_print("ID3 TAG SIZE: %d\t%s\n",offset,g_path_get_basename(filename));
359     return offset;
360 }
361 
362 #endif
363 
gbemol_get_song_image_from_id3(GbemolMpdSong * song)364 GdkPixbuf* gbemol_get_song_image_from_id3 (GbemolMpdSong* song)
365 {
366 	GdkPixbuf *pix = NULL;
367 #ifdef ENABLE_ID3_APIC
368 	size_t offset;
369 	ID3Tag* id3_tag = NULL;
370 	ID3Frame* id3_frame;
371 	ID3Field* id3_field;
372 	gchar *path, *songs_path;
373 	GdkPixbufLoader *load;
374 	gulong size = 0;
375 	guchar  *data = NULL;
376 
377 	if (!(songs_path = gbemol_cfg_get_string ("MPD", "music_dir")))
378 		return NULL;
379 
380 	path = g_strdup_printf ("%s/%s", songs_path, song->file);
381 
382 	id3_tag = ID3Tag_New();
383 	offset = ID3Tag_LinkWithFlags (id3_tag, path, ID3TT_ID3V1 | ID3TT_ID3V2);
384 
385 	g_free (path);
386 	g_free (songs_path);
387 
388 	if (!offset)
389 		return NULL;
390 
391 	id3_frame = ID3Tag_FindFrameWithID(id3_tag, ID3FID_PICTURE);
392 	if (!id3_frame)
393 		return NULL;
394 
395 	if ( (id3_field = ID3Frame_GetField(id3_frame, ID3FN_DATA)) )
396         {
397             size = ID3Field_Size(id3_field);
398             data = g_malloc(size);
399             ID3Field_GetBINARY(id3_field, data, size);
400         }
401 
402 	load = gdk_pixbuf_loader_new ();
403 	if (load)
404         	if (gdk_pixbuf_loader_write (load, data, size, 0))
405 			pix = gdk_pixbuf_loader_get_pixbuf (load);
406 
407 	g_free (data);
408 	g_free (id3_tag);
409 	g_free (id3_frame);
410 	g_free (id3_field);
411 #endif
412 	return pix;
413 }
414 
415 
416 /* Returns the cover filename and creates ~/.cover if it doesn't exists */
gbemol_utils_generate_cover_filename(GbemolMpdSong * song)417 gchar* gbemol_utils_generate_cover_filename(GbemolMpdSong* song)
418 {
419 	gchar* str_dir, *str;
420 	GDir* dir_covers;
421 
422 	str_dir = g_strdup_printf ("%s/.covers/", g_get_home_dir());
423 	dir_covers = g_dir_open(str_dir, 0, NULL);
424 	if (dir_covers == NULL) {
425 		g_mkdir(str_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
426 	} else {
427 		g_dir_close(dir_covers);
428 	}
429 	g_free(str_dir);
430 	str = g_strdup_printf ("%s/.covers/%s-%s.jpg", g_get_home_dir(), song->artist, song->album);
431 	return str;
432 }
433 
434 /**
435  * Try to load image from file in ~/.covers
436  */
gbemol_utils_get_cover_image_from_file(GbemolMpdSong * song)437 GdkPixbuf* gbemol_utils_get_cover_image_from_file(GbemolMpdSong* song)
438 {
439 	gchar *str;
440 	GdkPixbuf* img = NULL;
441 
442 	str = gbemol_utils_generate_cover_filename(song);
443 
444 	if (g_file_test (str, G_FILE_TEST_EXISTS))
445 	{
446 		GError *e = NULL;
447 		img = gdk_pixbuf_new_from_file (str, &e);
448 		if (e)
449 		{
450 			g_message (e->message);
451 			img = NULL;
452 		}
453 	}
454 	g_free(str);
455 
456 	return img;
457 }
458 
459 /**
460  * Try to fetch image from Amazon.com
461  */
gbemol_utils_get_cover_image_from_amazon(GbemolMpdSong * song)462 GdkPixbuf* gbemol_utils_get_cover_image_from_amazon(GbemolMpdSong* song)
463 {
464 	GdkPixbuf* img = NULL;;
465 	gchar *result = NULL;
466 	GSList* results;
467 
468 	results = gbemol_amazon_search_cover(song);
469 	if (results == NULL)
470 	{
471 		return NULL;
472 	}
473 	else
474 	{
475 		GError* err = NULL;
476 		gchar* str = gbemol_utils_generate_cover_filename(song);
477 		/* There's a cover, save it */
478 		result = results->data;
479 		gbemol_net_download_file(result, str, &err);
480 		if (err != NULL)
481 		{
482 			g_message(err->message);
483 		}
484 		else
485 		{
486 			img = gdk_pixbuf_new_from_file (str, NULL);
487 		}
488 		g_free (str);
489 	}
490 
491 	/* Free everything */
492 	g_slist_foreach(results, (GFunc) g_free, NULL);
493 
494 	return img;
495 }
496 
gbemol_utils_get_song_image(GbemolMpdSong * song)497 GdkPixbuf* gbemol_utils_get_song_image (GbemolMpdSong* song)
498 {
499 	GdkPixbuf *img = NULL;
500 
501 	if (gbemol_cfg_get_bool("cover_art", "file"))
502 		img = gbemol_utils_get_cover_image_from_file(song);
503 
504 	/* No picture in  ~/.covers, try amazon */
505 	if (!img && gbemol_cfg_get_bool("cover_art", "amazon"))
506 	{
507 		img = gbemol_utils_get_cover_image_from_amazon(song);
508 	}
509 
510 	return img;
511 }
512 
gbemol_utils_label_new_with_markup(gchar * text)513 GtkWidget* gbemol_utils_label_new_with_markup (gchar* text)
514 {
515 	GtkWidget* lbl;
516 
517 	lbl = gtk_label_new (text);
518 	gtk_label_set_use_markup (GTK_LABEL (lbl), TRUE);
519 	gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5);
520 
521 	return lbl;
522 }
523 
gbemol_debug_msg(char * msg,...)524 void gbemol_debug_msg (char *msg, ...)
525 {
526 #ifdef DEBUG
527 	va_list args;
528 	char *str;
529 
530 	va_start (args);
531 
532 	str = g_strdup_vprintf ("DEBUG: %s\n", args);
533 	g_print (str);
534 	g_free (str);
535 #endif
536 }
537 
538