~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to rhythmdb/rhythmdb-query-model.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
 
30
30
#include "rhythmdb-query-model.h"
31
31
#include "rb-debug.h"
32
 
#include "rb-thread-helpers.h"
33
32
#include "gsequence.h"
34
33
#include "rb-tree-dnd.h"
35
 
#include "rb-marshal.h"
 
34
#include "rhythmdb-marshal.h"
 
35
#include "rb-util.h"
36
36
 
37
 
static void rhythmdb_query_model_class_init (RhythmDBQueryModelClass *klass);
38
37
static void rhythmdb_query_model_tree_model_init (GtkTreeModelIface *iface);
39
38
static void rhythmdb_query_model_drag_source_init (RbTreeDragSourceIface *iface);
40
39
static void rhythmdb_query_model_drag_dest_init (RbTreeDragDestIface *iface);
 
40
 
 
41
G_DEFINE_TYPE_WITH_CODE(RhythmDBQueryModel, rhythmdb_query_model, G_TYPE_OBJECT,
 
42
                        G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
 
43
                                              rhythmdb_query_model_tree_model_init)
 
44
                        G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_SOURCE,
 
45
                                              rhythmdb_query_model_drag_source_init)
 
46
                        G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_DEST,
 
47
                                              rhythmdb_query_model_drag_dest_init))
 
48
 
41
49
static void rhythmdb_query_model_init (RhythmDBQueryModel *shell_player);
42
50
static GObject *rhythmdb_query_model_constructor (GType type, guint n_construct_properties,
43
51
                                                  GObjectConstructParam *construct_properties);
52
60
                                               GParamSpec *pspec);
53
61
static void rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
54
62
                                            RhythmDBEntry *entry);
55
 
static void rhythmdb_query_model_do_delete (RhythmDBQueryModel *model,
56
 
                                            RhythmDBEntry *entry);
57
63
static void rhythmdb_query_model_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
58
64
                                                 RhythmDBQueryModel *model);
59
65
static void rhythmdb_query_model_entry_changed_cb (RhythmDB *db, RhythmDBEntry *entry,
60
 
                                                   RhythmDBPropType prop, const GValue *old,
61
 
                                                   const GValue *new, RhythmDBQueryModel *model);
 
66
                                                   GSList *changes, RhythmDBQueryModel *model);
62
67
static void rhythmdb_query_model_entry_deleted_cb (RhythmDB *db, RhythmDBEntry *entry,
63
68
                                                   RhythmDBQueryModel *model);
64
69
 
 
70
static void rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
 
71
                                                   RhythmDBEntry *entry);
 
72
static void rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model, RhythmDBEntry *entry);
65
73
static gboolean rhythmdb_query_model_drag_data_get (RbTreeDragSource *dragsource,
66
74
                                                          GList *paths,
67
75
                                                          GtkSelectionData *selection_data);
106
114
static gboolean rhythmdb_query_model_iter_parent (GtkTreeModel *tree_model,
107
115
                                                  GtkTreeIter  *iter,
108
116
                                                  GtkTreeIter  *child);
109
 
static GSequencePtr choose_sequence_element (GSequence *seq);
110
 
 
111
 
static gboolean idle_poll_model (RhythmDBQueryModel *model);
112
 
 
113
 
static const GtkTargetEntry rhythmdb_query_model_drag_types[] = { { "text/uri-list", 0, 0 },};
114
 
 
115
 
static GtkTargetList *rhythmdb_query_model_drag_target_list = NULL;
 
117
 
116
118
 
117
119
struct RhythmDBQueryModelUpdate
118
120
{
 
121
        RhythmDBQueryModel *model;
119
122
        enum {
120
123
                RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED,
121
 
                RHYTHMDB_QUERY_MODEL_UPDATE_ROW_CHANGED,
122
 
                RHYTHMDB_QUERY_MODEL_UPDATE_ROW_DELETED,
123
124
                RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE,
124
125
        } type;
125
126
        RhythmDBEntry *entry;
126
127
        GPtrArray *entries;
127
 
 
128
 
        /* Only used for _ROW_CHANGED */
129
 
        RhythmDBPropType prop;
130
 
        GValue old;
131
 
        GValue new;
132
128
};
133
129
 
 
130
static void rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update);
 
131
 
 
132
static gboolean idle_process_update (struct RhythmDBQueryModelUpdate *update);
 
133
 
 
134
static const GtkTargetEntry rhythmdb_query_model_drag_types[] = { { "text/uri-list", 0, 0 },};
 
135
 
 
136
static GtkTargetList *rhythmdb_query_model_drag_target_list = NULL;
 
137
 
 
138
 
134
139
struct RhythmDBQueryModelPrivate
135
140
{
136
141
        RhythmDB *db;
137
142
 
138
143
        GCompareDataFunc sort_func;
139
144
        gpointer sort_user_data;
 
145
        GDestroyNotify sort_destroy_notify;
140
146
 
141
 
        GPtrArray *query;
 
147
        GPtrArray *query, *original_query;
142
148
 
143
149
        guint stamp;
144
150
 
145
151
        GnomeVFSFileSize max_size;
146
152
        guint max_count;
 
153
        guint max_time;
147
154
 
148
 
        gboolean cancelled;
149
 
        
150
155
        gboolean connected;
151
156
 
152
 
        guint model_poll_id;
153
 
 
154
157
        glong total_duration;
155
158
        GnomeVFSFileSize total_size;
156
159
 
157
160
        GSequence *entries;
158
161
        GHashTable *reverse_map;
159
 
 
160
 
        GAsyncQueue *query_complete;
161
 
 
162
 
        /* row_inserted/row_changed/row_deleted */
163
 
        GAsyncQueue *pending_updates;
 
162
        GSequence *limited_entries;
 
163
        GHashTable *limited_reverse_map;
 
164
 
 
165
        gint pending_update_count;
164
166
 
165
167
        gboolean reorder_drag_and_drop;
166
168
};
172
174
        PROP_QUERY,
173
175
        PROP_SORT_FUNC,
174
176
        PROP_SORT_DATA,
 
177
        PROP_SORT_DATA_DESTROY,
175
178
        PROP_MAX_SIZE,
176
179
        PROP_MAX_COUNT,
 
180
        PROP_MAX_TIME,
177
181
};
178
182
 
179
183
enum
180
184
{
181
185
        COMPLETE,
182
186
        ENTRY_PROP_CHANGED,
 
187
        ENTRY_REMOVED,
 
188
        NON_ENTRY_DROPPED,
183
189
        LAST_SIGNAL
184
190
};
185
191
 
186
192
static guint rhythmdb_query_model_signals[LAST_SIGNAL] = { 0 };
187
193
 
