1 /* -*- coding: utf-8; -*-
2 |
3 | Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
4 | Part of the gtkpod project.
5 |
6 | URL: http://www.gtkpod.org/
7 | URL: http://gtkpod.sourceforge.net/
8 |
9 | This program is free software; you can redistribute it and/or modify
10 | it under the terms of the GNU General Public License as published by
11 | the Free Software Foundation; either version 2 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU General Public License for more details.
18 |
19 | You should have received a copy of the GNU General Public License
20 | along with this program; if not, write to the Free Software
21 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 |
23 | iTunes and iPod are trademarks of Apple
24 |
25 | This product is not supported/written/published by Apple!
26 |
27 | $Id$
28 */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <errno.h>
35 #include <gtk/gtk.h>
36 #include <glib/gstdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41 #include "autodetection.h"
42 #include "charset.h"
43 #include "clientserver.h"
44 #include "file_convert.h"
45 #include "misc.h"
46 #include "mp4file.h"
47 #include "prefs.h"
48 #include "misc_track.h"
49 #include "display_photo.h"
50 #include "stock_icons.h"
51
52 #define DEBUG_MISC 0
53
54
55 /* where to find the scripts */
56 const gchar *SCRIPTDIR = PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "scripts" G_DIR_SEPARATOR_S;
57
58
59 /*------------------------------------------------------------------*\
60 * *
61 * Miscellaneous *
62 * *
63 \*------------------------------------------------------------------*/
64
utf8_strcasestr(const gchar * haystack,const gchar * needle)65 gchar *utf8_strcasestr(const gchar *haystack, const gchar *needle) {
66 g_return_val_if_fail (haystack != NULL, NULL);
67 g_return_val_if_fail (needle != NULL, NULL);
68
69 return g_strrstr(haystack, needle);
70 }
71
72 /* Calculate the time in ms passed since @old_time. @old_time is
73 updated with the current time if @update is TRUE*/
get_ms_since(GTimeVal * old_time,gboolean update)74 float get_ms_since (GTimeVal *old_time, gboolean update)
75 {
76 GTimeVal new_time;
77 float result;
78
79 g_get_current_time (&new_time);
80 result = (new_time.tv_sec - old_time->tv_sec) * 1000 +
81 (float)(new_time.tv_usec - old_time->tv_usec) / 1000;
82 if (update)
83 {
84 old_time->tv_sec = new_time.tv_sec;
85 old_time->tv_usec = new_time.tv_usec;
86 }
87 return result;
88 }
89
90 /* parse a bunch of track pointers delimited by \n
91 * @s - address of the character string we're parsing (gets updated)
92 * @track - pointer the track pointer parsed from the string
93 * Returns FALSE when the string is empty, TRUE when the string can still be
94 * parsed
95 */
96 gboolean
parse_tracks_from_string(gchar ** s,Track ** track)97 parse_tracks_from_string(gchar **s, Track **track)
98 {
99 g_return_val_if_fail (track, FALSE);
100 *track = NULL;
101 g_return_val_if_fail (s, FALSE);
102
103 if(*s)
104 {
105 gchar *str = *s;
106 gchar *strp = strchr (str, '\n');
107 int tokens;
108
109 if (strp == NULL)
110 {
111 *track = NULL;
112 *s = NULL;
113 return FALSE;
114 }
115 tokens = sscanf (str, "%p", track);
116 ++strp;
117 if (*strp) *s = strp;
118 else *s = NULL;
119 if (tokens == 1) return TRUE;
120 else return FALSE;
121 }
122 return FALSE;
123 }
124
125 gboolean
parse_artwork_from_string(gchar ** s,Artwork ** artwork)126 parse_artwork_from_string(gchar **s, Artwork **artwork)
127 {
128 g_return_val_if_fail (artwork, FALSE);
129 *artwork = NULL;
130 g_return_val_if_fail (s, FALSE);
131
132 if(*s)
133 {
134 gchar *str = *s;
135 gchar *strp = strchr (str, '\n');
136 int tokens;
137
138 if (strp == NULL)
139 {
140 *artwork = NULL;
141 *s = NULL;
142 return FALSE;
143 }
144 tokens = sscanf (str, "%p", artwork);
145 ++strp;
146 if (*strp) *s = strp;
147 else *s = NULL;
148 if (tokens == 1) return TRUE;
149 else return FALSE;
150 }
151 return FALSE;
152 }
153
154
155 /***************************************************************************
156 * gtkpod.in,out calls
157 *
158 **************************************************************************/
159
160
161 /* tries to call "/bin/sh @script" with command line options */
do_script(const gchar * script,va_list args)162 static void do_script (const gchar *script, va_list args)
163 {
164 char *str;
165 char **argv;
166 GPtrArray *ptra = g_ptr_array_sized_new (10);
167
168 /* prepend args with "sh" and the name of the script */
169 g_ptr_array_add (ptra, "sh");
170 g_ptr_array_add (ptra, (gpointer)script);
171 /* add remaining args */
172 while ((str = va_arg (args, char *)))
173 {
174 g_ptr_array_add (ptra, str);
175 }
176 g_ptr_array_add (ptra, NULL);
177 argv = (char **)g_ptr_array_free (ptra, FALSE);
178
179 if (script)
180 {
181 pid_t pid, tpid;
182 int status;
183
184 pid = fork ();
185 switch (pid)
186 {
187 case 0: /* child */
188 execv("/bin/sh", argv);
189 exit(0);
190 break;
191 case -1: /* parent and error */
192 break;
193 default: /* parent -- let's wait for the child to terminate */
194 tpid = waitpid (pid, &status, 0);
195 /* we could evaluate tpid and status now */
196 break;
197 }
198 }
199 g_free (argv);
200 }
201
202
203 /* tries to execute "/bin/sh ~/.gtkpod/@script" or
204 * "/bin/sh /etc/gtkpod/@script" if the former does not exist. This
205 * function accepts command line arguments that must be terminated by
206 * NULL. */
call_script(gchar * script,...)207 void call_script (gchar *script, ...)
208 {
209 gchar *cfgdir;
210 va_list args;
211 gchar *file;
212
213 if (!script) return;
214
215 cfgdir = prefs_get_cfgdir ();
216 file = g_build_filename (cfgdir, script, NULL);
217
218 va_start (args, script);
219 if (g_file_test (file, G_FILE_TEST_EXISTS))
220 {
221 do_script (file, args);
222 }
223 else
224 {
225 C_FREE (file);
226 file = g_build_filename ("/etc/gtkpod/", script, NULL);
227 if (g_file_test (file, G_FILE_TEST_EXISTS))
228 {
229 do_script (file, args);
230 }
231 }
232 va_end (args);
233
234 g_free (file);
235 g_free (cfgdir);
236 }
237
238
239
240 /* Create a NULL-terminated array of strings given in the command
241 line. The last argument must be NULL.
242
243 As a special feature, the first argument is split up into
244 individual strings to allow the use of "convert-2mp3 -q <special
245 settings>". Set the first argument to NULL if you don't want this.
246
247 You must free the returned array with g_strfreev() after use. */
build_argv_from_strings(const gchar * first_arg,...)248 gchar **build_argv_from_strings (const gchar *first_arg, ...)
249 {
250 gchar **argv;
251 va_list args;
252 const gchar *str;
253 GPtrArray *ptra = g_ptr_array_sized_new (20);
254
255 if (first_arg)
256 {
257 gchar **strings = g_strsplit (first_arg, " ", 0);
258 gchar **strp = strings;
259 while (*strp)
260 {
261 if (**strp)
262 { /* ignore empty strings */
263 g_ptr_array_add (ptra, g_strdup(*strp));
264 }
265 ++strp;
266 }
267 g_strfreev (strings);
268 }
269
270 va_start (args, first_arg);
271 do
272 {
273 str = va_arg (args, const gchar *);
274 g_ptr_array_add (ptra, g_strdup (str));
275 }
276 while (str);
277
278 va_end (args);
279
280 argv = (gchar **)g_ptr_array_free (ptra, FALSE);
281
282 return argv;
283 }
284
285
286
287
288 /* compare @str1 and @str2 case-sensitively or case-insensitively
289 * depending on prefs settings */
compare_string(const gchar * str1,const gchar * str2)290 gint compare_string (const gchar *str1, const gchar *str2)
291 {
292 gint result;
293 gchar *sortkey1 = make_sortkey (str1);
294 gchar *sortkey2 = make_sortkey (str2);
295
296 result = strcmp (sortkey1, sortkey2);
297
298 g_free (sortkey1);
299 g_free (sortkey2);
300 return result;
301 }
302
303 struct csfk
304 {
305 gint length;
306 gchar *key;
307 };
308
309 static GList *csfk_list = NULL;
310
311
312 /* Returns the sortkey for an entry name.
313 *
314 * The sort key can be compared with other sort keys using strcmp and
315 * it will give the expected result, according to the user
316 * settings. Must be regenerated if the user settings change.
317 *
318 * The caller is responsible of freeing the returned key with g_free.
319 */
320 gchar *
make_sortkey(const gchar * name)321 make_sortkey (const gchar *name)
322 {
323 if (prefs_get_int("case_sensitive"))
324 {
325 return g_utf8_collate_key (name, -1);
326 }
327 else
328 {
329 gchar *casefolded = g_utf8_casefold (name, -1);
330 gchar *key = g_utf8_collate_key (casefolded, -1);
331 g_free (casefolded);
332 return key;
333 }
334 }
335
336 /* needs to be called everytime the sort_ign_strings in the prefs were
337 changed */
compare_string_fuzzy_generate_keys(void)338 void compare_string_fuzzy_generate_keys (void)
339 {
340 GList *gl;
341 GList *sort_ign_strings;
342 GList *current;
343
344 /* remove old keys */
345 for (gl=csfk_list; gl; gl=gl->next)
346 {
347 struct csfk *csfk = gl->data;
348 g_return_if_fail (csfk);
349 g_free (csfk->key);
350 g_free (csfk);
351 }
352 g_list_free (csfk_list);
353 csfk_list = NULL;
354
355 /* create new keys */
356 sort_ign_strings = prefs_get_list("sort_ign_string_");
357 current = sort_ign_strings;
358 while (current)
359 {
360 gchar *str = current->data;
361 struct csfk *csfk;
362 gchar *tempStr;
363
364 current = g_list_next(current);
365
366 csfk = g_malloc (sizeof (struct csfk));
367 tempStr = g_utf8_casefold (str, -1 );
368 csfk->length = g_utf8_strlen (tempStr, -1 );
369 csfk->key = g_utf8_collate_key (tempStr, -1 );
370 g_free (tempStr);
371
372 csfk_list = g_list_prepend (csfk_list, csfk);
373 }
374 prefs_free_list(sort_ign_strings);
375 }
376
377 /* Returns a pointer inside the name, possibly skiping a prefix from
378 * the list generated by compare_string_fuzzy_generate_keys.
379 */
380 const gchar *
fuzzy_skip_prefix(const gchar * name)381 fuzzy_skip_prefix (const gchar *name)
382 {
383 const gchar *result = name;
384 const GList *gl;
385 gchar *cleanStr;
386
387 /* If the article collations keys have not been generated,
388 * do that first
389 */
390 if (!csfk_list)
391 compare_string_fuzzy_generate_keys ();
392
393 cleanStr = g_utf8_casefold (name, -1);
394
395 for (gl=csfk_list; gl; gl=g_list_next(gl))
396 {
397 struct csfk *csfk = gl->data;
398 gchar *tempStr;
399
400 g_return_val_if_fail (csfk, 0);
401
402 tempStr = g_utf8_collate_key (cleanStr, csfk->length);
403 if (strcmp (tempStr, csfk->key) == 0)
404 {
405 /* Found article, bump pointers ahead appropriate distance
406 */
407 result += csfk->length;
408 g_free (tempStr);
409 break;
410 }
411 g_free (tempStr);
412 }
413
414 g_free (cleanStr);
415
416 return result;
417 }
418
419 /* compare @str1 and @str2 case-sensitively or case-insensitively
420 * depending on prefs settings, and ignoring certain initial articles
421 * ("the", "le"/"la", etc) */
compare_string_fuzzy(const gchar * str1,const gchar * str2)422 gint compare_string_fuzzy (const gchar *str1, const gchar *str2)
423 {
424 return compare_string (fuzzy_skip_prefix (str1),
425 fuzzy_skip_prefix (str2));
426 }
427
428 /* compare @str1 and @str2 case-insensitively */
compare_string_case_insensitive(const gchar * str1,const gchar * str2)429 gint compare_string_case_insensitive (const gchar *str1, const gchar *str2)
430 {
431 gchar *string1 = g_utf8_casefold (str1, -1);
432 gchar *string2 = g_utf8_casefold (str2, -1);
433 gint result = g_utf8_collate (string1, string2);
434 g_free (string1);
435 g_free (string2);
436 return result;
437 }
438
439 /* todo: optionally ignore 'the', 'a,' etc. */
compare_string_start_case_insensitive(const gchar * haystack,const gchar * needle)440 gboolean compare_string_start_case_insensitive (const gchar *haystack, const gchar *needle)
441 {
442 gint cmp = 0;
443 gchar *nhaystack = g_utf8_normalize(haystack, -1, G_NORMALIZE_ALL);
444 gchar *lhaystack = g_utf8_casefold(nhaystack,-1);
445 gchar *nneedle = g_utf8_normalize(needle, -1, G_NORMALIZE_ALL);
446 gchar *lneedle = g_utf8_casefold(nneedle,-1);
447
448
449 cmp = strncmp(lhaystack, lneedle, strlen(lneedle));
450
451 /*
452 printf("searched for %s , matching against %s with %d bytes. say=%d\n",
453 lneedle, lhaystack, strlen(lneedle), cmp);
454 */
455
456 g_free(nhaystack);
457 g_free(lhaystack);
458 g_free(nneedle);
459 g_free(lneedle);
460 return cmp;
461 };
462
463
464 /* ------------------------------------------------------------
465 ------------------------------------------------------------------
466 -------- ---------
467 -------- UTF16 section ---------
468 -------- ---------
469 ------------------------------------------------------------------
470 ------------------------------------------------------------ */
471
472 /* Get length of utf16 string in number of characters (words) */
utf16_strlen(gunichar2 * utf16)473 guint32 utf16_strlen (gunichar2 *utf16)
474 {
475 guint32 i=0;
476 if (utf16)
477 while (utf16[i] != 0) ++i;
478 return i;
479 }
480
481 /* duplicate a utf16 string */
utf16_strdup(gunichar2 * utf16)482 gunichar2 *utf16_strdup (gunichar2 *utf16)
483 {
484 guint32 len;
485 gunichar2 *new = NULL;
486
487 if (utf16)
488 {
489 len = utf16_strlen (utf16);
490 new = g_malloc (sizeof (gunichar2) * (len+1));
491 if (new) memcpy (new, utf16, sizeof (gunichar2) * (len+1));
492 }
493 return new;
494 }
495
496
497
498
499 /*------------------------------------------------------------------*\
500 * *
501 * Generic functions to handle options in pop-up requesters *
502 * *
503 \*------------------------------------------------------------------*/
504
505
506
507
508 /* Set the toggle button to active that is specified by @prefs_string
509 (integer value). If no parameter is set in the prefs, use
510 @dflt. The corresponding widget names are stored in an array
511 @widgets and are member of @win */
option_set_radio_button(GladeXML * win_xml,const gchar * prefs_string,const gchar ** widgets,gint dflt)512 void option_set_radio_button (GladeXML *win_xml,
513 const gchar *prefs_string,
514 const gchar **widgets,
515 gint dflt)
516 {
517 gint wnum, num=0;
518 GtkWidget *w;
519
520 g_return_if_fail (win_xml && prefs_string && widgets);
521
522 /* number of available widgets */
523 num=0;
524 while (widgets[num]) ++num;
525
526 if (!prefs_get_int_value (prefs_string, &wnum))
527 wnum = dflt;
528
529 if ((wnum >= num) || (wnum < 0))
530 {
531 fprintf (stderr, "Programming error: wnum > num (%d,%d,%s)\n",
532 wnum, num, prefs_string);
533 /* set to reasonable default value */
534 prefs_set_int (prefs_string, 0);
535 wnum = 0;
536 }
537 w = gtkpod_xml_get_widget (win_xml, widgets[wnum]);
538 if (w)
539 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
540 }
541
542
543 /* Retrieve which toggle button was activated and store the state in
544 * the prefs */
option_get_radio_button(GladeXML * win_xml,const gchar * prefs_string,const gchar ** widgets)545 gint option_get_radio_button (GladeXML *win_xml,
546 const gchar *prefs_string,
547 const gchar **widgets)
548 {
549 gint i;
550
551 g_return_val_if_fail (win_xml && prefs_string && widgets, 0);
552
553 for (i=0; widgets[i]; ++i)
554 {
555 GtkWidget *w = gtkpod_xml_get_widget (win_xml, widgets[i]);
556 if (w)
557 {
558 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
559 break;
560 }
561 }
562 if (!widgets[i])
563 {
564 fprintf (stderr, "Programming error: no active toggle button (%s)", prefs_string);
565 /* set reasonable default */
566 i=0;
567 }
568 prefs_set_int (prefs_string, i);
569 return i;
570 }
571
572
573 /* Set the current folder to what is stored in the prefs */
option_set_folder(GtkFileChooser * fc,const gchar * prefs_string)574 void option_set_folder (GtkFileChooser *fc, const gchar *prefs_string)
575 {
576 gchar *folder;
577
578 g_return_if_fail (fc && prefs_string);
579
580 prefs_get_string_value (prefs_string, &folder);
581 if (!folder)
582 folder = g_strdup (g_get_home_dir ());
583 gtk_file_chooser_set_current_folder (fc, folder);
584 g_free (folder);
585 }
586
587
588 /* Retrieve the current folder and write it to the prefs */
589 /* If @value is != NULL, a copy of the folder is placed into
590 @value. It has to be g_free()d after use */
option_get_folder(GtkFileChooser * fc,const gchar * prefs_string,gchar ** value)591 void option_get_folder (GtkFileChooser *fc,
592 const gchar *prefs_string,
593 gchar **value)
594 {
595 gchar *folder;
596
597 g_return_if_fail (fc && prefs_string);
598
599 folder = gtk_file_chooser_get_current_folder (fc);
600 prefs_set_string (prefs_string, folder);
601
602 if (value) *value = folder;
603 else g_free (folder);
604 }
605
606
607 /* Set the current filename to what is stored in the prefs */
option_set_filename(GtkFileChooser * fc,const gchar * prefs_string)608 void option_set_filename (GtkFileChooser *fc, const gchar *prefs_string)
609 {
610 gchar *filename;
611
612 g_return_if_fail (fc && prefs_string);
613
614 prefs_get_string_value (prefs_string, &filename);
615 if (!filename)
616 filename = g_strdup (g_get_home_dir ());
617 gtk_file_chooser_set_current_name (fc, filename);
618 g_free (filename);
619 }
620
621
622 /* Retrieve the current filename and write it to the prefs */
623 /* If @value is != NULL, a copy of the filename is placed into
624 @value. It has to be g_free()d after use */
option_get_filename(GtkFileChooser * fc,const gchar * prefs_string,gchar ** value)625 void option_get_filename (GtkFileChooser *fc,
626 const gchar *prefs_string,
627 gchar **value)
628 {
629 gchar *filename;
630
631 g_return_if_fail (fc && prefs_string);
632
633 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(fc));
634 prefs_set_string (prefs_string, filename);
635
636 if (value) *value = filename;
637 else g_free (filename);
638 }
639
640
641 /* Set the string entry @name to the prefs value stored in @name or
642 to @default if @name is not yet defined. */
option_set_string(GladeXML * win_xml,const gchar * name,const gchar * dflt)643 void option_set_string (GladeXML *win_xml,
644 const gchar *name,
645 const gchar *dflt)
646 {
647 gchar *string;
648 GtkWidget *entry;
649
650 g_return_if_fail (win_xml && name && dflt);
651
652 prefs_get_string_value (name, &string);
653
654 if (!string)
655 string = g_strdup (dflt);
656
657 entry = gtkpod_xml_get_widget (win_xml, name);
658
659 if (entry)
660 gtk_entry_set_text(GTK_ENTRY(entry), string);
661
662 g_free (string);
663 }
664
665 /* Retrieve the current content of the string entry @name and write it
666 * to the prefs (@name) */
667 /* If @value is != NULL, a copy of the string is placed into
668 @value. It has to be g_free()d after use */
option_get_string(GladeXML * win_xml,const gchar * name,gchar ** value)669 void option_get_string (GladeXML *win_xml,
670 const gchar *name,
671 gchar **value)
672 {
673 GtkWidget *entry;
674
675 g_return_if_fail (win_xml && name);
676
677 entry = gtkpod_xml_get_widget (win_xml, name);
678
679 if (entry)
680 {
681 const gchar *str = gtk_entry_get_text (GTK_ENTRY (entry));
682 prefs_set_string (name, str);
683 if (value) *value = g_strdup (str);
684 }
685 }
686
687
688 /* Set the state of toggle button @name to the prefs value stored in
689 @name or to @default if @name is not yet defined. */
option_set_toggle_button(GladeXML * win_xml,const gchar * name,gboolean dflt)690 void option_set_toggle_button (GladeXML *win_xml,
691 const gchar *name,
692 gboolean dflt)
693 {
694 gboolean active;
695 GtkWidget *button;
696
697 g_return_if_fail (win_xml && name);
698
699 if (!prefs_get_int_value (name, &active))
700 active = dflt;
701
702 button = gtkpod_xml_get_widget (win_xml, name);
703
704 if (button)
705 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button),
706 active);
707 }
708
709 /* Retrieve the current state of the toggle button @name and write it
710 * to the prefs (@name) */
711 /* Return value: the current state */
option_get_toggle_button(GladeXML * win_xml,const gchar * name)712 gboolean option_get_toggle_button (GladeXML *win_xml,
713 const gchar *name)
714 {
715 gboolean active = FALSE;
716 GtkWidget *button;
717
718 g_return_val_if_fail (win_xml && name, active);
719
720 button = gtkpod_xml_get_widget (win_xml, name);
721
722 if (button)
723 {
724 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));
725 prefs_set_int (name, active);
726 }
727 return active;
728 }
729
730
731
732 /*------------------------------------------------------------------*\
733 * *
734 * Functions to create string/filename from a template *
735 * *
736 \*------------------------------------------------------------------*/
737
738
739
740
741 /*
742 | Copyright (C) 2004 Ero Carrera <ero at dkbza.org>
743 |
744 | Placed under GPL in agreement with Ero Carrera. (JCS -- 12 March 2004)
745 */
746
747 /**
748 * Check if supported char and return substitute.
749 */
check_char(gchar c)750 static gchar check_char(gchar c)
751 {
752 gint i;
753 static const gchar
754 invalid[]={'"', '*', ':', '<', '>', '?', '\\', '|', '/', 0};
755 static const gchar
756 replace[]={'_', '_', '-', '_', '_', '_', '-', '-', '-', 0};
757 for(i=0; invalid[i]!=0; i++)
758 if(c==invalid[i]) return replace[i];
759 return c;
760 }
761
762 /**
763 * Process a path. It will substitute all the invalid characters.
764 * The changes are made within the original string. A pointer to the
765 * original string is returned.
766 */
fix_path(gchar * orig)767 static gchar *fix_path(gchar *orig)
768 {
769 if(orig)
770 {
771 gchar *op = orig;
772 while (*op)
773 {
774 *op = check_char(*op);
775 ++op;
776 }
777 }
778 return orig;
779 }
780
781 /* End of code originally supplied by Ero Carrera */
782
783
784 /* Match a list of templates @p separated by ';' with the type of the
785 filename. E.g. '%s.mp3;%t.wav' will return '%s.mp3' if @track is an
786 mp3 file, or '%t.wav' if @track is a wav file. If no template can
787 be matched, an empty string is returned.
788
789 String be freed after use.
790 */
select_template(Track * track,const gchar * p)791 static gchar *select_template (Track *track, const gchar *p)
792 {
793 gchar **templates, **tplp;
794 gchar *ext = NULL;
795 const gchar *tname;
796 gchar *result;
797 ExtraTrackData *etr;
798
799 g_return_val_if_fail (track, strdup (""));
800 etr = track->userdata;
801 g_return_val_if_fail (etr, strdup (""));
802 if (etr->pc_path_locale && strlen(etr->pc_path_locale))
803 tname = etr->pc_path_locale;
804 else
805 tname = track->ipod_path;
806 if (!tname)
807 { /* this should not happen... */
808 gchar *buf = get_track_info (track, TRUE);
809 gtkpod_warning (_("Could not process '%s' (no filename available)"),
810 buf);
811 g_free (buf);
812 }
813 ext = strrchr (tname, '.'); /* pointer to filename extension */
814
815 templates = g_strsplit (p, ";", 0);
816 tplp = templates;
817 while (*tplp)
818 {
819 if (strcmp (*tplp, "%o") == 0)
820 { /* this is only a valid extension if the original filename
821 is present */
822 if (etr->pc_path_locale && strlen(etr->pc_path_locale)) break;
823 }
824 else if (strrchr (*tplp, '.') == NULL)
825 { /* this template does not have an extension and therefore
826 * matches */
827 if (ext)
828 { /* if we have an extension, add it */
829 gchar *str = g_strdup_printf ("%s%s", *tplp, ext);
830 g_free (*tplp);
831 *tplp = str;
832 }
833 break;
834 }
835 else if (ext && (strlen (*tplp) >= strlen (ext)))
836 { /* this template is valid if the extensions match */
837 if (strcasecmp (&((*tplp)[strlen (*tplp) - strlen (ext)]),
838 ext) == 0)
839 break;
840 }
841 ++tplp;
842 }
843 result = g_strdup (*tplp);
844 g_strfreev (templates);
845 return result;
846 }
847
848
849 /* Return a string for @track built according to @template.
850
851 @is_filename: if TRUE, remove potentially harmful characters.
852 @silent: don't print error messages (no gtk_*() calls -- thread
853 safe)
854 */
get_string_from_template(Track * track,const gchar * template,gboolean is_filename,gboolean silent)855 gchar *get_string_from_template (Track *track,
856 const gchar *template,
857 gboolean is_filename,
858 gboolean silent)
859 {
860 GString *result;
861 gchar *res_utf8;
862 const gchar *p;
863 gchar *basename = NULL;
864 gchar *basename_noext = NULL;
865 ExtraTrackData *etr;
866
867 g_return_val_if_fail (track, NULL);
868 g_return_val_if_fail (template, NULL);
869 etr = track->userdata;
870 g_return_val_if_fail (etr, NULL);
871
872 result = g_string_new ("");
873
874 /* try to get the original filename */
875 if (etr->pc_path_utf8)
876 basename = g_path_get_basename (etr->pc_path_utf8);
877 /* get original filename without extension */
878 if (basename)
879 {
880 gchar *ptr;
881 basename_noext = g_strdup (basename);
882 ptr = strrchr (basename_noext, '.');
883 if (ptr) *ptr = '\0';
884 }
885
886 p=template;
887 while (*p != '\0') {
888 if (*p == '%') {
889 const gchar* tmp = NULL;
890 gchar dummy[100];
891 Playlist *pl;
892 p++;
893 switch (*p) {
894 case 'o':
895 if (basename)
896 {
897 tmp = basename;
898 }
899 break;
900 case 'O':
901 if (basename_noext)
902 {
903 tmp = basename_noext;
904 }
905 break;
906 case 'p':
907 pl = pm_get_selected_playlist ();
908 if (pl)
909 tmp = pl->name;
910 break;
911 case 'a':
912 tmp = track_get_item (track, T_ARTIST);
913 break;
914 case 'A':
915 tmp = track_get_item (track, T_ALBUM);
916 break;
917 case 't':
918 tmp = track_get_item (track, T_TITLE);
919 break;
920 case 'c':
921 tmp = track_get_item (track, T_COMPOSER);
922 break;
923 case 'g':
924 case 'G':
925 tmp = track_get_item (track, T_GENRE);
926 break;
927 case 'C':
928 if (track->cds == 0)
929 sprintf (dummy, "%.2d", track->cd_nr);
930 else if (track->cds < 10)
931 sprintf(dummy, "%.1d", track->cd_nr);
932 else if (track->cds < 100)
933 sprintf (dummy, "%.2d", track->cd_nr);
934 else if (track->cds < 1000)
935 sprintf (dummy, "%.3d", track->cd_nr);
936 else
937 sprintf (dummy,"%.4d", track->cd_nr);
938 tmp = dummy;
939 break;
940 case 'T':
941 if (track->tracks == 0)
942 sprintf (dummy, "%.2d", track->track_nr);
943 else if (track->tracks < 10)
944 sprintf(dummy, "%.1d", track->track_nr);
945 else if (track->tracks < 100)
946 sprintf (dummy, "%.2d", track->track_nr);
947 else if (track->tracks < 1000)
948 sprintf (dummy, "%.3d", track->track_nr);
949 else
950 sprintf (dummy,"%.4d", track->track_nr);
951 tmp = dummy;
952 break;
953 case 'Y':
954 sprintf (dummy, "%4d", track->year);
955 tmp = dummy;
956 break;
957 case '%':
958 tmp = "%";
959 break;
960 default:
961 if (!silent)
962 {
963 gtkpod_warning (_("Unknown token '%%%c' in template '%s'"),
964 *p, template);
965 }
966 break;
967 }
968 if (tmp)
969 {
970 gchar *tmpcp = g_strdup (tmp);
971 if (is_filename)
972 {
973 /* remove potentially illegal/harmful characters */
974 fix_path (tmpcp);
975 /* strip spaces to avoid problems with vfat */
976 g_strstrip (tmpcp);
977 /* append to current string */
978 }
979 result = g_string_append (result, tmpcp);
980 tmp = NULL;
981 g_free (tmpcp);
982 }
983 }
984 else
985 result = g_string_append_c (result, *p);
986 p++;
987 }
988 /* get the utf8 version of the filename */
989 res_utf8 = g_string_free (result, FALSE);
990
991 if (is_filename)
992 { /* remove white space before the filename extension
993 (last '.') */
994 gchar *ext = strrchr (res_utf8, '.');
995 gchar *extst = NULL;
996 if (ext)
997 {
998 extst = g_strdup (ext);
999 *ext = '\0';
1000 }
1001 g_strstrip (res_utf8);
1002 if (extst)
1003 {
1004 /* The following strcat() is safe because g_strstrip()
1005 does not increase the original string size. Therefore
1006 the result of the strcat() call will not be longer than
1007 the original string. */
1008 strcat (res_utf8, extst);
1009 g_free (extst);
1010 }
1011 }
1012
1013 g_free (basename);
1014 g_free (basename_noext);
1015
1016 return res_utf8;
1017 }
1018
1019
1020
1021 /* Return a string for @track built according to @full_template.
1022 @full_template can contain several templates separated by ';',
1023 e.g. '%s.mp3;%t.wav'. The correct one is selected using
1024 select_template() defined above.
1025
1026 If @is_filename is TRUE, potentially harmful characters are
1027 replaced in an attempt to create a valid filename.
1028
1029 If @is_filename is FALSE, the extension (e.g. '.mp3' will be
1030 removed). */
get_string_from_full_template(Track * track,const gchar * full_template,gboolean is_filename)1031 gchar *get_string_from_full_template (Track *track,
1032 const gchar *full_template,
1033 gboolean is_filename)
1034 {
1035 gchar *res_utf8;
1036 gchar *template;
1037
1038 g_return_val_if_fail (track, NULL);
1039 g_return_val_if_fail (full_template, NULL);
1040
1041 template = select_template (track, full_template);
1042
1043 if (!template)
1044 {
1045 gchar *fn = get_file_name_from_source (track, SOURCE_PREFER_LOCAL);
1046 gtkpod_warning (_("Template ('%s') does not match file type '%s'\n"), full_template, fn ? fn:"");
1047 g_free (fn);
1048 return NULL;
1049 }
1050
1051 if (!is_filename)
1052 { /* remove an extension, if present ('.???' or '.????' at the
1053 end) */
1054 gchar *pnt = strrchr (template, '.');
1055 if (pnt)
1056 {
1057 if (pnt == template+strlen(template)-3)
1058 *pnt = 0;
1059 if (pnt == template+strlen(template)-4)
1060 *pnt = 0;
1061 }
1062 }
1063
1064 res_utf8 = get_string_from_template (track, template, is_filename, FALSE);
1065
1066 g_free (template);
1067
1068 return res_utf8;
1069 }
1070
1071
1072 /**
1073 * which - run the shell command which, useful for querying default values
1074 * for executable,
1075 * @name - the executable we're trying to find the path for
1076 * Returns the path to the executable, NULL on not found
1077 */
which(const gchar * exe)1078 gchar *which (const gchar *exe)
1079 {
1080 FILE *fp = NULL;
1081 gchar *result = NULL;
1082 gchar buf[PATH_MAX];
1083 gchar *which_exec = NULL;
1084
1085 g_return_val_if_fail (exe, NULL);
1086
1087 memset(&buf[0], 0, PATH_MAX);
1088 which_exec = g_strdup_printf("which %s", exe);
1089 if((fp = popen(which_exec, "r")))
1090 {
1091 int read_bytes = 0;
1092 if((read_bytes = fread(buf, sizeof(gchar), PATH_MAX, fp)) > 0)
1093 result = g_strndup(buf, read_bytes-1);
1094 pclose(fp);
1095 }
1096 g_free(which_exec);
1097 return(result);
1098 }
1099
1100 /**
1101 * Recursively make directories.
1102 *
1103 * @silent: don't print error messages via gtk (->thread safe)
1104 *
1105 * @return FALSE is this is not possible.
1106 */
mkdirhier(const gchar * dirname,gboolean silent)1107 gboolean mkdirhier(const gchar *dirname, gboolean silent)
1108 {
1109 gchar *dn, *p;
1110
1111 g_return_val_if_fail (dirname && *dirname, FALSE);
1112
1113 if (strncmp ("~/", dirname, 2) == 0)
1114 dn = g_build_filename (g_get_home_dir(), dirname+2, NULL);
1115 else dn = g_strdup (dirname);
1116
1117 p = dn;
1118
1119 do
1120 {
1121 ++p;
1122 p = index (p, G_DIR_SEPARATOR);
1123
1124 if (p) *p = '\0';
1125
1126 if (!g_file_test(dn, G_FILE_TEST_EXISTS))
1127 {
1128 if (g_mkdir(dn, 0777) == -1)
1129 {
1130 if (!silent)
1131 {
1132 gtkpod_warning (_("Error creating %s: %s\n"),
1133 dn, g_strerror(errno));
1134 }
1135 g_free (dn);
1136 return FALSE;
1137 }
1138 }
1139 if (p) *p = G_DIR_SEPARATOR;
1140 } while (p);
1141
1142 g_free (dn);
1143 return TRUE;
1144 }
1145
1146 /**
1147 * Recursively make directories in the given filename.
1148 * @return FALSE is this is not possible.
1149 */
mkdirhierfile(const gchar * filename)1150 gboolean mkdirhierfile(const gchar *filename)
1151 {
1152 gboolean result;
1153 gchar *dirname = g_path_get_dirname (filename);
1154 result = mkdirhier (dirname, FALSE);
1155 g_free (dirname);
1156 return result;
1157 }
1158
1159
1160 /**
1161 * Convert "~/" to "/home/.../"
1162 *
1163 * g_free() return value when no longer needed.
1164 */
convert_filename(const gchar * filename)1165 gchar *convert_filename (const gchar *filename)
1166 {
1167 if (filename)
1168 {
1169 if (strncmp ("~/", filename, 2) == 0)
1170 return g_build_filename (g_get_home_dir(), filename+2, NULL);
1171 else
1172 return g_strdup (filename);
1173 }
1174
1175 return NULL;
1176 }
1177
1178
1179 /**
1180 * get_size_of_directory
1181 *
1182 * Determine the total size in bytes of all files in @dir including
1183 * subdirectories. This function ignores errors in the sense that if a
1184 * directory or file cannot be accessed, a size of 0 is assumed.
1185 */
get_size_of_directory(const gchar * dir)1186 gint64 get_size_of_directory (const gchar *dir)
1187 {
1188 GDir *gdir;
1189 const gchar *fname;
1190 gint64 tsize = 0;
1191
1192 g_return_val_if_fail (dir, 0);
1193
1194 gdir = g_dir_open (dir, 0, NULL);
1195
1196 /* Check for error */
1197 if (!gdir)
1198 return 0;
1199
1200 while ((fname = g_dir_read_name (gdir)))
1201 {
1202 gchar *fullname = g_build_filename (dir, fname, NULL);
1203 if (g_file_test (fullname, G_FILE_TEST_IS_DIR))
1204 {
1205 tsize += get_size_of_directory (fullname);
1206 }
1207 else if (g_file_test (fullname, G_FILE_TEST_IS_REGULAR))
1208 {
1209 struct stat statbuf;
1210 if (g_stat (fullname, &statbuf) == 0)
1211 { /* OK, add size */
1212 tsize += statbuf.st_size;
1213 }
1214 }
1215 g_free (fullname);
1216 }
1217
1218 g_dir_close (gdir);
1219
1220 return tsize;
1221 }
1222
1223
1224
1225 /**
1226 * Wrapper for glade_xml_new() for cygwin compatibility issues
1227 *
1228 **/
gtkpod_xml_new(const gchar * xml_file,const gchar * name)1229 GladeXML *gtkpod_xml_new (const gchar *xml_file, const gchar *name)
1230 {
1231 GladeXML *xml;
1232
1233 #ifdef ENABLE_NLS
1234 xml = glade_xml_new (xml_file, name, GETTEXT_PACKAGE);
1235 #else
1236 xml = glade_xml_new (xml_file, name, NULL);
1237 #endif
1238
1239 if (!xml)
1240 fprintf (stderr, "*** Programming error: Cannot create glade XML: '%s'\n",
1241 name);
1242
1243 return xml;
1244 }
1245
1246
1247 /**
1248 * Wrapper for gtkpod_xml_get_widget() giving out a warning if widget
1249 * could not be found.
1250 *
1251 **/
gtkpod_xml_get_widget(GladeXML * xml,const gchar * name)1252 GtkWidget *gtkpod_xml_get_widget (GladeXML *xml, const gchar *name)
1253 {
1254 GtkWidget *w = glade_xml_get_widget (xml, name);
1255
1256 if (!w)
1257 fprintf (stderr, "*** Programming error: Widget not found: '%s'\n",
1258 name);
1259
1260 return w;
1261 }
1262
1263 /* ------------------------------------------------------------
1264 *
1265 * Helper functions for pref keys
1266 *
1267 * ------------------------------------------------------------ */
1268
1269
1270 /**
1271 * Helper function to construct prefs key for itdb,
1272 * e.g. itdb_1_mountpoint...
1273 * gfree() after use
1274 *
1275 **/
get_itdb_prefs_key(gint index,const gchar * subkey)1276 gchar *get_itdb_prefs_key (gint index, const gchar *subkey)
1277 {
1278 g_return_val_if_fail (subkey, NULL);
1279
1280 return g_strdup_printf ("itdb_%d_%s", index, subkey);
1281 }
1282
1283
1284 /**
1285 * Helper function to construct prefs key for playlists,
1286 * e.g. itdb_1_playlist_xxxxxxxxxxx_syncmode...
1287 *
1288 * gfree() after use
1289 **/
get_playlist_prefs_key(gint index,Playlist * pl,const gchar * subkey)1290 gchar *get_playlist_prefs_key (gint index,
1291 Playlist *pl, const gchar *subkey)
1292 {
1293 g_return_val_if_fail (pl, NULL);
1294 g_return_val_if_fail (subkey, NULL);
1295
1296 return g_strdup_printf ("itdb_%d_playlist_%llu_%s",
1297 index, (unsigned long long)pl->id, subkey);
1298 }
1299
1300
1301 /**
1302 * Helper function to retrieve the index number of @itdb needed to
1303 * construct any keys (see above)
1304 **/
get_itdb_index(iTunesDB * itdb)1305 gint get_itdb_index (iTunesDB *itdb)
1306 {
1307 struct itdbs_head *itdbs_head;
1308
1309 itdbs_head = gp_get_itdbs_head (gtkpod_window);
1310 g_return_val_if_fail (itdbs_head, 0);
1311
1312 return g_list_index (itdbs_head->itdbs, itdb);
1313 }
1314
1315
1316 /**
1317 * Helper function to retrieve a string prefs entry for @itdb.
1318 *
1319 * gfree() after use
1320 **/
get_itdb_prefs_string(iTunesDB * itdb,const gchar * subkey)1321 gchar *get_itdb_prefs_string (iTunesDB *itdb, const gchar *subkey)
1322 {
1323 gchar *key, *value;
1324
1325 g_return_val_if_fail (itdb, NULL);
1326 g_return_val_if_fail (subkey, NULL);
1327
1328 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1329 value = prefs_get_string (key);
1330 g_free (key);
1331
1332 return value;
1333 }
1334
1335
1336 /**
1337 * Helper function to retrieve a string prefs entry for @playlist.
1338 *
1339 **/
get_playlist_prefs_string(Playlist * playlist,const gchar * subkey)1340 gchar *get_playlist_prefs_string (Playlist *playlist, const gchar *subkey)
1341 {
1342 gchar *key, *value;
1343
1344 g_return_val_if_fail (playlist, 0);
1345 g_return_val_if_fail (subkey, 0);
1346
1347 key = get_playlist_prefs_key (get_itdb_index (playlist->itdb),
1348 playlist, subkey);
1349 value = prefs_get_string (key);
1350 g_free (key);
1351
1352 return value;
1353 }
1354
1355 /**
1356 * Helper function to retrieve an int prefs entry for @itdb.
1357 *
1358 **/
get_itdb_prefs_int(iTunesDB * itdb,const gchar * subkey)1359 gint get_itdb_prefs_int (iTunesDB *itdb, const gchar *subkey)
1360 {
1361 gchar *key;
1362 gint value;
1363
1364 g_return_val_if_fail (itdb, 0);
1365 g_return_val_if_fail (subkey, 0);
1366
1367 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1368 value = prefs_get_int (key);
1369 g_free (key);
1370
1371 return value;
1372 }
1373
1374 /**
1375 * Helper function to retrieve an int prefs entry for @playlist.
1376 *
1377 **/
get_playlist_prefs_int(Playlist * playlist,const gchar * subkey)1378 gint get_playlist_prefs_int (Playlist *playlist, const gchar *subkey)
1379 {
1380 gchar *key;
1381 gint value;
1382
1383 g_return_val_if_fail (playlist, 0);
1384 g_return_val_if_fail (subkey, 0);
1385
1386 key = get_playlist_prefs_key (get_itdb_index (playlist->itdb),
1387 playlist, subkey);
1388 value = prefs_get_int (key);
1389 g_free (key);
1390
1391 return value;
1392 }
1393
1394 /**
1395 * Helper function to retrieve a string prefs entry for @itdb.
1396 *
1397 * Returns TRUE if the key was actually set in the prefs.
1398 *
1399 * gfree() after use
1400 **/
get_itdb_prefs_string_value(iTunesDB * itdb,const gchar * subkey,gchar ** value)1401 gboolean get_itdb_prefs_string_value (iTunesDB *itdb, const gchar *subkey,
1402 gchar **value)
1403 {
1404 gchar *key;
1405 gboolean result;
1406
1407 g_return_val_if_fail (itdb, FALSE);
1408 g_return_val_if_fail (subkey, FALSE);
1409
1410 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1411 result = prefs_get_string_value (key, value);
1412 g_free (key);
1413
1414 return result;
1415 }
1416
1417
1418 /**
1419 * Helper function to retrieve an in prefs entry for @itdb.
1420 *
1421 **/
get_itdb_prefs_int_value(iTunesDB * itdb,const gchar * subkey,gint * value)1422 gboolean get_itdb_prefs_int_value (iTunesDB *itdb, const gchar *subkey,
1423 gint *value)
1424 {
1425 gchar *key;
1426 gboolean result;
1427
1428 g_return_val_if_fail (itdb, FALSE);
1429 g_return_val_if_fail (subkey, FALSE);
1430
1431 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1432 result = prefs_get_int_value (key, value);
1433 g_free (key);
1434
1435 return result;
1436 }
1437
1438
1439 /**
1440 * Helper function to set a string prefs entry for @itdb.
1441 *
1442 * gfree() after use
1443 **/
set_itdb_prefs_string(iTunesDB * itdb,const gchar * subkey,const gchar * value)1444 void set_itdb_prefs_string (iTunesDB *itdb,
1445 const gchar *subkey, const gchar *value)
1446 {
1447 gchar *key;
1448
1449 g_return_if_fail (itdb);
1450 g_return_if_fail (subkey);
1451
1452 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1453 prefs_set_string (key, value);
1454 g_free (key);
1455 }
1456
1457
1458 /**
1459 * Helper function to set a string prefs entry for @itdb.
1460 *
1461 * gfree() after use
1462 **/
set_itdb_index_prefs_string(gint index,const gchar * subkey,const gchar * value)1463 void set_itdb_index_prefs_string (gint index,
1464 const gchar *subkey, const gchar *value)
1465 {
1466 gchar *key;
1467
1468 g_return_if_fail (subkey);
1469
1470 key = get_itdb_prefs_key (index, subkey);
1471 prefs_set_string (key, value);
1472 g_free (key);
1473 }
1474
1475
1476 /**
1477 * Helper function to set an in prefs entry for @itdb.
1478 *
1479 **/
set_itdb_prefs_int(iTunesDB * itdb,const gchar * subkey,gint value)1480 void set_itdb_prefs_int (iTunesDB *itdb, const gchar *subkey, gint value)
1481 {
1482 gchar *key;
1483
1484 g_return_if_fail (itdb);
1485 g_return_if_fail (subkey);
1486
1487 key = get_itdb_prefs_key (get_itdb_index (itdb), subkey);
1488 prefs_set_int (key, value);
1489 g_free (key);
1490 }
1491
1492
1493 /**
1494 * Helper function to set an in prefs entry for @itdb.
1495 *
1496 **/
set_itdb_index_prefs_int(gint index,const gchar * subkey,gint value)1497 void set_itdb_index_prefs_int (gint index,
1498 const gchar *subkey, gint value)
1499 {
1500 gchar *key;
1501
1502 g_return_if_fail (subkey);
1503
1504 key = get_itdb_prefs_key (index, subkey);
1505 prefs_set_int (key, value);
1506 g_free (key);
1507 }
1508
1509 /**
1510 * Helper function to remove all prefs strings for itdb @index and
1511 * move all subsequent itdb strings forward. You can call this even
1512 * after your have removed the itdb from itdbs_head.
1513 *
1514 **/
remove_itdb_index_prefs(gint index)1515 static void remove_itdb_index_prefs (gint index)
1516 {
1517 struct itdbs_head *itdbs_head;
1518 gchar *subkey;
1519 gint i, n;
1520
1521 itdbs_head = gp_get_itdbs_head (gtkpod_window);
1522 g_return_if_fail (itdbs_head);
1523
1524 n = g_list_length (itdbs_head->itdbs);
1525 subkey = get_itdb_prefs_key (index, "");
1526 prefs_flush_subkey (subkey);
1527 g_free (subkey);
1528
1529 for (i=index; i<=n; ++i)
1530 {
1531 gchar *from_key = get_itdb_prefs_key (i+1, "");
1532 gchar *to_key = get_itdb_prefs_key (i, "");
1533 prefs_rename_subkey (from_key, to_key);
1534 g_free (from_key);
1535 g_free (to_key);
1536 }
1537 }
1538
1539
1540 /**
1541 * Helper function to remove all prefs strings for @itdb and move all
1542 * subsequent itdb strings forward. Call this before removing the itdb
1543 * from itdbs_head.
1544 *
1545 **/
remove_itdb_prefs(iTunesDB * itdb)1546 void remove_itdb_prefs (iTunesDB *itdb)
1547 {
1548 g_return_if_fail (itdb);
1549
1550 remove_itdb_index_prefs (get_itdb_index (itdb));
1551 }
1552
1553 /* Save all itdb_<index>_* keys to the iPod
1554 * (<ControlDir>/gtkpod.prefs).
1555 *
1556 * Return value: TRUE on succes, FALSE on error
1557 */
save_ipod_index_prefs(gint index,const gchar * mountpoint)1558 gboolean save_ipod_index_prefs (gint index, const gchar *mountpoint)
1559 {
1560 TempPrefs *temp_prefs;
1561 gboolean result = FALSE;
1562 gchar *subkey, *dir;
1563
1564 g_return_val_if_fail (mountpoint, FALSE);
1565
1566 /* isolate all 'itdb_<index>_*' keys */
1567 subkey = get_itdb_prefs_key (index, "");
1568 temp_prefs = prefs_create_subset (subkey);
1569
1570 /* rename to 'itdb_*' */
1571 temp_prefs_rename_subkey (temp_prefs, subkey, "itdb_");
1572
1573 /* remove some keys */
1574 temp_prefs_remove_key (temp_prefs, "itdb_mountpoint");
1575 temp_prefs_remove_key (temp_prefs, "itdb_name");
1576 temp_prefs_remove_key (temp_prefs, "itdb_type");
1577
1578 /* build filename path */
1579 dir = itdb_get_itunes_dir (mountpoint);
1580 if (dir)
1581 {
1582 GError *error = NULL;
1583 gchar *path = g_build_filename (dir, "gtkpod.prefs", NULL);
1584 result = temp_prefs_save (temp_prefs, path, &error);
1585 if (result == FALSE)
1586 {
1587 gtkpod_warning (_("Writing preferences file '%s' failed (%s).\n\n"),
1588 path,
1589 error? error->message:_("unspecified error"));
1590 g_error_free (error);
1591 }
1592 g_free (path);
1593 g_free (dir);
1594 }
1595 else
1596 {
1597 gtkpod_warning (_("Writing preferences to the iPod (%s) failed: could not get path to Control Directory.\n\n"),
1598 mountpoint);
1599 }
1600
1601 temp_prefs_destroy (temp_prefs);
1602 g_free (subkey);
1603
1604 return result;
1605 }
1606
1607
1608
1609 /* Save all itdb_<index>_* keys to the iPod
1610 * (<ControlDir>/gtkpod.prefs).
1611 *
1612 * Return value: TRUE on succes, FALSE on error
1613 */
save_ipod_prefs(iTunesDB * itdb,const gchar * mountpoint)1614 gboolean save_ipod_prefs (iTunesDB *itdb, const gchar *mountpoint)
1615 {
1616 g_return_val_if_fail (itdb && mountpoint, FALSE);
1617 return save_ipod_index_prefs (get_itdb_index (itdb), mountpoint);
1618 }
1619
1620
1621 /* Load preferences file from the iPod and merge them into the general
1622 * prefs system.
1623 */
load_ipod_index_prefs(gint index,const gchar * mountpoint)1624 static void load_ipod_index_prefs (gint index, const gchar *mountpoint)
1625 {
1626 gchar *dir;
1627
1628 g_return_if_fail (mountpoint);
1629
1630 /* build filename path */
1631 dir = itdb_get_itunes_dir (mountpoint);
1632 if (dir)
1633 {
1634 TempPrefs *temp_prefs;
1635 GError *error = NULL;
1636 gchar *path = g_build_filename (dir, "gtkpod.prefs", NULL);
1637 temp_prefs = temp_prefs_load (path, &error);
1638 g_free (path);
1639 if (temp_prefs)
1640 {
1641 gchar *subkey;
1642 subkey = get_itdb_prefs_key (index, "");
1643 /* rename 'itdb_*' to 'itdb_<index>_*' */
1644 temp_prefs_rename_subkey (temp_prefs, "itdb_", subkey);
1645 g_free (subkey);
1646 /* merge with real prefs */
1647 temp_prefs_apply (temp_prefs);
1648 /* destroy temp prefs */
1649 temp_prefs_destroy (temp_prefs);
1650 }
1651 else
1652 {
1653 /* we ignore errors -- no need to be concerned about them */
1654 g_error_free (error);
1655 }
1656 g_free (dir);
1657 }
1658 }
1659
1660
1661
1662 /* Load preferences file from the iPod and merge them into the general
1663 * prefs system.
1664 */
load_ipod_prefs(iTunesDB * itdb,const gchar * mountpoint)1665 void load_ipod_prefs (iTunesDB *itdb, const gchar *mountpoint)
1666 {
1667 g_return_if_fail (mountpoint);
1668 load_ipod_index_prefs (get_itdb_index (itdb), mountpoint);
1669 }
1670
1671
1672
1673
1674 /* retrieve offline mode from itdb (convenience function) */
get_offline(iTunesDB * itdb)1675 gboolean get_offline (iTunesDB *itdb)
1676 {
1677 ExtraiTunesDBData *eitdb;
1678
1679 g_return_val_if_fail (itdb, FALSE);
1680 eitdb = itdb->userdata;
1681 g_return_val_if_fail (eitdb, FALSE);
1682
1683 return eitdb->offline;
1684 }
1685
1686 /* ----------------------------------------------------------------
1687 *
1688 * Main program init and shutdown
1689 *
1690 * ---------------------------------------------------------------- */
1691
1692 /**
1693 * gtkpod_init
1694 *
1695 * initialize prefs and other services as well as display
1696 */
gtkpod_init(int argc,char * argv[])1697 void gtkpod_init (int argc, char *argv[])
1698 {
1699 gchar *progname;
1700
1701 /* initialize xml_file: if gtkpod is called in the build directory
1702 (".../src/gtkpod") use the local gtkpod.glade (in the data
1703 directory), otherwise use
1704 "PACKAGE_DATA_DIR/PACKAGE/data/gtkpod.glade" */
1705
1706 progname = g_find_program_in_path (argv[0]);
1707 if (progname)
1708 {
1709 static const gchar *SEPsrcSEPgtkpod = G_DIR_SEPARATOR_S "src" G_DIR_SEPARATOR_S "gtkpod";
1710
1711 if (!g_path_is_absolute (progname))
1712 {
1713 gchar *cur_dir = g_get_current_dir ();
1714 gchar *prog_absolute;
1715
1716 if (g_str_has_prefix (progname, "." G_DIR_SEPARATOR_S))
1717 prog_absolute = g_build_filename (cur_dir,progname+2,NULL);
1718 else
1719 prog_absolute = g_build_filename (cur_dir,progname,NULL);
1720
1721 g_free (progname);
1722 g_free (cur_dir);
1723 progname = prog_absolute;
1724 }
1725
1726 if (g_str_has_suffix (progname, SEPsrcSEPgtkpod))
1727 {
1728 gchar *suffix = g_strrstr (progname, SEPsrcSEPgtkpod);
1729
1730 if (suffix)
1731 {
1732 *suffix = 0;
1733 xml_file = g_build_filename (progname, "data", "gtkpod.glade", NULL);
1734 }
1735 }
1736
1737 if (!xml_file)
1738 {
1739 gchar *prog_path = g_path_get_dirname (progname);
1740 gchar *cmake_file = g_build_filename (prog_path, "CMakeCache.txt", NULL);
1741
1742 if (g_file_test (cmake_file, G_FILE_TEST_EXISTS))
1743 {
1744 gchar *source_root = g_path_get_dirname (prog_path);
1745 xml_file = g_build_filename(source_root, "data", "gtkpod.glade", NULL);
1746 g_free (source_root);
1747 }
1748
1749 g_free (cmake_file);
1750 g_free (prog_path);
1751 }
1752
1753 g_free (progname);
1754
1755 if (xml_file && !g_file_test (xml_file, G_FILE_TEST_EXISTS))
1756 {
1757 g_free (xml_file);
1758 xml_file = NULL;
1759 }
1760 }
1761 if (!xml_file)
1762 xml_file = g_build_filename (PACKAGE_DATA_DIR, PACKAGE, "data", "gtkpod.glade", NULL);
1763 else
1764 {
1765 printf ("Using local gtkpod.glade file since program was started from source directory:\n%s\n", xml_file);
1766 }
1767
1768 /* Attempt to load libmp4v2 */
1769 mp4_init();
1770
1771 /* Initialisation of libxml */
1772 LIBXML_TEST_VERSION;
1773
1774 main_window_xml = gtkpod_xml_new (xml_file, "gtkpod");
1775
1776 glade_xml_signal_autoconnect (main_window_xml);
1777
1778 gtkpod_window = gtkpod_xml_get_widget (main_window_xml, "gtkpod");
1779
1780 prefs_init (argc, argv);
1781
1782 coverart_init (argv[0]);
1783 stockid_init (argv[0]);
1784
1785 file_convert_init ();
1786
1787 display_create ();
1788
1789 gtk_widget_show (gtkpod_window);
1790
1791 init_data (gtkpod_window); /* setup base data, importing all local
1792 * repositories */
1793
1794 /* stuff to be done before starting gtkpod */
1795 call_script ("gtkpod.in", NULL);
1796
1797 autodetection_init ();
1798
1799 server_setup (); /* start server to accept playcount updates */
1800 }
1801
1802
1803
1804 /**
1805 * gtkpod_shutdown
1806 *
1807 * free memory, shutdown services and call gtk_main_quit ()
1808 */
gtkpod_shutdown()1809 void gtkpod_shutdown ()
1810 {
1811 /* stop accepting requests for playcount updates */
1812 server_shutdown ();
1813
1814 /* Change the windows back to track view to ensure the
1815 * sorttab state is saved correctly/
1816 */
1817 gphoto_change_to_photo_window (FALSE);
1818
1819 /* Sort column order needs to be stored */
1820 tm_store_col_order();
1821
1822 /* Update default sizes */
1823 display_update_default_sizes();
1824
1825 /* shut down conversion infrastructure */
1826 file_convert_shutdown ();
1827
1828 /* Save prefs */
1829 prefs_save ();
1830
1831 /* FIXME: release memory in a clean way */
1832 #if 0
1833 remove_all_playlists (); /* first remove playlists, then tracks!
1834 * (otherwise non-existing *tracks may
1835 * be accessed) */
1836 remove_all_tracks ();
1837 #endif
1838 display_cleanup ();
1839
1840 prefs_shutdown ();
1841
1842 xmlCleanupParser();
1843 xmlMemoryDump();
1844
1845 mp4_close();
1846
1847 call_script ("gtkpod.out", NULL);
1848 gtk_main_quit ();
1849 }
1850