~ubuntu-branches/ubuntu/oneiric/rhythmbox/oneiric

« back to all changes in this revision

Viewing changes to rhythmdb/rhythmdb-tree.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2006-06-26 19:06:10 UTC
  • mto: (2.1.1 lenny) (1.1.37 upstream)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20060626190610-08x8lgvvfs0gr619
Tags: upstream-0.9.5
ImportĀ upstreamĀ versionĀ 0.9.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 *
17
17
 *  You should have received a copy of the GNU General Public License
18
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.
 
19
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
20
20
 *
21
21
 */
22
22
 
40
40
#include <libxml/SAX.h>
41
41
#include <libxml/parserInternals.h>
42
42
 
 
43
#include "rhythmdb-private.h"
43
44
#include "rhythmdb-tree.h"
44
 
#include "rhythmdb-query-model.h"
45
45
#include "rhythmdb-property-model.h"
46
46
#include "rb-debug.h"
47
47
#include "rb-util.h"
74
74
static RhythmDBEntry * rhythmdb_tree_entry_lookup_by_location (RhythmDB *db, const char *uri);
75
75
static void rhythmdb_tree_entry_foreach (RhythmDB *adb, GFunc func, gpointer user_data);
76
76
static void rhythmdb_tree_do_full_query (RhythmDB *db, GPtrArray *query,
77
 
                                         GtkTreeModel *main_model, gboolean *cancel);
 
77
                                         RhythmDBQueryResults *results,
 
78
                                         gboolean *cancel);
78
79
static gboolean rhythmdb_tree_evaluate_query (RhythmDB *adb, GPtrArray *query,
79
80
                                       RhythmDBEntry *aentry);
80
81
 
94
95
                                        gpointer data);
95
96
 
96
97
 
97
 
#define RHYTHMDB_TREE_XML_VERSION "1.0"
 
98
#define RHYTHMDB_TREE_XML_VERSION "1.2"
98
99
 
99
100
static void destroy_tree_property (RhythmDBTreeProperty *prop);
100
101
static RhythmDBTreeProperty *get_or_create_album (RhythmDBTree *db, RhythmDBTreeProperty *artist,
208
209
        RhythmDBEntry *entry;
209
210
        GString *buf;
210
211
        RhythmDBPropType propid;
 
212
        gint batch_count;
211
213
 
 
214
        /* updating */
212
215
        gboolean has_date;
 
216
        gboolean canonicalise_uris;
213
217
};
214
218
 