188
 
static GObjectClass *parent_class = NULL;
189
 
 
190
 
GType
191
 
rhythmdb_query_model_get_type (void)
192
 
{
193
 
        static GType rhythmdb_query_model_type = 0;
194
 
 
195
 
        if (rhythmdb_query_model_type == 0)
196
 
        {
197
 
                static const GTypeInfo our_info =
198
 
                {
199
 
                        sizeof (RhythmDBQueryModelClass),
200
 
                        NULL,
201
 
                        NULL,
202
 
                        (GClassInitFunc) rhythmdb_query_model_class_init,
203
 
                        NULL,
204
 
                        NULL,
205
 
                        sizeof (RhythmDBQueryModel),
206
 
                        0,
207
 
                        (GInstanceInitFunc) rhythmdb_query_model_init
208
 
                };
209
 
 
210
 
                static const GInterfaceInfo tree_model_info =
211
 
                {
212
 
                        (GInterfaceInitFunc) rhythmdb_query_model_tree_model_init,
213
 
                        NULL,
214
 
                        NULL
215
 
                };
216
 
 
217
 
                static const GInterfaceInfo drag_source_info = {
218
 
                        (GInterfaceInitFunc) rhythmdb_query_model_drag_source_init,
219
 
                        NULL,
220
 
                        NULL
221
 
                };
222
 
 
223
 
                static const GInterfaceInfo drag_dest_info = {
224
 
                        (GInterfaceInitFunc) rhythmdb_query_model_drag_dest_init,
225
 
                        NULL,
226
 
                        NULL
227
 
                };
228
 
 
229
 
                rhythmdb_query_model_type = g_type_register_static (G_TYPE_OBJECT,
230
 
                                                                    "RhythmDBQueryModel",
231
 
                                                                    &our_info, 0);
232
 
 
233
 
                g_type_add_interface_static (rhythmdb_query_model_type,
234
 
                                             GTK_TYPE_TREE_MODEL,
235
 
                                             &tree_model_info);
236
 
 
237
 
                g_type_add_interface_static (rhythmdb_query_model_type,
238
 
                                             RB_TYPE_TREE_DRAG_SOURCE,
239
 
                                             &drag_source_info);
240
 
 
241
 
                g_type_add_interface_static (rhythmdb_query_model_type,
242
 
                                             RB_TYPE_TREE_DRAG_DEST,
243
 
                                             &drag_dest_info);
244
 
        }
245
 
 
246
 
        return rhythmdb_query_model_type;
247
 
}
248
 
 
249
194
static void
250
195
rhythmdb_query_model_class_init (RhythmDBQueryModelClass *klass)
251
196
{
252
197
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
253
198
 
254
 
        parent_class = g_type_class_peek_parent (klass);
255
 
 
256
199
        if (!rhythmdb_query_model_drag_target_list)
257
200
                rhythmdb_query_model_drag_target_list
258
201
                        = gtk_target_list_new (rhythmdb_query_model_drag_types,
294
237
                                                               G_PARAM_READWRITE));
295
238
 
296
239
        g_object_class_install_property (object_class,
 
240
                                         PROP_SORT_DATA_DESTROY,
 
241
                                         g_param_spec_pointer ("sort-data-destroy",
 
242
                                                              "GDestroyNotify",
 
243
                                                              "Sort data destroy function",
 
244
                                                               G_PARAM_READWRITE));
 
