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 /* This file provides functions for the info window as well as for the
30  * statusbar handling */
31 
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <gtk/gtk.h>
37 #include "info.h"
38 #include "misc.h"
39 #include "misc_track.h"
40 #include "prefs.h"
41 
42 /* pointer to info window */
43 static GtkWidget *info_window = NULL;
44 
45 GladeXML *info_xml;
46 
47 /* stuff for statusbar */
48 static GtkWidget *gtkpod_statusbar = NULL;
49 static GtkWidget *gtkpod_tracks_statusbar = NULL;
50 static GtkWidget *gtkpod_space_statusbar = NULL;
51 static guint statusbar_timeout_id = 0;
52 static guint statusbar_timeout = STATUSBAR_TIMEOUT;
53 
54 #define SPACE_TIMEOUT 1000
55 /* lock for size related variables (used by child and parent) */
56 static GMutex *space_mutex = NULL;
57 static GThread *space_thread = NULL;
58 static gboolean space_uptodate = FALSE;
59 static gchar *space_mp = NULL;      /* thread save access through mutex */
60 static iTunesDB *space_itdb = NULL; /* semi thread save access
61 				     * (space_itdb will not be changed
62 				     * while locked) */
63 static gdouble space_ipod_free = 0; /* thread save access through mutex */
64 static gdouble space_ipod_used = 0; /* thread save access through mutex */
65 
66 static GList *callbacks_info_update = NULL;
67 static GList *callbacks_info_update_track_view = NULL;
68 static GList *callbacks_info_update_playlist_view = NULL;
69 static GList *callbacks_info_update_totals_view = NULL;
70 
71 #if 0
72 static gdouble get_ipod_used_space(void);
73 #endif
74 
75 /* callback management */
register_callback(GList ** list,info_update_callback cb)76 static void register_callback (GList **list, info_update_callback cb)
77 {
78 	if(*list && g_list_index (*list, cb) != -1)
79 		return;
80 
81 	*list = g_list_append (*list, cb);
82 }
83 
unregister_callback(GList ** list,info_update_callback cb)84 static void unregister_callback (GList **list, info_update_callback cb)
85 {
86 	if(*list)
87 		*list = g_list_remove (*list, cb);
88 }
89 
callback_call_all(GList * list)90 static void callback_call_all (GList *list)
91 {
92 	for(; list; list = list->next)
93 	{
94 		((info_update_callback) list->data) ();
95 	}
96 }
97 
register_info_update(info_update_callback cb)98 void register_info_update (info_update_callback cb)
99 {
100 	register_callback (&callbacks_info_update, cb);
101 }
102 
register_info_update_track_view(info_update_callback cb)103 void register_info_update_track_view (info_update_callback cb)
104 {
105 	register_callback (&callbacks_info_update_track_view, cb);
106 }
107 
register_info_update_playlist_view(info_update_callback cb)108 void register_info_update_playlist_view (info_update_callback cb)
109 {
110 	register_callback (&callbacks_info_update_playlist_view, cb);
111 }
112 
register_info_update_totals_view(info_update_callback cb)113 void register_info_update_totals_view (info_update_callback cb)
114 {
115 	register_callback (&callbacks_info_update_totals_view, cb);
116 }
117 
unregister_info_update(info_update_callback cb)118 void unregister_info_update (info_update_callback cb)
119 {
120 	unregister_callback (&callbacks_info_update, cb);
121 }
122 
unregister_info_update_track_view(info_update_callback cb)123 void unregister_info_update_track_view (info_update_callback cb)
124 {
125 	unregister_callback (&callbacks_info_update_track_view, cb);
126 }
127 
unregister_info_update_playlist_view(info_update_callback cb)128 void unregister_info_update_playlist_view (info_update_callback cb)
129 {
130 	unregister_callback (&callbacks_info_update_playlist_view, cb);
131 }
132 
unregister_info_update_totals_view(info_update_callback cb)133 void unregister_info_update_totals_view (info_update_callback cb)
134 {
135 	unregister_callback (&callbacks_info_update_totals_view, cb);
136 }
137 
138 /* fill in tracks, playtime and filesize from track list @tl */
fill_in_info(GList * tl,guint32 * tracks,guint32 * playtime,gdouble * filesize)139 void fill_in_info (GList *tl, guint32 *tracks,
140 			  guint32 *playtime, gdouble *filesize)
141 {
142     GList *gl;
143 
144     g_return_if_fail (tracks);
145     g_return_if_fail (playtime);
146     g_return_if_fail (filesize);
147 
148     *tracks = 0;
149     *playtime = 0;
150     *filesize = 0;
151 
152     for (gl=tl; gl; gl=gl->next)
153     {
154 	Track *s = gl->data;
155 	*tracks += 1;
156 	*playtime += s->tracklen/1000;
157 	*filesize += s->size;
158     }
159 }
160 
fill_label_uint(gchar * w_name,guint32 nr)161 static void fill_label_uint (gchar *w_name, guint32 nr)
162 {
163     GtkWidget *w;
164 
165     g_return_if_fail (info_window);
166     g_return_if_fail (w_name);
167     w = gtkpod_xml_get_widget (info_xml, w_name);
168     if (w)
169     {
170 	gchar *str = g_strdup_printf ("%u", nr);
171 	gtk_label_set_text (GTK_LABEL (w), str);
172 	g_free (str);
173     }
174 }
175 
fill_label_time(gchar * w_name,guint32 secs)176 static void fill_label_time (gchar *w_name, guint32 secs)
177 {
178     GtkWidget *w;
179 
180     g_return_if_fail (info_window);
181     g_return_if_fail (w_name);
182     w = gtkpod_xml_get_widget (info_xml, w_name);
183     if (w)
184     {
185 	gchar *str = g_strdup_printf ("%u:%02u:%02u",
186 				      secs / 3600,
187 				      (secs % 3600) / 60,
188 				      secs % 60);
189 	gtk_label_set_text (GTK_LABEL (w), str);
190 	g_free (str);
191     }
192 }
193 
fill_label_size(gchar * w_name,gdouble size)194 static void fill_label_size (gchar *w_name, gdouble size)
195 {
196     GtkWidget *w;
197 
198     g_return_if_fail (info_window);
199     g_return_if_fail (w_name);
200     w = gtkpod_xml_get_widget (info_xml, w_name);
201     if (w)
202     {
203 	gchar *str = get_filesize_as_string (size);
204 	gtk_label_set_text (GTK_LABEL (w), str);
205 	g_free (str);
206     }
207 }
208 
fill_label_string(gchar * w_name,const char * str)209 static void fill_label_string (gchar *w_name, const char *str)
210 {
211     GtkWidget *w;
212 
213     g_return_if_fail (info_window);
214     g_return_if_fail (w_name);
215     w = gtkpod_xml_get_widget (info_xml, w_name);
216     if (w)
217     {
218 	gtk_label_set_text (GTK_LABEL (w), str);
219     }
220 }
221 
222 /* update all sections of info window */
info_update(void)223 void info_update (void)
224 {
225 	callback_call_all (callbacks_info_update);
226 
227     info_update_track_view ();
228     info_update_playlist_view ();
229     info_update_totals_view ();
230 }
231 
info_update_track_view_displayed(void)232 static void info_update_track_view_displayed (void)
233 {
234     guint32 tracks, playtime; /* playtime in secs */
235     gdouble  filesize;        /* in bytes */
236     GList *displayed;
237 
238     if (!info_window) return; /* not open */
239     displayed = display_get_selected_members (prefs_get_int("sort_tab_num")-1);
240     fill_in_info (displayed, &tracks, &playtime, &filesize);
241     fill_label_uint ("tracks_displayed", tracks);
242     fill_label_time ("playtime_displayed", playtime);
243     fill_label_size ("filesize_displayed", filesize);
244 }
245 
info_update_track_view_selected(void)246 static void info_update_track_view_selected (void)
247 {
248     guint32 tracks, playtime; /* playtime in secs */
249     gdouble  filesize;        /* in bytes */
250     GList *selected;
251 
252     if (!info_window) return; /* not open */
253     selected = display_get_selection (prefs_get_int("sort_tab_num"));
254     fill_in_info (selected, &tracks, &playtime, &filesize);
255     g_list_free (selected);
256     fill_label_uint ("tracks_selected", tracks);
257     fill_label_time ("playtime_selected", playtime);
258     fill_label_size ("filesize_selected", filesize);
259 }
260 
261 /* update track view section */
info_update_track_view(void)262 void info_update_track_view (void)
263 {
264 	callback_call_all (callbacks_info_update_track_view);
265 
266 	if (!info_window) return; /* not open */
267     info_update_track_view_displayed ();
268     info_update_track_view_selected ();
269 }
270 
271 /* update playlist view section */
info_update_playlist_view(void)272 void info_update_playlist_view (void)
273 {
274 	callback_call_all (callbacks_info_update_playlist_view);
275 
276 	guint32 tracks, playtime; /* playtime in secs */
277     gdouble  filesize;        /* in bytes */
278     GList   *tl;
279 
280     if (!info_window) return; /* not open */
281     tl = display_get_selected_members (-1);
282     fill_in_info (tl, &tracks, &playtime, &filesize);
283     fill_label_uint ("playlist_tracks", tracks);
284     fill_label_time ("playlist_playtime", playtime);
285     fill_label_size ("playlist_filesize", filesize);
286 }
287 
288 
289 /* Get the local itdb */
get_itdb_local(void)290 iTunesDB *get_itdb_local (void)
291 {
292     struct itdbs_head *itdbs_head;
293     GList *gl;
294 
295     g_return_val_if_fail (gtkpod_window, NULL);
296     itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
297 				    "itdbs_head");
298     if (!itdbs_head) return NULL;
299     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
300     {
301 	iTunesDB *itdb = gl->data;
302 	g_return_val_if_fail (itdb, NULL);
303 	if (itdb->usertype & GP_ITDB_TYPE_LOCAL)
304 	    return itdb;
305     }
306     return NULL;
307 }
308 
309 
310 /* Get the iPod itdb */
311 /* FIXME: This function must be expanded if support for several iPods
312    is implemented */
get_itdb_ipod(void)313 iTunesDB *get_itdb_ipod (void)
314 {
315     struct itdbs_head *itdbs_head;
316     GList *gl;
317 
318     g_return_val_if_fail (gtkpod_window, NULL);
319     itdbs_head = g_object_get_data (G_OBJECT (gtkpod_window),
320 				    "itdbs_head");
321     if (!itdbs_head) return NULL;
322     for (gl=itdbs_head->itdbs; gl; gl=gl->next)
323     {
324 	iTunesDB *itdb = gl->data;
325 	g_return_val_if_fail (itdb, NULL);
326 	if (itdb->usertype & GP_ITDB_TYPE_IPOD)
327 	    return itdb;
328     }
329     return NULL;
330 }
331 
332 /* update "free space" section of totals view */
info_update_totals_view_space(void)333 static void info_update_totals_view_space (void)
334 {
335     gdouble nt_filesize, del_filesize;
336     guint32 nt_tracks, del_tracks;
337     iTunesDB *itdb;
338 
339     if (!info_window) return;
340     itdb = get_itdb_ipod ();
341     if (itdb)
342     {
343 	gp_info_nontransferred_tracks (itdb, &nt_filesize, &nt_tracks);
344 	fill_label_uint ("non_transferred_tracks", nt_tracks);
345 	fill_label_size ("non_transferred_filesize", nt_filesize);
346 	gp_info_deleted_tracks (itdb, &del_filesize, &del_tracks);
347 	fill_label_uint ("deleted_tracks", del_tracks);
348 	fill_label_size ("deleted_filesize", del_filesize);
349 	if (!get_offline (itdb))
350 	{
351 	    if (ipod_connected ())
352 	    {
353 		gdouble free_space = get_ipod_free_space()
354 		    + del_filesize - nt_filesize;
355 		fill_label_size ("free_space", free_space);
356 	    }
357 	    else
358 	    {
359 		fill_label_string ("free_space", _("n/c"));
360 	    }
361 	}
362 	else
363 	{
364 	    fill_label_string ("free_space", _("offline"));
365 	}
366     }
367 }
368 
369 /* update "totals" view section */
info_update_totals_view(void)370 void info_update_totals_view (void)
371 {
372     guint32 tracks=0, playtime=0; /* playtime in secs */
373     gdouble  filesize=0;          /* in bytes */
374     Playlist *pl;
375     iTunesDB *itdb;
376 
377 	callback_call_all (callbacks_info_update_totals_view);
378 
379 	if (!info_window) return; /* not open */
380 
381     itdb = get_itdb_ipod ();
382     if (itdb)
383     {
384 	pl = itdb_playlist_mpl (itdb);
385 	g_return_if_fail (pl);
386 	fill_in_info (pl->members, &tracks, &playtime, &filesize);
387 	fill_label_uint ("total_playlists_ipod",
388 			 itdb_playlists_number (itdb)-1);
389 	fill_label_uint ("total_tracks_ipod", tracks);
390 	fill_label_time ("total_playtime_ipod", playtime);
391 	fill_label_size ("total_filesize_ipod", filesize);
392     }
393     itdb = get_itdb_local ();
394     if (itdb)
395     {
396 	pl = itdb_playlist_mpl (itdb);
397 	g_return_if_fail (pl);
398 	fill_in_info (pl->members, &tracks, &playtime, &filesize);
399 	fill_label_uint ("total_playlists_local",
400 			 itdb_playlists_number (itdb)-1);
401 	fill_label_uint ("total_tracks_local", tracks);
402 	fill_label_time ("total_playtime_local", playtime);
403 	fill_label_size ("total_filesize_local", filesize);
404     }
405     info_update_totals_view_space ();
406 }
407 
408 /*------------------------------------------------------------------*\
409  *                                                                  *
410  *                   Functions for Statusbar                        *
411  *                                                                  *
412 \*------------------------------------------------------------------*/
413 
414 void
gtkpod_statusbar_init(void)415 gtkpod_statusbar_init(void)
416 {
417     gtkpod_statusbar = gtkpod_xml_get_widget (main_window_xml, "gtkpod_status");
418     statusbar_timeout = STATUSBAR_TIMEOUT;
419 }
420 
421 static gint
gtkpod_statusbar_clear(gpointer data)422 gtkpod_statusbar_clear(gpointer data)
423 {
424     if(gtkpod_statusbar)
425     {
426 	gtk_statusbar_pop(GTK_STATUSBAR(gtkpod_statusbar), 1);
427     }
428     statusbar_timeout_id = 0; /* indicate that timeout handler is
429 				 clear (0 cannot be a handler id) */
430     return FALSE;
431 }
432 
433 
434 static void
gtkpod_statusbar_reset_timeout(void)435 gtkpod_statusbar_reset_timeout (void)
436 {
437     if (statusbar_timeout_id != 0) /* remove last timeout, if still present */
438 	gtk_timeout_remove (statusbar_timeout_id);
439     statusbar_timeout_id = gtk_timeout_add (statusbar_timeout,
440 					    (GtkFunction) gtkpod_statusbar_clear,
441 					    NULL);
442 }
443 
444 void
gtkpod_statusbar_timeout(guint timeout)445 gtkpod_statusbar_timeout (guint timeout)
446 {
447     if (timeout == 0)
448 	statusbar_timeout = STATUSBAR_TIMEOUT;
449     else
450 	statusbar_timeout = timeout;
451 
452     gtkpod_statusbar_reset_timeout ();
453 }
454 
455 
456 void
gtkpod_statusbar_message(const gchar * message,...)457 gtkpod_statusbar_message(const gchar *message, ...)
458 {
459     if(gtkpod_statusbar)
460     {
461 	va_list arg;
462 	gchar *text;
463 	guint context = 1;
464 
465 	va_start (arg, message);
466 	text = g_strdup_vprintf (message, arg);
467 	va_end (arg);
468 
469 	gtk_statusbar_pop(GTK_STATUSBAR(gtkpod_statusbar), context);
470 	gtk_statusbar_push(GTK_STATUSBAR(gtkpod_statusbar), context,  text);
471 
472 	g_free (text);
473 
474 	gtkpod_statusbar_reset_timeout ();
475     }
476 }
477 
478 void
gtkpod_tracks_statusbar_init()479 gtkpod_tracks_statusbar_init()
480 {
481     gtkpod_tracks_statusbar =
482 	gtkpod_xml_get_widget (main_window_xml, "tracks_statusbar");
483     gtkpod_tracks_statusbar_update();
484 }
485 
486 void
gtkpod_tracks_statusbar_update(void)487 gtkpod_tracks_statusbar_update(void)
488 {
489     if(gtkpod_tracks_statusbar)
490     {
491 	gchar *buf;
492 	Playlist *pl;
493 	pl = pm_get_selected_playlist ();
494 	/* select of which iTunesDB data should be displayed */
495 	if (pl)
496 	{
497 	    iTunesDB *itdb = pl->itdb;
498 	    g_return_if_fail (itdb);
499 
500 	    buf = g_strdup_printf (_(" P:%d T:%d/%d"),
501 				   itdb_playlists_number (itdb) - 1,
502 				   tm_get_nr_of_tracks (),
503 				   itdb_tracks_number (itdb));
504 	}
505 	else
506 	{
507 	    buf = g_strdup ("");
508 	}
509 	/* gets called before itdbs are setup up -> fail silently */
510 /*	g_return_if_fail (itdb);*/
511 
512 	gtk_statusbar_pop(GTK_STATUSBAR(gtkpod_tracks_statusbar), 1);
513 	gtk_statusbar_push(GTK_STATUSBAR(gtkpod_tracks_statusbar), 1,  buf);
514 	g_free (buf);
515     }
516     /* Update info window */
517     info_update ();
518 }
519 
520 /*------------------------------------------------------------------*\
521  *                                                                  *
522  *                       free space stuff                           *
523  *                                                                  *
524 \*------------------------------------------------------------------*/
525 
526 
527 /* Since the mount point is used by two separate threads, it can only
528    be accessed securely by using a locking mechanism. Therefore we
529    keep a copy of the mount point here. Access must only be done
530    after locking. */
space_set_ipod_itdb(iTunesDB * itdb)531 void space_set_ipod_itdb (iTunesDB *itdb)
532 {
533     const gchar *mp = NULL;
534 
535     if (itdb)
536     {
537 	ExtraiTunesDBData *eitdb = itdb->userdata;
538 	g_return_if_fail (eitdb);
539 
540 	if (!eitdb->ipod_ejected)
541 	{
542 	    mp = itdb_get_mountpoint (itdb);
543 	}
544     }
545 
546     if (space_mutex)  g_mutex_lock (space_mutex);
547 
548     space_itdb = itdb;
549 
550     /* update the free space data if mount point changed */
551     if (!space_mp || !mp || (strcmp (space_mp, mp) != 0))
552     {
553 	g_free (space_mp);
554 	space_mp = g_strdup (mp);
555 
556 	space_data_update ();
557     }
558 
559     if (space_mutex)   g_mutex_unlock (space_mutex);
560 
561 }
562 
563 /* retrieve the currently set ipod itdb -- needed in case the itdb is
564    deleted */
space_get_ipod_itdb(void)565 iTunesDB *space_get_ipod_itdb (void)
566 {
567     return space_itdb;
568 }
569 
570 
571 
572 /* iPod space has to be reread */
space_data_update(void)573 void space_data_update (void)
574 {
575     space_uptodate = FALSE;
576 }
577 
578 
579 /* Is the iPod connected? If space_ipod_used and space_ipod_free are
580    both zero, we assume the iPod is not connected */
ipod_connected(void)581 gboolean ipod_connected (void)
582 {
583     gboolean result;
584     g_return_val_if_fail (space_mutex!=NULL, FALSE);
585     g_mutex_lock (space_mutex);
586     if ((space_ipod_used == 0) && (space_ipod_free == 0)) result = FALSE;
587     else                                                  result = TRUE;
588     g_mutex_unlock (space_mutex);
589     return result;
590 }
591 
592 
593 
594 /* we'll use statvfs to determine free space on the iPod where
595    available, df otherwise */
596 #ifdef HAVE_STATVFS
597 #include <sys/types.h>
598 #include <sys/statvfs.h>
599 /* update space_ipod_free and space_ipod_used */
th_space_update(void)600 static void th_space_update (void)
601 {
602     gchar *mp=NULL;
603     struct statvfs stat;
604     int	status;
605 
606     g_mutex_lock (space_mutex);
607 
608     /* don't read info when in offline mode */
609     if (space_itdb && !get_offline (space_itdb))
610     {
611 	mp = g_strdup (space_mp);
612     }
613     if (mp)
614     {
615 	status = statvfs (mp, &stat);
616 	if (status != 0) {
617 	    /* XXX: why would this fail - what to do here??? */
618 	    goto done;
619 	}
620 	space_ipod_free = (gdouble)stat.f_bavail * stat.f_frsize;
621 	space_ipod_used = ((gdouble)stat.f_blocks * stat.f_frsize) -
622 	    space_ipod_free;
623 	space_uptodate = TRUE;
624 
625     } else { /* mp == NULL */
626 
627 	/* this is set even if offline mode */
628 	space_ipod_free = 0;
629 	space_ipod_used = 0;
630 	space_uptodate = FALSE;  /* this way we will detect when the
631 				    iPod is connected */
632     }
633 
634 done:
635     g_mutex_unlock (space_mutex);
636     g_free (mp);
637 }
638 
639 #else
640 static gchar*
get_drive_stats_from_df(const gchar * mp)641 get_drive_stats_from_df(const gchar *mp)
642 {
643     FILE *fp;
644     gchar buf[PATH_MAX+1];
645     gchar bufc[PATH_MAX+1];
646     gchar *bufp;
647     gchar *result = NULL;
648     guint bytes_read = 0;
649 
650 #if 0
651     GTimeVal gtv1, gtv2;
652     long micros;
653     g_get_current_time (&gtv1);
654 #endif
655 
656     if (g_file_test (mp, G_FILE_TEST_EXISTS))
657     {
658 	gchar *df_str = getenv ("GTKPOD_DF_COMMAND");
659 	if (df_str == NULL) df_str = "df -k -P";
660 	if (strlen (df_str))
661 	{
662 	    snprintf(bufc, PATH_MAX, "%s \"%s\"", df_str, mp);
663 	    fp = popen(bufc, "r");
664 	    if(fp)
665 	    {
666 		if((bytes_read = fread(buf, 1, PATH_MAX, fp)) > 0)
667 		{
668 		    if((bufp = strchr (buf, '\n')))
669 		    {
670 			int i = 0;
671 			int j = 0;
672 			gchar buf2[PATH_MAX+3];
673 
674 			++bufp; /* skip '\n' */
675 			while((bufp - buf + i < bytes_read) &&
676 			      (j < PATH_MAX))
677 			{
678 			    while(!g_ascii_isspace(bufp[i]) &&
679 				  (j<PATH_MAX))
680 			    {
681 				buf2[j++] = bufp[i++];
682 			    }
683 			    buf2[j++] = ' ';
684 			    while((bufp - buf + i < bytes_read) &&
685 				  g_ascii_isspace(bufp[i]))
686 			    {
687 				i++;
688 			    }
689 			}
690 			buf2[j] = '\0';
691 			result = g_strdup_printf("%s", buf2);
692 		    }
693 		}
694 		pclose(fp);
695 	    }
696 	}
697     }
698 #if 0
699     g_get_current_time (&gtv2);
700     micros = (gtv2.tv_sec-gtv1.tv_sec)*10000000 + (gtv2.tv_usec-gtv1.tv_usec);
701     printf ("df: %ld usec\n", micros);
702 #endif
703     return(result);
704 }
705 
706 
707 /* update space_ipod_free and space_ipod_used */
th_space_update(void)708 static void th_space_update (void)
709 {
710     gchar *mp=NULL, *line=NULL;
711     gchar **tokens = NULL;
712 
713 	g_mutex_lock (space_mutex);
714 	mp = g_strdup (space_mp);
715 	g_mutex_unlock (space_mutex);
716 
717     g_mutex_lock (space_mutex);
718 
719     if (line) tokens = g_strsplit(line, " ", 5);
720     if (tokens && tokens[0] && tokens[1] && tokens[2] && tokens[3])
721     {
722 	space_ipod_free = g_strtod (tokens[3], NULL) * 1024;
723 	space_ipod_used = g_strtod (tokens[2], NULL) * 1024;
724 	space_uptodate = TRUE;
725     }
726     else
727     {
728 	/* this is set even if offline mode */
729 	space_ipod_free = 0;
730 	space_ipod_used = 0;
731 	space_uptodate = FALSE;  /* this way we will detect when the
732 				    iPod is connected */
733     }
734     g_mutex_unlock (space_mutex);
735     g_free (mp);
736     g_strfreev(tokens);
737 }
738 #endif
739 
740 
741 /* keep space_ipod_free/used updated in regular intervals */
th_space_thread(gpointer gp)742 static gpointer th_space_thread (gpointer gp)
743 {
744     struct timespec req;
745 
746     req.tv_sec = SPACE_TIMEOUT / 1000;
747     req.tv_nsec = (SPACE_TIMEOUT % 1000) * 1000000;
748 
749     for (;;)
750     {
751 	nanosleep (&req, NULL);
752 	if (!space_uptodate)   th_space_update ();
753     }
754     /* To make gcc happy (never reached) */
755     return (gpointer)NULL;
756 }
757 
758 
759 /* in Bytes */
get_ipod_free_space(void)760 gdouble get_ipod_free_space(void)
761 {
762     gdouble result;
763     g_mutex_lock (space_mutex);
764     result = space_ipod_free;
765     g_mutex_unlock (space_mutex);
766     return result;
767 }
768 
769 #if 0
770 /* in Bytes */
771 static gdouble get_ipod_used_space(void)
772 {
773     gdouble result;
774     g_mutex_lock (space_mutex);
775     result = space_ipod_used;
776     g_mutex_unlock (space_mutex);
777     return result;
778 }
779 #endif
780 
781 
782 /* @size: size in B */
783 gchar*
get_filesize_as_string(gdouble size)784 get_filesize_as_string(gdouble size)
785 {
786     guint i = 0;
787     gchar *result = NULL;
788     gchar *sizes[] = { _("B"), _("kB"), _("MB"), _("GB"), _("TB"), NULL };
789 
790     while((fabs(size) > 1024) && (i<4))
791     {
792 	size /= 1024;
793 	++i;
794     }
795     if (i>0)
796     {
797 	if (fabs(size) < 10)
798 	    result = g_strdup_printf("%0.2f %s", size, sizes[i]);
799 	else if (fabs(size) < 100)
800 	    result = g_strdup_printf("%0.1f %s", size, sizes[i]);
801 	else
802 	    result = g_strdup_printf("%0.0f %s", size, sizes[i]);
803     }
804     else
805     {   /* Bytes do not have decimal places */
806 	result = g_strdup_printf ("%0.0f %s", size, sizes[i]);
807     }
808     return result;
809 }
810 
811 static guint
gtkpod_space_statusbar_update(void)812 gtkpod_space_statusbar_update(void)
813 {
814     if(space_itdb && gtkpod_space_statusbar)
815     {
816 	gchar *buf = NULL;
817 	gchar *str = NULL;
818 
819 	if (!get_offline (space_itdb))
820 	{
821 	    if (ipod_connected ())
822 	    {
823 		gdouble left, pending, deleted;
824 
825 		gp_info_deleted_tracks (space_itdb, &deleted, NULL);
826 		gp_info_nontransferred_tracks (space_itdb, &pending, NULL);
827 		left = get_ipod_free_space() + deleted;
828 		if((left-pending) > 0)
829 		{
830 		    str = get_filesize_as_string(left - pending);
831 		    buf = g_strdup_printf (_(" %s Free"), str);
832 		}
833 		else
834 		{
835 		    str = get_filesize_as_string(pending - left);
836 		    buf = g_strdup_printf (_(" %s Pending"), str);
837 		}
838 	    }
839 	    else
840 	    {
841 		buf = g_strdup (_(" disconnected"));
842 	    }
843 	}
844 	else
845 	{
846 	    buf = g_strdup (_("offline"));
847 	}
848 	gtk_statusbar_pop(GTK_STATUSBAR(gtkpod_space_statusbar), 1);
849 	gtk_statusbar_push(GTK_STATUSBAR(gtkpod_space_statusbar), 1,  buf);
850 	g_free (buf);
851 	g_free (str);
852     }
853     info_update_totals_view_space ();
854     return TRUE;
855 }
856 
857 void
gtkpod_space_statusbar_init(void)858 gtkpod_space_statusbar_init(void)
859 {
860     gtkpod_space_statusbar = gtkpod_xml_get_widget (main_window_xml, "space_statusbar");
861 
862     if (!space_mutex)
863     {
864 	space_mutex = g_mutex_new ();
865 	if (!space_mp)
866 	{
867 	    iTunesDB *itdb = gp_get_ipod_itdb ();
868 
869 	    if (itdb)
870 	    {
871 		space_mp = get_itdb_prefs_string (itdb, KEY_MOUNTPOINT);
872 		th_space_update ();  /* make sure we have current data */
873 	    }
874 	}
875 	space_thread = g_thread_create (th_space_thread,
876 					    NULL, FALSE, NULL);
877     }
878     gtkpod_space_statusbar_update();
879     gtk_timeout_add(1000, (GtkFunction) gtkpod_space_statusbar_update, NULL);
880 }
881 
882 
883 
884 /*------------------------------------------------------------------*\
885  *                                                                  *
886  *              Frequently used error messages                      *
887  *                                                                  *
888 \*------------------------------------------------------------------*/
889 
message_sb_no_itdb_selected()890 void message_sb_no_itdb_selected ()
891 {
892     gtkpod_statusbar_message (_("No database or playlist selected"));
893 }
894 
message_sb_no_tracks_selected()895 void message_sb_no_tracks_selected ()
896 {
897     gtkpod_statusbar_message (_("No tracks selected"));
898 }
899 
message_sb_no_playlist_selected()900 void message_sb_no_playlist_selected ()
901 {
902     gtkpod_statusbar_message (_("No playlist selected"));
903 }
904 
message_sb_no_ipod_itdb_selected()905 void message_sb_no_ipod_itdb_selected ()
906 {
907     gtkpod_statusbar_message (_("No iPod or iPod playlist selected"));
908 }
909 
910