215
219
static void
228
232
        {
229
233
        case RHYTHMDB_TREE_PARSER_STATE_START:
230
234
        {
231
 
                if (!strcmp (name, "rhythmdb"))
 
235
                if (!strcmp (name, "rhythmdb")) {
232
236
                        ctx->state = RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB;
233
 
                else
 
237
                        for (; *attrs; attrs +=2) {
 
238
                                if (!strcmp (*attrs, "version")) {
 
239
                                        const char *version = *(attrs+1);
 
240
 
 
241
                                        if (!strcmp (version, "1.0") || !strcmp (version, "1.1")) {
 
242
                                                ctx->canonicalise_uris = TRUE;
 
243
                                                rb_debug ("old version of rhythmdb, performing URI canonicalisation for all entries");
 
244
                                        } else if (!strcmp (version, "1.2")) {
 
245
                                                /* current version*/
 
246
                                        } else {
 
247
                                                /* too new */
 
248
                                                g_assert_not_reached ();
 
249
                                        }
 
250
                                } else {
 
251
                                        g_assert_not_reached ();
 
252
                                }
 
253
                        }
 
254
 
 
255
                } else {
234
256
                        ctx->in_unknown_elt = TRUE;
 
257
                }
 
258
 
235
259
                break;
236
260
        }
237
261
        case RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB:
238
262
        {
239
263
                if (!strcmp (name, "entry")) {
240
 
                        RhythmDBEntryType type = -1;
 
264
                        RhythmDBEntryType type = RHYTHMDB_ENTRY_TYPE_INVALID;
241
265
                        gboolean type_set = FALSE;
242
266
                        for (; *attrs; attrs +=2) {
243
267
                                if (!strcmp (*attrs, "type")) {
244
268
                                        const char *typename = *(attrs+1);
245
 
                                        if (!strcmp (typename, "song"))
246
 
                                                type = RHYTHMDB_ENTRY_TYPE_SONG;
247
 
                                        else if (!strcmp (typename, "iradio"))
248
 
                                                type = RHYTHMDB_ENTRY_TYPE_IRADIO_STATION;
249
 
                                        else if (!strcmp (typename, "podcast-post"))
250
 
                                                type = RHYTHMDB_ENTRY_TYPE_PODCAST_POST;
251
 
                                        else if (!strcmp (typename, "podcast-feed"))
252
 
                                                type = RHYTHMDB_ENTRY_TYPE_PODCAST_FEED;
253
 
                                        else
 
269
                                        type = rhythmdb_entry_type_get_by_name (typename);
 
270
                                        if (!type)
254
271
                                                return;
255
272
                                        type_set = TRUE;
256
273
                                        break;
259
276
                        g_assert (type_set);
260
277
                        ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
261
278
                        ctx->entry = rhythmdb_entry_allocate (RHYTHMDB (ctx->db), type);
 
279
                        ctx->entry->flags |= RHYTHMDB_ENTRY_TREE_LOADING;
262
280
                        ctx->has_date = FALSE;
263
281
                } else
264
282
                        ctx->in_unknown_elt = TRUE;
283
301
        }
284
302
}
285
303
 
286
 
static gulong
287
 
parse_ulong (const char *buffer)
288
 
{
289
 
        guint64 val;
290
 
 
291
 
        val = g_ascii_strtoull (buffer, NULL, 10);
292
 
        if (val == G_MAXUINT64)
293
 
                return 0;
294
 
        else
295
 
                return MIN (val, G_MAXUINT32);
296
 
}
297
 
 
298
304
static void
299
305
rhythmdb_tree_parser_end_element (struct RhythmDBTreeLoadContext *ctx, const char *name)
300
306
{
322
328
                        rb_debug ("pre-Date entry found, causing re-read");
323
329
                        ctx->entry->mtime = 0;
324
330
                }
325
 
                if (ctx->entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED && ctx->entry->podcast->post_time == 0) {
 
331
                if (ctx->entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
 
332
                        RhythmDBPodcastFields *podcast = RHYTHMDB_ENTRY_GET_TYPE_DATA (ctx->entry, RhythmDBPodcastFields);
326
333
                        /* Handle upgrades from 0.9.2.
327
334
                         * Previously, last-seen for podcast feeds was the time of the last post,
328
335
                         * and post-time was unused.  Now, we want last-seen to be the time we
329
336
                         * last updated the feed, and post-time to be the time of the last post.
330
337
                         */
331
 
                        ctx->entry->podcast->post_time = ctx->entry->last_seen;
 
338
                        if (podcast->post_time == 0) {
 
339
                                podcast->post_time = ctx->entry->last_seen;
 
340
                        }
332
341
                }
333
342
                
334
343
                if (ctx->entry->location != NULL) {
335
 
                        rhythmdb_tree_entry_new (RHYTHMDB (ctx->db), ctx->entry);
336
 
                        rhythmdb_entry_insert (RHYTHMDB (ctx->db), ctx->entry);
337
 
                        rhythmdb_commit (RHYTHMDB (ctx->db));
 
344
                        RhythmDBEntry *entry;
 
345
 
 
346
                        entry = g_hash_table_lookup (ctx->db->priv->entries, ctx->entry->location);
 
347
                        if (entry == NULL) {
 
348
                                rhythmdb_tree_entry_new (RHYTHMDB (ctx->db), ctx->entry);
 
349
                                rhythmdb_entry_insert (RHYTHMDB (ctx->db), ctx->entry);
 
350
                                if (++ctx->batch_count == RHYTHMDB_QUERY_MODEL_SUGGESTED_UPDATE_CHUNK) {
 
351
                                        rhythmdb_commit (RHYTHMDB (ctx->db));
 
352
                                        ctx->batch_count = 0;
 
353
                                }
 
354
                        } else {
 
355
                                rb_debug ("found entry with duplicate location %s. merging metadata", ctx->entry->location);
 
356
                                entry->play_count += ctx->entry->play_count;
 
357
 
 
358
                                if (entry->rating < 0.01)
 
359
                                        entry->rating = ctx->entry->rating;
 
360
                                else if (ctx->entry->rating > 0.01)
 
361
                                        entry->rating = (entry->rating + ctx->entry->rating) / 2;
 
362
 
 
363
                                if (ctx->entry->last_played > entry->last_played)
 
364
                                        entry->last_played = ctx->entry->last_played;
 
365
 
 
366
                                if (ctx->entry->first_seen < entry->first_seen)
 
367
                                        entry->first_seen = ctx->entry->first_seen;
 
368
 
 
369
                                if (ctx->entry->last_seen > entry->last_seen)
 
370
                                        entry->last_seen = ctx->entry->last_seen;
 
371
                                
 
372
                                rhythmdb_entry_unref (RHYTHMDB (ctx->db), ctx->entry);
 
373
                        }
338
374
                } else {
339
375
                        rb_debug ("found entry without location");
340
376
                        rhythmdb_entry_unref (RHYTHMDB (ctx->db), ctx->entry);
344
380
        }
345
381
        case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
346
382
        {
347
 
                /* Handle indexed properties. */
348
 
                switch (ctx->propid)
349
 
                {
350
 
                case RHYTHMDB_PROP_TYPE:
351
 
                        g_assert_not_reached ();
352
 
                        break;
353
 
                case RHYTHMDB_PROP_TITLE:
354
 
                        ctx->entry->title = rb_refstring_new (ctx->buf->str);
355
 
                        break;
356
 
                case RHYTHMDB_PROP_GENRE:
357
 
                        ctx->entry->genre = rb_refstring_new (ctx->buf->str);
358
 
                        break;
359
 
                case RHYTHMDB_PROP_ARTIST:
360
 
                        ctx->entry->artist = rb_refstring_new (ctx->buf->str);
361
 
                        break;
362
 
                case RHYTHMDB_PROP_ALBUM:
363
 
                        ctx->entry->album = rb_refstring_new (ctx->buf->str);
364
 
                        break;
365
 
                case RHYTHMDB_PROP_TRACK_NUMBER:
366
 
                        ctx->entry->tracknum = parse_ulong (ctx->buf->str);
367
 
                        break;
368
 
                case RHYTHMDB_PROP_DISC_NUMBER:
369
 
                        ctx->entry->discnum = parse_ulong (ctx->buf->str);
370
 
                        break;
 
383
                GValue value = {0,};
 
384
                gboolean set = FALSE;
 
385
                gboolean skip = FALSE;
 
386
 
 
387
                /* special case some properties for upgrade handling etc. */
 
388
                switch (ctx->propid) {
371
389
                case RHYTHMDB_PROP_DATE:
372
 
                {
373
 
                        gulong value = parse_ulong (ctx->buf->str);
374
 
                        
375
 
                        if (value > 0)
376
 
                                ctx->entry->date = g_date_new_julian (value);
377
 
                        else
378
 
                                ;
379
390
                        ctx->has_date = TRUE;
380
391
                        break;
381
 
                }
382
 
                case RHYTHMDB_PROP_DURATION:
383
 
                        ctx->entry->duration = parse_ulong (ctx->buf->str);
384
 
                        break;
385
 
                case RHYTHMDB_PROP_FILE_SIZE:
386
 
                        ctx->entry->file_size = parse_ulong (ctx->buf->str);
387
 
                        break;
388
392
                case RHYTHMDB_PROP_LOCATION:
389
 
                        ctx->entry->location = g_strdup (ctx->buf->str);
 
393
                        if (ctx->canonicalise_uris) {
 
394
                                char *canon = rb_canonicalise_uri (ctx->buf->str);
 
395
 
 
396
                                g_value_init (&value, G_TYPE_STRING);
 
397
                                g_value_take_string (&value, canon);
 
398
                                set = TRUE;
 
399
                        }
390
400
                        break;
391
401
                case RHYTHMDB_PROP_MOUNTPOINT:
392
 
                        /* remove this from old podcast-post entries */
393
 
                        if (!g_str_has_prefix (ctx->buf->str, "http://"))
394
 
                                ctx->entry->mountpoint = rb_refstring_new (ctx->buf->str);
395
 
                        break;
396
 
                case RHYTHMDB_PROP_MTIME:
397
 
                        ctx->entry->mtime = parse_ulong (ctx->buf->str);
398
 
                        break;
399
 
                case RHYTHMDB_PROP_FIRST_SEEN:
400
 
                        ctx->entry->first_seen = parse_ulong (ctx->buf->str);
401
 
                        break;
402
 
                case RHYTHMDB_PROP_LAST_SEEN:
403
 
                        ctx->entry->last_seen = parse_ulong (ctx->buf->str);
404
 
                        break;
405
 
                case RHYTHMDB_PROP_RATING:
406
 
                        ctx->entry->rating = g_ascii_strtod (ctx->buf->str, NULL);
407
 
                        break;
408
 
                case RHYTHMDB_PROP_PLAY_COUNT:
409
 
                        ctx->entry->play_count = parse_ulong (ctx->buf->str);
410
 
                        break;
411
 
                case RHYTHMDB_PROP_LAST_PLAYED:
412
 
                        ctx->entry->last_played = parse_ulong (ctx->buf->str);
413
 
                        break;
414
 
                case RHYTHMDB_PROP_BITRATE:
415
 
                        ctx->entry->bitrate = parse_ulong (ctx->buf->str);
416
 
                        break;
417
 
                case RHYTHMDB_PROP_TRACK_GAIN:
418
 
                        ctx->entry->track_gain = g_ascii_strtod (ctx->buf->str, NULL);
419
 
                        break;
420
 
                case RHYTHMDB_PROP_TRACK_PEAK:
421
 
                        ctx->entry->track_peak = g_ascii_strtod (ctx->buf->str, NULL);
422
 
                        break;
423
 
                case RHYTHMDB_PROP_ALBUM_GAIN:
424
 
                        ctx->entry->album_gain = g_ascii_strtod (ctx->buf->str, NULL);
425
 
                        break;
426
 
                case RHYTHMDB_PROP_ALBUM_PEAK:
427
 
                        ctx->entry->album_peak = g_ascii_strtod (ctx->buf->str, NULL);
428
 
                        break;
429
 
                case RHYTHMDB_PROP_MIMETYPE:
430
 
                        ctx->entry->mimetype = rb_refstring_new (ctx->buf->str);
431
 
                        break;
432
 
                case RHYTHMDB_PROP_STATUS:
433
 
                        ctx->entry->podcast->status = parse_ulong (ctx->buf->str);
434
 
                        break;                  
435
 
                case RHYTHMDB_PROP_DESCRIPTION:
436
 
                        ctx->entry->podcast->description = rb_refstring_new (ctx->buf->str);
437
 
                        break;
438
 
                case RHYTHMDB_PROP_SUBTITLE:
439
 
                        ctx->entry->podcast->subtitle = rb_refstring_new (ctx->buf->str);
440
 
                        break;
441
 
                case RHYTHMDB_PROP_SUMMARY:
442
 
                        ctx->entry->podcast->summary = rb_refstring_new (ctx->buf->str);
443
 
                        break;
444
 
                case RHYTHMDB_PROP_LANG:
445
 
                        ctx->entry->podcast->lang = rb_refstring_new (ctx->buf->str);
446
 
                        break;
447
 
                case RHYTHMDB_PROP_COPYRIGHT:
448
 
                        ctx->entry->podcast->copyright = rb_refstring_new (ctx->buf->str);
449
 
                        break;
450
 
                case RHYTHMDB_PROP_IMAGE:
451
 
                        ctx->entry->podcast->image = rb_refstring_new (ctx->buf->str);
452
 
                        break;
453
 
                case RHYTHMDB_PROP_POST_TIME:                   
454
 
                        ctx->entry->podcast->post_time = parse_ulong (ctx->buf->str);
455
 
                        break;
456
 
                case RHYTHMDB_PROP_TITLE_SORT_KEY:
457
 
                case RHYTHMDB_PROP_GENRE_SORT_KEY:
458
 
                case RHYTHMDB_PROP_ARTIST_SORT_KEY:
459
 
                case RHYTHMDB_PROP_ALBUM_SORT_KEY:
460
 
                case RHYTHMDB_PROP_TITLE_FOLDED:
461
 
                case RHYTHMDB_PROP_GENRE_FOLDED:
462
 
                case RHYTHMDB_PROP_ARTIST_FOLDED:
463
 
                case RHYTHMDB_PROP_ALBUM_FOLDED:
464
 
                case RHYTHMDB_PROP_LAST_PLAYED_STR:
465
 
                case RHYTHMDB_PROP_HIDDEN:
466
 
                case RHYTHMDB_PROP_PLAYBACK_ERROR:
467
 
                case RHYTHMDB_PROP_FIRST_SEEN_STR:
468
 
                case RHYTHMDB_PROP_SEARCH_MATCH:
469
 
                case RHYTHMDB_NUM_PROPERTIES:
470
 
                        g_assert_not_reached ();
471
 
                        break;
472
 
                }
473
 
                        
474
 
                rhythmdb_entry_sync_mirrored (RHYTHMDB (ctx->db), ctx->entry, ctx->propid);
 
402
                        /* fix old podcast posts */
 
403
                        if (g_str_has_prefix (ctx->buf->str, "http://"))
 
404
                                skip = TRUE;
 
405
                        break;
 
406
                default:
 
407
                        break;
 
408
                }
 
409
 
 
410
                if (!skip) {
 
411
                        if (!set) {
 
412
                                rhythmdb_read_encoded_property (RHYTHMDB (ctx->db), ctx->buf->str, ctx->propid, &value);
 
413
                        }
 
414
 
 
415
                        rhythmdb_entry_set_internal (RHYTHMDB (ctx->db), ctx->entry, FALSE, ctx->propid, &value);
 
416
                        g_value_unset (&value);
 
417
                }
475
418
 
476
419
                ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
477
420
                break;
533
476
                xmlParseDocument (ctxt);
534
477
                ctxt->sax = NULL;
535
478
                xmlFreeParserCtxt (ctxt);
 
479
 
 
480
                if (ctx->batch_count)
 
481
                        rhythmdb_commit (RHYTHMDB (ctx->db));
536
482
                        
537
483
        }
538
484
        g_string_free (ctx->buf, TRUE);
631
577
}
632
578
 
633
579
static void
 
580
save_entry_boolean (struct RhythmDBTreeSaveContext *ctx,
 
581
                    const xmlChar *elt_name, gboolean val)
 
582
{
 
583
        save_entry_ulong (ctx, elt_name, val ? 1 : 0, FALSE);
 
584
}
 
585
 
 
586
static void
634
587
save_entry_uint64 (struct RhythmDBTreeSaveContext *ctx, const xmlChar *elt_name,
635
588
                   guint64 num)
636
589
{
667
620
save_entry (RhythmDBTree *db, RhythmDBEntry *entry, struct RhythmDBTreeSaveContext *ctx)
668
621
{
669
622
        RhythmDBPropType i;
 
623
        RhythmDBPodcastFields *podcast = NULL;
670
624
 
671
625
        if (ctx->error)
672
626
                return;
673
627
 
 
628
        if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED ||
 
629
            entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_POST)
 
630
                podcast = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RhythmDBPodcastFields);
 
631
 
674
632
        RHYTHMDB_FWRITE_STATICSTR ("  <entry type=\"", ctx->handle, ctx->error);
675
633
 
676
634
        if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
681
639
                RHYTHMDB_FWRITE_STATICSTR ("podcast-post", ctx->handle, ctx->error);
682
640
        } else if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
683
641
                RHYTHMDB_FWRITE_STATICSTR ("podcast-feed", ctx->handle, ctx->error);
684
 
        } else
 