245
 
 
246
        g_object_class_install_property (object_class,
297
247
                                         PROP_MAX_SIZE,
298
248
                                         g_param_spec_int ("max-size",
299
249
                                                           "maxsize",
309
259
                                                           0, G_MAXINT, 0,
310
260
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
311
261
 
 
262
        g_object_class_install_property (object_class,
 
263
                                         PROP_MAX_TIME,
 
264
                                         g_param_spec_int ("max-time",
 
265
                                                           "maxtime",
 
266
                                                           "maximum time (seconds)",
 
267
                                                           0, G_MAXINT, 0,
 
268
                                                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
269
 
312
270
        rhythmdb_query_model_signals[ENTRY_PROP_CHANGED] =
313
271
                g_signal_new ("entry-prop-changed",
314
272
                              RHYTHMDB_TYPE_QUERY_MODEL,
315
273
                              G_SIGNAL_RUN_LAST,
316
274
                              G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_prop_changed),
317
275
                              NULL, NULL,
318
 
                              rb_marshal_VOID__POINTER_INT_POINTER_POINTER,
 
276
                              rhythmdb_marshal_VOID__POINTER_INT_POINTER_POINTER,
319
277
                              G_TYPE_NONE, 4, G_TYPE_POINTER,
320
278
                              G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER);
 
279
        rhythmdb_query_model_signals[ENTRY_REMOVED] =
 
280
                g_signal_new ("entry-removed",
 
281
                              RHYTHMDB_TYPE_QUERY_MODEL,
 
282
                              G_SIGNAL_RUN_LAST,
 
283
                              G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_removed),
 
284
                              NULL, NULL,
 
285
                              rhythmdb_marshal_VOID__POINTER,
 
286
                              G_TYPE_NONE, 1, G_TYPE_POINTER);
 
287
        rhythmdb_query_model_signals[NON_ENTRY_DROPPED] =
 
288
                g_signal_new ("non-entry-dropped",
 
289
                              RHYTHMDB_TYPE_QUERY_MODEL,
 
290
                              G_SIGNAL_RUN_LAST,
 
291
                              G_STRUCT_OFFSET (RhythmDBQueryModelClass, non_entry_dropped),
 
292
                              NULL, NULL,
 
293
                              rhythmdb_marshal_VOID__POINTER,
 
294
                              G_TYPE_NONE, 1, G_TYPE_STRING);
321
295
        rhythmdb_query_model_signals[COMPLETE] =
322
296
                g_signal_new ("complete",
323
297
                              RHYTHMDB_TYPE_QUERY_MODEL,
372
346
        switch (prop_id)
373
347
        {
374
348
        case PROP_RHYTHMDB:
375
 
        {
376
349
                model->priv->db = g_value_get_object (value);
377
350
                break;
378
 
        }
379
351
        case PROP_QUERY:
380
352
                model->priv->query = rhythmdb_query_copy (g_value_get_pointer (value));
 
353
                model->priv->original_query = rhythmdb_query_copy (model->priv->query);
 
354
                rhythmdb_query_preprocess (model->priv->query);
381
355
                break;
382
356
        case PROP_SORT_FUNC:
383
357
                model->priv->sort_func = g_value_get_pointer (value);
385
359
        case PROP_SORT_DATA:
386
360
                model->priv->sort_user_data = g_value_get_pointer (value);
387
361
                break;
 
362
        case PROP_SORT_DATA_DESTROY:
 
363
                model->priv->sort_destroy_notify  = g_value_get_pointer (value);
 
364
                break;
388
365
        case PROP_MAX_SIZE:
389
366
                model->priv->max_size = g_value_get_int (value) * 1024 * 1024;
390
367
                break;
391
368
        case PROP_MAX_COUNT:
392
369
                model->priv->max_count = g_value_get_int (value);
393
370
                break;
 
371
        case PROP_MAX_TIME:
 
372
                model->priv->max_time = g_value_get_int (value);
 
373
                break;
394
374
        default:
395
375
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
396
376
                break;
411
391
                g_value_set_object (value, model->priv->db);
412
392
                break;
413
393
        case PROP_QUERY:
414
 
                g_value_set_pointer (value, model->priv->query);
 
394
                g_value_set_pointer (value, model->priv->original_query);
415
395
                break;
416
396
        case PROP_SORT_FUNC:
417
397
                g_value_set_pointer (value, model->priv->sort_func);
419
399
        case PROP_SORT_DATA:
420
400
                g_value_set_pointer (value, model->priv->sort_user_data);
421
401
                break;
 
402
        case PROP_SORT_DATA_DESTROY:
 
403
                g_value_set_pointer (value, model->priv->sort_destroy_notify);
 
404
                break;
422
405
        case PROP_MAX_SIZE:
423
406
                g_value_set_int (value, model->priv->max_size / (1024 * 1024));
424
407
                break;
425
408
        case PROP_MAX_COUNT:
426
409
                g_value_set_int (value, model->priv->max_count);
427
410
                break;
 
411
        case PROP_MAX_TIME:
 
412
                g_value_set_int (value, model->priv->max_time);
 
413
                break;
428
414
        default:
429
415
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430
416
                break;
440
426
 
441
427
        model->priv->entries = g_sequence_new (NULL);
442
428
        model->priv->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
443
 
 
444
 
        model->priv->query_complete = g_async_queue_new ();
445
 
        model->priv->pending_updates = g_async_queue_new ();
 
429
        model->priv->limited_entries = g_sequence_new (NULL);
 
430
        model->priv->limited_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal);
446
431
 
447
432
        model->priv->reorder_drag_and_drop = FALSE;
448
 
 
449
 
        model->priv->model_poll_id = g_idle_add ((GSourceFunc) idle_poll_model, model);
450
 
 
451
433
}
452
434
 
453
435
static GObject *
484
466
}
485
467
 
486
468
static void
487
 
rhythmdb_query_model_free_pending_update (RhythmDBQueryModel *model,
488
 
                                          struct RhythmDBQueryModelUpdate *update)
 
469
_unref_entry (RhythmDBEntry *entry, gpointer stuff, RhythmDB *db)
489
470
{
490
 
        switch (update->type) {
491
 
        case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED:
492
 
        {
493
 
                guint i;
494
 
                for (i = 0; i < update->entries->len; i++)
495
 
                        rhythmdb_entry_unref (model->priv->db,
496
 
                                              g_ptr_array_index (update->entries, i));
497
 
                g_ptr_array_free (update->entries, TRUE);
498
 
                break;
499
 
        }
500
 
        case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_CHANGED:
501
 
                rhythmdb_entry_unref (model->priv->db, update->entry);
502
 
                g_value_unset (&update->old);
503
 
                g_value_unset (&update->new);
504
 
                break;
505
 
        case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_DELETED:
506
 
                rhythmdb_entry_unref (model->priv->db, update->entry);
507
 
                break;
508
 
        case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE:
509
 
                break;
510
 
        }
511
 
 
512
 
        g_free (update);
 
471
        rhythmdb_entry_unref (db, entry);
513
472
}
514
473
 
515
474
static void
516
475
rhythmdb_query_model_finalize (GObject *object)
517
476
{
518
477
        RhythmDBQueryModel *model;
519
 
        struct RhythmDBQueryModelUpdate *update;
520
478
 
521
479
        g_return_if_fail (object != NULL);
522
480
        g_return_if_fail (RHYTHMDB_IS_QUERY_MODEL (object));
527
485
 
528
486
        rb_debug ("finalizing query model");
529
487
 
530
 
        g_source_remove (model->priv->model_poll_id);
 
488
        if (model->priv->sort_user_data &&
 
489
            model->priv->sort_destroy_notify)
 
490
                model->priv->sort_destroy_notify (model->priv->sort_user_data);
531
491
 
 
492
        g_hash_table_foreach (model->priv->reverse_map, (GHFunc) _unref_entry, model->priv->db);
 
493
        g_hash_table_foreach (model->priv->limited_reverse_map, (GHFunc) _unref_entry, model->priv->db);
 
494
                
532
495
        g_hash_table_destroy (model->priv->reverse_map);
533
496
        g_sequence_free (model->priv->entries);
 
497
        g_hash_table_destroy (model->priv->limited_reverse_map);
 
498
        g_sequence_free (model->priv->limited_entries);
534
499
 
535
500
        if (model->priv->query)
536
501
                rhythmdb_query_free (model->priv->query);
537
 
 
538
 
        while ((update = g_async_queue_try_pop (model->priv->pending_updates)) != NULL)
539
 
                rhythmdb_query_model_free_pending_update (model, update);
540
 
 
541
 
        g_async_queue_unref (model->priv->query_complete);
542
 
        g_async_queue_unref (model->priv->pending_updates);
 
502
        if (model->priv->original_query)
 
503
                rhythmdb_query_free (model->priv->original_query);
543
504
 
544
505
        g_free (model->priv);
545
506
 
546
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
 
507
        G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->finalize (object);
547
508
}
548
509
 
549
510
RhythmDBQueryModel *
569
530
}
570
531
 
571
532
void
572
 
rhythmdb_query_model_cancel (RhythmDBQueryModel *model)
573
 
{
574
 
        rb_debug ("cancelling query");
575
 
        g_async_queue_push (model->priv->query_complete, GINT_TO_POINTER (1));
576
 
}
577
 
 
578
 
void
579
533
rhythmdb_query_model_signal_complete (RhythmDBQueryModel *model)
580
534
{
581
535
        struct RhythmDBQueryModelUpdate *update;
582
536
 
583
537
        update = g_new0 (struct RhythmDBQueryModelUpdate, 1);
584
538
        update->type = RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE;
 
539
        update->model = model;
 
540
        g_object_ref (G_OBJECT (model));
585
541
 
586
 
        g_async_queue_push (model->priv->pending_updates, update);
 
542
        rhythmdb_query_model_process_update (update);
587
543
}
588
544
 
