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