642
        } else if (entry->type == RHYTHMDB_ENTRY_TYPE_IGNORE) {
 
643
                RHYTHMDB_FWRITE_STATICSTR ("ignore", ctx->handle, ctx->error);
 
644
        } else {
685
645
                g_assert_not_reached ();
 
646
        }
686
647
 
687
648
        RHYTHMDB_FWRITE_STATICSTR ("\">\n", ctx->handle, ctx->error);
688
649
                
718
679
                        save_entry_ulong (ctx, elt_name, entry->discnum, FALSE);
719
680
                        break;
720
681
                case RHYTHMDB_PROP_DATE:
721
 
                        if (entry->date)
722
 
                                save_entry_ulong (ctx, elt_name, g_date_get_julian (entry->date), TRUE);
 
682
                        if (g_date_valid (&entry->date))
 
683
                                save_entry_ulong (ctx, elt_name, g_date_get_julian (&entry->date), TRUE);
723
684
                        else
724
685
                                save_entry_ulong (ctx, elt_name, 0, TRUE);
725
686
                        break;
775
736
                case RHYTHMDB_PROP_LAST_PLAYED:
776
737
                        save_entry_ulong (ctx, elt_name, entry->last_played, FALSE);
777
738
                        break;
 
739
                case RHYTHMDB_PROP_HIDDEN:
 