589
545
void
592
548
        model->priv->connected = connected;
593
549
}
594
550
 
595
 
void
596
 
rhythmdb_query_model_finish_complete (RhythmDBQueryModel *model)
597
 
{
598
 
        if (!rb_thread_helpers_in_main_thread ())
599
 
                g_async_queue_pop (model->priv->query_complete);
600
 
}
601
 
 
602
551
gboolean
603
552
rhythmdb_query_model_has_pending_changes (RhythmDBQueryModel *model)
604
553
{
605
 
        return g_async_queue_length (model->priv->pending_updates) > 0;
606
 
}
607
 
 
608
 
static inline GSequencePtr
609
 
choose_sequence_element (GSequence *seq)
610
 
{
611
 
        return g_sequence_get_ptr_at_pos (seq, 0);
612
 
}
613
 
 
614
 
/* Threading: should be entered via database thread, with db lock held
615
 
 */
 
554
        return g_atomic_int_get (&model->priv->pending_update_count) > 0;
 
555
}
 
556
 
616
557
static void
617
558
rhythmdb_query_model_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
618
559
                                     RhythmDBQueryModel *model)
619
560
{
620
 
        if (G_LIKELY (model->priv->query)) {
621
 
                if (model->priv->max_count > 0
622
 
                    && g_hash_table_size (model->priv->reverse_map) >= model->priv->max_count)
 
561
        if (model->priv->query) {
 
562
                if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
623
563
                        return;
624
 
                /* Check size later */
625
 
                
 
564
 
626
565
                if (rhythmdb_evaluate_query (db, model->priv->query, entry)) {
627
 
                        rhythmdb_query_model_add_entry (model, entry);
 
566
                        rhythmdb_query_model_do_insert (model, entry);
628
567
                }
629
568
        }
630
569
}
631
570
 
632
571
static void
633
572
rhythmdb_query_model_entry_changed_cb (RhythmDB *db, RhythmDBEntry *entry,
634
 
                                       RhythmDBPropType prop, const GValue *old,
635
 
                                       const GValue *new, RhythmDBQueryModel *model)
 
573
                                       GSList *changes, RhythmDBQueryModel *model)
636
574
{
637
 
        if (!model->priv->connected) {
638
 
                return;
639
 
        }
640
 
 
641
 
        if (g_hash_table_lookup (model->priv->reverse_map, entry) != NULL) {
642
 
                struct RhythmDBQueryModelUpdate *update;
643
 
 
644
 
                if (model->priv->query &&
645
 
                    !rhythmdb_evaluate_query (db, model->priv->query, entry)) {
646
 
                        rhythmdb_query_model_remove_entry (model, entry);
647
 
                        return;
648
 
                }
649
 
 
650
 
                rb_debug ("queueing entry change");
651
 
 
652
 
                update = g_new0 (struct RhythmDBQueryModelUpdate, 1);
653
 
                update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROW_CHANGED;
654
 
                update->entry = entry;
655
 
                update->prop = prop;
656
 
                g_value_init (&update->old, G_VALUE_TYPE (old));
657
 
                g_value_copy (old, &update->old);
658
 
                g_value_init (&update->new, G_VALUE_TYPE (new));
659
 
                g_value_copy (new, &update->new);
660
 
        
661
 
                /* Called with a locked database */
662
 
                rhythmdb_entry_ref_unlocked (model->priv->db, entry);
663
 
 
664
 
                g_async_queue_push (model->priv->pending_updates, update);
665
 
        } else {
666
 
                /* the changed entry may now satisfy the query so we test it */
667
 
                rhythmdb_query_model_entry_added_cb (db, entry, model);
668
 
        }
 
575
        gboolean hidden = FALSE;
 
576
        GSList *t;
 
577
 
 
578
        if (!model->priv->connected)
 
579
                return;
 
580
 
 
581
 
 
582
        hidden = rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN);
 
583
 
 
584
        if (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) {
 
585
                if (hidden == FALSE) {
 
586
                        /* the changed entry may now satisfy the query 
 
587
                         * so we test it */
 
588
                        rhythmdb_query_model_entry_added_cb (db, entry, model);
 
589
                }
 
590
                return;
 
591
        }
 
592
 
 
593
        if (hidden) {
 
594
                rhythmdb_query_model_remove_entry (model, entry);
 
595
                return;
 
596
        }
 
597
 
 
598
        /* emit separate change signals for each property */
 
599
        for (t = changes; t; t = t->next) {
 
600
                RhythmDBEntryChange *change = t->data;
 
601
                g_signal_emit (G_OBJECT (model),
 
602
                               rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
 
603
                               entry, change->prop, &change->old, &change->new);
 
604
 
 
605
                if (change->prop == RHYTHMDB_PROP_DURATION) {
 
606
                        model->priv->total_duration -= g_value_get_ulong (&change->old);
 
607
                        model->priv->total_duration += g_value_get_ulong (&change->new);
 
608
                } else if (change->prop == RHYTHMDB_PROP_FILE_SIZE) {
 
609
                        model->priv->total_size -= g_value_get_uint64 (&change->old);
 
610
                        model->priv->total_size += g_value_get_uint64 (&change->new);
 
611
                }
 
612
        }
 
613
 
 
614
        if (model->priv->query &&
 
615
            !rhythmdb_evaluate_query (db, model->priv->query, entry)) {
 
616
                rhythmdb_query_model_filter_out_entry (model, entry);
 
617
                return;
 
618
        }
 
619
 
 
620
        /* it may have moved, so we can't just emit a changed entry */
 
621
        rhythmdb_query_model_do_reorder (model, entry);
669
622
}
670
623
 
671
624
static void
672
625
rhythmdb_query_model_entry_deleted_cb (RhythmDB *db, RhythmDBEntry *entry,
673
626
                                       RhythmDBQueryModel *model)
674
627
{
675
 
        rhythmdb_query_model_remove_entry (model, entry);
676
 
}
677
 
 
678
 
