1 /*
2 |  Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users.sourceforge.net>
3 |  Part of the gtkpod project.
4 |
5 |  URL: http://gtkpod.sourceforge.net/
6 |
7 |  This program is free software; you can redistribute it and/or modify
8 |  it under the terms of the GNU General Public License as published by
9 |  the Free Software Foundation; either version 2 of the License, or
10 |  (at your option) any later version.
11 |
12 |  This program is distributed in the hope that it will be useful,
13 |  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 |  GNU General Public License for more details.
16 |
17 |  You should have received a copy of the GNU General Public License
18 |  along with this program; if not, write to the Free Software
19 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 |
21 |  iTunes and iPod are trademarks of Apple
22 |
23 |  This product is not supported/written/published by Apple!
24 |
25 |  $Id$
26 */
27 
28 #ifdef HAVE_CONFIG_H
29 #  include <config.h>
30 #endif
31 
32 #include <gtk/gtk.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 
37 #include "itdb.h"
38 #include "misc.h"
39 #include "display.h"
40 #include "prefs.h"
41 
42 static const gchar *SPL_WINDOW_DEFX="spl_window_defx";
43 static const gchar *SPL_WINDOW_DEFY="spl_window_defy";
44 
45 static void spl_display_checklimits (GtkWidget *spl_window);
46 static void spl_update_rule (GtkWidget *spl_window, Itdb_SPLRule *splr);
47 static void spl_update_rules_from_row (GtkWidget *spl_window, gint row);
48 static void spl_action_changed (GtkComboBox *combobox, GtkWidget *spl_window);
49 
50 #define WNLEN 100 /* max length for widget names */
51 #define XPAD 1    /* padding for g_table_attach () */
52 #define YPAD 1    /* padding for g_table_attach () */
53 
54 typedef struct
55 {
56     guint32 id;
57     const gchar *str;
58 } ComboEntry;
59 
60 GladeXML *spl_window_xml;
61 
62 static const ComboEntry splat_inthelast_units_comboentries[] =
63 {
64     { ITDB_SPLACTION_LAST_DAYS_VALUE,   N_("days") },
65     { ITDB_SPLACTION_LAST_WEEKS_VALUE,  N_("weeks") },
66     { ITDB_SPLACTION_LAST_MONTHS_VALUE, N_("months") },
67     { 0,                           NULL }
68 };
69 
70 
71 static const ComboEntry splfield_units[] =
72 {
73     { ITDB_SPLFIELD_BITRATE,        N_("kbps") },
74     { ITDB_SPLFIELD_SAMPLE_RATE,    N_("Hz") },
75     { ITDB_SPLFIELD_SIZE,           N_("MB") },
76     { ITDB_SPLFIELD_TIME,           N_("secs") },
77     { 0,                       NULL }
78 };
79 
80 
81 static const ComboEntry splfield_comboentries[] =
82 {
83     { ITDB_SPLFIELD_SONG_NAME,      N_("Title") },
84     { ITDB_SPLFIELD_ALBUM,          N_("Album") },
85     { ITDB_SPLFIELD_ARTIST,         N_("Artist") },
86     { ITDB_SPLFIELD_BITRATE,        N_("Bitrate") },
87     { ITDB_SPLFIELD_SAMPLE_RATE,    N_("Samplerate") },
88     { ITDB_SPLFIELD_YEAR,           N_("Year") },
89     { ITDB_SPLFIELD_GENRE,          N_("Genre") },
90     { ITDB_SPLFIELD_KIND,           N_("Kind") },
91     { ITDB_SPLFIELD_DATE_MODIFIED,  N_("Date modified") },
92     { ITDB_SPLFIELD_TRACKNUMBER,    N_("Track number") },
93     { ITDB_SPLFIELD_SIZE,           N_("Size") },
94     { ITDB_SPLFIELD_TIME,           N_("Play time") },
95     { ITDB_SPLFIELD_COMMENT,        N_("Comment") },
96     { ITDB_SPLFIELD_DATE_ADDED,     N_("Date added") },
97     { ITDB_SPLFIELD_COMPOSER,       N_("Composer") },
98     { ITDB_SPLFIELD_PLAYCOUNT,      N_("Playcount") },
99     { ITDB_SPLFIELD_LAST_PLAYED,    N_("Last played") },
100     { ITDB_SPLFIELD_DISC_NUMBER,    N_("Disc number") },
101     { ITDB_SPLFIELD_RATING,         N_("Rating") },
102     { ITDB_SPLFIELD_COMPILATION,    N_("Compilation") },
103     { ITDB_SPLFIELD_BPM,            N_("BPM") },
104     { ITDB_SPLFIELD_GROUPING,       N_("Grouping") },
105     { ITDB_SPLFIELD_PLAYLIST,       N_("Playlist") },
106     { ITDB_SPLFIELD_VIDEO_KIND,     N_("Video Kind") },
107     { ITDB_SPLFIELD_TVSHOW,         N_("TV Show") },
108     { ITDB_SPLFIELD_SEASON_NR,      N_("Season number") },
109     { ITDB_SPLFIELD_SKIPCOUNT,      N_("Skip count") },
110     { ITDB_SPLFIELD_LAST_SKIPPED,   N_("Last skipped") },
111     { ITDB_SPLFIELD_ALBUMARTIST,    N_("Album artist") },
112     { 0,                       NULL }
113 };
114 
115 static const ComboEntry splaction_ftstring_comboentries[] =
116 {
117     { ITDB_SPLACTION_CONTAINS,         N_("contains") },
118     { ITDB_SPLACTION_DOES_NOT_CONTAIN, N_("does not contain") },
119     { ITDB_SPLACTION_IS_STRING,        N_("is") },
120     { ITDB_SPLACTION_IS_NOT,           N_("is not") },
121     { ITDB_SPLACTION_STARTS_WITH,      N_("starts with") },
122     { ITDB_SPLACTION_ENDS_WITH,        N_("ends with") },
123     { 0,                          NULL }
124 };
125 
126 static const ComboEntry splaction_ftint_comboentries[] =
127 {
128     { ITDB_SPLACTION_IS_INT,          N_("is") },
129     { ITDB_SPLACTION_IS_NOT_INT,      N_("is not") },
130     { ITDB_SPLACTION_IS_GREATER_THAN, N_("is greater than") },
131     { ITDB_SPLACTION_IS_LESS_THAN,    N_("is less than") },
132     { ITDB_SPLACTION_IS_IN_THE_RANGE, N_("is in the range") },
133     { 0,                         NULL }
134 };
135 
136 static const ComboEntry splaction_ftdate_comboentries[] =
137 {
138     { ITDB_SPLACTION_IS_INT,             N_("is") },
139     { ITDB_SPLACTION_IS_NOT_INT,         N_("is not") },
140     { ITDB_SPLACTION_IS_GREATER_THAN,    N_("is after") },
141     { ITDB_SPLACTION_IS_LESS_THAN,       N_("is before") },
142     { ITDB_SPLACTION_IS_IN_THE_LAST,     N_("in the last") },
143     { ITDB_SPLACTION_IS_NOT_IN_THE_LAST, N_("not in the last") },
144     { ITDB_SPLACTION_IS_IN_THE_RANGE,    N_("is in the range") },
145     { 0,                            NULL }
146 };
147 
148 static const ComboEntry splaction_ftboolean_comboentries[] =
149 {
150     { ITDB_SPLACTION_IS_INT,     N_("is set") },
151     { ITDB_SPLACTION_IS_NOT_INT, N_("is not set") },
152     { 0,                    NULL }
153 };
154 
155 static const ComboEntry splaction_ftplaylist_comboentries[] =
156 {
157     { ITDB_SPLACTION_IS_INT,     N_("is") },
158     { ITDB_SPLACTION_IS_NOT_INT, N_("is not") },
159     { 0,                    NULL }
160 };
161 
162 static const ComboEntry splaction_ftbinaryand_comboentries[] =
163 {
164     { ITDB_SPLACTION_BINARY_AND, N_("is") },
165     { ITDB_SPLACTION_BINARY_AND, N_("is not") },
166     { 0,                    NULL }
167 };
168 
169 static const ComboEntry splaction_notsupported_comboentries[] =
170 {
171     { -1,     N_("Not supported") },
172     { 0,                    NULL }
173 };
174 
175 /* Strings for limittypes */
176 static const ComboEntry limittype_comboentries[] =
177 {
178     { ITDB_LIMITTYPE_MINUTES, N_("minutes") },
179     { ITDB_LIMITTYPE_MB,      N_("MB") },
180     { ITDB_LIMITTYPE_SONGS,   N_("tracks") },
181     { ITDB_LIMITTYPE_HOURS,   N_("hours") },
182     { ITDB_LIMITTYPE_GB,      N_("GB") },
183     { 0,                 NULL }
184 };
185 
186 /* Strings for limitsort */
187 static const ComboEntry limitsort_comboentries[] =
188 {
189     { ITDB_LIMITSORT_RANDOM,                N_("random order") },
190     { ITDB_LIMITSORT_SONG_NAME,             N_("title") },
191     { ITDB_LIMITSORT_ALBUM,                 N_("album") },
192     { ITDB_LIMITSORT_ARTIST,                N_("artist") },
193     { ITDB_LIMITSORT_GENRE,                 N_("genre") },
194     { ITDB_LIMITSORT_MOST_RECENTLY_ADDED,   N_("most recently added") },
195     { ITDB_LIMITSORT_LEAST_RECENTLY_ADDED,  N_("least recently added") },
196     { ITDB_LIMITSORT_MOST_OFTEN_PLAYED,     N_("most often played") },
197     { ITDB_LIMITSORT_LEAST_OFTEN_PLAYED,    N_("least often played") },
198     { ITDB_LIMITSORT_MOST_RECENTLY_PLAYED,  N_("most recently played") },
199     { ITDB_LIMITSORT_LEAST_RECENTLY_PLAYED, N_("least recently played") },
200     { ITDB_LIMITSORT_HIGHEST_RATING,        N_("highest rating") },
201     { ITDB_LIMITSORT_LOWEST_RATING,         N_("lowest rating") },
202     { 0,                               NULL }
203 };
204 
205 /* Strings for Video Kind */
206 static const ComboEntry videokind_comboentries_is[] =
207 {
208     { ITDB_MEDIATYPE_MOVIE,      N_("Movie") },
209     { ITDB_MEDIATYPE_MUSICVIDEO, N_("Music Video") },
210     { ITDB_MEDIATYPE_TVSHOW,     N_("TV Show") },
211     { 0,                         NULL }
212 };
213 
214 /* Strings for Video Kind */
215 static const ComboEntry videokind_comboentries_is_not[] =
216 {
217     { 0x0e00 | ITDB_MEDIATYPE_MUSICVIDEO | ITDB_MEDIATYPE_TVSHOW,    N_("Movie") },
218     { 0x0e00 | ITDB_MEDIATYPE_MOVIE | ITDB_MEDIATYPE_TVSHOW,   N_("Music Video") },
219     { 0x0e00 | ITDB_MEDIATYPE_MOVIE | ITDB_MEDIATYPE_MUSICVIDEO,   N_("TV Show") },
220     { 0,                                                                    NULL }
221 };
222 
223 /* types used for entries (from, to, date...) */
224 enum entrytype
225 {
226     spl_ET_FROMVALUE = 1,
227     spl_ET_FROMVALUE_DATE,  /* fromvalue interpreted as datestamp */
228     spl_ET_FROMDATE,
229     spl_ET_TOVALUE,
230     spl_ET_TOVALUE_DATE,    /* tovalue interpreted as datestamp */
231     spl_ET_TODATE,
232     spl_ET_INTHELAST,
233     spl_ET_STRING,
234 };
235 
236 enum matchmode
237 {
238 	spl_MATCH_ANY = 0,
239 	spl_MATCH_ALL,
240 	spl_MATCH_IGNORE
241 };
242 
243 static const gchar *entry_get_string (gchar *str, Itdb_SPLRule *splr,
244 				      enum entrytype et);
245 
246 /* Get index from ID (returns -1 if ID could not be found) */
comboentry_index_from_id(const ComboEntry centries[],guint32 id)247 static gint comboentry_index_from_id (const ComboEntry centries[],
248 				      guint32 id)
249 {
250     gint i;
251 
252     g_return_val_if_fail (centries, -1);
253 
254     /* handle field types not yet supported */
255     if (centries == splaction_notsupported_comboentries)
256 	return 0;
257 
258     for (i=0; centries[i].str; ++i)
259     {
260 	if (centries[i].id == id)  return i;
261     }
262     return -1;
263 }
264 
265 
266 /* Get index in @array from playlist ID @id */
267 /* returns -1 in case of error */
pl_ids_index_from_id(GArray * pl_ids,guint64 id)268 static gint pl_ids_index_from_id (GArray *pl_ids, guint64 id)
269 {
270     gint index;
271 
272     g_return_val_if_fail (pl_ids, -1);
273 
274     for (index=0; ; ++index)
275     {
276 	guint64 sid = g_array_index (pl_ids, guint64, index);
277 	if (sid == id)  return index;
278 	if (sid == 0)   return -1;
279     }
280 }
281 
282 
283 /* called by spl_set_combobox() */
spl_setup_combobox(GtkComboBox * cb,const ComboEntry centries[],gint index,GCallback cb_func,gpointer cb_data)284 static void spl_setup_combobox (GtkComboBox *cb,
285 				const ComboEntry centries[], gint index,
286 				GCallback cb_func, gpointer cb_data)
287 {
288     const ComboEntry *old_centries = g_object_get_data (G_OBJECT (cb),
289 							"spl_centries");
290 
291     if ((g_object_get_data (G_OBJECT (cb), "combo_set") == NULL) ||
292 	(centries != old_centries))
293     {   /* the combo has not yet been initialized */
294 	const ComboEntry *ce = centries;
295 	GtkCellRenderer *cell;
296 	GtkListStore *store;
297 
298 	/* Set the model -- that is you cannot do a
299 	   gtk_combo_box_new_text()! This gives us the flexibility to
300 	   expand this function to set some graphic next to the text. */
301 	store = gtk_list_store_new (1, G_TYPE_STRING);
302 	gtk_combo_box_set_model (cb, GTK_TREE_MODEL (store));
303 	g_object_unref (store);
304 
305 	cell = gtk_cell_renderer_text_new ();
306 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), cell, TRUE);
307 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cb), cell,
308 					"text", 0,
309 					NULL);
310 
311 	while (ce->str != NULL)
312 	{
313 	    gtk_combo_box_append_text (cb, _(ce->str));
314 	    ++ce;
315 	}
316 	g_object_set_data (G_OBJECT (cb), "spl_centries", (gpointer)centries);
317 	g_object_set_data (G_OBJECT (cb), "combo_set", "set");
318 	if (cb_func)
319 	    g_signal_connect (cb, "changed", cb_func, cb_data);
320     }
321     if (index != -1)
322     {
323 	gtk_combo_box_set_active (cb, index);
324     }
325 }
326 
327 
328 /* Initialize/update ComboBox @cb with strings from @centries and
329    select @id. If @cb_func is != NULL, connect @cb_func to signal
330    "changed" with data @cb_data. */
spl_set_combobox(GtkComboBox * cb,const ComboEntry centries[],guint32 id,GCallback cb_func,gpointer cb_data)331 static void spl_set_combobox (GtkComboBox *cb,
332 			      const ComboEntry centries[], guint32 id,
333 			      GCallback cb_func, gpointer cb_data)
334 {
335     gint index;
336 
337     g_return_if_fail (cb);
338     g_return_if_fail (centries);
339 
340     index = comboentry_index_from_id (centries, id);
341 
342     spl_setup_combobox (cb, centries, index, cb_func, cb_data);
343 
344 }
345 
spl_matchcheckedonly_toggled(GtkToggleButton * togglebutton,GtkWidget * spl_window)346 static void spl_matchcheckedonly_toggled (GtkToggleButton *togglebutton,
347 					  GtkWidget *spl_window)
348 {
349     Playlist *spl;
350 
351     g_return_if_fail (spl_window);
352     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
353     g_return_if_fail (spl);
354     spl->splpref.matchcheckedonly =
355 	gtk_toggle_button_get_active (togglebutton);
356 }
357 
spl_liveupdate_toggled(GtkToggleButton * togglebutton,GtkWidget * spl_window)358 static void spl_liveupdate_toggled (GtkToggleButton *togglebutton,
359 				    GtkWidget *spl_window)
360 {
361     Playlist *spl;
362 
363     g_return_if_fail (spl_window);
364     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
365     g_return_if_fail (spl);
366     spl->splpref.liveupdate =
367 	gtk_toggle_button_get_active (togglebutton);
368 }
369 
370 
spl_checklimits_toggled(GtkToggleButton * togglebutton,GtkWidget * spl_window)371 static void spl_checklimits_toggled (GtkToggleButton *togglebutton,
372 				     GtkWidget *spl_window)
373 {
374     Playlist *spl;
375 
376     g_return_if_fail (spl_window);
377     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
378     g_return_if_fail (spl);
379     spl->splpref.checklimits =
380 	gtk_toggle_button_get_active (togglebutton);
381     spl_display_checklimits (spl_window);
382 }
383 
384 
385 /* The limitvalue has changed */
spl_limitvalue_changed(GtkEditable * editable,GtkWidget * spl_window)386 static void spl_limitvalue_changed (GtkEditable *editable,
387 				    GtkWidget *spl_window)
388 {
389     Playlist *spl;
390     gchar *str;
391 
392     g_return_if_fail (spl_window);
393     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
394     g_return_if_fail (spl);
395     str = gtk_editable_get_chars (editable, 0, -1);
396     spl->splpref.limitvalue = atol (str);
397     g_free (str);
398 }
399 
400 
401 
402 /* Limittype has been changed */
spl_limittype_changed(GtkComboBox * combobox,GtkWidget * spl_window)403 static void spl_limittype_changed (GtkComboBox *combobox,
404 				   GtkWidget *spl_window)
405 {
406     Playlist *spl;
407     gint index = gtk_combo_box_get_active (combobox);
408 
409     g_return_if_fail (index != -1);
410     g_return_if_fail (spl_window);
411     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
412     g_return_if_fail (spl);
413     spl->splpref.limittype = limittype_comboentries[index].id;
414 }
415 
416 
417 /* Limitsort has been changed */
spl_limitsort_changed(GtkComboBox * combobox,GtkWidget * spl_window)418 static void spl_limitsort_changed (GtkComboBox *combobox,
419 				   GtkWidget *spl_window)
420 {
421     Playlist *spl;
422     gint index = gtk_combo_box_get_active (combobox);
423 
424     g_return_if_fail (index != -1);
425     g_return_if_fail (spl_window);
426     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
427     g_return_if_fail (spl);
428     spl->splpref.limitsort = limitsort_comboentries[index].id;
429 }
430 
431 
432 /* Rule field has been changed */
spl_field_changed(GtkComboBox * combobox,GtkWidget * spl_window)433 static void spl_field_changed (GtkComboBox *combobox, GtkWidget *spl_window)
434 {
435     Playlist *spl;
436     Itdb_SPLRule *splr;
437     gint index = gtk_combo_box_get_active (combobox);
438 
439     g_return_if_fail (index != -1);
440     g_return_if_fail (spl_window);
441     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
442     g_return_if_fail (spl);
443 
444     splr = g_object_get_data (G_OBJECT (combobox), "spl_rule");
445     g_return_if_fail (splr);
446 
447     if (splr->field != splfield_comboentries[index].id)
448     {   /* data changed */
449 	splr->field = splfield_comboentries[index].id;
450 	/* update display */
451 	spl_update_rule (spl_window, splr);
452     }
453 }
454 
455 
456 /* Action field has been changed */
spl_action_changed(GtkComboBox * combobox,GtkWidget * spl_window)457 static void spl_action_changed (GtkComboBox *combobox, GtkWidget *spl_window)
458 {
459     Playlist *spl;
460     Itdb_SPLRule *splr;
461     ItdbSPLFieldType ft;
462     const ComboEntry *centries;
463     gint index = gtk_combo_box_get_active (combobox);
464 
465     g_return_if_fail (index != -1);
466     g_return_if_fail (spl_window);
467     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
468     g_return_if_fail (spl);
469 
470     splr = g_object_get_data (G_OBJECT (combobox), "spl_rule");
471     g_return_if_fail (splr);
472 
473     centries = g_object_get_data (G_OBJECT (combobox), "spl_centries");
474     g_return_if_fail (centries);
475 
476     ft = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combobox), "spl_fieldtype"));
477 
478 /* printf ("(action) value changed: %04x...", (gint)splr->fromvalue); */
479     switch (ft)
480     {
481     case ITDB_SPLFT_BINARY_AND:
482 	if (splr->field == ITDB_SPLFIELD_VIDEO_KIND)
483 	{   /* traet Video Kind differently */
484 	    gint oldindex = GPOINTER_TO_INT (
485 		g_object_get_data (G_OBJECT (combobox), "spl_binary_and_index"));
486 	    if (oldindex != index)
487 	    {   /* data changed */
488 		/* inverse the video bits */
489 		splr->fromvalue = (~splr->fromvalue) &
490 		    (0x0e00 | ITDB_MEDIATYPE_MOVIE | ITDB_MEDIATYPE_MUSICVIDEO | ITDB_MEDIATYPE_TVSHOW);
491 		spl_update_rule (spl_window, splr);
492 	    }
493 	}
494 	else
495 	{   /* treat as standard */
496 	    if (splr->action != centries[index].id)
497 	    {   /* data changed */
498 		splr->action = centries[index].id;
499 		/* update display */
500 		spl_update_rule (spl_window, splr);
501 	    }
502 	}
503 	break;
504     default:
505 	if (splr->action != centries[index].id)
506 	{   /* data changed */
507 	    splr->action = centries[index].id;
508 	    /* update display */
509 	    spl_update_rule (spl_window, splr);
510 	}
511 	break;
512     }
513 /* printf ("%04x\n", (gint)splr->fromvalue); */
514 }
515 
516 
517 /*  field has been changed */
spl_videokind_comboentry_changed(GtkComboBox * combobox,GtkWidget * spl_window)518 static void spl_videokind_comboentry_changed (GtkComboBox *combobox,
519 					      GtkWidget *spl_window)
520 {
521     Playlist *spl;
522     Itdb_SPLRule *splr;
523     const ComboEntry *centries;
524     gint index = gtk_combo_box_get_active (combobox);
525 
526     g_return_if_fail (index != -1);
527     g_return_if_fail (spl_window);
528 
529     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
530     g_return_if_fail (spl);
531 
532     splr = g_object_get_data (G_OBJECT (combobox), "spl_rule");
533     g_return_if_fail (splr);
534 
535     centries = g_object_get_data (G_OBJECT (combobox), "spl_centries");
536     g_return_if_fail (centries);
537 
538 /* printf ("(entry) value changed: %04x...", (gint)splr->fromvalue); */
539     if (splr->fromvalue != centries[index].id)
540     {   /* data changed */
541 	splr->fromvalue = centries[index].id;
542     }
543 /* printf ("%04x\n", (gint)splr->fromvalue); */
544 }
545 
546 
547 /* The enter key was pressed inside a rule entry (fromvalue, fromdate,
548  * tovalue, todate, string...)  --> redisplay */
splr_entry_redisplay(GtkEditable * editable,GtkWidget * spl_window)549 static void splr_entry_redisplay (GtkEditable *editable, GtkWidget *spl_window)
550 {
551     Itdb_SPLRule *splr;
552     enum entrytype type;
553     gchar str[WNLEN];
554     const gchar *strp;
555 
556     g_return_if_fail (spl_window);
557     splr =  g_object_get_data (G_OBJECT (editable), "spl_rule");
558     g_return_if_fail (splr);
559     type = (enum entrytype)g_object_get_data (
560 	G_OBJECT (editable), "spl_entrytype");
561     g_return_if_fail (type != 0);
562     strp = entry_get_string (str, splr, type);
563     if (strp)  gtk_entry_set_text (GTK_ENTRY (editable), strp);
564 }
565 
566 
567 
568 /* The content of a rule entry (fromvalue, fromdate, tovalue, todate,
569  * string...)  has changed --> update */
splr_entry_changed(GtkEditable * editable,GtkWidget * spl_window)570 static void splr_entry_changed (GtkEditable *editable,
571 				GtkWidget *spl_window)
572 {
573     Itdb_SPLRule *splr;
574     gchar *str;
575     time_t t;
576     enum entrytype type;
577 
578     g_return_if_fail (spl_window);
579     splr =  g_object_get_data (G_OBJECT (editable), "spl_rule");
580     g_return_if_fail (splr);
581     type = (enum entrytype)g_object_get_data (
582 	G_OBJECT (editable), "spl_entrytype");
583     g_return_if_fail (type != 0);
584 
585     str = gtk_editable_get_chars (editable, 0, -1);
586     switch (type)
587     {
588     case spl_ET_FROMVALUE:
589 	switch (splr->field)
590 	{
591 	case ITDB_SPLFIELD_RATING:
592 	    splr->fromvalue = ITDB_RATING_STEP * atol (str);
593 	    break;
594 	case ITDB_SPLFIELD_TIME:
595 	    splr->fromvalue = 1000 * strtod (str, NULL);
596 	    break;
597 	default:
598 	    splr->fromvalue = atol (str);
599 	    break;
600 	}
601 	break;
602     case spl_ET_FROMVALUE_DATE:
603 	t = time_string_to_fromtime (str);
604 	if (t != -1)
605 	    splr->fromvalue = t;
606 	break;
607     case spl_ET_FROMDATE:
608 	splr->fromdate = atol (str);
609 	break;
610     case spl_ET_TOVALUE:
611 	switch (splr->field)
612 	{
613 	case ITDB_SPLFIELD_RATING:
614 	    splr->tovalue = ITDB_RATING_STEP * atol (str);
615 	    break;
616 	case ITDB_SPLFIELD_TIME:
617 	    splr->tovalue = 1000 * strtod (str, NULL);
618 	    break;
619 	default:
620 	    splr->tovalue = atol (str);
621 	    break;
622 	}
623 	break;
624     case spl_ET_TOVALUE_DATE:
625 	t = time_string_to_totime (str);
626 	if (t != -1)
627 	    splr->tovalue = t;
628 	break;
629     case spl_ET_TODATE:
630 	splr->todate = atol (str);
631 	break;
632     case spl_ET_INTHELAST:
633 	splr->fromdate = -atol (str);
634 	break;
635     case spl_ET_STRING:
636 	g_free (splr->string);
637 	splr->string = g_strdup (str);
638 	break;
639     default:
640 	/* must not happen */
641 	g_free (str);
642 	g_return_if_fail (FALSE);
643 	break;
644     }
645     g_free (str);
646 }
647 
648 
649 /* splat_inthelast's fromunits have changed --> update */
spl_fromunits_changed(GtkComboBox * combobox,GtkWidget * spl_window)650 static void spl_fromunits_changed (GtkComboBox *combobox,
651 				   GtkWidget *spl_window)
652 {
653     Itdb_SPLRule *splr;
654     gint index = gtk_combo_box_get_active (combobox);
655 
656     g_return_if_fail (index != -1);
657     g_return_if_fail (spl_window);
658     splr =  g_object_get_data (G_OBJECT (combobox), "spl_rule");
659     g_return_if_fail (splr);
660     splr->fromunits =
661 	splat_inthelast_units_comboentries[index].id;
662 }
663 
664 
665 
666 
667 /* splat_playlist has changed --> update */
spl_playlist_changed(GtkComboBox * combobox,GtkWidget * spl_window)668 static void spl_playlist_changed (GtkComboBox *combobox,
669 				  GtkWidget *spl_window)
670 {
671     Itdb_SPLRule *splr;
672     GArray *pl_ids;
673     gint index;
674 
675     g_return_if_fail (combobox);
676     g_return_if_fail (spl_window);
677     splr =  g_object_get_data (G_OBJECT (combobox), "spl_rule");
678     g_return_if_fail (splr);
679     pl_ids = g_object_get_data (G_OBJECT (combobox), "spl_pl_ids");
680     g_return_if_fail (pl_ids);
681     index = gtk_combo_box_get_active (combobox);
682     g_return_if_fail (index != -1);
683     splr->fromvalue = g_array_index (pl_ids, guint64, index);
684 }
685 
686 
687 /* Deactivate "minus" (delete rule) button if only one rule is
688    displayed, activate the "minus" (delete rule) buttons if more than
689    one rule is displayed. This affects only the first button
690    (spl_button-0) */
spl_check_number_of_rules(GtkWidget * spl_window)691 static void spl_check_number_of_rules (GtkWidget *spl_window)
692 {
693     Playlist *spl;
694     GtkTable *table;
695     gint numrules;
696     GtkWidget *button;
697 
698     g_return_if_fail (spl_window);
699 
700     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
701     g_return_if_fail (spl);
702 
703     table = g_object_get_data (G_OBJECT (spl_window), "spl_rules_table");
704     g_return_if_fail (table);
705 
706     numrules = g_list_length (spl->splrules.rules);
707     g_return_if_fail (numrules > 0);
708 
709     button = g_object_get_data (G_OBJECT (table), "spl_button-0");
710     g_return_if_fail (button);
711     if (numrules > 1)
712 	gtk_widget_set_sensitive (button, TRUE);
713     else
714 	gtk_widget_set_sensitive (button, FALSE);
715 }
716 
717 
spl_button_minus_clicked(GtkButton * button,GtkWidget * spl_window)718 static void spl_button_minus_clicked (GtkButton *button,
719 				      GtkWidget *spl_window)
720 {
721     Playlist *spl;
722     Itdb_SPLRule *splr;
723     gint row;
724 
725     g_return_if_fail (spl_window);
726 
727     splr =  g_object_get_data (G_OBJECT (button), "spl_rule");
728     g_return_if_fail (splr);
729 
730     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
731     g_return_if_fail (spl);
732 
733     row = g_list_index (spl->splrules.rules, splr);
734     g_return_if_fail (row != -1);
735 
736     itdb_splr_remove (spl, splr);
737     spl_update_rules_from_row (spl_window, row);
738 
739     spl_check_number_of_rules (spl_window);
740 }
741 
742 
spl_button_plus_clicked(GtkButton * button,GtkWidget * spl_window)743 static void spl_button_plus_clicked (GtkButton *button,
744 				     GtkWidget *spl_window)
745 {
746     Playlist *spl;
747     Itdb_SPLRule *splr;
748     gint row;
749 
750     g_return_if_fail (spl_window);
751 
752     splr =  g_object_get_data (G_OBJECT (button), "spl_rule");
753     g_return_if_fail (splr);
754 
755     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
756     g_return_if_fail (spl);
757 
758     row = g_list_index (spl->splrules.rules, splr);
759     g_return_if_fail (row != -1);
760 
761     itdb_splr_add_new (spl, row+1);
762     spl_update_rules_from_row (spl_window, row+1);
763 
764     spl_check_number_of_rules (spl_window);
765 }
766 
767 
spl_store_window_size(GtkWidget * spl_window)768 static void spl_store_window_size (GtkWidget *spl_window)
769 {
770     gint defx, defy;
771 
772     gtk_window_get_size (GTK_WINDOW (spl_window), &defx, &defy);
773     prefs_set_int (SPL_WINDOW_DEFX, defx);
774     prefs_set_int (SPL_WINDOW_DEFY, defy);
775 }
776 
spl_cancel(GtkButton * button,GtkWidget * spl_window)777 static void spl_cancel (GtkButton *button, GtkWidget *spl_window)
778 {
779     Playlist *spl_dup = g_object_get_data (G_OBJECT (spl_window),
780 					   "spl_work");
781     Playlist *spl_orig = g_object_get_data (G_OBJECT (spl_window),
782 					    "spl_orig");
783     iTunesDB *itdb = g_object_get_data (G_OBJECT (spl_window),
784 					"spl_itdb");
785 
786     g_return_if_fail (spl_dup != NULL);
787     g_return_if_fail (spl_orig != NULL);
788     g_return_if_fail (itdb != NULL);
789 
790     itdb_playlist_free (spl_dup);
791     /* does playlist already exist in display? */
792     if (!itdb_playlist_exists (itdb, spl_orig))
793     {   /* Delete */
794 	itdb_playlist_free (spl_orig);
795     }
796 
797     spl_store_window_size (spl_window);
798 
799     gtk_widget_destroy (spl_window);
800 
801     release_widgets ();
802 }
803 
804 
spl_delete_event(GtkWidget * widget,GdkEvent * event,GtkWidget * spl_window)805 static void spl_delete_event (GtkWidget *widget,
806 			      GdkEvent *event,
807 			      GtkWidget *spl_window)
808 {
809     spl_cancel (NULL, spl_window);
810 }
811 
812 
spl_ok(GtkButton * button,GtkWidget * spl_window)813 static void spl_ok (GtkButton *button, GtkWidget *spl_window)
814 {
815     GtkWidget *w;
816     Playlist *spl_dup;
817     Playlist *spl_orig;
818     iTunesDB *itdb;
819     gint32 pos;
820 
821     g_return_if_fail (spl_window_xml != NULL);
822 
823     spl_dup = g_object_get_data (G_OBJECT (spl_window), "spl_work");
824     spl_orig = g_object_get_data (G_OBJECT (spl_window), "spl_orig");
825     pos =  (gint32)GPOINTER_TO_INT(g_object_get_data (G_OBJECT (spl_window), "spl_pos"));
826     itdb = g_object_get_data (G_OBJECT (spl_window), "spl_itdb");
827 
828     g_return_if_fail (spl_dup != NULL);
829     g_return_if_fail (spl_orig != NULL);
830     g_return_if_fail (itdb != NULL);
831 
832     /* Read out new playlist name */
833     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_name_entry")))
834     {
835 	g_free (spl_orig->name);
836 	spl_orig->name = gtk_editable_get_chars (GTK_EDITABLE (w), 0, -1);
837     }
838 
839     itdb_spl_copy_rules (spl_orig, spl_dup);
840 
841     itdb_playlist_free (spl_dup);
842 
843     /* does playlist already exist in itdb? */
844     if (!itdb_playlist_exists (itdb, spl_orig))
845     {   /* Insert at specified position */
846 	gp_playlist_add (itdb, spl_orig, pos);
847     }
848 
849     itdb_spl_update (spl_orig);
850 
851     if (pm_get_selected_playlist () == spl_orig)
852     {   /* redisplay */
853 	pm_unselect_playlist (spl_orig);
854 	pm_select_playlist (spl_orig);
855     }
856 
857     data_changed (itdb);
858 
859     spl_store_window_size (spl_window);
860 
861     gtk_widget_destroy (spl_window);
862 
863     release_widgets ();
864 }
865 
866 
867 /* Display the "checklimits" data correctly */
spl_display_checklimits(GtkWidget * spl_window)868 static void spl_display_checklimits (GtkWidget *spl_window)
869 {
870     Playlist *spl;
871     GtkWidget *w;
872 
873     g_return_if_fail (spl_window);
874     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
875     g_return_if_fail (spl);
876 
877     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_checklimits_button")))
878     {
879 	gtk_toggle_button_set_active (
880 	    GTK_TOGGLE_BUTTON (w), spl->splpref.checklimits);
881 	g_signal_connect (w, "toggled",
882 			  G_CALLBACK (spl_checklimits_toggled),
883 			  spl_window);
884     }
885 
886     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_limitvalue_entry")))
887     {
888 	gchar str[WNLEN];
889 	snprintf (str, WNLEN, "%d", spl->splpref.limitvalue);
890 	gtk_entry_set_text (GTK_ENTRY (w), str);
891 	gtk_widget_set_sensitive (w, spl->splpref.checklimits);
892 	g_signal_connect (w, "changed",
893 			  G_CALLBACK (spl_limitvalue_changed),
894 			  spl_window);
895     }
896 
897     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_limittype_combobox")))
898     {
899 	spl_set_combobox (GTK_COMBO_BOX (w),
900 			  limittype_comboentries,
901 			  spl->splpref.limittype,
902 			  G_CALLBACK (spl_limittype_changed),
903 			  spl_window);
904 	gtk_widget_set_sensitive (w, spl->splpref.checklimits);
905     }
906 
907     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_limitsort_label")))
908     {
909 	gtk_widget_set_sensitive (w, spl->splpref.checklimits);
910     }
911 
912     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_limitsort_combobox")))
913     {
914 	spl_set_combobox (GTK_COMBO_BOX (w),
915 			  limitsort_comboentries,
916 			  spl->splpref.limitsort,
917 			  G_CALLBACK (spl_limitsort_changed),
918 			  spl_window);
919 	gtk_widget_set_sensitive (w, spl->splpref.checklimits);
920     }
921 }
922 
923 
924 
925 /* from "man strftime 3" ("%x" behaves just like "%c"):
926 
927        Some buggy versions of gcc complain about the use of %c:
928        warning: `%c' yields only last 2 digits of year in some
929        locales.  Of course program- mers are encouraged to use %c, it
930        gives the preferred date and time representation. One meets all
931        kinds of strange obfuscations to circum- vent this gcc
932        problem. A relatively clean one is to add an intermediate
933        function
934 */
my_strftime(char * s,size_t max,const char * fmt,const struct tm * tm)935 size_t my_strftime(char *s, size_t max, const char  *fmt,  const
936 		   struct tm *tm)
937 {
938     return strftime(s, max, fmt, tm);
939 }
940 
941 
942 /* set @str to a timestring corresponding to mac timestamp @value */
set_timestring(gchar * str,guint64 value,enum entrytype et)943 void set_timestring (gchar *str, guint64 value, enum entrytype et)
944 {
945     time_t t;
946     gchar *resstr;
947 
948     g_return_if_fail (str != NULL);
949 
950     t = (time_t)value;
951     if (et == spl_ET_FROMVALUE_DATE)
952     {
953 	resstr = time_fromtime_to_string (t);
954     }
955     else
956     {
957 	resstr = time_totime_to_string (t);
958     }
959     strncpy (str, resstr, WNLEN);
960     str[WNLEN-1] = 0;
961     g_free (resstr);
962 }
963 
964 
965 /* set the string @str for rule @splr (entrytype: @et) */
966 /* @str must be WNLEN chars long. Returns a pointer to the string to
967  * be used */
entry_get_string(gchar * str,Itdb_SPLRule * splr,enum entrytype et)968 const gchar *entry_get_string (gchar *str, Itdb_SPLRule *splr,
969 			       enum entrytype et)
970 {
971     gchar *strp = str;
972 
973     g_return_val_if_fail (str, NULL);
974     g_return_val_if_fail (splr, NULL);
975 
976     switch (et)
977     {
978     case spl_ET_FROMVALUE:
979 	if (splr->fromvalue == ITDB_SPL_DATE_IDENTIFIER)
980 	    splr->fromvalue = 0;
981 	switch (splr->field)
982 	{
983 	case ITDB_SPLFIELD_RATING:
984 	    snprintf (str, WNLEN, "%lld", (long long int)(splr->fromvalue / ITDB_RATING_STEP));
985 	    break;
986 	case ITDB_SPLFIELD_TIME:
987 	    snprintf (str, WNLEN, "%.10g", ((gdouble)splr->fromvalue/1000));
988 	    break;
989 	default:
990 	    snprintf (str, WNLEN, "%lld", (long long int)(splr->fromvalue));
991 	    break;
992 	}
993 	break;
994     case spl_ET_FROMVALUE_DATE:
995 	if (splr->fromvalue == ITDB_SPL_DATE_IDENTIFIER)
996 	    splr->fromvalue = 0;
997 	set_timestring (str, splr->fromvalue, et);
998 	break;
999     case spl_ET_FROMDATE:
1000 	snprintf (str, WNLEN, "%lld",  (long long int)splr->fromdate);
1001 	break;
1002     case spl_ET_TOVALUE:
1003 	if (splr->tovalue == ITDB_SPL_DATE_IDENTIFIER)
1004 	    splr->tovalue = 0;
1005 	switch (splr->field )
1006 	{
1007 	case ITDB_SPLFIELD_RATING:
1008 	    snprintf (str, WNLEN, "%lld",  (long long int)(splr->tovalue / ITDB_RATING_STEP));
1009 	    break;
1010 	case ITDB_SPLFIELD_TIME:
1011 	    snprintf (str, WNLEN, "%.10g", ((gdouble)splr->tovalue/1000));
1012 	    break;
1013 	default:
1014 	    snprintf (str, WNLEN, "%lld", (long long int)(splr->tovalue));
1015 	    break;
1016 	}
1017 	break;
1018     case spl_ET_TOVALUE_DATE:
1019 	if (splr->tovalue == ITDB_SPL_DATE_IDENTIFIER)
1020 	    splr->tovalue = 0;
1021 	set_timestring (str, splr->tovalue, et);
1022 	break;
1023     case spl_ET_TODATE:
1024 	snprintf (str, WNLEN, "%lld",  (long long int)splr->todate);
1025 	break;
1026     case spl_ET_INTHELAST:
1027 	snprintf (str, WNLEN, "%lld",  (long long int)-splr->fromdate);
1028 	break;
1029     case spl_ET_STRING:
1030 /*	gtk_entry_set_width_chars (GTK_ENTRY (entry), 20);*/
1031 	strp = splr->string;
1032 	break;
1033     default:
1034 	/* must not happen */
1035 	g_return_val_if_fail (FALSE, NULL);
1036 	break;
1037     }
1038     return strp;
1039 }
1040 
1041 
1042 
hbox_add_entry(GtkWidget * hbox,Itdb_SPLRule * splr,enum entrytype et)1043 static GtkWidget *hbox_add_entry (GtkWidget *hbox,
1044 				  Itdb_SPLRule *splr,
1045 				  enum entrytype et)
1046 {
1047     GtkWidget *spl_window;
1048     GtkWidget *entry;
1049     gchar str[WNLEN];
1050     const gchar *strp;
1051 
1052     g_return_val_if_fail (hbox, NULL);
1053 
1054     str[0] = 0;
1055 
1056     spl_window = g_object_get_data (G_OBJECT (hbox), "spl_window");
1057     g_return_val_if_fail (spl_window, NULL);
1058 
1059     entry = gtk_entry_new ();
1060     gtk_widget_show (entry);
1061     gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1062     if (et == spl_ET_STRING)
1063 	  gtk_entry_set_max_length (GTK_ENTRY (entry),
1064 				    ITDB_SPL_STRING_MAXLEN);
1065     else  gtk_entry_set_max_length (GTK_ENTRY (entry), 50);
1066     strp = entry_get_string (str, splr, et);
1067     if (strp)  gtk_entry_set_text (GTK_ENTRY (entry), strp);
1068     g_object_set_data (G_OBJECT (entry), "spl_rule", splr);
1069     g_object_set_data (G_OBJECT (entry), "spl_entrytype",
1070 		       (gpointer)et);
1071     g_signal_connect (entry, "changed",
1072 		      G_CALLBACK (splr_entry_changed),
1073 		      spl_window);
1074     g_signal_connect (entry, "activate",
1075 		      G_CALLBACK (splr_entry_redisplay),
1076 		      spl_window);
1077     return entry;
1078 }
1079 
1080 
1081 /* Called to destroy the memory used by the array holding the playlist
1082    IDs. It is called automatically when the associated object
1083    (combobox) is destroyed */
spl_pl_ids_destroy(GArray * array)1084 void spl_pl_ids_destroy (GArray *array)
1085 {
1086     g_return_if_fail (array);
1087     g_array_free (array, TRUE);
1088 }
1089 
1090 
1091 /* Create the widgets to hold the action data (range, date,
1092  * string...) */
spl_create_hbox(GtkWidget * spl_window,Itdb_SPLRule * splr)1093 GtkWidget *spl_create_hbox (GtkWidget *spl_window, Itdb_SPLRule *splr)
1094 {
1095     GtkWidget *hbox = NULL;
1096     ItdbSPLActionType at;
1097     GtkWidget *entry, *label, *combobox;
1098     gint index;
1099     GArray *pl_ids = NULL;
1100     Playlist *spl_orig;
1101     iTunesDB *itdb;
1102     GList *gl;
1103 
1104 
1105     g_return_val_if_fail (spl_window, NULL);
1106     g_return_val_if_fail (splr, NULL);
1107 
1108     spl_orig =  g_object_get_data (G_OBJECT (spl_window), "spl_orig");
1109     g_return_val_if_fail (spl_orig, NULL);
1110 
1111     itdb =  g_object_get_data (G_OBJECT (spl_window), "spl_itdb");
1112     g_return_val_if_fail (itdb, NULL);
1113 
1114     at = itdb_splr_get_action_type (splr);
1115     g_return_val_if_fail (at != ITDB_SPLAT_UNKNOWN, NULL);
1116     g_return_val_if_fail (at != ITDB_SPLAT_INVALID, NULL);
1117 
1118     hbox = gtk_hbox_new (FALSE, 3);
1119     gtk_widget_show (hbox);
1120     g_object_set_data (G_OBJECT (hbox), "spl_window", spl_window);
1121 
1122     switch (at)
1123     {
1124     case ITDB_SPLAT_STRING:
1125 	entry = hbox_add_entry (hbox, splr, spl_ET_STRING);
1126 	break;
1127     case ITDB_SPLAT_INT:
1128 	entry = hbox_add_entry (hbox, splr, spl_ET_FROMVALUE);
1129 	/* check for unit */
1130 	index = comboentry_index_from_id (splfield_units, splr->field);
1131 	if (index != -1)
1132 	{
1133 	    label = gtk_label_new (_(splfield_units[index].str));
1134 	    gtk_widget_show (label);
1135 	    gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
1136 	}
1137 	break;
1138     case ITDB_SPLAT_DATE:
1139 	entry = hbox_add_entry (hbox, splr, spl_ET_FROMVALUE_DATE);
1140 	break;
1141     case ITDB_SPLAT_RANGE_INT:
1142 	entry = hbox_add_entry (hbox, splr, spl_ET_FROMVALUE);
1143 	label = gtk_label_new (_("to"));
1144 	gtk_widget_show (label);
1145 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1146 	entry = hbox_add_entry (hbox, splr, spl_ET_TOVALUE),
1147 	/* check for unit */
1148 	index = comboentry_index_from_id (splfield_units, splr->field);
1149 	if (index != -1)
1150 	{
1151 	    label = gtk_label_new (_(splfield_units[index].str));
1152 	    gtk_widget_show (label);
1153 	    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1154 	}
1155 	break;
1156     case ITDB_SPLAT_RANGE_DATE:
1157 	entry = hbox_add_entry (hbox, splr, spl_ET_FROMVALUE_DATE);
1158 	label = gtk_label_new (_("to"));
1159 	gtk_widget_show (label);
1160 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1161 	entry = hbox_add_entry (hbox, splr, spl_ET_TOVALUE_DATE);
1162 	/* check for unit */
1163 	index = comboentry_index_from_id (splfield_units, splr->field);
1164 	if (index != -1)
1165 	{
1166 	    label = gtk_label_new (_(splfield_units[index].str));
1167 	    gtk_widget_show (label);
1168 	    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1169 	}
1170 	break;
1171     case ITDB_SPLAT_INTHELAST:
1172 	if (comboentry_index_from_id (
1173 		splat_inthelast_units_comboentries,
1174 		splr->fromunits) == -1)
1175 	{   /* unit currently set is not known */
1176 	    /* adapt to known units */
1177 	    guint64 units = splr->fromunits;
1178 	    splr->fromunits = splat_inthelast_units_comboentries[0].id;
1179 	    splr->fromvalue *= ((double)units)/splr->fromunits;
1180 	}
1181 	entry = hbox_add_entry (hbox, splr, spl_ET_INTHELAST);
1182 	combobox = gtk_combo_box_new ();
1183 	gtk_widget_show (combobox);
1184 	gtk_box_pack_start (GTK_BOX (hbox), combobox, TRUE, TRUE, 0);
1185 	g_object_set_data (G_OBJECT (combobox), "spl_rule", splr);
1186 	spl_set_combobox (GTK_COMBO_BOX (combobox),
1187 			  splat_inthelast_units_comboentries,
1188 			  splr->fromunits,
1189 			  G_CALLBACK (spl_fromunits_changed),
1190 			  spl_window);
1191 	break;
1192     case ITDB_SPLAT_PLAYLIST:
1193 	combobox = gtk_combo_box_new_text ();
1194 	gtk_widget_show (combobox);
1195 	gtk_box_pack_start (GTK_BOX (hbox), combobox, TRUE, TRUE, 0);
1196 	pl_ids = g_array_sized_new (TRUE, TRUE, sizeof (guint64),
1197 				    itdb_playlists_number (itdb));
1198 	gl=itdb->playlists;
1199 	while (gl && gl->next)
1200 	{
1201 	    Playlist *pl = gl->next->data;
1202 	    g_return_val_if_fail (pl, NULL);
1203 	    if (pl != spl_orig)
1204 	    {
1205 		gtk_combo_box_append_text (GTK_COMBO_BOX (combobox),
1206 					   pl->name);
1207 		g_array_append_val (pl_ids, pl->id);
1208 	    }
1209 	    gl = gl->next;
1210 	}
1211 	g_object_set_data (G_OBJECT (combobox), "spl_rule", splr);
1212 	g_object_set_data_full (G_OBJECT (combobox), "spl_pl_ids",
1213 				pl_ids,
1214 				(GDestroyNotify)spl_pl_ids_destroy);
1215 	if (splr->fromvalue == ITDB_SPL_DATE_IDENTIFIER)
1216 	    splr->fromvalue = g_array_index (pl_ids, guint64, 0);
1217 	index = pl_ids_index_from_id (pl_ids, splr->fromvalue);
1218 	gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), index);
1219 	g_signal_connect (combobox, "changed",
1220 			  G_CALLBACK (spl_playlist_changed),
1221 			  spl_window);
1222 	break;
1223     case ITDB_SPLAT_BINARY_AND:
1224 	if (splr->field == ITDB_SPLFIELD_VIDEO_KIND)
1225 	{
1226 	    const ComboEntry *use_centries = NULL;
1227 	    combobox = gtk_combo_box_new ();
1228 	    gtk_widget_show (combobox);
1229 	    gtk_box_pack_start (GTK_BOX (hbox), combobox, TRUE, TRUE, 0);
1230 	    if (comboentry_index_from_id (videokind_comboentries_is,
1231 					  splr->fromvalue) != -1)
1232 	    {
1233 		use_centries = videokind_comboentries_is;
1234 	    }
1235 	    else
1236 	    {
1237 		use_centries = videokind_comboentries_is_not;
1238 	    }
1239 	    g_object_set_data (G_OBJECT (combobox), "spl_rule", splr);
1240 	    spl_set_combobox (GTK_COMBO_BOX (combobox),
1241 			      use_centries,
1242 			      splr->fromvalue,
1243 			      G_CALLBACK (spl_videokind_comboentry_changed),
1244 			      spl_window);
1245 	}
1246 	else
1247 	{   /* not supported: display as standard INT */
1248 	    entry = hbox_add_entry (hbox, splr, spl_ET_FROMVALUE);
1249 	}
1250 	break;
1251     case ITDB_SPLAT_NONE:
1252 	break;
1253     case ITDB_SPLAT_UNKNOWN:
1254     case ITDB_SPLAT_INVALID:
1255 	/* hopefully never reached !! */
1256 	break;
1257     }
1258     return hbox;
1259 }
1260 
1261 
1262 /* Display/update rule @n in @spl_window */
spl_update_rule(GtkWidget * spl_window,Itdb_SPLRule * splr)1263 static void spl_update_rule (GtkWidget *spl_window, Itdb_SPLRule *splr)
1264 {
1265     GtkTable *table;
1266     Playlist *spl;
1267     GtkWidget *combobox, *hbox, *button;
1268     gchar name[WNLEN];
1269     ItdbSPLFieldType ft;
1270     ItdbSPLActionType at;
1271     gint row, index;
1272     const ComboEntry *centries = NULL;
1273 
1274 
1275     g_return_if_fail (spl_window);
1276     g_return_if_fail (splr);
1277 
1278     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
1279     g_return_if_fail (spl);
1280     table = g_object_get_data (G_OBJECT (spl_window), "spl_rules_table");
1281     g_return_if_fail (table);
1282 
1283     row = g_list_index (spl->splrules.rules, splr);
1284     g_return_if_fail (row != -1);
1285 
1286 
1287     /* Combobox for field */
1288     /* ------------------ */
1289     snprintf (name, WNLEN, "spl_fieldcombo%d", row);
1290     combobox = g_object_get_data (G_OBJECT (table), name);
1291     if (!combobox)
1292     {  /* create combo for field */
1293 	combobox = gtk_combo_box_new ();
1294 	gtk_widget_show (combobox);
1295 	gtk_table_attach (table, combobox, 0,1, row,row+1,
1296 			  0,0,         /* expand options */
1297 			  XPAD,YPAD);  /* padding options */
1298 	g_object_set_data (G_OBJECT (table), name, combobox);
1299     }
1300     g_object_set_data (G_OBJECT (combobox), "spl_rule", splr);
1301     spl_set_combobox (GTK_COMBO_BOX (combobox),
1302 		      splfield_comboentries,
1303 		      splr->field,
1304 		      G_CALLBACK (spl_field_changed),
1305 		      spl_window);
1306 
1307     /* Combobox for action */
1308     /* ------------------- */
1309     ft = itdb_splr_get_field_type (splr);
1310     snprintf (name, WNLEN, "spl_actioncombo%d", row);
1311     combobox = g_object_get_data (G_OBJECT (table), name);
1312 
1313     if (combobox)
1314     {   /* check if existing combobox is of same type */
1315 	ItdbSPLFieldType old_ft = GPOINTER_TO_INT (
1316 	    g_object_get_data (G_OBJECT (combobox), "spl_fieldtype"));
1317 	if (old_ft != ft)
1318 	{
1319 	    gtk_widget_destroy (combobox);
1320 	    combobox = NULL;
1321 	}
1322     }
1323 
1324     if (!combobox)
1325     {  /* create combo for action */
1326 	combobox = gtk_combo_box_new ();
1327 	gtk_widget_show (combobox);
1328 	gtk_table_attach (table, combobox, 1,2, row,row+1,
1329 			  GTK_FILL,0,   /* expand options */
1330 			  XPAD,YPAD);   /* padding options */
1331 	g_object_set_data (G_OBJECT (table), name, combobox);
1332     }
1333     g_object_set_data (G_OBJECT (combobox), "spl_rule", splr);
1334     g_object_set_data (G_OBJECT (combobox), "spl_fieldtype", GINT_TO_POINTER (ft));
1335 
1336     switch (ft)
1337     {
1338     case ITDB_SPLFT_STRING:
1339 	centries = splaction_ftstring_comboentries;
1340 	break;
1341     case ITDB_SPLFT_INT:
1342 	centries = splaction_ftint_comboentries;
1343 	break;
1344     case ITDB_SPLFT_BOOLEAN:
1345 	centries = splaction_ftboolean_comboentries;
1346 	break;
1347     case ITDB_SPLFT_DATE:
1348 	centries = splaction_ftdate_comboentries;
1349 	break;
1350     case ITDB_SPLFT_PLAYLIST:
1351 	centries = splaction_ftplaylist_comboentries;
1352 	break;
1353     case ITDB_SPLFT_BINARY_AND:
1354 	centries = splaction_ftbinaryand_comboentries;
1355 	break;
1356     case ITDB_SPLFT_UNKNOWN:
1357 	centries = splaction_notsupported_comboentries;
1358 	break;
1359     }
1360 
1361     if (centries == NULL)
1362     {   /* Handle non-supported action types */
1363 	centries = splaction_notsupported_comboentries;
1364     }
1365 
1366     if (comboentry_index_from_id (centries, splr->action) == -1)
1367     {   /* Action currently set is not a legal action for the type of
1368 	   field. --> adjust */
1369 	if (centries)    splr->action = centries[0].id;
1370     }
1371 
1372     if ((splr->field == ITDB_SPLFIELD_VIDEO_KIND) && (ft == ITDB_SPLFT_BINARY_AND))
1373     {   /* this field needs to be handled differently from everything
1374 	   else */
1375 	if (comboentry_index_from_id (videokind_comboentries_is,
1376 				      splr->fromvalue) != -1)
1377 	{   /* found value to be part of "Is" */
1378 	    index = 0;
1379 	}
1380 	else
1381 	{   /* assume value to be part of "Is Not" */
1382 	    index = 1;
1383 	}
1384 	g_object_set_data (G_OBJECT (combobox), "spl_binary_and_index",
1385 			   GINT_TO_POINTER (index));
1386 	spl_setup_combobox (GTK_COMBO_BOX (combobox), centries, index,
1387 			    G_CALLBACK (spl_action_changed), spl_window);
1388     }
1389     else
1390     {
1391 	spl_set_combobox (GTK_COMBO_BOX (combobox), centries, splr->action,
1392 			  G_CALLBACK (spl_action_changed), spl_window);
1393 
1394     }
1395 
1396     /* input fields (range, string, date...) */
1397     /* ------------------------------------- */
1398     at = itdb_splr_get_action_type (splr);
1399     snprintf (name, WNLEN, "spl_actionhbox%d", row);
1400     hbox = g_object_get_data (G_OBJECT (table), name);
1401     if (hbox)
1402     {
1403 	gtk_widget_destroy (hbox);
1404 	g_object_set_data (G_OBJECT (table), name, NULL);
1405     }
1406     if (centries != splaction_notsupported_comboentries)
1407     {
1408 	g_return_if_fail (at != ITDB_SPLAT_UNKNOWN);
1409 	g_return_if_fail (at != ITDB_SPLAT_INVALID);
1410 	hbox = spl_create_hbox (spl_window, splr);
1411 	gtk_table_attach (table, hbox, 2,3, row,row+1,
1412 			  GTK_FILL,0,   /* expand options */
1413 			  XPAD,YPAD);   /* padding options */
1414 	g_object_set_data (G_OBJECT (table), name, hbox);
1415     }
1416 
1417     /* +/- buttons */
1418     /* ----------- */
1419     snprintf (name, WNLEN, "spl_buttonhbox%d", row);
1420     hbox = g_object_get_data (G_OBJECT (table), name);
1421     if (!hbox)
1422     {
1423 	/* create hbox with buttons */
1424 	hbox = gtk_hbox_new (TRUE, 2);
1425 	gtk_widget_show (hbox);
1426 	g_object_set_data (G_OBJECT (table), name, hbox);
1427 	gtk_table_attach (table, hbox, 3,4, row,row+1,
1428 			  0,0,   /* expand options */
1429 			  XPAD,YPAD);  /* padding options */
1430     }
1431 
1432     snprintf (name, WNLEN, "spl_button-%d", row);
1433     button = g_object_get_data (G_OBJECT (table), name);
1434     if (!button)
1435     {
1436 	button = gtk_button_new_with_label (_("-"));
1437 	gtk_widget_show (button);
1438 	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
1439 	g_signal_connect (button, "clicked",
1440 			  G_CALLBACK (spl_button_minus_clicked),
1441 			  spl_window);
1442 	g_object_set_data (G_OBJECT (table), name, button);
1443     }
1444     g_object_set_data (G_OBJECT (button), "spl_rule", splr);
1445 
1446 
1447     snprintf (name, WNLEN, "spl_button+%d", row);
1448     button = g_object_get_data (G_OBJECT (table), name);
1449     if (!button)
1450     {
1451 	button = gtk_button_new_with_label (_("+"));
1452 	gtk_widget_show (button);
1453 	gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
1454 	g_signal_connect (button, "clicked",
1455 			  G_CALLBACK (spl_button_plus_clicked),
1456 			  spl_window);
1457 	g_object_set_data (G_OBJECT (table), name, button);
1458 	snprintf (name, WNLEN, "spl_button+%d", row);
1459     }
1460     g_object_set_data (G_OBJECT (button), "spl_rule", splr);
1461 }
1462 
1463 /* Display all rules stored in "spl_work" */
spl_display_rules(GtkWidget * spl_window)1464 static void spl_display_rules (GtkWidget *spl_window)
1465 {
1466     Playlist *spl;
1467     GtkWidget *align, *table;
1468     GList *gl;
1469 
1470     g_return_if_fail (spl_window);
1471     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
1472     g_return_if_fail (spl);
1473     align = gtkpod_xml_get_widget (spl_window_xml, "spl_rules_table_align");
1474     g_return_if_fail (align);
1475     /* Destroy table if it already exists */
1476     table = g_object_get_data (G_OBJECT (spl_window),
1477 			       "spl_rules_table");
1478     if (table)  gtk_widget_destroy (table);
1479     table = gtk_table_new (1, 4, FALSE);
1480     gtk_widget_show (table);
1481     gtk_container_add (GTK_CONTAINER (align), table);
1482     g_object_set_data (G_OBJECT (spl_window),
1483 		       "spl_rules_table", table);
1484 
1485     for (gl=spl->splrules.rules; gl; gl=gl->next)
1486 	spl_update_rule (spl_window, gl->data);
1487 
1488     spl_check_number_of_rules (spl_window);
1489 }
1490 
1491 
1492 /* destroy widget @wname in row @row of table @table (used by
1493    spl_update_rules_from_row() */
splremove(GtkWidget * table,const gchar * wname,gint row)1494 gboolean splremove (GtkWidget *table, const gchar *wname, gint row)
1495 {
1496     GtkWidget *w;
1497     gchar name[WNLEN];
1498     gboolean removed = FALSE;
1499 
1500     snprintf (name, WNLEN, "%s%d", wname, row);
1501     w = g_object_get_data (G_OBJECT (table), name);
1502     if (w)
1503     {
1504 	gtk_widget_destroy (w);
1505 	g_object_set_data (G_OBJECT (table), name, NULL);
1506 	removed = TRUE;
1507     }
1508     return removed;
1509 }
1510 
1511 
1512 /* Update rules starting in row @row */
spl_update_rules_from_row(GtkWidget * spl_window,gint row)1513 static void spl_update_rules_from_row (GtkWidget *spl_window, gint row)
1514 {
1515     gint i, numrules;
1516     Playlist *spl;
1517     GtkWidget *table;
1518     gboolean removed;
1519 
1520     g_return_if_fail (spl_window);
1521     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
1522     g_return_if_fail (spl);
1523     table = g_object_get_data (G_OBJECT (spl_window), "spl_rules_table");
1524     g_return_if_fail (table);
1525 
1526     numrules = g_list_length (spl->splrules.rules);
1527 
1528     /* update all rules starting in row @row */
1529     for (i=row; i<numrules; ++i)
1530     {
1531 	spl_update_rule (spl_window,
1532 			 g_list_nth_data (spl->splrules.rules, i));
1533     }
1534     /* remove rules that do no longer exist */
1535     for (removed=TRUE; removed==TRUE; ++i)
1536     {
1537 	removed =  splremove (table, "spl_fieldcombo", i);
1538 	removed |= splremove (table, "spl_actioncombo", i);
1539 	removed |= splremove (table, "spl_actionhbox", i);
1540 	/* remove spl_button+/- BEFORE removing spl_buttonhbox, as
1541 	   removing spl_buttonhbox will destroy the buttons as well --
1542 	   we'd have to g_object_set_data(..., NULL) manually. */
1543 	removed |= splremove (table, "spl_button+", i);
1544 	removed |= splremove (table, "spl_button-", i);
1545 	removed |= splremove (table, "spl_buttonhbox", i);
1546     }
1547 }
1548 
1549 
1550 /* Edit a smart playlist. If it is a new smartlist, it will be
1551  * inserted at position @pos when 'OK' is pressed. */
spl_edit_all(iTunesDB * itdb,Playlist * spl,gint32 pos)1552 void spl_edit_all (iTunesDB *itdb, Playlist *spl, gint32 pos)
1553 {
1554     GtkWidget *spl_window, *w;
1555     gint defx, defy;
1556     Playlist *spl_dup;
1557 
1558     g_return_if_fail (spl != NULL);
1559     g_return_if_fail (spl->is_spl);
1560     g_return_if_fail (itdb != NULL);
1561 
1562     spl_window_xml = gtkpod_xml_new (xml_file, "spl_window");
1563     spl_window = gtkpod_xml_get_widget (spl_window_xml, "spl_window");
1564 
1565     g_return_if_fail (spl_window != NULL);
1566 
1567     /* Duplicate playlist to work on */
1568     spl_dup = itdb_playlist_duplicate (spl);
1569 
1570     /* Store pointers to original playlist and duplicate */
1571     g_object_set_data (G_OBJECT (spl_window), "spl_orig", spl);
1572     g_object_set_data (G_OBJECT (spl_window), "spl_work", spl_dup);
1573     g_object_set_data (G_OBJECT (spl_window), "spl_pos",  GINT_TO_POINTER(pos));
1574     g_object_set_data (G_OBJECT (spl_window), "spl_itdb", itdb);
1575 
1576 	/* Set checkboxes and connect signal handlers */
1577     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_name_entry")))
1578     {
1579 		if (spl_dup->name)
1580 			gtk_entry_set_text (GTK_ENTRY (w), spl_dup->name);
1581     }
1582 
1583     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_matchcheckedonly_button")))
1584     {
1585 		gtk_toggle_button_set_active (
1586 			GTK_TOGGLE_BUTTON (w), spl_dup->splpref.matchcheckedonly);
1587 		g_signal_connect (w, "toggled",
1588 				  G_CALLBACK (spl_matchcheckedonly_toggled),
1589 				  spl_window);
1590     }
1591 
1592     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_liveupdate_button")))
1593     {
1594 		gtk_toggle_button_set_active (
1595 			GTK_TOGGLE_BUTTON (w), spl_dup->splpref.liveupdate);
1596 		g_signal_connect (w, "toggled",
1597 				  G_CALLBACK (spl_liveupdate_toggled),
1598 				  spl_window);
1599     }
1600 
1601     /* Signals for Cancel, OK, Delete */
1602     if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_cancel_button")))
1603     {
1604 		g_signal_connect (w, "clicked",
1605 				  G_CALLBACK (spl_cancel), spl_window);
1606     }
1607 
1608 	if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_ok_button")))
1609     {
1610 		g_signal_connect (w, "clicked",
1611 				  G_CALLBACK (spl_ok), spl_window);
1612     }
1613 
1614 	if ((w = gtkpod_xml_get_widget (spl_window_xml, "spl_match_rules")))
1615     {
1616 		gtk_combo_box_set_active (GTK_COMBO_BOX (w), spl_MATCH_ALL);
1617     }
1618 
1619     g_signal_connect (spl_window, "delete_event",
1620 		      G_CALLBACK (spl_delete_event), spl_window);
1621 
1622     spl_display_checklimits (spl_window);
1623 
1624     spl_display_rules (spl_window);
1625 
1626     /* set default size */
1627     defx = prefs_get_int (SPL_WINDOW_DEFX);
1628     defy = prefs_get_int (SPL_WINDOW_DEFY);
1629     if ((defx != 0) && (defy != 0))
1630 	gtk_window_set_default_size (GTK_WINDOW (spl_window), defx, defy);
1631 
1632 	glade_xml_signal_autoconnect (spl_window_xml);
1633     gtk_widget_show (spl_window);
1634 
1635     block_widgets ();
1636 }
1637 
1638 
1639 /* Edit an existing smart playlist */
spl_edit(Playlist * spl)1640 void spl_edit (Playlist *spl)
1641 {
1642     g_return_if_fail (spl);
1643     g_return_if_fail (spl->itdb);
1644     spl_edit_all (spl->itdb, spl, -1);
1645 }
1646 
1647 
1648 /* Edit a non-existing smartlist. If successful, it will be entered at
1649    position @pos. Default name is @name */
spl_edit_new(iTunesDB * itdb,gchar * name,gint32 pos)1650 void spl_edit_new (iTunesDB *itdb, gchar *name, gint32 pos)
1651 {
1652     Playlist *spl = gp_playlist_new (name? name:_("New Playlist"), TRUE);
1653 
1654     spl_edit_all (itdb, spl, pos);
1655 }
1656 
1657 /*
1658 	glade callback
1659 */
spl_match_rules_changed(GtkComboBox * sender,gpointer e)1660 G_MODULE_EXPORT void spl_match_rules_changed (GtkComboBox *sender, gpointer e)
1661 {
1662     Playlist *spl;
1663 	GtkWidget *spl_window = gtkpod_xml_get_widget (spl_window_xml, "spl_window");
1664 	GtkWidget *frame = gtkpod_xml_get_widget (spl_window_xml, "spl_rules_frame");
1665 
1666     g_return_if_fail (spl_window);
1667 	g_return_if_fail (frame);
1668     spl =  g_object_get_data (G_OBJECT (spl_window), "spl_work");
1669     g_return_if_fail (spl);
1670 
1671 	switch (gtk_combo_box_get_active (sender))
1672 	{
1673 	case spl_MATCH_ANY:
1674 		gtk_widget_set_sensitive (frame, TRUE);
1675 		spl->splpref.checkrules = TRUE;
1676 		spl->splrules.match_operator = ITDB_SPLMATCH_OR;
1677 		break;
1678 	case spl_MATCH_ALL:
1679 		gtk_widget_set_sensitive (frame, TRUE);
1680 		spl->splpref.checkrules = TRUE;
1681 		spl->splrules.match_operator = ITDB_SPLMATCH_AND;
1682 		break;
1683 	default:
1684 		gtk_widget_set_sensitive (frame, FALSE);
1685 		spl->splpref.checkrules = FALSE;
1686 		break;
1687     }
1688 }
1689 
1690