740
                        {
 
741
                                gboolean hidden = ((entry->flags & RHYTHMDB_ENTRY_HIDDEN) != 0);
 
742
                                save_entry_boolean (ctx, elt_name, hidden);
 
743
                        }
 
744
                        break;
778
745
                case RHYTHMDB_PROP_STATUS:
779
 
                        if (entry->podcast)
780
 
                                save_entry_ulong (ctx, elt_name, entry->podcast->status, FALSE);
 
746
                        if (podcast)
 
747
                                save_entry_ulong (ctx, elt_name, podcast->status, FALSE);
781
748
                        break;
782
749
                case RHYTHMDB_PROP_DESCRIPTION:
783
 
                        if (entry->podcast && entry->podcast->description)
784
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->description));
 
750
                        if (podcast && podcast->description)
 
751
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->description));
785
752
                        break;
786
753
                case RHYTHMDB_PROP_SUBTITLE:
787
 
                        if (entry->podcast && entry->podcast->subtitle)
788
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->subtitle));
 
754
                        if (podcast && podcast->subtitle)
 
755
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->subtitle));
789
756
                        break;
790
757
                case RHYTHMDB_PROP_SUMMARY:
791
 
                        if (entry->podcast && entry->podcast->summary)
792
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->summary));
 
758
                        if (podcast && podcast->summary)
 