/* Threading: Called from the database context, holding a db write lock
 
628
        
 
629
        if (g_hash_table_lookup (model->priv->reverse_map, entry) ||
 
630
            g_hash_table_lookup (model->priv->limited_reverse_map, entry))
 
631
                rhythmdb_query_model_remove_entry (model, entry);
 
632
}
 
633
 
 
634
static void 
 
635
rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update)
 
636
{
 
637
        g_atomic_int_inc (&update->model->priv->pending_update_count);
 
638
        if (rb_is_main_thread ())
 
639
                idle_process_update (update);
 
640
        else
 
641
                g_idle_add ((GSourceFunc) idle_process_update, update);
 
642
}
 
643
 
 
644
gboolean
 
645
idle_process_update (struct RhythmDBQueryModelUpdate *update)
 
646
{
 
647
        switch (update->type) {
 
648
        case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED:
 
649
        {
 
650
                guint i;
 
651
                rb_debug ("inserting %d rows", update->entries->len);
 
652
                for (i = 0; i < update->entries->len; i++ ) {
 
653
                        RhythmDBEntry *entry = g_ptr_array_index (update->entries, i);
 
654
 
 
655
                        if (!rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
 
656
                                rhythmdb_query_model_do_insert (update->model, entry);
 
657
                        
 
658
                        rhythmdb_entry_unref (update->model->priv->db, entry);
 
659
                }
 
660
                g_ptr_array_free (update->entries, TRUE);
 
661
                break;
 
662
        }
 
663
        case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE:
 
664
                g_signal_emit (G_OBJECT (update->model), rhythmdb_query_model_signals[COMPLETE], 0);
 
665
                break;
 
666
        }
 
667
 
 
668
        g_atomic_int_add (&update->model->priv->pending_update_count, -1);
 
669
        g_object_unref (G_OBJECT (update->model));
 
670
        g_free (update);
 
671
        return FALSE;
 
672
}
 
673
 
 
674
/* Threading: Called from the database query thread for async queries,
 
675
 *  from the main thread for synchronous queries.
679
676
 */
680
677
void
681
678
rhythmdb_query_model_add_entries (RhythmDBQueryModel *model, GPtrArray *entries)
683
680
        struct RhythmDBQueryModelUpdate *update;
684
681
        guint i;
685
682
 
686
 
        if (model->priv->max_count > 0
687
 
            && g_hash_table_size (model->priv->reverse_map) >= model->priv->max_count) {
688
 
                g_ptr_array_free (entries, TRUE);
689
 
                return;
690
 
        }
691
 
        /* Check size later */
 
683
        rb_debug ("adding %d entries", entries->len);
692
684
 
693
685
        update = g_new (struct RhythmDBQueryModelUpdate, 1);
694
686
        update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED;
695
687
        update->entries = entries;
 
688
        update->model = model;
 
689
        g_object_ref (G_OBJECT (model));
696
690
 
697
 
        /* Called with a locked database */
698
691
        for (i = 0; i < update->entries->len; i++)
699
 
                rhythmdb_entry_ref_unlocked (model->priv->db,
700
 
                                             g_ptr_array_index (update->entries, i));
 
692
                rhythmdb_entry_ref (model->priv->db, g_ptr_array_index (update->entries, i));
701
693
 
702
 
        g_async_queue_push (model->priv->pending_updates, update);
 
694
        rhythmdb_query_model_process_update (update);
703
695
}
704
696
 
705
697
void
711
703
        rhythmdb_query_model_add_entries (model, entries);
712
704
}
713
705
 
714
 
 
715
 
/* Threading: Called from the database context, holding a db write lock
716
 
 */
717
 
void
718
 
rhythmdb_query_model_remove_entry (RhythmDBQueryModel *model, RhythmDBEntry *entry)
719
 
{
720
 
        struct RhythmDBQueryModelUpdate *update;
721
 
        
722
 
        update = g_new (struct RhythmDBQueryModelUpdate, 1);
723
 
        update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROW_DELETED;
724
 
        update->entry = entry;
725
 
        
726
 
        /* Called with a locked database */
727
 
        rhythmdb_entry_ref_unlocked (model->priv->db, entry);
728
 
        
729
 
        g_async_queue_push (model->priv->pending_updates, update);
730
 
}
731
 
 
732
706
GnomeVFSFileSize
733
707
rhythmdb_query_model_get_size (RhythmDBQueryModel *model)
734
708
{
741
715
        return model->priv->total_duration;
742
716
}
743
717
 
744
 
static int
745
 
compare_times (GTimeVal *a, GTimeVal *b)
746
 
{
747
 
        if (a->tv_sec == b->tv_sec)
748
 
                /* It's quite unlikely that microseconds are equal,
749
 
                 * so just ignore that case, we don't need a lot
750
 
                 * of precision.
751
 
                 */
752
 
                return a->tv_usec > b->tv_usec ? 1 : -1;
753
 
        else if (a->tv_sec > b->tv_sec)
754
 
                return 1;
755
 
        else
756
 
                return -1;
757
 
}
758
 
 
759
718
static void
760
 
rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
761
 
                                RhythmDBEntry *entry)
 
719
rhythmdb_query_model_insert_into_main_list (RhythmDBQueryModel *model, RhythmDBEntry *entry)
762
720
{
763
721
        GSequencePtr ptr;
764
 
        guint64 size;
765
 
        long duration;
766
 
        GtkTreePath *path;
767
 
        GtkTreeIter iter;
768
 
 
769
 
        /* we check again if the entry already exists in the hash table */
770
 
        if (g_hash_table_lookup (model->priv->reverse_map, entry) != NULL)
771
 
                return;
772
 
 
773
 
        if (model->priv->max_count > 0
774
 
            && g_hash_table_size (model->priv->reverse_map) >= model->priv->max_count)
775
 
                return;
776
 
 
777
 
        rhythmdb_read_lock (model->priv->db);
778
 
        size = rhythmdb_entry_get_uint64 (model->priv->db, entry,
779
 
                                          RHYTHMDB_PROP_FILE_SIZE);
780
 
        duration = rhythmdb_entry_get_long (model->priv->db,
781
 
                                            entry, RHYTHMDB_PROP_DURATION);
782
 
        rhythmdb_read_unlock (model->priv->db);
783
 
 
784
 
        if (model->priv->max_size > 0
785
 
            && (model->priv->total_size + size >= model->priv->max_size))
786
 
                return;
787
722
 
788
723
        rhythmdb_entry_ref (model->priv->db, entry);
789
724
 
790
725
        if (model->priv->sort_func) {
791
 
                rhythmdb_read_lock (model->priv->db);
792
726
                ptr = g_sequence_insert_sorted (model->priv->entries, entry,
793
727
                                                model->priv->sort_func,
794
728
                                                model->priv->sort_user_data);
795
 
                rhythmdb_read_unlock (model->priv->db);
796
729
        } else {
797
730
                ptr = g_sequence_get_end_ptr (model->priv->entries);
798
731
                g_sequence_insert (ptr, entry);
801
734
 
802
735
        g_hash_table_insert (model->priv->reverse_map, entry, ptr);
803
736
 
804
 
        model->priv->total_duration += duration;
805
 
        model->priv->total_size += size;
806
 
        
807
 
        iter.stamp = model->priv->stamp;
808
 
        iter.user_data = ptr;
809
 
        path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
810
 
                                              &iter);
811
 
        gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
812
 
                                     path, &iter);
