1 /*
2 |  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
3 |  Part of the gtkpod project.
4 |
5 |  URL: http://www.gtkpod.org/
6 |  URL: http://gtkpod.sourceforge.net/
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, write to the Free Software
20 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 |
22 |  iTunes and iPod are trademarks of Apple
23 |
24 |  This product is not supported/written/published by Apple!
25 |
26 |  $Id$
27 */
28 
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32 
33 #include "display_itdb.h"
34 #include "sha1.h"
35 #include "prefs.h"
36 #include "misc.h"
37 #include "misc_track.h"
38 #include "info.h"
39 #include "charset.h"
40 #include <math.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 
45 /* ------------------------------------------------------------ *\
46 |                                                                |
47 |         functions for sha1 checksums                            |
48 |                                                                |
49 \* ------------------------------------------------------------ */
50 
51 /**
52  * Register all tracks in the sha1 hash and remove duplicates (while
53  * preserving playlists)
54  */
gp_sha1_hash_tracks_itdb(iTunesDB * itdb)55 void gp_sha1_hash_tracks_itdb (iTunesDB *itdb)
56 {
57    gint ns, count;
58    GList *gl;
59 
60    g_return_if_fail (itdb);
61 
62    if (!prefs_get_int("sha1")) return;
63    ns = itdb_tracks_number (itdb);   /* number of tracks */
64    if (ns == 0)                 return;
65 
66    block_widgets (); /* block widgets -- this might take a while,
67 			so we'll do refreshs */
68    sha1_free (itdb);  /* release sha1 hash */
69    count = 0;
70    /* populate the hash table */
71    gl=itdb->tracks;
72    while (gl)
73    {
74        Track *track=gl->data;
75        Track *oldtrack = sha1_track_exists_insert (itdb, track);
76 
77        /* need to get next track now because it might be a duplicate and
78 	  thus be removed when we call gp_duplicate_remove() */
79        gl = gl->next;
80 
81        if (oldtrack)
82        {
83 	   gp_duplicate_remove (oldtrack, track);
84        }
85 
86        ++count;
87        if (((count % 20) == 1) || (count == ns))
88        { /* update for count == 1, 21, 41 ... and for count == n */
89 	   gtkpod_statusbar_message  (ngettext ("Hashed %d of %d track.",
90 						"Hashed %d of %d tracks.", ns),
91 				      count, ns);
92 	   while (widgets_blocked && gtk_events_pending ())
93 	       gtk_main_iteration ();
94        }
95    }
96    gp_duplicate_remove (NULL, NULL); /* show info dialogue */
97    release_widgets (); /* release widgets again */
98 }
99 
100 
101 /**
102  * Call gp_hash_tracks_itdb() for each itdb.
103  *
104  */
gp_sha1_hash_tracks(void)105 void gp_sha1_hash_tracks (void)
106 {
107     GList *gl;
108     struct itdbs_head *itdbs_head;
109 
110     g_return_if_fail (gtkpod_window);
111     itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
112 				    "itdbs_head");
113     g_return_if_fail (itdbs_head);
114 
115     block_widgets ();
116     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
117     {
118 	gp_sha1_hash_tracks_itdb (gl->data);
119     }
120     release_widgets ();
121 }
122 
123 
rm_sha1(gpointer track,gpointer user_data)124 static void rm_sha1 (gpointer track, gpointer user_data)
125 {
126     ExtraTrackData *etr;
127     g_return_if_fail (track);
128     etr = ((Track *)track)->userdata;
129     g_return_if_fail (etr);
130     C_FREE (etr->sha1_hash);
131 }
132 
133 /**
134  * Call sha1_free() for each itdb and delete sha1 checksums in all tracks.
135  *
136  */
gp_sha1_free_hash(void)137 void gp_sha1_free_hash (void)
138 {
139     GList *gl;
140     struct itdbs_head *itdbs_head;
141 
142     g_return_if_fail (gtkpod_window);
143     itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
144 				    "itdbs_head");
145     g_return_if_fail (itdbs_head);
146 
147     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
148     {
149 	iTunesDB *itdb = gl->data;
150 	g_return_if_fail (itdb);
151 	sha1_free (itdb);
152 	g_list_foreach (itdb->tracks, rm_sha1, NULL);
153     }
154 }
155 
156 
157 /* This function removes a duplicate track "track" from memory while
158  * preserving the playlists.
159  *
160  * The sha1 hash is not modified.
161  *
162  * The playcount/recent_playcount are modified to show the cumulative
163  * playcounts for that track.
164  *
165  * The star rating is set to the average of both star ratings if both
166  * ratings are not 0, or the higher rating if one of the ratings is 0
167  * (it is assumed that a rating of "0" means that no rating has been
168  * set).
169  *
170  * The "added" timestamp is set to the older entry (unless that one
171  * is 0).
172  *
173  * The "modified" and "last played" timestamps are set to the more
174  * recent entry.
175  *
176  * You should call "gp_duplicate_remove (NULL, NULL)" to pop up the info
177  * dialogue with the list of duplicate tracks afterwards. Call with
178  * "NULL, (void *)-1" to just clean up without dialoge.
179  *
180  * If "track" does not exist in
181  * the master play list, only a message is logged (to be displayed
182  * later when called with "NULL, NULL" */
gp_duplicate_remove(Track * oldtrack,Track * track)183 void gp_duplicate_remove (Track *oldtrack, Track *track)
184 {
185    gchar *buf, *buf2;
186    static gint deltrack_nr = 0;
187    static gboolean removed = FALSE;
188    static GString *str = NULL;
189 
190 /*   printf ("%p, %p, '%s'\n", oldtrack, track, str?str->str:"empty");*/
191 
192    if (prefs_get_int("show_duplicates") && !oldtrack && !track && str)
193    {
194        if (str->len)
195        { /* Some tracks have been deleted. Print a notice */
196 	   if (removed)
197 	   {
198 	       buf = g_strdup_printf (
199 		   ngettext ("The following duplicate track has been removed.",
200 			     "The following %d duplicate tracks have been removed.",
201 			     deltrack_nr), deltrack_nr);
202 	   }
203 	   else
204 	   {
205 	       buf = g_strdup_printf (
206 		   ngettext ("The following duplicate track has not been added to the master play list.",
207 			     "The following %d duplicate tracks have not been added to the master play list.",
208 			     deltrack_nr), deltrack_nr);
209 	   }
210 	   gtkpod_confirmation
211 	       (-1,                      /* gint id, */
212 		FALSE,                   /* gboolean modal, */
213 		_("Duplicate detection"),/* title */
214 		buf,                     /* label */
215 		str->str,                /* scrolled text */
216 		NULL, 0, NULL,      /* option 1 */
217 		NULL, 0, NULL,      /* option 2 */
218 		TRUE,               /* gboolean confirm_again, */
219 		"show_duplicates",
220 		                    /* ConfHandlerCA confirm_again_handler,*/
221 		CONF_NULL_HANDLER,  /* ConfHandler ok_handler,*/
222 		NULL,               /* don't show "Apply" button */
223 		NULL,               /* don't show "Cancel" button */
224 		NULL,               /* gpointer user_data1,*/
225 		NULL);              /* gpointer user_data2,*/
226 	   g_free (buf);
227        }
228    }
229 
230    if (oldtrack == NULL)
231    { /* clean up */
232        if (str)       g_string_free (str, TRUE);
233        str = NULL;
234        removed = FALSE;
235        deltrack_nr = 0;
236        gtkpod_tracks_statusbar_update();
237    }
238 
239    if (oldtrack && track)
240    {
241        ExtraTrackData *oldetr = oldtrack->userdata;
242        ExtraTrackData *etr = track->userdata;
243        iTunesDB *itdb = oldtrack->itdb;
244        g_return_if_fail (itdb);
245        g_return_if_fail (oldetr);
246        g_return_if_fail (etr);
247 
248        if (prefs_get_int("show_duplicates"))
249        {
250 	   /* add info about it to str */
251 	   buf = get_track_info (track, TRUE);
252 	   buf2 = get_track_info (oldtrack, TRUE);
253 	   if (!str)
254 	   {
255 	       deltrack_nr = 0;
256 	       str = g_string_sized_new (2000); /* used to keep record
257 						 * of duplicate
258 						 * tracks */
259 	   }
260 	   g_string_append_printf (str, "'%s': identical to '%s'\n",
261 				   buf, buf2);
262 	   g_free (buf);
263 	   g_free (buf2);
264        }
265        /* Set playcount */
266        oldtrack->playcount += track->playcount;
267        oldtrack->recent_playcount += track->recent_playcount;
268        /* Set rating */
269        if (oldtrack->rating && track->rating)
270 	   oldtrack->rating =
271 	       floor((double)(oldtrack->rating + track->rating + ITDB_RATING_STEP) /
272 		     (2 * ITDB_RATING_STEP)) * ITDB_RATING_STEP;
273        else
274 	   oldtrack->rating = MAX (oldtrack->rating, track->rating);
275        /* Set 'modified' timestamp */
276        oldtrack->time_modified =  MAX (oldtrack->time_modified,
277 				      track->time_modified);
278        /* Set 'played' timestamp */
279        oldtrack->time_played =  MAX (oldtrack->time_played, track->time_played);
280        /* Set 'added' timestamp */
281        oldtrack->time_added =  MIN (oldtrack->time_added, track->time_added);
282 
283        /* Update filename if new track has filename set (should be
284 	  always!?) and old filename is not available or no longer
285 	  valid */
286        if (etr->pc_path_locale)
287        {
288 	   if (!oldetr->pc_path_locale ||
289 	       !g_file_test (oldetr->pc_path_locale, G_FILE_TEST_IS_REGULAR))
290 	   {
291 	       g_free (oldetr->pc_path_locale);
292 	       g_free (oldetr->pc_path_utf8);
293 	       oldetr->pc_path_locale = g_strdup (etr->pc_path_locale);
294 	       oldetr->pc_path_utf8 = g_strdup (etr->pc_path_utf8);
295 	   }
296        }
297        if (itdb_playlist_contains_track (itdb_playlist_mpl (itdb),
298 					 track))
299        { /* track is already added to memory -> replace with "oldtrack" */
300 	   /* check for "track" in all playlists (except for MPL) */
301 	   GList *gl;
302 	   gl = g_list_nth (itdb->playlists, 1);
303 	   while (gl)
304 	   {
305 	       Playlist *pl = gl->data;
306 	       g_return_if_fail (pl);
307 	       /* if "track" is in playlist pl, we remove it and add
308 		  the "oldtrack" instead (this way round we don't have
309 		  to worry about changing sha1 hash entries */
310 	       if (itdb_playlist_contains_track (pl, track))
311 	       {
312 		   gp_playlist_remove_track (pl, track,
313 					     DELETE_ACTION_PLAYLIST);
314 		   if (!itdb_playlist_contains_track (pl, oldtrack))
315 		       gp_playlist_add_track (pl, oldtrack, TRUE);
316 	       }
317 	       gl = gl->next;
318 	   }
319 	   /* remove track from MPL, i.e. from the ipod (or the local
320 	    * database */
321 	   if (itdb->usertype & GP_ITDB_TYPE_IPOD)
322 	   {
323 	       gp_playlist_remove_track (NULL, track, DELETE_ACTION_IPOD);
324 	   }
325 	   if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
326 	   {
327 	       gp_playlist_remove_track (NULL, track,
328 					 DELETE_ACTION_DATABASE);
329 	   }
330 	   removed = TRUE;
331        }
332        ++deltrack_nr; /* count duplicate tracks */
333        data_changed (itdb);
334    }
335 }
336 
337 
338 /**
339  * Register all tracks in the sha1 hash and remove duplicates (while
340  * preserving playlists).
341  * Call  gp_duplicate_remove (NULL, NULL); to show an info dialogue
342  */
gp_itdb_hash(iTunesDB * itdb)343 void gp_itdb_hash (iTunesDB *itdb)
344 {
345    gint ns, track_nr;
346    Track *track, *oldtrack;
347 
348    g_return_if_fail (itdb);
349 
350 
351    if (!prefs_get_int("sha1")) return;
352 
353    ns = itdb_tracks_number (itdb);
354    if (ns == 0)                 return;
355 
356    block_widgets (); /* block widgets -- this might take a while,
357 			so we'll do refreshs */
358    sha1_free (itdb);  /* release sha1 hash */
359    track_nr = 0;
360    /* populate the hash table */
361    while ((track = g_list_nth_data (itdb->tracks, track_nr)))
362    {
363        oldtrack = sha1_track_exists_insert (itdb, track);
364 /*        printf("%d:%d:%p:%p\n", count, track_nr, track, oldtrack); */
365        if (oldtrack)
366        {
367 	   gp_duplicate_remove (oldtrack, track);
368        }
369        else
370        { /* if we removed a track (above), we don't need to increment
371 	    the track_nr here */
372 	   ++track_nr;
373        }
374    }
375    release_widgets (); /* release widgets again */
376 }
377 
378 
379 
380 
381 /* ------------------------------------------------------------ *\
382 |                                                                |
383 |         functions to locate tracks                             |
384 |                                                                |
385 \* ------------------------------------------------------------ */
386 
387 
388 /* Returns the track with the filename @name or NULL, if none can be
389  * found. This function also works if @filename is on the iPod. */
gp_track_by_filename(iTunesDB * itdb,gchar * filename)390 Track *gp_track_by_filename (iTunesDB *itdb, gchar *filename)
391 {
392     gchar *musicdir = NULL;
393     Track *result = NULL;
394 
395     g_return_val_if_fail (itdb, NULL);
396     g_return_val_if_fail (filename, NULL);
397 
398     if (itdb->usertype & GP_ITDB_TYPE_IPOD)
399     {
400 	gchar *mountpoint = get_itdb_prefs_string (itdb, KEY_MOUNTPOINT);
401 	g_return_val_if_fail (mountpoint, NULL);
402 	musicdir = itdb_get_music_dir (mountpoint);
403 	if (!musicdir)
404 	{
405 	    /* FIXME: guess */
406 	    musicdir = g_build_filename (mountpoint, "iPod_Control",
407 					 "Music", NULL);
408 	}
409 	g_free (mountpoint);
410     }
411     if ((itdb->usertype & GP_ITDB_TYPE_IPOD) &&
412 	(musicdir != NULL) &&
413 	(strncmp (filename, musicdir, strlen (musicdir)) == 0))
414     {   /* handle track on iPod (in music dir) */
415 	GList *gl;
416 	for (gl=itdb->tracks; gl&&!result; gl=gl->next)
417 	{
418 	    Track *track = gl->data;
419 	    gchar *ipod_path;
420 	    g_return_val_if_fail (track, NULL);
421 	    ipod_path = itdb_filename_on_ipod (track);
422 	    if (ipod_path)
423 	    {
424 		if (strcasecmp (ipod_path, filename) == 0)
425 		{
426 		    result = track;
427 		}
428 		g_free (ipod_path);
429 	    }
430 	}
431     }
432     else
433     {   /* handle track on local filesystem */
434 	GList *gl;
435 	for (gl=itdb->tracks; gl&&!result; gl=gl->next)
436 	{
437 	    Track *track = gl->data;
438 	    ExtraTrackData *etr;
439 	    g_return_val_if_fail (track, NULL);
440 	    etr = track->userdata;
441 	    g_return_val_if_fail (etr, NULL);
442 	    if (etr->pc_path_locale)
443 	    {
444 		if (strcmp (etr->pc_path_locale, filename) == 0)
445 		    result = track;
446 	    }
447 	}
448     }
449     g_free (musicdir);
450     return result;
451 }
452 
453 
454 /* Find @track in repository @itdb by the following methods and return
455    the matches:
456 
457    1) DND origin data
458    2) filename matches
459    3) SHA1 match
460 
461    If DND origin data is available and valid only one track is
462    returned. Otherwise all tracks matching the filename and the SHA1
463    are returned.
464 
465    If DND origin data is found to be invalid it is deleted.
466 
467    Return value: a GList with matching tracks. You must call
468    g_list_free() on the list when it is no longer needed.
469 */
gp_itdb_find_same_tracks(iTunesDB * itdb,Track * track)470 GList *gp_itdb_find_same_tracks (iTunesDB *itdb, Track *track)
471 {
472     ExtraTrackData *etr;
473     Track *itr;
474     GList *tracks = NULL;
475 
476     g_return_val_if_fail (itdb, NULL);
477     g_return_val_if_fail (track, NULL);
478 
479     etr = track->userdata;
480     g_return_val_if_fail (etr, NULL);
481 
482     if (itdb->id == etr->local_itdb_id)
483     {   /* we can probably find the original track from the DND data */
484 	GList *gl;
485 	for (gl=itdb->tracks; gl; gl=gl->next)
486 	{
487 	    itr = gl->data;
488 	    g_return_val_if_fail (itr, NULL);
489 	    if (itr->dbid == etr->local_track_dbid)
490 	    {   /* found track */
491 		tracks = g_list_prepend (tracks, itr);
492 		return tracks;
493 	    }
494 	}
495 	/* DND origin data is no longer valid */
496 	etr->local_itdb_id = 0;
497 	etr->local_track_dbid = 0;
498     }
499 
500     /* No luck so far -- let's get filename matches */
501     tracks = gp_itdb_pc_path_hash_find_tracks (itdb, etr->pc_path_utf8);
502 
503     /* And also try SHA1 match */
504     itr = sha1_sha1_exists (itdb, etr->sha1_hash);
505 
506     if (itr)
507     {   /* insert into tracks list if not already present */
508 	if (!g_list_find (tracks, itr))
509 	{
510 	    tracks = g_list_prepend (tracks, itr);
511 	}
512     }
513 
514     return tracks;
515 }
516 
517 
518 /* Find @track in all local repositories and return a list.
519 
520    This function calls gp_itdb_find_same_tracks() for each local
521    repository and concatenates the results into one list.
522 
523    Return value: a GList with matching tracks. You must call
524    g_list_free() on the list when it is no longer needed.
525 */
gp_itdb_find_same_tracks_in_local_itdbs(Track * track)526 GList *gp_itdb_find_same_tracks_in_local_itdbs (Track *track)
527 {
528     GList *gl, *tracks=NULL;
529     struct itdbs_head *ih = gp_get_itdbs_head (gtkpod_window);
530 
531     g_return_val_if_fail (ih, NULL);
532     g_return_val_if_fail (track, NULL);
533 
534     for (gl=ih->itdbs; gl; gl=gl->next)
535     {
536 	iTunesDB *itdb = gl->data;
537 	g_return_val_if_fail (itdb, tracks);
538 	if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
539 	{
540 	    GList *addtracks = gp_itdb_find_same_tracks (itdb, track);
541 	    tracks = g_list_concat (tracks, addtracks);
542 	}
543     }
544     return tracks;
545 }
546 
547 
548 
549 /* Find @track in all repositories (local and iPod) and return a list.
550 
551    This function calls gp_itdb_find_same_tracks() for each
552    repository and concatenates the results into one list.
553 
554    Return value: a GList with matching tracks. You must call
555    g_list_free() on the list when it is no longer needed.
556 */
gp_itdb_find_same_tracks_in_itdbs(Track * track)557 GList *gp_itdb_find_same_tracks_in_itdbs (Track *track)
558 {
559     GList *gl, *tracks=NULL;
560     struct itdbs_head *ih = gp_get_itdbs_head (gtkpod_window);
561 
562     g_return_val_if_fail (ih, NULL);
563     g_return_val_if_fail (track, NULL);
564 
565     for (gl=ih->itdbs; gl; gl=gl->next)
566     {
567 	GList *addtracks;
568 	iTunesDB *itdb = gl->data;
569 	g_return_val_if_fail (itdb, tracks);
570 	addtracks = gp_itdb_find_same_tracks (itdb, track);
571 	tracks = g_list_concat (tracks, addtracks);
572     }
573     return tracks;
574 }
575 
576 
577 
578 /* ------------------------------------------------------------ *\
579 |                                                                |
580 |         functions for local path hashtable                     |
581 |                                                                |
582 \* ------------------------------------------------------------ */
583 
584 /* set up hash table for local filenames */
gp_itdb_pc_path_hash_init(ExtraiTunesDBData * eitdb)585 void gp_itdb_pc_path_hash_init (ExtraiTunesDBData *eitdb)
586 {
587     g_return_if_fail (eitdb);
588 
589     if (!eitdb->pc_path_hash)
590     {
591 	eitdb->pc_path_hash = g_hash_table_new_full (
592 	    g_str_hash, g_str_equal,
593 	    g_free, NULL);
594     }
595 }
596 
597 
598 /* function for destroying hash value, used when destroying the hash
599  * table in gp_itdb_local_path_hash_destroy() below. */
pc_path_hash_free_value(gpointer key,gpointer value,gpointer userdata)600 static void pc_path_hash_free_value (gpointer key,
601 					gpointer value,
602 					gpointer userdata)
603 {
604     g_list_free (value);
605 }
606 
607 
608 /* free all memory associated with the local_path_hash */
gp_itdb_pc_path_hash_destroy(ExtraiTunesDBData * eitdb)609 void gp_itdb_pc_path_hash_destroy (ExtraiTunesDBData *eitdb)
610 {
611     g_return_if_fail (eitdb);
612 
613     if (eitdb->pc_path_hash)
614     {
615 	g_hash_table_foreach (eitdb->pc_path_hash,
616 			      pc_path_hash_free_value,
617 			      NULL);
618 	g_hash_table_destroy (eitdb->pc_path_hash);
619 	eitdb->pc_path_hash = NULL;
620     }
621 }
622 
623 
624 /* Add track to filehash. This function must only be called once for
625  * each track. */
gp_itdb_pc_path_hash_add_track(Track * track)626 void gp_itdb_pc_path_hash_add_track (Track *track)
627 {
628     iTunesDB *itdb;
629     ExtraTrackData *etr;
630     ExtraiTunesDBData *eitdb;
631 
632     g_return_if_fail (track);
633     etr = track->userdata;
634     g_return_if_fail (etr);
635 
636     itdb = track->itdb;
637     g_return_if_fail (itdb);
638 
639     eitdb = itdb->userdata;
640     g_return_if_fail (eitdb);
641     g_return_if_fail (eitdb->pc_path_hash);
642 
643     g_return_if_fail (track);
644     etr = track->userdata;
645     g_return_if_fail (etr);
646 
647     /* This is only to detect programming errors */
648     g_return_if_fail (!etr->pc_path_hashed);
649 
650     if (etr->pc_path_utf8 && *etr->pc_path_utf8)
651     {   /* add to hash table */
652 	GList *tracks;
653 	tracks = g_hash_table_lookup (eitdb->pc_path_hash, etr->pc_path_utf8);
654 	tracks = g_list_prepend (tracks, track);
655 	g_hash_table_replace (eitdb->pc_path_hash,
656 			      g_strdup (etr->pc_path_utf8),
657 			      tracks);
658 	/* This is only to detect programming errors */
659 	etr->pc_path_hashed = TRUE;
660     }
661 }
662 
663 /* used in the next two functions */
664 struct pc_path_hash_find_track_data
665 {
666     Track *track;
667     gchar *key;
668 };
669 
670 
671 /* Used in the next function. Return TRUE and the current key if
672    td->track is contained in the list for key */
pc_path_hash_find_track(gpointer key,gpointer value,gpointer user_data)673 static gboolean pc_path_hash_find_track (gpointer key,
674 					 gpointer value,
675 					 gpointer user_data)
676 {
677     GList *tracks = value;
678     struct pc_path_hash_find_track_data *td = user_data;
679 
680     if (g_list_find (tracks, td->track))
681     {
682 	td->key = g_strdup (key);
683 	return TRUE;
684     }
685     return FALSE;
686 }
687 
688 
689 
690 /* remove track from filehash */
gp_itdb_pc_path_hash_remove_track(Track * track)691 void gp_itdb_pc_path_hash_remove_track (Track *track)
692 {
693     struct pc_path_hash_find_track_data td;
694     ExtraTrackData *etr;
695     iTunesDB *itdb;
696     ExtraiTunesDBData *eitdb;
697     GList *tracks;
698 
699     g_return_if_fail (track);
700     etr = track->userdata;
701     g_return_if_fail (etr);
702 
703     itdb = track->itdb;
704     g_return_if_fail (itdb);
705 
706     eitdb = itdb->userdata;
707     g_return_if_fail (eitdb);
708     g_return_if_fail (eitdb->pc_path_hash);
709 
710     if (!etr->pc_path_hashed)	return;
711 
712     if (etr->pc_path_utf8 && *etr->pc_path_utf8)
713     {   /* try lookup with filename */
714 	GList *tracks;
715 	tracks = g_hash_table_lookup (eitdb->pc_path_hash, etr->pc_path_utf8);
716 	if (tracks)
717 	{   /* filename exists */
718 	    GList *link = g_list_find (tracks, track);
719 	    if (link)
720 	    {   /* track found */
721 		tracks = g_list_remove_link (tracks, link);
722 		if (tracks)
723 		{   /* still tracks left under this filename */
724 		    g_hash_table_replace (eitdb->pc_path_hash,
725 					  g_strdup (etr->pc_path_utf8),
726 					  tracks);
727 		}
728 		else
729 		{   /* no more tracks left under this filename */
730 		    g_hash_table_remove (eitdb->pc_path_hash,
731 					 etr->pc_path_utf8);
732 		}
733 		etr->pc_path_hashed = FALSE;
734 		return;
735 	    }
736 	}
737     }
738 
739     /* We didn't find the track by filename, or now filename is
740      * available any more) -> search through the list */
741     td.track = track;
742     td.key = NULL;
743     tracks = g_hash_table_find (eitdb->pc_path_hash,
744 				pc_path_hash_find_track,
745 				&td);
746     if (tracks)
747     {
748 	tracks = g_list_remove (tracks, track);
749 	if (tracks)
750 	{   /* still tracks left under this filename */
751 	    g_hash_table_replace (eitdb->pc_path_hash,
752 				  g_strdup (td.key),
753 				  tracks);
754 	}
755 	else
756 	{   /* no more tracks left under this filename */
757 	    g_hash_table_remove (eitdb->pc_path_hash,
758 				 td.key);
759 	}
760 	etr->pc_path_hashed = FALSE;
761 	g_free (td.key);
762     }
763 }
764 
765 
766 /* Return all tracks with @filename (@filename in UTF8).
767    You must g_list_free the returned list when it is not longer needed. */
gp_itdb_pc_path_hash_find_tracks(iTunesDB * itdb,const gchar * filename)768 GList *gp_itdb_pc_path_hash_find_tracks (iTunesDB *itdb, const gchar *filename)
769 {
770     ExtraiTunesDBData *eitdb;
771     GList *tracks = NULL;
772 
773     g_return_val_if_fail (itdb, NULL);
774     eitdb = itdb->userdata;
775     g_return_val_if_fail (eitdb, NULL);
776     g_return_val_if_fail (eitdb->pc_path_hash, NULL);
777 
778     if (filename && *filename)
779 	tracks = g_hash_table_lookup (eitdb->pc_path_hash, filename);
780 
781     return g_list_copy (tracks);
782 }
783 
784 
785 
786 /* ------------------------------------------------------------ *\
787 |                                                                |
788 |         functions to retrieve information from tracks          |
789 |                                                                |
790 \* ------------------------------------------------------------ */
791 
792 /* return the address of the UTF8 field @t_item. @t_item is one of
793  * (the applicable) T_* defined in track.h */
track_get_item_pointer(Track * track,T_item t_item)794 gchar **track_get_item_pointer (Track *track, T_item t_item)
795 {
796     gchar **result = NULL;
797     ExtraTrackData *etr;
798 
799     g_return_val_if_fail (track, NULL);
800     etr = track->userdata;
801     g_return_val_if_fail (etr, NULL);
802 
803     switch (t_item)
804     {
805     case T_ALBUM:
806 	result = &track->album;
807 	break;
808     case T_ARTIST:
809 	result = &track->artist;
810 	break;
811     case T_TITLE:
812 	result = &track->title;
813 	break;
814     case T_GENRE:
815 	result = &track->genre;
816 	break;
817     case T_COMMENT:
818 	result = &track->comment;
819 	break;
820     case T_COMPOSER:
821 	result = &track->composer;
822 	break;
823     case T_FILETYPE:
824 	result = &track->filetype;
825 	break;
826     case T_IPOD_PATH:
827 	result = &track->ipod_path;
828 	break;
829     case T_PC_PATH:
830 	result = &etr->pc_path_utf8;
831 	break;
832     case T_YEAR:
833 	result = &etr->year_str;
834 	break;
835     case T_GROUPING:
836 	result = &track->grouping;
837 	break;
838     case T_CATEGORY:
839 	result = &track->category;
840 	break;
841     case T_DESCRIPTION:
842 	result = &track->description;
843 	break;
844     case T_PODCASTURL:
845 	result = &track->podcasturl;
846 	break;
847     case T_PODCASTRSS:
848 	result = &track->podcastrss;
849 	break;
850     case T_SUBTITLE:
851 	result = &track->subtitle;
852 	break;
853     case T_THUMB_PATH:
854 	result = &etr->thumb_path_utf8;
855 	break;
856     case T_TV_SHOW:
857         result = &track->tvshow;
858         break;
859     case T_TV_EPISODE:
860         result = &track->tvepisode;
861         break;
862     case T_TV_NETWORK:
863         result = &track->tvnetwork;
864         break;
865     case T_ALBUMARTIST:
866 	result = &track->albumartist;
867 	break;
868     case T_SORT_ARTIST:
869 	result = &track->sort_artist;
870 	break;
871     case T_SORT_TITLE:
872 	result = &track->sort_title;
873 	break;
874     case T_SORT_ALBUM:
875 	result = &track->sort_album;
876 	break;
877     case T_SORT_ALBUMARTIST:
878 	result = &track->sort_albumartist;
879 	break;
880     case T_SORT_COMPOSER:
881 	result = &track->sort_composer;
882 	break;
883     case T_SORT_TVSHOW:
884 	result = &track->sort_tvshow;
885 	break;
886     case T_LYRICS:
887 	result = &etr->lyrics;
888 	break;
889     case T_ALL:
890     case T_IPOD_ID:
891     case T_TRACK_NR:
892     case T_TRANSFERRED:
893     case T_SIZE:
894     case T_TRACKLEN:
895     case T_STARTTIME:
896     case T_STOPTIME:
897     case T_BITRATE:
898     case T_SAMPLERATE:
899     case T_BPM:
900     case T_PLAYCOUNT:
901     case T_RATING:
902     case T_TIME_ADDED:
903     case T_TIME_PLAYED:
904     case T_TIME_MODIFIED:
905     case T_TIME_RELEASED:
906     case T_VOLUME:
907     case T_SOUNDCHECK:
908     case T_CD_NR:
909     case T_COMPILATION:
910     case T_REMEMBER_PLAYBACK_POSITION:
911     case T_SKIP_WHEN_SHUFFLING:
912     case T_CHECKED:
913     case T_MEDIA_TYPE:
914     case T_SEASON_NR:
915     case T_EPISODE_NR:
916     case T_GAPLESS_TRACK_FLAG:
917     case T_ITEM_NUM:
918 	g_return_val_if_reached (NULL);
919     }
920     return result;
921 }
922 
923 
924 
925 
926 
927 
928 
929 /* return the UTF8 item @t_item. @t_item is one of
930    (the applicable) T_* defined in track.h */
track_get_item(Track * track,T_item t_item)931 const gchar *track_get_item (Track *track, T_item t_item)
932 {
933     gchar **ptr;
934 
935     g_return_val_if_fail (track, NULL);
936 
937     ptr = track_get_item_pointer (track, t_item);
938 
939     if (ptr)     return *ptr;
940     else         return NULL;
941 }
942 
943 
944 /* Copy item @item from @frtrack to @totrack.
945    Return value:
946      TRUE: @totrack was changed
947      FALSE: @totrack is unchanged
948 */
track_copy_item(Track * frtrack,Track * totrack,T_item item)949 gboolean track_copy_item (Track *frtrack, Track *totrack, T_item item)
950 {
951     gboolean changed = FALSE;
952     const gchar *fritem;
953     gchar **toitem_ptr;
954     ExtraTrackData *efrtr, *etotr;
955 
956     g_return_val_if_fail (frtrack, FALSE);
957     g_return_val_if_fail (totrack, FALSE);
958     g_return_val_if_fail ((item > 0) && (item < T_ITEM_NUM), FALSE);
959 
960     efrtr = frtrack->userdata;
961     etotr = totrack->userdata;
962     g_return_val_if_fail (efrtr, FALSE);
963     g_return_val_if_fail (etotr, FALSE);
964 
965     if (frtrack == totrack) return FALSE;
966 
967     switch (item)
968     {
969     case T_ALBUM:
970     case T_ARTIST:
971     case T_TITLE:
972     case T_GENRE:
973     case T_COMMENT:
974     case T_COMPOSER:
975     case T_FILETYPE:
976     case T_IPOD_PATH:
977     case T_PC_PATH:
978     case T_YEAR:
979     case T_GROUPING:
980     case T_CATEGORY:
981     case T_DESCRIPTION:
982     case T_PODCASTURL:
983     case T_PODCASTRSS:
984     case T_SUBTITLE:
985     case T_THUMB_PATH:
986     case T_TV_SHOW:
987     case T_TV_EPISODE:
988     case T_TV_NETWORK:
989     case T_ALBUMARTIST:
990     case T_SORT_ARTIST:
991     case T_SORT_TITLE:
992     case T_SORT_ALBUM:
993     case T_SORT_ALBUMARTIST:
994     case T_SORT_COMPOSER:
995     case T_SORT_TVSHOW:
996     case T_LYRICS:
997 	fritem = track_get_item (frtrack, item);
998 	toitem_ptr = track_get_item_pointer (totrack, item);
999 	g_return_val_if_fail (fritem, FALSE);
1000 	g_return_val_if_fail (toitem_ptr, FALSE);
1001 	if ((*toitem_ptr == NULL) || (strcmp (fritem, *toitem_ptr) != 0))
1002 	{
1003 	    g_free (*toitem_ptr);
1004 	    *toitem_ptr = g_strdup (fritem);
1005 	    changed = TRUE;
1006 	}
1007 	if (item == T_YEAR)
1008 	{
1009 	    if (totrack->year != frtrack->year)
1010 	    {
1011 		totrack->year = frtrack->year;
1012 		changed = TRUE;
1013 	    }
1014 	}
1015 	else if ((changed) && (item == T_LYRICS))
1016 	{
1017 	    write_lyrics_to_file (totrack);
1018 	}
1019 	/* handle items that have two entries */
1020 	if (item == T_PC_PATH)
1021 	{
1022 	    if ((etotr->pc_path_locale == NULL) ||
1023 		(strcmp (efrtr->pc_path_locale, etotr->pc_path_locale) != 0))
1024 	    {
1025 		g_free (etotr->pc_path_locale);
1026 		etotr->pc_path_locale = g_strdup (efrtr->pc_path_locale);
1027 		changed = TRUE;
1028 	    }
1029 	}
1030 	if (item == T_THUMB_PATH)
1031 	{
1032 	    if ((etotr->thumb_path_locale == NULL) ||
1033 		(strcmp (efrtr->thumb_path_locale, etotr->thumb_path_locale) != 0))
1034 	    {
1035 		g_free (etotr->thumb_path_locale);
1036 		etotr->thumb_path_locale = g_strdup (efrtr->thumb_path_locale);
1037 		changed = TRUE;
1038 	    }
1039 	}
1040 	break;
1041     case T_IPOD_ID:
1042 	if (frtrack->id != totrack->id)
1043 	{
1044 	    totrack->id = frtrack->id;
1045 	    changed = TRUE;
1046 	}
1047 	break;
1048     case T_TRACK_NR:
1049 	if (frtrack->track_nr != totrack->track_nr)
1050 	{
1051 	    totrack->track_nr = frtrack->track_nr;
1052 	    changed = TRUE;
1053 	}
1054 	if (frtrack->tracks != totrack->tracks)
1055 	{
1056 	    totrack->tracks = frtrack->tracks;
1057 	    changed = TRUE;
1058 	}
1059 	break;
1060     case T_TRANSFERRED:
1061 	if (frtrack->transferred != totrack->transferred)
1062 	{
1063 	    totrack->transferred = frtrack->transferred;
1064 	    changed = TRUE;
1065 	}
1066 	break;
1067     case T_SIZE:
1068 	if (frtrack->size != totrack->size)
1069 	{
1070 	    totrack->size = frtrack->size;
1071 	    changed = TRUE;
1072 	}
1073 	break;
1074     case T_TRACKLEN:
1075 	if (frtrack->tracklen != totrack->tracklen)
1076 	{
1077 	    totrack->tracklen = frtrack->tracklen;
1078 	    changed = TRUE;
1079 	}
1080 	break;
1081     case T_STARTTIME:
1082 	if (frtrack->starttime != totrack->starttime)
1083 	{
1084 	    totrack->starttime = frtrack->starttime;
1085 	    changed = TRUE;
1086 	}
1087 	break;
1088     case T_STOPTIME:
1089 	if (frtrack->stoptime != totrack->stoptime)
1090 	{
1091 	    totrack->stoptime = frtrack->stoptime;
1092 	    changed = TRUE;
1093 	}
1094 	break;
1095     case T_BITRATE:
1096 	if (frtrack->bitrate != totrack->bitrate)
1097 	{
1098 	    totrack->bitrate = frtrack->bitrate;
1099 	    changed = TRUE;
1100 	}
1101 	break;
1102     case T_SAMPLERATE:
1103 	if (frtrack->samplerate != totrack->samplerate)
1104 	{
1105 	    totrack->samplerate = frtrack->samplerate;
1106 	    changed = TRUE;
1107 	}
1108 	break;
1109     case T_BPM:
1110 	if (frtrack->BPM != totrack->BPM)
1111 	{
1112 	    totrack->BPM = frtrack->BPM;
1113 	    changed = TRUE;
1114 	}
1115 	break;
1116     case T_PLAYCOUNT:
1117 	if (frtrack->playcount != totrack->playcount)
1118 	{
1119 	    totrack->playcount = frtrack->playcount;
1120 	    changed = TRUE;
1121 	}
1122 	break;
1123     case T_RATING:
1124 	if (frtrack->rating != totrack->rating)
1125 	{
1126 	    totrack->rating = frtrack->rating;
1127 	    changed = TRUE;
1128 	}
1129 	break;
1130     case T_TIME_ADDED:
1131     case T_TIME_PLAYED:
1132     case T_TIME_MODIFIED:
1133     case T_TIME_RELEASED:
1134 	if (time_get_time (frtrack, item) !=
1135 	    time_get_time (totrack, item))
1136 	{
1137 	    time_set_time (totrack, time_get_time (frtrack, item), item);
1138 	    changed = TRUE;
1139 	}
1140 	break;
1141     case T_VOLUME:
1142 	if (frtrack->volume != totrack->volume)
1143 	{
1144 	    totrack->volume = frtrack->volume;
1145 	    changed = TRUE;
1146 	}
1147 	break;
1148     case T_SOUNDCHECK:
1149 	if (frtrack->soundcheck != totrack->soundcheck)
1150 	{
1151 	    totrack->soundcheck = frtrack->soundcheck;
1152 	    changed = TRUE;
1153 	}
1154 	break;
1155     case T_CD_NR:
1156 	if (frtrack->cd_nr != totrack->cd_nr)
1157 	{
1158 	    totrack->cd_nr = frtrack->cd_nr;
1159 	    changed = TRUE;
1160 	}
1161 	if (frtrack->cds != totrack->cds)
1162 	{
1163 	    totrack->cds = frtrack->cds;
1164 	    changed = TRUE;
1165 	}
1166 	break;
1167     case T_COMPILATION:
1168 	if (frtrack->compilation != totrack->compilation)
1169 	{
1170 	    totrack->compilation = frtrack->compilation;
1171 	    changed = TRUE;
1172 	}
1173 	break;
1174     case T_REMEMBER_PLAYBACK_POSITION:
1175 	if (frtrack->remember_playback_position != totrack->remember_playback_position)
1176 	{
1177 	    totrack->remember_playback_position = frtrack->remember_playback_position;
1178 	    changed = TRUE;
1179 	}
1180 	break;
1181     case T_SKIP_WHEN_SHUFFLING:
1182 	if (frtrack->skip_when_shuffling != totrack->skip_when_shuffling)
1183 	{
1184 	    totrack->skip_when_shuffling = frtrack->skip_when_shuffling;
1185 	    changed = TRUE;
1186 	}
1187 	break;
1188     case T_CHECKED:
1189 	if (frtrack->checked != totrack->checked)
1190 	{
1191 	    totrack->checked = frtrack->checked;
1192 	    changed = TRUE;
1193 	}
1194 	break;
1195     case T_MEDIA_TYPE:
1196 	if (frtrack->mediatype != totrack->mediatype)
1197 	{
1198 	    totrack->mediatype = frtrack->mediatype;
1199 	    changed = TRUE;
1200 	}
1201 	break;
1202     case T_SEASON_NR:
1203 	if (frtrack->season_nr != totrack->season_nr)
1204 	{
1205 	    totrack->season_nr = frtrack->season_nr;
1206 	    changed = TRUE;
1207 	}
1208 	break;
1209     case T_EPISODE_NR:
1210 	if (frtrack->episode_nr != totrack->episode_nr)
1211 	{
1212 	    totrack->episode_nr = frtrack->episode_nr;
1213 	    changed = TRUE;
1214 	}
1215 	break;
1216     case T_GAPLESS_TRACK_FLAG:
1217 	if (frtrack->gapless_track_flag != totrack->gapless_track_flag)
1218 	{
1219 	    totrack->gapless_track_flag = frtrack->gapless_track_flag;
1220 	    changed = TRUE;
1221 	}
1222 	break;
1223     case T_ITEM_NUM:
1224     case T_ALL:
1225 	g_return_val_if_reached (FALSE);
1226 
1227     }
1228     return changed;
1229 }
1230 
1231 
1232 /* return a pointer to the specified timestamp. @t_item is one of (the
1233    applicable) T_* defined in track.h.  If the parameters are illegal,
1234    "0" is returned. */
track_get_timestamp_ptr(Track * track,T_item t_item)1235 time_t *track_get_timestamp_ptr (Track *track, T_item t_item)
1236 {
1237     g_return_val_if_fail (track, NULL);
1238 
1239     switch (t_item)
1240     {
1241     case T_TIME_PLAYED:
1242 	return &track->time_played;
1243     case T_TIME_MODIFIED:
1244 	return &track->time_modified;
1245     case T_TIME_RELEASED:
1246 	return &track->time_released;
1247     case T_TIME_ADDED:
1248 	return &track->time_added;
1249     default:
1250 	g_return_val_if_reached (0);
1251     }
1252 }
1253 
1254 
1255 /* return the specified timestamp. @t_item is one of
1256    (the * applicable) T_* defined in track.h. If the parameters are
1257    illegal, "0" is returned. */
track_get_timestamp(Track * track,T_item t_item)1258 time_t track_get_timestamp (Track *track, T_item t_item)
1259 {
1260     time_t *ptr;
1261     g_return_val_if_fail (track, 0);
1262 
1263     ptr = track_get_timestamp_ptr (track, t_item);
1264     if (ptr)  return *ptr;
1265     else      return 0;
1266 }
1267 
1268 
1269 /* unified format for TRACKLEN, STARTTIME, STOPTIME */
track_get_length_string(gint32 length)1270 static gchar *track_get_length_string (gint32 length)
1271 {
1272     return g_strdup_printf ("%d:%06.3f",
1273 			    length/60000,
1274 			    ((float)(length%60000)) / 1000);
1275 }
1276 
1277 
1278 /* Return text for display. g_free() after use. */
track_get_text(Track * track,T_item item)1279 gchar *track_get_text (Track *track, T_item item)
1280 {
1281     gchar *text = NULL;
1282     ExtraTrackData *etr;
1283     iTunesDB *itdb;
1284 
1285     g_return_val_if_fail ((item > 0) && (item < T_ITEM_NUM), NULL);
1286     g_return_val_if_fail (track, NULL);
1287     etr = track->userdata;
1288     g_return_val_if_fail (etr, NULL);
1289     itdb = track->itdb;
1290     g_return_val_if_fail (itdb, NULL);
1291 
1292     switch (item)
1293     {
1294     case T_TITLE:
1295 	text = g_strdup (track->title);
1296 	break;
1297     case T_ARTIST:
1298 	text = g_strdup (track->artist);
1299 	break;
1300     case T_ALBUM:
1301 	text = g_strdup (track->album);
1302 	break;
1303     case T_GENRE:
1304 	text = g_strdup (track->genre);
1305 	break;
1306     case T_COMPOSER:
1307 	text = g_strdup (track->composer);
1308 	break;
1309     case T_COMMENT:
1310 	text = g_strdup (track->comment);
1311 	break;
1312     case T_FILETYPE:
1313 	text = g_strdup (track->filetype);
1314 	break;
1315     case T_GROUPING:
1316 	text = g_strdup (track->grouping);
1317 	break;
1318     case T_CATEGORY:
1319 	text = g_strdup (track->category);
1320 	break;
1321     case T_DESCRIPTION:
1322 	text = g_strdup (track->description);
1323 	break;
1324     case T_PODCASTURL:
1325 	text = g_strdup (track->podcasturl);
1326 	break;
1327     case T_PODCASTRSS:
1328 	text = g_strdup (track->podcastrss);
1329 	break;
1330     case T_SUBTITLE:
1331 	text = g_strdup (track->subtitle);
1332 	break;
1333     case T_TV_SHOW:
1334 	text = g_strdup (track->tvshow);
1335 	break;
1336     case T_TV_EPISODE:
1337 	text = g_strdup (track->tvepisode);
1338 	break;
1339     case T_TV_NETWORK:
1340 	text = g_strdup (track->tvnetwork);
1341 	break;
1342     case T_ALBUMARTIST:
1343 	text = g_strdup (track->albumartist);
1344 	break;
1345     case T_SORT_ARTIST:
1346 	text = g_strdup (track->sort_artist);
1347 	break;
1348     case T_SORT_TITLE:
1349 	text = g_strdup (track->sort_title);
1350 	break;
1351     case T_SORT_ALBUM:
1352 	text = g_strdup (track->sort_album);
1353 	break;
1354     case T_SORT_ALBUMARTIST:
1355 	text = g_strdup (track->sort_albumartist);
1356 	break;
1357     case T_SORT_COMPOSER:
1358 	text = g_strdup (track->sort_composer);
1359 	break;
1360     case T_SORT_TVSHOW:
1361 	text = g_strdup (track->sort_tvshow);
1362 	break;
1363     case T_TRACK_NR:
1364 	if (track->tracks == 0)
1365 	    text = g_strdup_printf ("%d", track->track_nr);
1366 	else
1367 	    text = g_strdup_printf ("%d/%d",
1368 				    track->track_nr, track->tracks);
1369 	break;
1370     case T_CD_NR:
1371 	if (track->cds == 0)
1372 	    text = g_strdup_printf ("%d", track->cd_nr);
1373 	else
1374 	    text = g_strdup_printf ("%d/%d", track->cd_nr, track->cds);
1375 	break;
1376     case T_IPOD_ID:
1377 	if (track->id != -1)
1378 	    text = g_strdup_printf ("%d", track->id);
1379 	else
1380 	    text = g_strdup ("--");
1381 	break;
1382     case T_PC_PATH:
1383 	text = g_strdup (etr->pc_path_utf8);
1384 	break;
1385     case T_IPOD_PATH:
1386 	if (itdb->usertype & GP_ITDB_TYPE_IPOD)
1387 	{
1388 	    text = g_strdup (track->ipod_path);
1389 	}
1390 	if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
1391 	{
1392 	    text = g_strdup (_("Local Database"));
1393 	}
1394 	break;
1395     case T_THUMB_PATH:
1396 	text = g_strdup (etr->thumb_path_utf8);
1397 	if (!text || (strlen (text) == 0))
1398 	{   /* no path set */
1399 	    g_free (text);
1400 	    text = NULL;
1401 	    if (itdb_track_has_thumbnails (track))
1402 	    {   /* artwork is set */
1403 		text = g_strdup (_("Embedded or filename was lost"));
1404 	    }
1405 	    else
1406 	    {
1407 		text = g_strdup (_("Artwork not set"));
1408 	    }
1409 	}
1410 	break;
1411     case T_SIZE:
1412 	text = g_strdup_printf ("%d", track->size);
1413 	break;
1414     case T_TRACKLEN:
1415 	text = track_get_length_string (track->tracklen);
1416 	break;
1417     case T_STARTTIME:
1418 	text = track_get_length_string (track->starttime);
1419 	break;
1420     case T_STOPTIME:
1421 	if (track->stoptime == 0)
1422 	    text = track_get_length_string (track->tracklen);
1423 	else
1424 	    text = track_get_length_string (track->stoptime);
1425 	break;
1426     case T_BITRATE:
1427 	text = g_strdup_printf ("%dk", track->bitrate);
1428 	break;
1429     case T_SAMPLERATE:
1430 	text = g_strdup_printf ("%d", track->samplerate);
1431 	break;
1432     case T_BPM:
1433 	text = g_strdup_printf ("%d", track->BPM);
1434 	break;
1435     case T_PLAYCOUNT:
1436 	text = g_strdup_printf ("%d", track->playcount);
1437 	break;
1438     case T_YEAR:
1439 	text = g_strdup_printf ("%d", track->year);
1440 	break;
1441     case T_RATING:
1442 	text = g_strdup_printf ("%d", track->rating/ITDB_RATING_STEP);
1443 	break;
1444     case T_TIME_PLAYED:
1445     case T_TIME_MODIFIED:
1446     case T_TIME_ADDED:
1447     case T_TIME_RELEASED:
1448 	text = time_field_to_string (track, item);
1449 	break;
1450     case T_VOLUME:
1451 	text = g_strdup_printf ("%d", track->volume);
1452 	break;
1453     case T_SOUNDCHECK:
1454 	text = g_strdup_printf ("%0.2f", soundcheck_to_replaygain (track->soundcheck));
1455 	break;
1456     case T_SEASON_NR:
1457         text = g_strdup_printf ("%d", track->season_nr);
1458 	break;
1459     case T_EPISODE_NR:
1460         text = g_strdup_printf ("%d", track->episode_nr);
1461 	break;
1462     case T_MEDIA_TYPE:
1463         text = g_strdup_printf ("%#.8x", track->mediatype);
1464 	break;
1465     case T_TRANSFERRED:
1466     case T_COMPILATION:
1467     case T_REMEMBER_PLAYBACK_POSITION:
1468     case T_SKIP_WHEN_SHUFFLING:
1469     case T_ALL:
1470     case T_CHECKED:
1471     case T_ITEM_NUM:
1472     case T_GAPLESS_TRACK_FLAG:
1473 	break;
1474     case T_LYRICS:
1475 	read_lyrics_from_file (track, &text);
1476 	break;
1477     }
1478     return text;
1479 }
1480 
1481 
1482 /* unified scanner for TRACKLEN, STARTTIME, STOPTIME */
track_scan_length(const gchar * new_text)1483 static gint32 track_scan_length (const gchar *new_text)
1484 {
1485     gint32 nr;
1486     const gchar *str;
1487 
1488     g_return_val_if_fail (new_text, 0);
1489 
1490     str = strrchr (new_text, ':');
1491     if (str)
1492     {   /* MM:SS */
1493 	/* A simple cast to gint32 can sometimes produce a number
1494 	   that's "1" too small (14.9999999999 -> 14 instead of 15) ->
1495 	   add 0.1 */
1496 	nr = 1000 * (((gdouble)(60 * atoi (new_text))) + atof (str+1)) + 0.1;
1497     }
1498     else
1499     {   /* SS */
1500 	nr = 1000 * atof (new_text) + 0.1;
1501     }
1502 
1503     return nr;
1504 }
1505 
1506 
1507 /* Set track data according to @new_text
1508 
1509    Return value: TRUE, if the track data was modified, FALSE otherwise
1510 */
track_set_text(Track * track,const gchar * new_text,T_item item)1511 gboolean track_set_text (Track *track, const gchar *new_text, T_item item)
1512 {
1513     gboolean changed = FALSE;
1514     gchar **itemp_utf8;
1515     const gchar *str;
1516     ExtraTrackData *etr;
1517     gint32 nr;
1518     time_t t;
1519 
1520     g_return_val_if_fail (track, FALSE);
1521     g_return_val_if_fail (new_text, FALSE);
1522 
1523     etr = track->userdata;
1524     g_return_val_if_fail (etr, FALSE);
1525 
1526 
1527     switch(item)
1528     {
1529     case T_TITLE:
1530     case T_ALBUM:
1531     case T_ARTIST:
1532     case T_GENRE:
1533     case T_COMPOSER:
1534     case T_COMMENT:
1535     case T_FILETYPE:
1536     case T_GROUPING:
1537     case T_CATEGORY:
1538     case T_DESCRIPTION:
1539     case T_PODCASTURL:
1540     case T_PODCASTRSS:
1541     case T_SUBTITLE:
1542     case T_TV_SHOW:
1543     case T_TV_EPISODE:
1544     case T_TV_NETWORK:
1545     case T_ALBUMARTIST:
1546     case T_SORT_ARTIST:
1547     case T_SORT_TITLE:
1548     case T_SORT_ALBUM:
1549     case T_SORT_ALBUMARTIST:
1550     case T_SORT_COMPOSER:
1551     case T_SORT_TVSHOW:
1552         itemp_utf8 = track_get_item_pointer (track, item);
1553         if (g_utf8_collate (*itemp_utf8, new_text) != 0)
1554         {
1555 	    g_free (*itemp_utf8);
1556 	    *itemp_utf8 = g_strdup (new_text);
1557 	    changed = TRUE;
1558         }
1559         break;
1560     case T_LYRICS:
1561         if ((etr->lyrics == NULL) ||
1562 	    (strcmp(etr->lyrics,new_text) != 0))
1563 	{
1564 	    g_free(etr->lyrics);
1565 	    etr->lyrics=g_strdup(new_text);
1566 	    changed = TRUE;
1567 	}
1568         break;
1569     case T_TRACK_NR:
1570         nr = atoi (new_text);
1571         if ((nr >= 0) && (nr != track->track_nr))
1572         {
1573 	    track->track_nr = nr;
1574 	    changed = TRUE;
1575         }
1576 	str = strrchr (new_text, '/');
1577 	if (str)
1578 	{
1579 	    nr = atoi (str+1);
1580 	    if ((nr >= 0) && (nr != track->tracks))
1581 	    {
1582 		track->tracks = nr;
1583 		changed = TRUE;
1584 	    }
1585 	}
1586         break;
1587     case T_CD_NR:
1588         nr = atoi (new_text);
1589         if ((nr >= 0) && (nr != track->cd_nr))
1590         {
1591 	    track->cd_nr = nr;
1592 	    changed = TRUE;
1593         }
1594 	str = strrchr (new_text, '/');
1595 	if (str)
1596 	{
1597 	    nr = atoi (str+1);
1598 	    if ((nr >= 0) && (nr != track->cds))
1599 	    {
1600 		track->cds = nr;
1601 		changed = TRUE;
1602 	    }
1603 	}
1604         break;
1605     case T_YEAR:
1606         nr = atoi (new_text);
1607         if ((nr >= 0) && (nr != track->year))
1608         {
1609 	    g_free (etr->year_str);
1610 	    etr->year_str = g_strdup_printf ("%d", nr);
1611 	    track->year = nr;
1612 	    changed = TRUE;
1613         }
1614         break;
1615     case T_PLAYCOUNT:
1616         nr = atoi (new_text);
1617         if ((nr >= 0) && (nr != track->playcount))
1618         {
1619 	    track->playcount = nr;
1620 	    changed = TRUE;
1621         }
1622         break;
1623     case T_RATING:
1624         nr = atoi (new_text);
1625         if ((nr >= 0) && (nr <= 5) && (nr != track->rating))
1626         {
1627 	    track->rating = nr*ITDB_RATING_STEP;
1628 	    changed = TRUE;
1629         }
1630         break;
1631     case T_TIME_ADDED:
1632     case T_TIME_PLAYED:
1633     case T_TIME_MODIFIED:
1634     case T_TIME_RELEASED:
1635 	t = time_string_to_time (new_text);
1636 	if ((t != -1) && (t != time_get_time (track, item)))
1637 	{
1638 	    time_set_time (track, t, item);
1639 	    changed = TRUE;
1640 	}
1641 	break;
1642     case T_VOLUME:
1643         nr = atoi (new_text);
1644         if (nr != track->volume)
1645         {
1646 	    track->volume = nr;
1647 	    changed = TRUE;
1648         }
1649         break;
1650     case T_SOUNDCHECK:
1651 	nr = replaygain_to_soundcheck (atof (new_text));
1652 /* 	printf("%d : %f\n", nr, atof (new_text)); */
1653         if (nr != track->soundcheck)
1654         {
1655 	    track->soundcheck = nr;
1656 	    changed = TRUE;
1657         }
1658         break;
1659     case T_SIZE:
1660         nr = atoi (new_text);
1661         if (nr != track->size)
1662         {
1663 	    track->size = nr;
1664 	    changed = TRUE;
1665         }
1666         break;
1667     case T_BITRATE:
1668         nr = atoi (new_text);
1669         if (nr != track->bitrate)
1670         {
1671 	    track->bitrate = nr;
1672 	    changed = TRUE;
1673         }
1674         break;
1675     case T_SAMPLERATE:
1676         nr = atoi (new_text);
1677         if (nr != track->samplerate)
1678         {
1679 	    track->samplerate = nr;
1680 	    changed = TRUE;
1681         }
1682         break;
1683     case T_BPM:
1684         nr = atoi (new_text);
1685         if (nr != track->BPM)
1686         {
1687 	    track->BPM = nr;
1688 	    changed = TRUE;
1689         }
1690         break;
1691     case T_TRACKLEN:
1692 	nr = track_scan_length (new_text);
1693 	if (nr != track->tracklen)
1694 	{
1695 	    track->tracklen = nr;
1696 	    changed = TRUE;
1697 	}
1698 	break;
1699     case T_STARTTIME:
1700 	nr = track_scan_length (new_text);
1701 	if (nr != track->starttime)
1702 	{
1703 	    track->starttime = nr;
1704 	    changed = TRUE;
1705 	    /* Set stoptime to 0 if stoptime is the same as tracklen */
1706 	    if (track->stoptime == track->tracklen)
1707 		track->stoptime = 0;
1708 	}
1709 	break;
1710     case T_STOPTIME:
1711 	nr = track_scan_length (new_text);
1712 	/* if stoptime is identical to tracklen, set stoptime to 0 if
1713 	 * starttime is 0 as well */
1714 	if ((nr == track->tracklen) &&
1715 	    (track->starttime == 0))
1716 	{
1717 	    nr = 0;
1718 	}
1719 	if (nr != track->stoptime)
1720 	{
1721 	    track->stoptime = nr;
1722 	    changed = TRUE;
1723 	}
1724 	break;
1725     case T_SEASON_NR:
1726         nr = atoi (new_text);
1727         if ((nr >= 0) && (nr != track->season_nr))
1728         {
1729 	    track->season_nr = nr;
1730 	    changed = TRUE;
1731         }
1732         break;
1733     case T_EPISODE_NR:
1734         nr = atoi (new_text);
1735         if ((nr >= 0) && (nr != track->episode_nr))
1736         {
1737 	    track->episode_nr = nr;
1738 	    changed = TRUE;
1739         }
1740         break;
1741     case T_MEDIA_TYPE:
1742     case T_PC_PATH:
1743     case T_IPOD_PATH:
1744     case T_IPOD_ID:
1745     case T_TRANSFERRED:
1746     case T_COMPILATION:
1747     case T_REMEMBER_PLAYBACK_POSITION:
1748     case T_SKIP_WHEN_SHUFFLING:
1749     case T_CHECKED:
1750     case T_ALL:
1751     case T_GAPLESS_TRACK_FLAG:
1752     case T_ITEM_NUM:
1753     case T_THUMB_PATH: // TODO: this should in fact be settable
1754 	gtkpod_warning ("Programming error: track_set_text() called with illegal argument (item: %d)\n", item);
1755 	break;
1756     }
1757 
1758     return changed;
1759 }
1760 
1761 
1762 
1763 
1764 /* Fills @size with the size and @num with the number of
1765    non-transferred tracks. The size is in Bytes, minus the space taken
1766    by tracks that will be overwritten. */
1767 /* @size and @num may be NULL */
gp_info_nontransferred_tracks(iTunesDB * itdb,gdouble * size,guint32 * num)1768 void gp_info_nontransferred_tracks (iTunesDB *itdb,
1769 				    gdouble *size, guint32 *num)
1770 {
1771     GList *gl;
1772 
1773     if (size) *size = 0;
1774     if (num)  *num = 0;
1775     g_return_if_fail (itdb);
1776 
1777     for (gl = itdb->tracks; gl; gl=gl->next)
1778     {
1779 	Track *tr = gl->data;
1780 	ExtraTrackData *etr;
1781 	g_return_if_fail (tr);
1782 	etr = tr->userdata;
1783 	g_return_if_fail (etr);
1784 	if (!tr->transferred)
1785 	{
1786 	    if (size)  *size += tr->size;
1787 	    if (num)   *num += 1;
1788 	}
1789     }
1790 }
1791 
1792 
1793 
intern_add_track(Playlist * pl,Track * track)1794 static void intern_add_track (Playlist *pl, Track *track)
1795 {
1796     iTunesDB *from_itdb, *to_itdb;
1797     Playlist *to_mpl;
1798     from_itdb = track->itdb;
1799     g_return_if_fail (from_itdb);
1800     to_itdb = pl->itdb;
1801     to_mpl = itdb_playlist_mpl (to_itdb);
1802 
1803 /* 	    printf ("add tr %p to pl: %p\n", track, pl); */
1804     if (from_itdb == to_itdb)
1805     {   /* DND within the same itdb */
1806 
1807 	/* set flags to 'podcast' if adding to podcast list */
1808 	if (itdb_playlist_is_podcasts (pl))
1809 	    gp_track_set_flags_podcast (track);
1810 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1811 	if (!itdb_playlist_contains_track (to_mpl, track))
1812 	{   /* add to MPL if not already present (will happen
1813 	     * if dragged from the podcasts playlist */
1814 	    gp_playlist_add_track (to_mpl, track, TRUE);
1815 	}
1816 #endif
1817 	if (!itdb_playlist_is_mpl (pl))
1818 	{
1819 	    /* add to designated playlist -- unless adding
1820 	     * to podcasts list and track already exists there */
1821 	    if (itdb_playlist_is_podcasts (pl) &&
1822 		g_list_find (pl->members, track))
1823 	    {
1824 		gchar *buf = get_track_info (track, FALSE);
1825 		gtkpod_warning (_("Podcast already present: '%s'\n\n"), buf);
1826 		g_free (buf);
1827 	    }
1828 	    else
1829 	    {
1830 		gp_playlist_add_track (pl, track, TRUE);
1831 	    }
1832 	}
1833     }
1834     else
1835     {   /* DND between different itdbs -- need to duplicate the
1836 	   track before inserting */
1837 	Track *duptr, *addtr;
1838 	ExtraTrackData *eduptr;
1839 	/* duplicate track */
1840 	duptr = itdb_track_duplicate (track);
1841 	eduptr = duptr->userdata;
1842 	g_return_if_fail (eduptr);
1843 
1844 	duptr->transferred = FALSE;
1845 
1846 	/* check if adding to iPod and track is on different iPod */
1847 	if ((from_itdb->usertype & GP_ITDB_TYPE_IPOD) &&
1848 	    (to_itdb->usertype & GP_ITDB_TYPE_IPOD))
1849 	{
1850 	    /* Check if track exists locally */
1851 	    if (!(eduptr->pc_path_locale &&
1852 		  g_file_test (eduptr->pc_path_locale, G_FILE_TEST_EXISTS)))
1853 	    {   /* No. Use iPod path as source */
1854 		g_free (eduptr->pc_path_locale);
1855 		g_free (eduptr->pc_path_utf8);
1856 		eduptr->pc_path_locale = itdb_filename_on_ipod (track);
1857 		eduptr->pc_path_utf8 = charset_to_utf8 (eduptr->pc_path_locale);
1858 	    }
1859 	    /* Remove old reference to iPod path */
1860 	    g_free (duptr->ipod_path);
1861 	    duptr->ipod_path = g_strdup ("");
1862 	}
1863 
1864 	if (!eduptr->pc_path_locale)
1865 	{
1866 	    gchar *buf;
1867 	    buf = get_track_info (track, FALSE);
1868 	    gtkpod_warning (_("Could not find source file for '%s'. Track not copied."));
1869 	    g_free (buf);
1870 	    itdb_track_free (duptr);
1871 	    return;
1872 	}
1873 
1874 	if ((from_itdb->usertype & GP_ITDB_TYPE_LOCAL) &&
1875 	    (to_itdb->usertype & GP_ITDB_TYPE_IPOD))
1876 	{   /* make sure the DND origin data is set correctly */
1877 	    eduptr->local_itdb_id = from_itdb->id;
1878 	    eduptr->local_track_dbid = track->dbid;
1879 	}
1880 
1881 	/* add to database -- if duplicate detection is on and the
1882 	   same track already exists in the database, the already
1883 	   existing track is returned and @duptr is freed */
1884 	addtr = gp_track_add (to_itdb, duptr);
1885 
1886 	/* set flags to 'podcast' if adding to podcast list */
1887 	if (itdb_playlist_is_podcasts (pl))
1888 	    gp_track_set_flags_podcast (addtr);
1889 
1890 	if (addtr == duptr)
1891 	{   /* no duplicate */
1892 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1893 	    /* we need to add to the MPL if the track is no
1894 	       duplicate and will not be added to the podcasts
1895 	       playlist */
1896 	    if (!itdb_playlist_is_podcasts (pl))
1897 	    {   /* don't add to mpl if we add to the podcasts
1898 		   playlist */
1899 		gp_playlist_add_track (to_mpl, addtr, TRUE);
1900 	    }
1901 #else
1902 	    /* we need to add to the MPL if the track is no
1903 	       duplicate */
1904 	    gp_playlist_add_track (to_mpl, addtr, TRUE);
1905 #endif
1906 	}
1907 #if 0 /* initially iTunes didn't add podcasts to the MPL */
1908 	else
1909 	{   /* duplicate */
1910 	    /* we also need to add to the MPL if the track is a
1911 	       duplicate, does not yet exist in the MPL and will
1912 	       not be added to a podcast list (this happens if
1913 	       it's already in the podcast list) */
1914 	    if ((!itdb_playlist_contains_track (to_mpl, addtr)) &&
1915 		(!itdb_playlist_is_podcasts (pl)))
1916 	    {
1917 		gp_playlist_add_track (to_mpl, addtr, TRUE);
1918 	    }
1919 	}
1920 #endif
1921 	/* add to designated playlist (if not mpl) -- unless
1922 	 * adding to podcasts list and track already * exists
1923 	 * there */
1924 	if (!itdb_playlist_is_mpl (pl))
1925 	{
1926 	    if (itdb_playlist_is_podcasts (pl) &&
1927 		g_list_find (pl->members, addtr))
1928 	    {
1929 		gchar *buf = get_track_info (addtr, FALSE);
1930 		gtkpod_warning (_("Podcast already present: '%s'\n\n"), buf);
1931 		g_free (buf);
1932 	    }
1933 	    else
1934 	    {
1935 		gp_playlist_add_track (pl, addtr, TRUE);
1936 	    }
1937 	}
1938     }
1939 }
1940 
1941 
1942 /*------------------------------------------------------------------*\
1943  *                                                                  *
1944  *             DND to playlists                                     *
1945  *                                                                  *
1946 \*------------------------------------------------------------------*/
1947 
1948 /* DND: add either a GList of tracks or an ASCII list of tracks to
1949  * Playlist @pl */
add_tracks_to_playlist(Playlist * pl,gchar * string,GList * tracks)1950 static void add_tracks_to_playlist (Playlist *pl,
1951 				    gchar *string, GList *tracks)
1952 {
1953 
1954 
1955     g_return_if_fail (!(string && tracks));
1956     g_return_if_fail (pl);
1957     g_return_if_fail (pl->itdb);
1958     g_return_if_fail (itdb_playlist_mpl (pl->itdb));
1959     if (!(string || tracks)) return;
1960 
1961     if (string)
1962     {
1963 	Track *track;
1964 	gchar *str = string;
1965 	while(parse_tracks_from_string(&str, &track))
1966 	{
1967 	    g_return_if_fail (track);
1968 	    intern_add_track (pl, track);
1969 	}
1970     }
1971     if (tracks)
1972     {
1973 	GList *gl;
1974 	for (gl=tracks; gl; gl=gl->next)
1975 	{
1976 	    Track *track = gl->data;
1977 	    g_return_if_fail (track);
1978 	    intern_add_track (pl, track);
1979 	}
1980     }
1981 }
1982 
1983 
1984 /* DND: add a glist of tracks to Playlist @pl */
add_trackglist_to_playlist(Playlist * pl,GList * tracks)1985 void add_trackglist_to_playlist (Playlist *pl, GList *tracks)
1986 {
1987 	add_tracks_to_playlist (pl, NULL, tracks);
1988 }
1989 
1990 
1991 /* DND: add a list of tracks to Playlist @pl */
add_tracklist_to_playlist(Playlist * pl,gchar * string)1992 void add_tracklist_to_playlist (Playlist *pl, gchar *string)
1993 {
1994     add_tracks_to_playlist (pl, string, NULL);
1995 }
1996 
1997 /* DND: add a list of files to Playlist @pl.
1998 
1999    @pl: playlist to add to or NULL. If NULL, a "New Playlist" will be
2000    created and inserted at position @pl_pos for adding tracks and when
2001    adding a playlist file, a playlist with the name of the playlist
2002    file will be added.
2003 
2004    @pl_pos: position to add playlist file, ignored if @pl!=NULL.
2005 
2006    @trackaddfunc: passed on to add_track_by_filename() etc. */
2007 
2008 /* Return value: playlist to where the tracks were added. Note: when
2009    adding playlist files, additional playlists may have been created */
add_text_plain_to_playlist(iTunesDB * itdb,Playlist * pl,gchar * str,gint pl_pos,AddTrackFunc trackaddfunc,gpointer data)2010 Playlist *add_text_plain_to_playlist (iTunesDB *itdb, Playlist *pl,
2011 				      gchar *str, gint pl_pos,
2012 				      AddTrackFunc trackaddfunc,
2013 				      gpointer data)
2014 {
2015     gchar **files = NULL, **filesp = NULL;
2016     Playlist *pl_playlist = pl; /* playlist for playlist file */
2017     Playlist *pl_playlist_created = NULL;
2018 
2019     g_return_val_if_fail (itdb, NULL);
2020 
2021     if (!str)  return NULL;
2022 
2023     /*   printf("pl: %x, pl_pos: %d\n%s\n", pl, pl_pos, str);*/
2024 
2025     block_widgets ();
2026 
2027     files = g_strsplit (str, "\n", -1);
2028     if (files)
2029     {
2030 	filesp = files;
2031 	while (*filesp)
2032 	{
2033 	    gboolean added = FALSE;
2034 	    gint file_len = -1;
2035 
2036 	    gchar *file = NULL;
2037 	    gchar *decoded_file = NULL;
2038 
2039 	    file = *filesp;
2040 	    /* file is in uri form (the ones we're looking for are
2041 	       file:///), file can include the \n or \r\n, which isn't
2042 	       a valid character of the filename and will cause the
2043 	       uri decode / file test to fail, so we'll cut it off if
2044 	       its there. */
2045 	    file_len = strlen (file);
2046 	    if (file_len && (file[file_len-1] == '\n'))
2047 	    {
2048 		file[file_len-1] = 0;
2049 		--file_len;
2050 	    }
2051 	    if (file_len && (file[file_len-1] == '\r'))
2052 	    {
2053 		file[file_len-1] = 0;
2054 		--file_len;
2055 	    }
2056 
2057 	    decoded_file = filename_from_uri (file, NULL, NULL);
2058 	    if (decoded_file != NULL)
2059 	    {
2060 		if (g_file_test (decoded_file, G_FILE_TEST_IS_DIR))
2061 		{   /* directory */
2062 		    if (!pl)
2063 		    {  /* no playlist yet -- create new one */
2064 			pl = add_new_pl_user_name (itdb, NULL, pl_pos);
2065 			if (!pl)  break; /* while (*filesp) */
2066 		    }
2067 		    add_directory_by_name (itdb, decoded_file, pl,
2068 					   prefs_get_int("add_recursively"),
2069 					   trackaddfunc, data);
2070 		    added = TRUE;
2071 		}
2072 		if (g_file_test (decoded_file, G_FILE_TEST_IS_REGULAR))
2073 		{   /* regular file */
2074 		    FileType ftype = determine_file_type (decoded_file);
2075 		    switch (ftype)
2076 		    {
2077 		    case FILE_TYPE_MP3:
2078 		    case FILE_TYPE_M4A:
2079 		    case FILE_TYPE_M4P:
2080 		    case FILE_TYPE_M4B:
2081 		    case FILE_TYPE_WAV:
2082 		    case FILE_TYPE_M4V:
2083 		    case FILE_TYPE_MP4:
2084 		    case FILE_TYPE_MOV:
2085 		    case FILE_TYPE_MPG:
2086                     case FILE_TYPE_OGG:
2087                     case FILE_TYPE_FLAC:
2088 			if (!pl)
2089 			{  /* no playlist yet -- create new one */
2090 			    pl = add_new_pl_user_name (itdb, NULL,
2091 						       pl_pos);
2092 			    if (!pl)  break; /* while (*filesp) */
2093 			}
2094 			add_track_by_filename (itdb, decoded_file, pl,
2095 					       prefs_get_int("add_recursively"),
2096 					       trackaddfunc, data);
2097 			added = TRUE;
2098 			break;
2099 		    case FILE_TYPE_M3U:
2100 		    case FILE_TYPE_PLS:
2101 			pl_playlist_created = add_playlist_by_filename (
2102 			    itdb, decoded_file,
2103 			    pl_playlist, pl_pos, trackaddfunc, data);
2104 			added = TRUE;
2105 			break;
2106 		    case FILE_TYPE_UNKNOWN:
2107 		    case FILE_TYPE_DIRECTORY:
2108 		    case FILE_TYPE_IMAGE:
2109 			break;
2110 		    }
2111 		}
2112 		g_free (decoded_file);
2113 	    }
2114 	    if (!added)
2115 	    {
2116 		if (strlen (*filesp) != 0)
2117 		    gtkpod_warning (_("drag and drop: ignored '%s'\n"), *filesp);
2118 	    }
2119 	    ++filesp;
2120 	}
2121 	g_strfreev (files);
2122     }
2123     /* display log of non-updated tracks */
2124     display_non_updated (NULL, NULL);
2125     /* display log updated tracks */
2126     display_updated (NULL, NULL);
2127     /* display log of detected duplicates */
2128     gp_duplicate_remove (NULL, NULL);
2129 
2130     release_widgets ();
2131 
2132     if (pl) return pl;
2133     if (pl_playlist_created) return pl_playlist_created;
2134     return NULL;
2135 }
2136 
2137 /*------------------------------------------------------------------*\
2138  *                                                                  *
2139  * Functions setting default values on tracks                       *
2140  *                                                                  *
2141 \*------------------------------------------------------------------*/
2142 
2143 /* set podcast-specific flags for @track */
gp_track_set_flags_podcast(Track * track)2144 void gp_track_set_flags_podcast (Track *track)
2145 {
2146     g_return_if_fail (track);
2147     track->skip_when_shuffling = 0x01;         /* skip when shuffling */
2148     track->remember_playback_position = 0x01;  /* remember playback
2149 						* position */
2150     track->flag4 = 0x01;  /* Show Title/Album on the 'Now Playing' page */
2151     track->mediatype = ITDB_MEDIATYPE_PODCAST; /* show up under Podcasts */
2152 }
2153 
2154 /* set podcast-specific flags for @track */
gp_track_set_flags_default(Track * track)2155 void gp_track_set_flags_default (Track *track)
2156 {
2157     g_return_if_fail (track);
2158     track->skip_when_shuffling = 0x00;  /* do not skip when shuffling */
2159     track->remember_playback_position = 0x00;  /* do not remember
2160 						* playback position */
2161     track->flag4 = 0x00;  /* Show Title/Album/Artist on the 'Now
2162 			     Playing' page */
2163 }
2164 
2165 
2166 /*------------------------------------------------------------------*\
2167  *                                                                  *
2168  *  Generic functions to "do" things on selected playlist / entry   *
2169  *  / tracks                                                         *
2170  *                                                                  *
2171 \*------------------------------------------------------------------*/
2172 
2173 /* Make a list of all selected tracks and call @do_func with that list
2174    as argument */
gp_do_selected_tracks(void (* do_func)(GList * tracks))2175 void gp_do_selected_tracks (void (*do_func)(GList *tracks))
2176 {
2177     GList *selected_tracks = NULL;
2178 
2179     g_return_if_fail (do_func);
2180 
2181     /* I'm using ids instead of "Track *" -pointer because it would be
2182      * possible that a track gets removed during the process */
2183     selected_tracks = tm_get_selected_tracks ();
2184     do_func (selected_tracks);
2185     g_list_free (selected_tracks);
2186 }
2187 
2188 
2189 /* Make a list of all tracks in the currently selected entry of sort
2190    tab @inst and call @do_func with that list as argument */
gp_do_selected_entry(void (* do_func)(GList * tracks),gint inst)2191 void gp_do_selected_entry (void (*do_func)(GList *tracks), gint inst)
2192 {
2193     GList *selected_tracks = NULL;
2194     TabEntry *entry;
2195     GList *gl;
2196 
2197     g_return_if_fail (do_func);
2198 
2199     g_return_if_fail ((inst >= 0) && (inst <= prefs_get_int("sort_tab_num")));
2200 
2201     entry = st_get_selected_entry (inst);
2202     if (entry == NULL)
2203     {  /* no entry selected */
2204 	gtkpod_statusbar_message (_("No entry selected."));
2205 	return;
2206     }
2207     for (gl=entry->members; gl; gl=gl->next)
2208     { /* make a list with all trackids in this entry */
2209 	Track *track = gl->data;
2210 	g_return_if_fail (track);
2211 	selected_tracks = g_list_prepend (selected_tracks, track);
2212     }
2213     selected_tracks = g_list_reverse (selected_tracks);
2214     do_func (selected_tracks);
2215     g_list_free (selected_tracks);
2216 }
2217 
2218 
2219 /* Make a list of the tracks in the current playlist and call @do_func
2220    with that list as argument */
gp_do_selected_playlist(void (* do_func)(GList * tracks))2221 void gp_do_selected_playlist (void (*do_func)(GList *tracks))
2222 {
2223     GList *selected_tracks = NULL;
2224     Playlist *pl;
2225     GList *gl;
2226 
2227     g_return_if_fail (do_func);
2228 
2229     pl = pm_get_selected_playlist();
2230     if (!pl)
2231     { /* no playlist selected */
2232 	message_sb_no_playlist_selected ();
2233 	return;
2234     }
2235     for (gl=pl->members; gl; gl=gl->next)
2236     { /* make a list with all trackids in this entry */
2237 	Track *track = gl->data;
2238 	g_return_if_fail (track);
2239 	selected_tracks = g_list_prepend (selected_tracks, track);
2240     }
2241     selected_tracks = g_list_reverse (selected_tracks);
2242     do_func (selected_tracks);
2243     g_list_free (selected_tracks);
2244 }
2245 
2246 
2247 /* return some sensible input about the "track". You must free the
2248  * return string after use. */
get_track_info(Track * track,gboolean prefer_filename)2249 gchar *get_track_info (Track *track, gboolean prefer_filename)
2250 {
2251     ExtraTrackData *etr;
2252 
2253     g_return_val_if_fail (track, NULL);
2254     etr = track->userdata;
2255     g_return_val_if_fail (etr, NULL);
2256 
2257     if (prefer_filename)
2258     {
2259 	if (etr->pc_path_utf8 && strlen(etr->pc_path_utf8))
2260 	    return g_path_get_basename (etr->pc_path_utf8);
2261     }
2262     if ((track->title && strlen(track->title)))
2263 	return g_strdup (track->title);
2264     if ((track->album && strlen(track->album)))
2265 	return g_strdup (track->album);
2266     if ((track->artist && strlen(track->artist)))
2267 	return g_strdup (track->artist);
2268     if ((track->composer && strlen(track->composer)))
2269 	return g_strdup (track->composer);
2270     if (!prefer_filename)
2271     {
2272 	if (etr->pc_path_utf8 && strlen(etr->pc_path_utf8))
2273 	    return g_path_get_basename (etr->pc_path_utf8);
2274     }
2275 
2276     return g_strdup_printf ("iPod ID: %d", track->id);
2277 }
2278