759
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->summary));
793
760
                        break;
794
761
                case RHYTHMDB_PROP_LANG:
795
 
                        if (entry->podcast && entry->podcast->lang)
796
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->lang));
 
762
                        if (podcast && podcast->lang)
 
763
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->lang));
797
764
                        break;
798
765
                case RHYTHMDB_PROP_COPYRIGHT:
799
 
                        if (entry->podcast && entry->podcast->copyright)
800
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->copyright));
 
766
                        if (podcast && podcast->copyright)
 
767
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->copyright));
801
768
                        break;
802
769
                case RHYTHMDB_PROP_IMAGE:
803
 
                        if (entry->podcast && entry->podcast->image)
804
 
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->image));
 
770
                        if (podcast && podcast->image)
 
771
                                save_entry_string(ctx, elt_name, rb_refstring_get (podcast->image));
805
772
                        break;
806
773
                case RHYTHMDB_PROP_POST_TIME:
807
 
                        if (entry->podcast)
808
 
                                save_entry_ulong (ctx, elt_name, entry->podcast->post_time, FALSE);
 
774
                        if (podcast)
 
775
                                save_entry_ulong (ctx, elt_name, podcast->post_time, FALSE);
809
776
                        break;                  
810
777
                case RHYTHMDB_PROP_TITLE_SORT_KEY:
811
778
                case RHYTHMDB_PROP_GENRE_SORT_KEY:
816
783
                case RHYTHMDB_PROP_ARTIST_FOLDED:
817
784
                case RHYTHMDB_PROP_ALBUM_FOLDED:
818
785
                case RHYTHMDB_PROP_LAST_PLAYED_STR:
819
 
                case RHYTHMDB_PROP_HIDDEN:
820
786
                case RHYTHMDB_PROP_PLAYBACK_ERROR:
821
787
                case RHYTHMDB_PROP_FIRST_SEEN_STR:
 
788
                case RHYTHMDB_PROP_LAST_SEEN_STR:
822
789
                case RHYTHMDB_PROP_SEARCH_MATCH:
 
790
                case RHYTHMDB_PROP_YEAR:
823
791
                case RHYTHMDB_NUM_PROPERTIES:
824
792
                        break;
825
793
                }
853
821
        ctx.handle = f;
854
822
        ctx.error = NULL;
855
823
        RHYTHMDB_FWRITE_STATICSTR ("<?xml version=\"1.0\" standalone=\"yes\"?>\n"
856
 
                                   "<rhythmdb version=\"" RHYTHMDB_TREE_XML_VERSION "\">", 
 
824
                                   "<rhythmdb version=\"" RHYTHMDB_TREE_XML_VERSION "\">\n", 
857
825
                                   ctx.handle, ctx.error);
858
826
 
859
 
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_SONG, 
860
 
                                    (RBTreeEntryItFunc)save_entry, 
861
 
                                    NULL, NULL, NULL, &ctx);
862
 
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IRADIO_STATION, 
863
 
                                    (RBTreeEntryItFunc)save_entry, 
864
 
                                    NULL, NULL, NULL, &ctx);
865
 
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_POST, 
866
 
                                    (RBTreeEntryItFunc)save_entry, 
867
 
                                    NULL, NULL, NULL, &ctx);
868
 
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED, 
869
 
                                    (RBTreeEntryItFunc)save_entry, 
 
827
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_SONG,
 
828
                                    (RBTreeEntryItFunc)save_entry,
 
829
                                    NULL, NULL, NULL, &ctx);
 
830
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IRADIO_STATION,
 
831
                                    (RBTreeEntryItFunc)save_entry,
 
832
                                    NULL, NULL, NULL, &ctx);
 
833
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
 
834
                                    (RBTreeEntryItFunc)save_entry,
 
835
                                    NULL, NULL, NULL, &ctx);
 
836
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED,
 
837
                                    (RBTreeEntryItFunc)save_entry,
 
838
                                    NULL, NULL, NULL, &ctx);
 
839
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IGNORE,
 
840
                                    (RBTreeEntryItFunc)save_entry,
870
841
                                    NULL, NULL, NULL, &ctx);
871
842
 