813
 
        gtk_tree_path_free (path);
 
737
        model->priv->total_duration += rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
 
738
        model->priv->total_size += rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
814
739
}
815
740
 
816
741
static void
817
 
rhythmdb_query_model_do_delete (RhythmDBQueryModel *model,
818
 
                                RhythmDBEntry *entry)
 
742
rhythmdb_query_model_insert_into_limited_list (RhythmDBQueryModel *model, RhythmDBEntry *entry)
819
743
{
820
 
        GtkTreePath *path;
821
744
        GSequencePtr ptr;
 
745
 
 
746
        rhythmdb_entry_ref (model->priv->db, entry);
 
747
 
 
748
        if (model->priv->sort_func) {
 
749
                ptr = g_sequence_insert_sorted (model->priv->limited_entries, entry,
 
750
                                                model->priv->sort_func,
 
751
                                                model->priv->sort_user_data);
 
752
        } else {
 
753
                ptr = g_sequence_get_end_ptr (model->priv->limited_entries);
 
754
                g_sequence_insert (ptr, entry);
 
755
                ptr = g_sequence_ptr_prev (ptr);
 
756
        }
 
757
 
 
758
        g_hash_table_insert (model->priv->limited_reverse_map, entry, ptr);
 
759
}
 
760
 
 
761
static void
 
762
rhythmdb_query_model_remove_from_main_list (RhythmDBQueryModel *model, RhythmDBEntry *entry)
 
763
{
 
764
        GSequencePtr ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
822
765
        int index;
823
 
 
824
 
        ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
825
 
 
826
 
        if (ptr == NULL)
827
 
                return;
 
766
        GtkTreePath *path;
828
767
 
829
768
        index = g_sequence_ptr_get_position (ptr);
830
769
        
831
770
        path = gtk_tree_path_new ();
832
771
        gtk_tree_path_append_index (path, index);
833
 
        rb_debug ("emitting row deleted");
 
772
 
834
773
        gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
835
774
        gtk_tree_path_free (path);
836
775
        
837
 
        rhythmdb_read_lock (model->priv->db);
838
 
        model->priv->total_duration -= rhythmdb_entry_get_long (model->priv->db, entry,
839
 
                                                                RHYTHMDB_PROP_DURATION);
840
 
        model->priv->total_size -= rhythmdb_entry_get_uint64 (model->priv->db, entry,
841
 
                                                              RHYTHMDB_PROP_FILE_SIZE);
842
 
        rhythmdb_read_unlock (model->priv->db);
 
776
        model->priv->total_duration -= entry->duration;
 
777
        model->priv->total_size -= entry->file_size;
843
778
 
844
779
        g_sequence_remove (ptr);
845
780
        g_hash_table_remove (model->priv->reverse_map, entry);
847
782
        rhythmdb_entry_unref (model->priv->db, entry);
848
783
}
849
784
 
850
 
static gboolean
851
 
idle_poll_model (RhythmDBQueryModel *model)
 
785
static void
 
786
rhythmdb_query_model_remove_from_limited_list (RhythmDBQueryModel *model, RhythmDBEntry *entry)
852
787
{
853
 
        gboolean did_sync;
854
 
        GTimeVal timeout;
855
 
 
856
 
        g_get_current_time (&timeout);
857
 
        g_time_val_add (&timeout, G_USEC_PER_SEC*0.75);
858
 
 
859
 
        GDK_THREADS_ENTER ();
860
 
 
861
 
        did_sync = rhythmdb_query_model_poll (model, &timeout);
862
 
 
863
 
        if (did_sync)
864
 
                model->priv->model_poll_id =
865
 
                        g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) idle_poll_model, model, NULL);
866
 
        else
867
 
                model->priv->model_poll_id =
868
 
                        g_timeout_add (300, (GSourceFunc) idle_poll_model, model);
869
 
 
870
 
        GDK_THREADS_LEAVE ();
871
 
 
872
 
        return FALSE;
 
788
        GSequencePtr ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
 
789
 
 
790
        g_sequence_remove (ptr);
 
791
        g_hash_table_remove (model->priv->limited_reverse_map, entry);
 
792
        rhythmdb_entry_unref (model->priv->db, entry);
873
793
}
874
 
        
875
 
/* Threading: main thread only, should hold GDK lock
876
 
 */
877
 
gboolean
878
 
rhythmdb_query_model_poll (RhythmDBQueryModel *model, GTimeVal *timeout)
 
794
 
 
795
static void
 