872
843
        RHYTHMDB_FWRITE_STATICSTR ("</rhythmdb>\n", ctx.handle, ctx.error);
960
931
        artist = get_or_create_artist (db, genre, entry->artist);
961
932
        set_entry_album (db, entry, artist, entry->album);
962
933
 
 
934
        /* this accounts for the initial reference on the entry */
963
935
        g_hash_table_insert (db->priv->entries, entry->location, entry);
 
936
        
 
937
        entry->flags &= ~RHYTHMDB_ENTRY_TREE_LOADING;
964
938
}
965
939
 
966
940
static RhythmDBTreeProperty *
978
952
{
979
953
        GHashTable *table;
980
954
 
981
 
        table = g_hash_table_lookup (db->priv->genres, GINT_TO_POINTER (type));
 
955
        table = g_hash_table_lookup (db->priv->genres, type);
982
956
        if (table == NULL) {
983
957
                table = g_hash_table_new_full (rb_refstring_hash,
984
958
                                               rb_refstring_equal,
989
963
                        return NULL;
990
964
                }
991
965
                g_hash_table_insert (db->priv->genres, 
992
 
                                     GINT_TO_POINTER (type), 
 
966
                                     type,
993
967
                                     table);
994
968
        } 
995
969
        return table;
1135
1109
 
1136
1110
        type = entry->type;
1137
1111
 
 
1112
        /* don't process changes to entries we're loading, we'll get them
 
1113
         * when the entry is complete.
 
1114
         */
 
1115
        if (entry->flags & RHYTHMDB_ENTRY_TREE_LOADING)
 
1116
                return FALSE;
 
1117
 
1138
1118
        /* Handle special properties */
1139
1119
        switch (propid)
1140
1120
        {
1246
1226
        g_assert (g_hash_table_lookup (db->priv->entries, entry->location) != NULL);
1247
1227
 
1248
1228
        g_hash_table_remove (db->priv->entries, entry->location);
 
1229
        rhythmdb_entry_unref (adb, entry);
1249
1230
}
1250
1231
 
1251
1232
typedef struct {
1262
1243
        if (entry->type == ctxt->type) {
1263
1244
                rhythmdb_emit_entry_deleted (ctxt->db, entry);
1264
1245
                remove_entry_from_album (RHYTHMDB_TREE (ctxt->db), entry); 
 
1246
                rhythmdb_entry_unref (ctxt->db, entry);
1265
1247
                return TRUE;
1266
1248
        }
1267
1249
        return FALSE;
1318
1300
                        if (evaluate_conjunctive_subquery (db, query, last_disjunction, i, entry))
1319
1301
                                return TRUE;
1320
1302
 
1321
 
                        last_disjunction = i;
 
1303
                        last_disjunction = i + 1;
1322
1304
                }
1323
1305
        }
1324
1306
        if (evaluate_conjunctive_subquery (db, query, last_disjunction, query->len, entry))
1353
1335
                                    g_value_get_double (data->val)) \
1354
1336
                                        return FALSE; \
1355
1337
                                break; \
 
1338
                        case G_TYPE_POINTER: \
 
1339
                                if (rhythmdb_entry_get_pointer (entry, data->propid) OP \
 
1340
                                    g_value_get_pointer (data->val)) \
 
1341
                                        return FALSE; \
 
1342
                                break; \
1356
1343
                        default: \
 
1344
                                g_warning ("Unexpected type: %s", g_type_name (rhythmdb_get_property_type (db, data->propid))); \
1357
1345
                                g_assert_not_reached (); \
1358
1346
                        }
1359
1347
 
1440
1428
                        relative_time = g_value_get_ulong (data->val);
1441
1429
                        g_get_current_time  (&current_time);
1442
1430
 
1443
 
                        if (data->type == RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN)
1444
 
                                return (rhythmdb_entry_get_ulong (entry, data->propid) >= (current_time.tv_sec - relative_time));
1445
 
                        else
1446
 
                                return (rhythmdb_entry_get_ulong (entry, data->propid) < (current_time.tv_sec - relative_time));
1447
 
 
 
1431
                        if (data->type == RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN) {
 
1432
                                if (!(rhythmdb_entry_get_ulong (entry, data->propid) >= (current_time.tv_sec - relative_time)))
 
1433
                                        return FALSE;
 
1434
                        } else {
 
1435
                                if (!(rhythmdb_entry_get_ulong (entry, data->propid) < (current_time.tv_sec - relative_time)))
 
1436
                                        return FALSE;
 
1437
                        }
 
1438
                        break;
 
1439
                }
 