796
rhythmdb_query_model_update_limited_entries (RhythmDBQueryModel *model)
879
797
{
880
 
        GList *processed = NULL, *tem;
881
 
        GTimeVal now;
882
 
        struct RhythmDBQueryModelUpdate *update;
883
 
        guint count = 0;
884
 
 
885
 
        if (G_UNLIKELY (model->priv->cancelled))
886
 
                return FALSE;
887
 
 
888
 
        while ((update = g_async_queue_try_pop (model->priv->pending_updates)) != NULL) {
 
798
        RhythmDBEntry *entry;
 
799
        GSequencePtr ptr;
 
800
 
 
801
        /* make it fit inside the limits */
 
802
        while ((model->priv->max_count > 0 && g_hash_table_size (model->priv->reverse_map) > model->priv->max_count) ||
 
803
              (model->priv->max_size > 0 && model->priv->total_size > model->priv->max_size) ||
 
804
              (model->priv->max_time > 0 && model->priv->total_duration > model->priv->max_time)) {
 
805
 
 
806
                ptr = g_sequence_ptr_prev (g_sequence_get_end_ptr (model->priv->entries));
 
807
                entry = (RhythmDBEntry*) g_sequence_ptr_get_data (ptr);
 
808
 
 
809
                rb_debug ("query: moving entry to limited list");
 
810
                rhythmdb_query_model_remove_from_main_list (model, entry);
 
811
                rhythmdb_query_model_insert_into_limited_list (model, entry);
 
812
        }
 
813
 
 
814
        /* move entries that were previously limited, back to the main list */
 
815
        while (TRUE) {
 
816
                int size;
 
817
                int duration;
889
818
                GtkTreePath *path;
890
819
                GtkTreeIter iter;
891
 
                GSequencePtr ptr;
892
 
 
893
 
                if (update == NULL)
894
 
                        break;
895
 
 
 
820
 
 
821
                ptr = g_sequence_get_begin_ptr (model->priv->limited_entries);
 
822
                if (!ptr || ptr == g_sequence_get_end_ptr (model->priv->limited_entries))
 
823
                        break;
 
824
                entry = (RhythmDBEntry*) g_sequence_ptr_get_data (ptr);
 
825
                if (!entry)
 
826
                        break;
 
827
 
 
828
                size = entry->file_size;
 
829
                duration = entry->duration;
 
830
 
 
831
                if ((model->priv->max_count > 0 && (g_hash_table_size (model->priv->reverse_map) + 1) > model->priv->max_count) ||
 
832
                    (model->priv->max_size > 0 && model->priv->total_size + size > model->priv->max_size) ||
 
833
                    (model->priv->max_time > 0 && model->priv->total_duration + duration > model->priv->max_time))
 
834
                        break;
 
835
 
 
836
                rb_debug ("query: moving entry from limited list");
 
837
                rhythmdb_query_model_remove_from_limited_list (model, entry);
 
838
                rhythmdb_query_model_insert_into_main_list (model, entry);
 
839
 
 
840
                ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
896
841
                iter.stamp = model->priv->stamp;
897
 
 
898
 
                switch (update->type) {
899
 
                case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED:
900
 
                {
901
 
                        guint i;
902
 
                        rb_debug ("inserting %d rows", update->entries->len);
903
 
                        for (i = 0; i < update->entries->len; i++)
904
 
                                rhythmdb_query_model_do_insert (model, g_ptr_array_index (update->entries, i));
905
 
                        break;
906
 
                }
907
 
                case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_CHANGED:
908
 
                {
909
 
                        ptr = g_hash_table_lookup (model->priv->reverse_map,
910
 
                                                   update->entry);
911
 
 
912
 
                        if (ptr == NULL)
913
 
                                break;
914
 
 
915
 
                        g_signal_emit (G_OBJECT (model),
916
 
                                       rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
917
 
                                       update->entry, update->prop, &update->old, &update->new);
918
 
 
919
 
                        iter.user_data = ptr;
920
 
                        path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
921
 
                                                              &iter);
922
 
                        rb_debug ("emitting row changed");
923
 
                        gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
924
 
                                                    path, &iter);
925
 
                        gtk_tree_path_free (path);
926
 
                        break;
927
 
                }
928
 
                case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_DELETED:
929
 
                {
930
 
                        rhythmdb_query_model_do_delete (model, update->entry);
931
 
                        break;
932
 
                }
933
 
                case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE:
934
 
                {
935
 
                        g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[COMPLETE], 0);
936
 
                        g_async_queue_push (model->priv->query_complete, GINT_TO_POINTER (1));
937
 
                        break;
938
 
                }
939
 
                }
940
 
 
941
 
                processed = g_list_prepend (processed, update);
942
 
 
943
 
                count++;
944
 
                if (timeout && count / 8 > 0) {
945
 
                        /* Do this here at the bottom, so we do at least one update. */
946
 
                        g_get_current_time (&now);
947
 
                        if (compare_times (timeout,&now) < 0)
948
 
                                break;
949
 
                }
950
 
        }
951
 
        
952
 
        for (tem = processed; tem; tem = tem->next)
953
 
                rhythmdb_query_model_free_pending_update (model, tem->data);
954
 
                
955
 
        g_list_free (processed);
956
 
        return processed != NULL;
 
842
                iter.user_data = ptr;
 
843
                path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
 
844
                                                      &iter);
 
845
                gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
 
846
                                             path, &iter);
 
847
                gtk_tree_path_free (path);
 
848
        }
 
849
}
 
850
 
 
851
 
 
852
static void
 
853
rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model, RhythmDBEntry *entry)
 
854
{
 
855
        GSequencePtr ptr;
 
856
        int old_pos, new_pos;
 
857
        gint *reorder_map;
 
858
        int length, i;
 
859
        GtkTreePath *path;
 
860
        GtkTreeIter iter;
 
861
 
 
862
        if (model->priv->sort_func == NULL)
 
863
                return;
 
864
 
 
865
        ptr = g_sequence_get_begin_ptr (model->priv->limited_entries);
 
866
 
 
867
        if (ptr != NULL && !g_sequence_ptr_is_end (ptr)) {
 
868
                RhythmDBEntry *first_limited = g_sequence_ptr_get_data (ptr);
 
869
                int cmp = (model->priv->sort_func) (entry, first_limited, model->priv->sort_user_data);
 
870
 
 
871
                if (cmp > 0) {
 
872
                        /* the entry belongs in the limited list, so we don't need a re-order */
 
873
                        rhythmdb_query_model_remove_entry (model, entry);
 
874
                        rhythmdb_query_model_do_insert (model, entry);
 
875
                        return;
 
876
                }
 
877
        }
 
878
 
 
879
        ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
 
880
        iter.stamp = model->priv->stamp;
 
881
        iter.user_data = ptr;
 
882
        path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
 
883
                                              &iter);
 
884
        gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
 
885
                                     path, &iter);
 
886
        gtk_tree_path_free (path);
 
887
 
 
888
        /* it may have moved, check for a re-order */
 
889
        length = g_sequence_get_length (model->priv->entries);
 
890
 
 
891
        g_hash_table_remove (model->priv->reverse_map, entry);
 
892
        old_pos = g_sequence_ptr_get_position (ptr);
 
893
        g_sequence_remove (ptr);
 
894
 
 
895
        ptr = g_sequence_insert_sorted (model->priv->entries, entry,
 
896
                                        model->priv->sort_func,
 
897
                                        model->priv->sort_user_data);
 
898
        new_pos = g_sequence_ptr_get_position (ptr);
 
899
        g_hash_table_insert (model->priv->reverse_map, entry, ptr);
 
900
 
 
901
        if (new_pos == old_pos) {
 
902
                /* it hasn't moved, don't emit a re-order */
 
903
                return;
 
904
        }
 
905
 
 
906
        reorder_map = malloc (length * sizeof(gint));
 
907
 
 
908
        if (new_pos > old_pos) {
 
909
                /* it has mover further down the list */
 
910
                for (i = 0; i < old_pos; i++)
 
911
                        reorder_map[i] = i;
 
912
                for (i = old_pos; i < new_pos; i++)
 
913
                        reorder_map[i] = i + 1;
 
914
                reorder_map[new_pos] = old_pos;
 
915
                for (i = new_pos + 1; i < length; i++)
 
916
                        reorder_map[i] = i;
 
917
        } else {
 
918
                /* it has moved up the list */
 
919
                for (i = 0; i < new_pos; i++)
 
920
                        reorder_map[i] = i;
 
921
                reorder_map[new_pos] = old_pos;
 
922
                for (i = new_pos + 1; i < old_pos + 1; i++)
 
923
                        reorder_map[i] = i - 1;
 
924
                for (i = old_pos + 1; i < length; i++)
 
925
                        reorder_map[i] = i;
 
926
        }
 
927
 
 
928
        gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
 
929
        path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
 
930
 
 
931
        gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
 
932
                                       path, &iter,
 
933
                                       reorder_map);
 
934
        gtk_tree_path_free (path);
 
935
        free (reorder_map);
 
936
}
 
937
 
 
938
static void
 
939
rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
 
940
                                RhythmDBEntry *entry)
 
941
{
 
942
        GSequencePtr ptr;
 
943
        GtkTreePath *path;
 
944
        GtkTreeIter iter;
 
945
 
 
946
        g_assert (!rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN));
 
947
 
 
948
        /* we check again if the entry already exists in the hash table */
 
949
        if (g_hash_table_lookup (model->priv->reverse_map, entry) != NULL)
 
950
                return;
 
951
        ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
 
952
        if (ptr != NULL)
 
953
                rhythmdb_query_model_remove_from_limited_list (model, entry);
 
954
 
 
955
        rhythmdb_query_model_insert_into_main_list (model, entry);
 
956
 
 
957
        /* it was added to the main list, send out the inserted signal */
 
958
        ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
 
959
        iter.stamp = model->priv->stamp;
 
960
        iter.user_data = ptr;
 
961
        path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
 
962
                                              &iter);
 
963
        gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
 
964
                                     path, &iter);
 
965
        gtk_tree_path_free (path);
 
966
 
 
967
        rhythmdb_query_model_update_limited_entries (model);
 
968
}
 
969
 
 
970
static void
 
971
rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
 
972
                                       RhythmDBEntry *entry)
 
973
{
 
974
        GSequencePtr ptr;
 
975
 
 
976
        ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
 
977
        if (ptr != NULL) {
 
978
                rhythmdb_query_model_remove_from_main_list (model, entry);
 
979
                rhythmdb_query_model_update_limited_entries (model);
 
980
                return;
 
981
        }
 
982
 
 
983
        ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
 
984
        if (ptr != NULL) {
 
985
                rhythmdb_query_model_remove_from_limited_list (model, entry);
 
986
                rhythmdb_query_model_update_limited_entries (model);
 
987
                return;
 
988
        }
 
989
}
 
990
                                
 
991
 
 
992
gboolean
 
993
rhythmdb_query_model_remove_entry (RhythmDBQueryModel *model, 
 
994
                                   RhythmDBEntry *entry)
 
995
{
 
996
        gboolean present = (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) ||
 
997
                            (g_hash_table_lookup (model->priv->limited_reverse_map, entry) == NULL);
 
998
        g_return_val_if_fail (present, FALSE);
 
999
 
 
1000
        /* emit entry-removed, so listeners know the
 
1001
         * entry has actually been removed, rather than filtered
 
1002
         * out.
 
1003
         */
 
1004
        g_signal_emit (G_OBJECT (model),
 
1005
                       rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
 
1006
                       entry);
 
1007
        rhythmdb_query_model_filter_out_entry (model, entry);
 
1008
 
 
1009
        return TRUE;
957
1010
}
958
1011
 
959
1012
gboolean
1044
1097
 
1045
1098
                        entry = g_sequence_ptr_get_data (iter.user_data);
1046
1099
 
1047
 
                        rhythmdb_read_lock (model->priv->db);
1048
 
 
1049
 
                        location = rhythmdb_entry_get_string (model->priv->db, entry, RHYTHMDB_PROP_LOCATION);
 
1100
                        location = entry->location;
1050
1101
                        g_string_append (data, location);
1051
1102
 
1052
 
                        rhythmdb_read_unlock (model->priv->db);
1053
 
 
1054
1103
                        if (tem->next)
1055
1104
                                g_string_append (data, "\r\n");
1056
1105
                }
1057
1106
 
1058
1107
                gtk_selection_data_set (selection_data,
1059
1108
                                        selection_data->target,
1060
 
                                        8, data->str,
1061
 
                                        strlen (data->str));
 
1109
                                        8, (guchar *) data->str,
 
1110
                                        data->len);
1062
1111
 
1063
1112
                g_string_free (data, TRUE);
1064
1113
 
1089
1138
                RhythmDBEntry *entry;
1090
1139
                int i = 0;
1091
1140
 
1092
 
                strv = g_strsplit (selection_data->data, "\r\n", -1);
 
1141
                strv = g_strsplit ((char *) selection_data->data, "\r\n", -1);
1093
1142
 
1094
1143
                if (dest == NULL || !rhythmdb_query_model_get_iter (GTK_TREE_MODEL (model), &iter, dest))
1095
1144
                        ptr = g_sequence_get_end_ptr (model->priv->entries);
1104
1153
                        GtkTreeIter tem_iter;
1105
1154
                        GtkTreePath *tem_path;
1106
1155
 
1107
 
                        rhythmdb_read_lock (model->priv->db);
1108
1156
 
1109
1157
                        entry = rhythmdb_entry_lookup_by_location (model->priv->db,
1110
1158
                                                                   strv[i]);
1111
 
                        rhythmdb_read_unlock (model->priv->db);
1112
 
 
1113
 
                        if (entry == NULL)
1114
 
                                rhythmdb_add_uri_async (model->priv->db, strv[i]);
1115
 
                        else {
 
1159
 
 
1160
                        if (entry == NULL) {
 
1161
                                rhythmdb_add_uri (model->priv->db, strv[i]);
 
1162
 
 
1163
                                g_signal_emit (G_OBJECT (model),
 
1164
                                               rhythmdb_query_model_signals[NON_ENTRY_DROPPED],
 
1165
                                               0, g_strdup (strv[i]));
 
1166
                        } else {
1116
1167
                                GSequencePtr old_ptr = g_hash_table_lookup (model->priv->reverse_map,
1117
1168
                                                                            entry);
1118
1169
 
1220
1271
{
1221
1272
        RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1222
1273
        guint index;
1223
 
        RhythmDBEntry *ret;
 
1274
        GSequencePtr ptr;
1224
1275
 
1225
1276
        index = gtk_tree_path_get_indices (path)[0];
1226
1277
 
1227
1278
        if (index >= g_sequence_get_length (model->priv->entries))
1228
1279
                return FALSE;
1229
1280
 
1230
 
        ret = g_sequence_get_ptr_at_pos (model->priv->entries, index);
1231
 
        g_assert (ret);
 
1281
        ptr = g_sequence_get_ptr_at_pos (model->priv->entries, index);
 
1282
        g_assert (ptr);
1232
1283
 
1233
1284
        iter->stamp = model->priv->stamp;
1234
 
        iter->user_data = ret;
 
1285
        iter->user_data = ptr;
1235
1286
 
1236
1287
        return TRUE;
1237
1288
}