1440
                case RHYTHMDB_QUERY_PROP_PREFIX:
 
1441
                case RHYTHMDB_QUERY_PROP_SUFFIX:
 
1442
                {
 
1443
                        const char *value_s;
 
1444
                        const char *entry_s;
 
1445
                        
 
1446
                        g_assert (rhythmdb_get_property_type (db, data->propid) == G_TYPE_STRING);
 
1447
 
 
1448
                        value_s = g_value_get_string (data->val);
 
1449
                        entry_s = rhythmdb_entry_get_string (entry, data->propid);
 
1450
 
 
1451
                        if (data->type == RHYTHMDB_QUERY_PROP_PREFIX && !g_str_has_prefix (entry_s, value_s))
 
1452
                                return FALSE;
 
1453
                        if (data->type == RHYTHMDB_QUERY_PROP_SUFFIX && !g_str_has_suffix (entry_s, value_s))
 
1454
                                return FALSE;
 
1455
                        
1448
1456
                        break;
1449
1457
                }
1450
1458
                case RHYTHMDB_QUERY_PROP_LIKE:
1477
1485
                        /* Fall through */
1478
1486
                }
1479
1487
                case RHYTHMDB_QUERY_PROP_EQUALS:
1480
 
                {
1481
1488
                        RHYTHMDB_PROPERTY_COMPARE (!=)
1482
1489
                        break;
1483
 
                }
1484
1490
                case RHYTHMDB_QUERY_PROP_GREATER:
1485
1491
                        RHYTHMDB_PROPERTY_COMPARE (<)
1486
1492
                        break;
1694
1700
                
1695
1701
                g_ptr_array_remove_index_fast (query, type_query_idx);
1696
1702
                
1697
 
                etype = g_value_get_ulong (qdata->val);
 
1703
                etype = g_value_get_pointer (qdata->val);
1698
1704
                genres = get_genres_hash_for_type (db, etype);
1699
1705
                if (genres != NULL) {
1700
1706
                        conjunctive_query_genre (db, genres, traversal_data);
 
1707
                } else {
 
1708
                        g_assert_not_reached ();
1701
1709
                }
1702
1710
        } else {
1703
1711
                /* FIXME */
1750
1758
        RhythmDBTree *db;
1751
1759
        GPtrArray *queue;
1752
1760
        GHashTable *entries;
1753
 
        RhythmDBQueryModel *main_model;
 
1761
        RhythmDBQueryResults *results;
1754
1762
};
1755
1763
 
1756
1764
static void
1759
1767
{
1760
1768
        GList *conjunctions, *tem;
1761
1769
 
 
1770
        if (query == NULL)
 
1771
                return;
 
1772
        
1762
1773
        conjunctions = split_query_by_disjunctions (db, query);
1763
 
 
1764
1774
        rb_debug ("doing recursive query, %d conjunctions", g_list_length (conjunctions));
1765
1775
 
1766
1776
        if (conjunctions == NULL)
1796
1806
 
1797
1807
        g_ptr_array_add (data->queue, entry);
1798
1808
        if (data->queue->len > RHYTHMDB_QUERY_MODEL_SUGGESTED_UPDATE_CHUNK) {
1799
 
                rhythmdb_query_model_add_entries (data->main_model, data->queue);
 
1809
                rhythmdb_query_results_add_results (data->results, data->queue);
1800
1810
                data->queue = g_ptr_array_new ();
1801
1811
        }
1802
1812
}
1804
1814
static void
1805
1815
rhythmdb_tree_do_full_query (RhythmDB *adb,
1806
1816
                             GPtrArray *query,
1807
 
                             GtkTreeModel *main_model,
 
1817
                             RhythmDBQueryResults *results,
1808
1818
                             gboolean *cancel)
1809
1819
{
1810
1820
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1811
1821
        struct RhythmDBTreeQueryGatheringData *data = g_new0 (struct RhythmDBTreeQueryGatheringData, 1);
1812
1822
 
1813
 
        data->main_model = RHYTHMDB_QUERY_MODEL (main_model);
 
1823
        data->results = results;
1814
1824
        data->queue = g_ptr_array_new ();
1815
1825
 
1816
1826
        do_query_recurse (db, query, (RhythmDBTreeTraversalFunc) handle_entry_match, data, cancel);
1817
1827
 
1818
 
        rhythmdb_query_model_add_entries (data->main_model, data->queue);
 
1828
        rhythmdb_query_results_add_results (data->results, data->queue);
1819
1829
 
1820
1830
        g_free (data);
1821
1831
}