Updated Finnish translation
[rhythmbox.git] / rhythmdb / rhythmdb-query-model.c
blob2141b8df9ccf5e5b9f0462f711ee99a4169a9178
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of RhythmDB query result GtkTreeModel
5 * Copyright (C) 2003 Colin Walters <walters@gnome.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "config.h"
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <math.h>
31 #include <gtk/gtktreednd.h>
33 #include "rhythmdb-query-model.h"
34 #include "rb-debug.h"
35 #include "gsequence.h"
36 #include "rb-tree-dnd.h"
37 #include "rb-marshal.h"
38 #include "rb-util.h"
40 struct ReverseSortData
42 GCompareDataFunc func;
43 gpointer data;
46 static void rhythmdb_query_model_query_results_init (RhythmDBQueryResultsIface *iface);
47 static void rhythmdb_query_model_tree_model_init (GtkTreeModelIface *iface);
48 static void rhythmdb_query_model_drag_source_init (RbTreeDragSourceIface *iface);
49 static void rhythmdb_query_model_drag_dest_init (RbTreeDragDestIface *iface);
51 G_DEFINE_TYPE_WITH_CODE(RhythmDBQueryModel, rhythmdb_query_model, G_TYPE_OBJECT,
52 G_IMPLEMENT_INTERFACE(RHYTHMDB_TYPE_QUERY_RESULTS,
53 rhythmdb_query_model_query_results_init)
54 G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
55 rhythmdb_query_model_tree_model_init)
56 G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_SOURCE,
57 rhythmdb_query_model_drag_source_init)
58 G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_DEST,
59 rhythmdb_query_model_drag_dest_init))
61 static void rhythmdb_query_model_init (RhythmDBQueryModel *shell_player);
62 static GObject *rhythmdb_query_model_constructor (GType type, guint n_construct_properties,
63 GObjectConstructParam *construct_properties);
64 static void rhythmdb_query_model_dispose (GObject *object);
65 static void rhythmdb_query_model_finalize (GObject *object);
66 static void rhythmdb_query_model_set_property (GObject *object,
67 guint prop_id,
68 const GValue *value,
69 GParamSpec *pspec);
70 static void rhythmdb_query_model_get_property (GObject *object,
71 guint prop_id,
72 GValue *value,
73 GParamSpec *pspec);
74 static void rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
75 RhythmDBEntry *entry,
76 gint index);
77 static void rhythmdb_query_model_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
78 RhythmDBQueryModel *model);
79 static void rhythmdb_query_model_entry_changed_cb (RhythmDB *db, RhythmDBEntry *entry,
80 GSList *changes, RhythmDBQueryModel *model);
81 static void rhythmdb_query_model_entry_deleted_cb (RhythmDB *db, RhythmDBEntry *entry,
82 RhythmDBQueryModel *model);
84 static void rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
85 RhythmDBEntry *entry);
86 static gboolean rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model, RhythmDBEntry *entry);
87 static gboolean rhythmdb_query_model_emit_reorder (RhythmDBQueryModel *model, gint old_pos, gint new_pos);
88 static gboolean rhythmdb_query_model_drag_data_get (RbTreeDragSource *dragsource,
89 GList *paths,
90 GtkSelectionData *selection_data);
91 static gboolean rhythmdb_query_model_drag_data_delete (RbTreeDragSource *dragsource,
92 GList *paths);
93 static gboolean rhythmdb_query_model_row_draggable (RbTreeDragSource *dragsource,
94 GList *paths);
95 static gboolean rhythmdb_query_model_drag_data_received (RbTreeDragDest *drag_dest,
96 GtkTreePath *dest,
97 GtkTreeViewDropPosition pos,
98 GtkSelectionData *selection_data);
99 static gboolean rhythmdb_query_model_row_drop_possible (RbTreeDragDest *drag_dest,
100 GtkTreePath *dest,
101 GtkTreeViewDropPosition pos,
102 GtkSelectionData *selection_data);
103 static gboolean rhythmdb_query_model_row_drop_position (RbTreeDragDest *drag_dest,
104 GtkTreePath *dest_path,
105 GList *targets,
106 GtkTreeViewDropPosition *pos);
108 static void rhythmdb_query_model_set_query (RhythmDBQueryResults *results, GPtrArray *query);
109 static void rhythmdb_query_model_add_results (RhythmDBQueryResults *results, GPtrArray *entries);
110 static void rhythmdb_query_model_query_complete (RhythmDBQueryResults *results);
112 static GtkTreeModelFlags rhythmdb_query_model_get_flags (GtkTreeModel *model);
113 static gint rhythmdb_query_model_get_n_columns (GtkTreeModel *tree_model);
114 static GType rhythmdb_query_model_get_column_type (GtkTreeModel *tree_model, int index);
115 static gboolean rhythmdb_query_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter,
116 GtkTreePath *path);
117 static GtkTreePath * rhythmdb_query_model_get_path (GtkTreeModel *tree_model,
118 GtkTreeIter *iter);
119 static void rhythmdb_query_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter,
120 gint column, GValue *value);
121 static gboolean rhythmdb_query_model_iter_next (GtkTreeModel *tree_model,
122 GtkTreeIter *iter);
123 static gboolean rhythmdb_query_model_iter_children (GtkTreeModel *tree_model,
124 GtkTreeIter *iter,
125 GtkTreeIter *parent);
126 static gboolean rhythmdb_query_model_iter_has_child (GtkTreeModel *tree_model,
127 GtkTreeIter *iter);
128 static gint rhythmdb_query_model_iter_n_children (GtkTreeModel *tree_model,
129 GtkTreeIter *iter);
130 static gboolean rhythmdb_query_model_iter_nth_child (GtkTreeModel *tree_model,
131 GtkTreeIter *iter, GtkTreeIter *parent,
132 gint n);
133 static gboolean rhythmdb_query_model_iter_parent (GtkTreeModel *tree_model,
134 GtkTreeIter *iter,
135 GtkTreeIter *child);
137 static void rhythmdb_query_model_base_row_inserted (GtkTreeModel *base_model,
138 GtkTreePath *path,
139 GtkTreeIter *iter,
140 RhythmDBQueryModel *model);
141 static void rhythmdb_query_model_base_row_deleted (GtkTreeModel *base_model,
142 GtkTreePath *path,
143 RhythmDBQueryModel *model);
144 static void rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel *base_model,
145 const char *location,
146 int position,
147 RhythmDBQueryModel *model);
148 static void rhythmdb_query_model_base_complete (GtkTreeModel *base_model,
149 RhythmDBQueryModel *model);
150 static void rhythmdb_query_model_base_rows_reordered (GtkTreeModel *base_model,
151 GtkTreePath *arg1,
152 GtkTreeIter *arg2,
153 gint *order_map,
154 RhythmDBQueryModel *model);
155 static void rhythmdb_query_model_base_entry_removed (RhythmDBQueryModel *base_model,
156 RhythmDBEntry *entry,
157 RhythmDBQueryModel *model);
158 static void rhythmdb_query_model_base_entry_prop_changed (RhythmDBQueryModel *base_model,
159 RhythmDBEntry *entry,
160 RhythmDBPropType prop,
161 const GValue *old,
162 const GValue *new_value,
163 RhythmDBQueryModel *model);
164 static int rhythmdb_query_model_child_index_to_base_index (RhythmDBQueryModel *model, int index);
166 static gint _reverse_sorting_func (gpointer a, gpointer b, struct ReverseSortData *model);
167 static gboolean rhythmdb_query_model_within_limit (RhythmDBQueryModel *model,
168 RhythmDBEntry *entry);
169 static gboolean rhythmdb_query_model_reapply_query_cb (RhythmDBQueryModel *model);
171 struct RhythmDBQueryModelUpdate
173 RhythmDBQueryModel *model;
174 enum {
175 RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED,
176 RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX,
177 RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE,
178 } type;
180 union {
181 struct {
182 RhythmDBEntry *entry;
183 gint index;
184 } data;
185 GPtrArray *entries;
186 } entrydata;
189 static void rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update);
191 static void idle_process_update (struct RhythmDBQueryModelUpdate *update);
193 static const GtkTargetEntry rhythmdb_query_model_drag_types[] = { { "text/uri-list", 0, 0 },};
195 static GtkTargetList *rhythmdb_query_model_drag_target_list = NULL;
197 struct RhythmDBQueryModelPrivate
199 RhythmDB *db;
201 RhythmDBQueryModel *base_model;
203 GCompareDataFunc sort_func;
204 gpointer sort_data;
205 GDestroyNotify sort_data_destroy;
206 gboolean sort_reverse;
208 GPtrArray *query;
209 GPtrArray *original_query;
211 guint stamp;
213 RhythmDBQueryModelLimitType limit_type;
214 GValueArray *limit_value;
216 glong total_duration;
217 guint64 total_size;
219 GSequence *entries;
220 GHashTable *reverse_map;
221 GSequence *limited_entries;
222 GHashTable *limited_reverse_map;
224 gint pending_update_count;
226 gboolean reorder_drag_and_drop;
227 gboolean show_hidden;
229 gint query_reapply_timeout_id;
232 #define RHYTHMDB_QUERY_MODEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RHYTHMDB_TYPE_QUERY_MODEL, RhythmDBQueryModelPrivate))
234 enum
236 PROP_0,
237 PROP_RHYTHMDB,
238 PROP_QUERY,
239 PROP_SORT_FUNC,
240 PROP_SORT_DATA,
241 PROP_SORT_DATA_DESTROY,
242 PROP_SORT_REVERSE,
243 PROP_LIMIT_TYPE,
244 PROP_LIMIT_VALUE,
245 PROP_SHOW_HIDDEN,
246 PROP_BASE_MODEL,
249 enum
251 COMPLETE,
252 ENTRY_PROP_CHANGED,
253 ENTRY_REMOVED,
254 NON_ENTRY_DROPPED,
255 POST_ENTRY_DELETE,
256 LAST_SIGNAL
259 static guint rhythmdb_query_model_signals[LAST_SIGNAL] = { 0 };
261 static void
262 rhythmdb_query_model_class_init (RhythmDBQueryModelClass *klass)
264 GObjectClass *object_class = G_OBJECT_CLASS (klass);
266 if (!rhythmdb_query_model_drag_target_list)
267 rhythmdb_query_model_drag_target_list
268 = gtk_target_list_new (rhythmdb_query_model_drag_types,
269 G_N_ELEMENTS (rhythmdb_query_model_drag_types));
271 object_class->set_property = rhythmdb_query_model_set_property;
272 object_class->get_property = rhythmdb_query_model_get_property;
274 object_class->dispose = rhythmdb_query_model_dispose;
275 object_class->finalize = rhythmdb_query_model_finalize;
276 object_class->constructor = rhythmdb_query_model_constructor;
278 g_object_class_install_property (object_class,
279 PROP_RHYTHMDB,
280 g_param_spec_object ("db",
281 "RhythmDB",
282 "RhythmDB object",
283 RHYTHMDB_TYPE,
284 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
285 g_object_class_install_property (object_class,
286 PROP_QUERY,
287 g_param_spec_pointer ("query",
288 "Query",
289 "RhythmDBQuery",
290 G_PARAM_READWRITE));
291 g_object_class_install_property (object_class,
292 PROP_SORT_FUNC,
293 g_param_spec_pointer ("sort-func",
294 "SortFunc",
295 "Sort function",
296 G_PARAM_READWRITE));
297 g_object_class_install_property (object_class,
298 PROP_SORT_DATA,
299 g_param_spec_pointer ("sort-data",
300 "Sort data",
301 "Sort data",
302 G_PARAM_READWRITE));
303 g_object_class_install_property (object_class,
304 PROP_SORT_DATA_DESTROY,
305 g_param_spec_pointer ("sort-data-destroy",
306 "Sort data destroy",
307 "Sort data destroy function",
308 G_PARAM_READWRITE));
309 g_object_class_install_property (object_class,
310 PROP_SORT_REVERSE,
311 g_param_spec_boolean ("sort-reverse",
312 "sort-reverse",
313 "Reverse sort order flag",
314 FALSE,
315 G_PARAM_READWRITE));
316 g_object_class_install_property (object_class,
317 PROP_LIMIT_TYPE,
318 g_param_spec_enum ("limit-type",
319 "limit-type",
320 "type of limit",
321 RHYTHMDB_TYPE_QUERY_MODEL_LIMIT_TYPE,
322 RHYTHMDB_QUERY_MODEL_LIMIT_NONE,
323 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
324 g_object_class_install_property (object_class,
325 PROP_LIMIT_VALUE,
326 g_param_spec_boxed ("limit-value",
327 "limit-value",
328 "value of limit",
329 G_TYPE_VALUE_ARRAY,
330 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
331 g_object_class_install_property (object_class,
332 PROP_SHOW_HIDDEN,
333 g_param_spec_boolean ("show-hidden",
334 "show hidden",
335 "maximum time (seconds)",
336 FALSE,
337 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
338 g_object_class_install_property (object_class,
339 PROP_BASE_MODEL,
340 g_param_spec_object ("base-model",
341 "base-model",
342 "base RhythmDBQueryModel",
343 RHYTHMDB_TYPE_QUERY_MODEL,
344 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
346 rhythmdb_query_model_signals[ENTRY_PROP_CHANGED] =
347 g_signal_new ("entry-prop-changed",
348 RHYTHMDB_TYPE_QUERY_MODEL,
349 G_SIGNAL_RUN_LAST,
350 G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_prop_changed),
351 NULL, NULL,
352 rb_marshal_VOID__BOXED_INT_POINTER_POINTER,
353 G_TYPE_NONE,
354 4, RHYTHMDB_TYPE_ENTRY, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER);
355 rhythmdb_query_model_signals[ENTRY_REMOVED] =
356 g_signal_new ("entry-removed",
357 RHYTHMDB_TYPE_QUERY_MODEL,
358 G_SIGNAL_RUN_LAST,
359 G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_removed),
360 NULL, NULL,
361 g_cclosure_marshal_VOID__BOXED,
362 G_TYPE_NONE,
363 1, RHYTHMDB_TYPE_ENTRY);
364 rhythmdb_query_model_signals[NON_ENTRY_DROPPED] =
365 g_signal_new ("non-entry-dropped",
366 RHYTHMDB_TYPE_QUERY_MODEL,
367 G_SIGNAL_RUN_LAST,
368 G_STRUCT_OFFSET (RhythmDBQueryModelClass, non_entry_dropped),
369 NULL, NULL,
370 rb_marshal_VOID__POINTER_INT,
371 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
372 rhythmdb_query_model_signals[COMPLETE] =
373 g_signal_new ("complete",
374 RHYTHMDB_TYPE_QUERY_MODEL,
375 G_SIGNAL_RUN_LAST,
376 G_STRUCT_OFFSET (RhythmDBQueryModelClass, complete),
377 NULL, NULL,
378 g_cclosure_marshal_VOID__VOID,
379 G_TYPE_NONE, 0);
380 rhythmdb_query_model_signals[POST_ENTRY_DELETE] =
381 g_signal_new ("post-entry-delete",
382 RHYTHMDB_TYPE_QUERY_MODEL,
383 G_SIGNAL_RUN_LAST,
384 G_STRUCT_OFFSET (RhythmDBQueryModelClass, post_entry_delete),
385 NULL, NULL,
386 g_cclosure_marshal_VOID__BOXED,
387 G_TYPE_NONE,
388 1, RHYTHMDB_TYPE_ENTRY);
390 g_type_class_add_private (klass, sizeof (RhythmDBQueryModelPrivate));
393 static void
394 rhythmdb_query_model_query_results_init (RhythmDBQueryResultsIface *iface)
396 iface->set_query = rhythmdb_query_model_set_query;
397 iface->add_results = rhythmdb_query_model_add_results;
398 iface->query_complete = rhythmdb_query_model_query_complete;
401 static void
402 rhythmdb_query_model_tree_model_init (GtkTreeModelIface *iface)
404 iface->get_flags = rhythmdb_query_model_get_flags;
405 iface->get_n_columns = rhythmdb_query_model_get_n_columns;
406 iface->get_column_type = rhythmdb_query_model_get_column_type;
407 iface->get_iter = rhythmdb_query_model_get_iter;
408 iface->get_path = rhythmdb_query_model_get_path;
409 iface->get_value = rhythmdb_query_model_get_value;
410 iface->iter_next = rhythmdb_query_model_iter_next;
411 iface->iter_children = rhythmdb_query_model_iter_children;
412 iface->iter_has_child = rhythmdb_query_model_iter_has_child;
413 iface->iter_n_children = rhythmdb_query_model_iter_n_children;
414 iface->iter_nth_child = rhythmdb_query_model_iter_nth_child;
415 iface->iter_parent = rhythmdb_query_model_iter_parent;
418 static void
419 rhythmdb_query_model_drag_source_init (RbTreeDragSourceIface *iface)
421 iface->row_draggable = rhythmdb_query_model_row_draggable;
422 iface->drag_data_delete = rhythmdb_query_model_drag_data_delete;
423 iface->drag_data_get = rhythmdb_query_model_drag_data_get;
426 static void
427 rhythmdb_query_model_drag_dest_init (RbTreeDragDestIface *iface)
429 iface->drag_data_received = rhythmdb_query_model_drag_data_received;
430 iface->row_drop_possible = rhythmdb_query_model_row_drop_possible;
431 iface->row_drop_position = rhythmdb_query_model_row_drop_position;
434 static void
435 rhythmdb_query_model_set_query_internal (RhythmDBQueryModel *model,
436 GPtrArray *query)
438 if (query == model->priv->original_query)
439 return;
441 rhythmdb_query_free (model->priv->query);
442 rhythmdb_query_free (model->priv->original_query);
444 model->priv->query = rhythmdb_query_copy (query);
445 model->priv->original_query = rhythmdb_query_copy (model->priv->query);
446 rhythmdb_query_preprocess (model->priv->db, model->priv->query);
448 /* if the query contains time-relative criteria, re-run it periodically.
449 * currently it's just every half hour, but perhaps it could be smarter.
451 if (rhythmdb_query_is_time_relative (model->priv->db, model->priv->query)) {
452 if (model->priv->query_reapply_timeout_id == 0) {
453 model->priv->query_reapply_timeout_id =
454 g_timeout_add (1 * 60 * 1000,
455 (GSourceFunc) rhythmdb_query_model_reapply_query_cb,
456 model);
458 } else if (model->priv->query_reapply_timeout_id) {
459 g_source_remove (model->priv->query_reapply_timeout_id);
460 model->priv->query_reapply_timeout_id = 0;
464 static void
465 rhythmdb_query_model_set_property (GObject *object,
466 guint prop_id,
467 const GValue *value,
468 GParamSpec *pspec)
470 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (object);
472 switch (prop_id) {
473 case PROP_RHYTHMDB:
474 model->priv->db = g_value_get_object (value);
475 break;
476 case PROP_QUERY:
477 rhythmdb_query_model_set_query_internal (model, g_value_get_pointer (value));
478 break;
479 case PROP_SORT_FUNC:
480 model->priv->sort_func = g_value_get_pointer (value);
481 break;
482 case PROP_SORT_DATA:
483 if (model->priv->sort_data_destroy && model->priv->sort_data)
484 model->priv->sort_data_destroy (model->priv->sort_data);
485 model->priv->sort_data = g_value_get_pointer (value);
486 break;
487 case PROP_SORT_DATA_DESTROY:
488 model->priv->sort_data_destroy = g_value_get_pointer (value);
489 break;
490 case PROP_SORT_REVERSE:
491 model->priv->sort_reverse = g_value_get_boolean (value);
492 break;
493 case PROP_LIMIT_TYPE:
494 model->priv->limit_type = g_value_get_enum (value);
495 break;
496 case PROP_LIMIT_VALUE:
497 if (model->priv->limit_value)
498 g_value_array_free (model->priv->limit_value);
499 model->priv->limit_value = (GValueArray*)g_value_dup_boxed (value);
500 break;
501 case PROP_SHOW_HIDDEN:
502 model->priv->show_hidden = g_value_get_boolean (value);
503 /* FIXME: this will have funky issues if this is changed after entries are added */
504 break;
505 case PROP_BASE_MODEL:
506 rhythmdb_query_model_chain (model, g_value_get_object (value), TRUE);
507 break;
508 default:
509 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
510 break;
514 static void
515 rhythmdb_query_model_get_property (GObject *object,
516 guint prop_id,
517 GValue *value,
518 GParamSpec *pspec)
520 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (object);
522 switch (prop_id) {
523 case PROP_RHYTHMDB:
524 g_value_set_object (value, model->priv->db);
525 break;
526 case PROP_QUERY:
527 g_value_set_pointer (value, model->priv->original_query);
528 break;
529 case PROP_SORT_FUNC:
530 g_value_set_pointer (value, model->priv->sort_func);
531 break;
532 case PROP_SORT_DATA:
533 g_value_set_pointer (value, model->priv->sort_data);
534 break;
535 case PROP_SORT_DATA_DESTROY:
536 g_value_set_pointer (value, model->priv->sort_data_destroy);
537 break;
538 case PROP_SORT_REVERSE:
539 g_value_set_boolean (value, model->priv->sort_reverse);
540 break;
541 case PROP_LIMIT_TYPE:
542 g_value_set_enum (value, model->priv->limit_type);
543 break;
544 case PROP_LIMIT_VALUE:
545 g_value_set_boxed (value, model->priv->limit_value);
546 break;
547 case PROP_SHOW_HIDDEN:
548 g_value_set_boolean (value, model->priv->show_hidden);
549 break;
550 case PROP_BASE_MODEL:
551 g_value_set_object (value, model->priv->base_model);
552 break;
553 default:
554 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
555 break;
559 static void
560 rhythmdb_query_model_init (RhythmDBQueryModel *model)
562 model->priv = RHYTHMDB_QUERY_MODEL_GET_PRIVATE (model);
564 model->priv->stamp = g_random_int ();
566 model->priv->entries = g_sequence_new (NULL);
567 model->priv->reverse_map = g_hash_table_new_full (g_direct_hash,
568 g_direct_equal,
569 (GDestroyNotify)rhythmdb_entry_unref,
570 NULL);
572 model->priv->limited_entries = g_sequence_new (NULL);
573 model->priv->limited_reverse_map = g_hash_table_new_full (g_direct_hash,
574 g_direct_equal,
575 (GDestroyNotify)rhythmdb_entry_unref,
576 NULL);
578 model->priv->reorder_drag_and_drop = FALSE;
581 static GObject *
582 rhythmdb_query_model_constructor (GType type,
583 guint n_construct_properties,
584 GObjectConstructParam *construct_properties)
586 RhythmDBQueryModel *model;
588 model = RHYTHMDB_QUERY_MODEL (G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->
589 constructor (type, n_construct_properties, construct_properties));
591 g_signal_connect_object (G_OBJECT (model->priv->db),
592 "entry_added",
593 G_CALLBACK (rhythmdb_query_model_entry_added_cb),
594 model, 0);
595 g_signal_connect_object (G_OBJECT (model->priv->db),
596 "entry_changed",
597 G_CALLBACK (rhythmdb_query_model_entry_changed_cb),
598 model, 0);
599 g_signal_connect_object (G_OBJECT (model->priv->db),
600 "entry_deleted",
601 G_CALLBACK (rhythmdb_query_model_entry_deleted_cb),
602 model, 0);
604 return G_OBJECT (model);
607 static void
608 rhythmdb_query_model_dispose (GObject *object)
610 RhythmDBQueryModel *model;
612 g_return_if_fail (object != NULL);
613 g_return_if_fail (RHYTHMDB_IS_QUERY_MODEL (object));
615 model = RHYTHMDB_QUERY_MODEL (object);
617 g_return_if_fail (model->priv != NULL);
619 rb_debug ("disposing query model %p", object);
621 if (model->priv->base_model) {
622 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
623 G_CALLBACK (rhythmdb_query_model_base_row_inserted),
624 model);
625 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
626 G_CALLBACK (rhythmdb_query_model_base_row_deleted),
627 model);
628 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
629 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
630 model);
631 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
632 G_CALLBACK (rhythmdb_query_model_base_complete),
633 model);
634 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
635 G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
636 model);
637 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
638 G_CALLBACK (rhythmdb_query_model_base_entry_removed),
639 model);
640 g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
641 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
642 model);
643 g_object_unref (model->priv->base_model);
644 model->priv->base_model = NULL;
647 G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->dispose (object);
650 static void
651 rhythmdb_query_model_finalize (GObject *object)
653 RhythmDBQueryModel *model;
655 g_return_if_fail (object != NULL);
656 g_return_if_fail (RHYTHMDB_IS_QUERY_MODEL (object));
658 model = RHYTHMDB_QUERY_MODEL (object);
660 g_return_if_fail (model->priv != NULL);
662 rb_debug ("finalizing query model %p", object);
664 g_hash_table_destroy (model->priv->reverse_map);
665 g_sequence_free (model->priv->entries);
667 g_hash_table_destroy (model->priv->limited_reverse_map);
668 g_sequence_free (model->priv->limited_entries);
670 if (model->priv->query)
671 rhythmdb_query_free (model->priv->query);
672 if (model->priv->original_query)
673 rhythmdb_query_free (model->priv->original_query);
675 if (model->priv->sort_data_destroy && model->priv->sort_data)
676 model->priv->sort_data_destroy (model->priv->sort_data);
678 if (model->priv->limit_value)
679 g_value_array_free (model->priv->limit_value);
681 if (model->priv->query_reapply_timeout_id)
682 g_source_remove (model->priv->query_reapply_timeout_id);
684 G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->finalize (object);
687 RhythmDBQueryModel *
688 rhythmdb_query_model_new (RhythmDB *db,
689 GPtrArray *query,
690 GCompareDataFunc sort_func,
691 gpointer sort_data,
692 GDestroyNotify sort_data_destroy,
693 gboolean sort_reverse)
695 RhythmDBQueryModel *model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
696 "db", db, "query", query,
697 "sort-func", sort_func,
698 "sort-data", sort_data,
699 "sort-data-destroy", sort_data_destroy,
700 "sort-reverse", sort_reverse,
701 NULL);
703 g_return_val_if_fail (model->priv != NULL, NULL);
705 return model;
708 RhythmDBQueryModel *
709 rhythmdb_query_model_new_empty (RhythmDB *db)
711 return g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
712 "db", db, NULL);
715 void
716 rhythmdb_query_model_copy_contents (RhythmDBQueryModel *dest,
717 RhythmDBQueryModel *src)
719 GSequencePtr ptr, next;
720 RhythmDBEntry *entry;
722 if (src->priv->entries == NULL)
723 return;
725 ptr = g_sequence_get_begin_ptr (src->priv->entries);
726 while (!g_sequence_ptr_is_end (ptr)) {
727 next = g_sequence_ptr_next (ptr);
728 entry = (RhythmDBEntry *)g_sequence_ptr_get_data (ptr);
729 if (dest->priv->query == NULL ||
730 rhythmdb_evaluate_query (dest->priv->db, dest->priv->query, entry)) {
731 if (dest->priv->show_hidden || (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN) == FALSE))
732 rhythmdb_query_model_do_insert (dest, entry, -1);
735 ptr = next;
739 void
740 rhythmdb_query_model_chain (RhythmDBQueryModel *model,
741 RhythmDBQueryModel *base,
742 gboolean import_entries)
744 rb_debug ("query model %p chaining to base model %p", model, base);
746 if (model->priv->base_model != NULL) {
747 g_signal_handlers_disconnect_by_func (model->priv->base_model,
748 G_CALLBACK (rhythmdb_query_model_base_row_inserted),
749 model);
750 g_signal_handlers_disconnect_by_func (model->priv->base_model,
751 G_CALLBACK (rhythmdb_query_model_base_row_deleted),
752 model);
753 g_signal_handlers_disconnect_by_func (model->priv->base_model,
754 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
755 model);
756 g_signal_handlers_disconnect_by_func (model->priv->base_model,
757 G_CALLBACK (rhythmdb_query_model_base_complete),
758 model);
759 g_signal_handlers_disconnect_by_func (model->priv->base_model,
760 G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
761 model);
762 g_signal_handlers_disconnect_by_func (model->priv->base_model,
763 G_CALLBACK (rhythmdb_query_model_base_entry_removed),
764 model);
765 g_signal_handlers_disconnect_by_func (model->priv->base_model,
766 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
767 model);
768 g_object_unref (model->priv->base_model);
771 model->priv->base_model = base;
773 if (model->priv->base_model != NULL) {
774 g_object_ref (model->priv->base_model);
776 g_assert (model->priv->base_model->priv->db == model->priv->db);
778 g_signal_connect_object (model->priv->base_model,
779 "row-inserted",
780 G_CALLBACK (rhythmdb_query_model_base_row_inserted),
781 model, 0);
782 g_signal_connect_object (model->priv->base_model,
783 "row-deleted",
784 G_CALLBACK (rhythmdb_query_model_base_row_deleted),
785 model, 0);
786 g_signal_connect_object (model->priv->base_model,
787 "non-entry-dropped",
788 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
789 model, 0);
790 g_signal_connect_object (model->priv->base_model,
791 "complete",
792 G_CALLBACK (rhythmdb_query_model_base_complete),
793 model, 0);
794 g_signal_connect_object (model->priv->base_model,
795 "rows-reordered",
796 G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
797 model, 0);
798 g_signal_connect_object (model->priv->base_model,
799 "entry-removed",
800 G_CALLBACK (rhythmdb_query_model_base_entry_removed),
801 model, 0);
802 g_signal_connect_object (model->priv->base_model,
803 "entry-prop-changed",
804 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
805 model, 0);
807 if (import_entries)
808 rhythmdb_query_model_copy_contents (model, model->priv->base_model);
812 gboolean
813 rhythmdb_query_model_has_pending_changes (RhythmDBQueryModel *model)
815 gboolean result;
817 result = g_atomic_int_get (&model->priv->pending_update_count) > 0;
818 if (model->priv->base_model)
819 result |= rhythmdb_query_model_has_pending_changes (model->priv->base_model);
821 return result;
824 static void
825 rhythmdb_query_model_entry_added_cb (RhythmDB *db,
826 RhythmDBEntry *entry,
827 RhythmDBQueryModel *model)
829 if (model->priv->query) {
830 if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
831 return;
833 /* check if it's in the base model */
834 if (model->priv->base_model)
835 if (g_hash_table_lookup (model->priv->base_model->priv->reverse_map, entry) == NULL)
836 return;
838 if (rhythmdb_evaluate_query (db, model->priv->query, entry)) {
839 rhythmdb_query_model_do_insert (model, entry, -1);
844 static void
845 rhythmdb_query_model_entry_changed_cb (RhythmDB *db,
846 RhythmDBEntry *entry,
847 GSList *changes,
848 RhythmDBQueryModel *model)
850 gboolean hidden = FALSE;
851 GSList *t;
853 hidden = (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN));
855 if (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) {
856 if (hidden == FALSE) {
857 /* the changed entry may now satisfy the query
858 * so we test it */
859 rhythmdb_query_model_entry_added_cb (db, entry, model);
861 return;
864 if (hidden) {
865 /* emit an entry-prop-changed signal so property models
866 * can be updated correctly. if we have a base model,
867 * we'll propagate the parent's signal instead.
869 if (model->priv->base_model == NULL) {
870 GValue true_val = { 0, };
871 GValue false_val = { 0, };
873 g_value_init (&true_val, G_TYPE_BOOLEAN);
874 g_value_set_boolean (&true_val, TRUE);
875 g_value_init (&false_val, G_TYPE_BOOLEAN);
876 g_value_set_boolean (&false_val, FALSE);
878 rb_debug ("emitting hidden-removal notification for %s",
879 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
880 g_signal_emit (G_OBJECT (model),
881 rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
882 entry, RHYTHMDB_PROP_HIDDEN, &false_val, &true_val);
883 g_value_unset (&true_val);
884 g_value_unset (&false_val);
887 rhythmdb_query_model_filter_out_entry (model, entry);
888 return;
891 /* emit separate change signals for each property
892 * unless this is a chained query model, in which
893 * case we propagate the parent model's signals instead.
895 for (t = changes; t; t = t->next) {
896 RhythmDBEntryChange *change = t->data;
898 if (model->priv->base_model == NULL) {
899 g_signal_emit (G_OBJECT (model),
900 rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
901 entry, change->prop, &change->old, &change->new);
904 if (change->prop == RHYTHMDB_PROP_DURATION) {
905 model->priv->total_duration -= g_value_get_ulong (&change->old);
906 model->priv->total_duration += g_value_get_ulong (&change->new);
907 } else if (change->prop == RHYTHMDB_PROP_FILE_SIZE) {
908 model->priv->total_size -= g_value_get_uint64 (&change->old);
909 model->priv->total_size += g_value_get_uint64 (&change->new);
913 if (model->priv->query &&
914 !rhythmdb_evaluate_query (db, model->priv->query, entry)) {
915 rhythmdb_query_model_filter_out_entry (model, entry);
916 return;
919 /* it may have moved, so we can't just emit a changed entry */
920 if (!rhythmdb_query_model_do_reorder (model, entry)) {
921 /* but if it didn't, we can */
922 GtkTreeIter iter;
923 GtkTreePath *path;
925 if (rhythmdb_query_model_entry_to_iter (model, entry, &iter)) {
926 path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
927 &iter);
928 gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
929 path, &iter);
930 gtk_tree_path_free (path);
935 static void
936 rhythmdb_query_model_base_entry_prop_changed (RhythmDBQueryModel *base_model,
937 RhythmDBEntry *entry,
938 RhythmDBPropType prop,
939 const GValue *old,
940 const GValue *new_value,
941 RhythmDBQueryModel *model)
943 if (g_hash_table_lookup (model->priv->reverse_map, entry)) {
944 /* propagate the signal */
945 g_signal_emit (G_OBJECT (model),
946 rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
947 entry, prop, old, new_value);
951 static void
952 rhythmdb_query_model_entry_deleted_cb (RhythmDB *db,
953 RhythmDBEntry *entry,
954 RhythmDBQueryModel *model)
957 if (g_hash_table_lookup (model->priv->reverse_map, entry) ||
958 g_hash_table_lookup (model->priv->limited_reverse_map, entry))
959 rhythmdb_query_model_remove_entry (model, entry);
962 static gboolean
963 idle_process_update_idle (struct RhythmDBQueryModelUpdate *update)
965 GDK_THREADS_ENTER ();
966 idle_process_update (update);
967 GDK_THREADS_LEAVE ();
968 return FALSE;
971 static void
972 rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update)
974 g_atomic_int_inc (&update->model->priv->pending_update_count);
975 if (rb_is_main_thread ())
976 idle_process_update (update);
977 else
978 g_idle_add ((GSourceFunc) idle_process_update_idle, update);
981 static void
982 idle_process_update (struct RhythmDBQueryModelUpdate *update)
984 switch (update->type) {
985 case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED:
987 guint i;
989 rb_debug ("inserting %d rows", update->entrydata.entries->len);
991 for (i = 0; i < update->entrydata.entries->len; i++ ) {
992 RhythmDBEntry *entry = g_ptr_array_index (update->entrydata.entries, i);
994 if (update->model->priv->show_hidden || !rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) {
995 RhythmDBQueryModel *base_model = update->model->priv->base_model;
996 if (base_model &&
997 g_hash_table_lookup (base_model->priv->reverse_map, entry) == NULL)
998 continue;
1000 rhythmdb_query_model_do_insert (update->model, entry, -1);
1003 rhythmdb_entry_unref (entry);
1006 g_ptr_array_free (update->entrydata.entries, TRUE);
1008 break;
1010 case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX:
1012 rb_debug ("inserting row at index %d", update->entrydata.data.index);
1013 rhythmdb_query_model_do_insert (update->model, update->entrydata.data.entry, update->entrydata.data.index);
1014 rhythmdb_entry_unref (update->entrydata.data.entry);
1015 break;
1017 case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE:
1018 g_signal_emit (G_OBJECT (update->model), rhythmdb_query_model_signals[COMPLETE], 0);
1019 break;
1022 g_atomic_int_add (&update->model->priv->pending_update_count, -1);
1023 g_object_unref (update->model);
1024 g_free (update);
1027 void
1028 rhythmdb_query_model_add_entry (RhythmDBQueryModel *model,
1029 RhythmDBEntry *entry,
1030 gint index)
1032 struct RhythmDBQueryModelUpdate *update;
1034 if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) {
1035 rb_debug ("attempting to add hidden entry");
1036 return;
1039 if (model->priv->base_model) {
1040 /* add it to the base model, which will cause it to be added to this one */
1041 rhythmdb_query_model_add_entry (model->priv->base_model, entry,
1042 rhythmdb_query_model_child_index_to_base_index (model, index));
1043 return;
1046 rb_debug ("inserting entry %p at index %d", entry, index);
1048 update = g_new (struct RhythmDBQueryModelUpdate, 1);
1049 update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX;
1050 update->entrydata.data.entry = entry;
1051 update->entrydata.data.index = index;
1052 update->model = model;
1054 /* take references; released in update idle */
1055 g_object_ref (model);
1057 rhythmdb_entry_ref (entry);
1059 rhythmdb_query_model_process_update (update);
1062 guint64
1063 rhythmdb_query_model_get_size (RhythmDBQueryModel *model)
1065 return model->priv->total_size;
1068 long
1069 rhythmdb_query_model_get_duration (RhythmDBQueryModel *model)
1071 return model->priv->total_duration;
1074 static void
1075 rhythmdb_query_model_insert_into_main_list (RhythmDBQueryModel *model,
1076 RhythmDBEntry *entry,
1077 gint index)
1079 GSequencePtr ptr;
1081 /* take reference; released when removed from hash */
1082 rhythmdb_entry_ref (entry);
1084 if (model->priv->sort_func) {
1085 GCompareDataFunc sort_func;
1086 gpointer sort_data;
1087 struct ReverseSortData reverse_data;
1089 if (model->priv->sort_reverse) {
1090 sort_func = (GCompareDataFunc) _reverse_sorting_func;
1091 sort_data = &reverse_data;
1092 reverse_data.func = model->priv->sort_func;
1093 reverse_data.data = model->priv->sort_data;
1094 } else {
1095 sort_func = model->priv->sort_func;
1096 sort_data = model->priv->sort_data;
1099 ptr = g_sequence_insert_sorted (model->priv->entries,
1100 entry,
1101 sort_func,
1102 sort_data);
1103 } else {
1104 if (index == -1) {
1105 ptr = g_sequence_get_end_ptr (model->priv->entries);
1106 } else {
1107 ptr = g_sequence_get_ptr_at_pos (model->priv->entries, index);
1110 g_sequence_insert (ptr, entry);
1111 ptr = g_sequence_ptr_prev (ptr);
1114 /* the hash now owns this reference to the entry */
1115 g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1117 model->priv->total_duration += rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
1118 model->priv->total_size += rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
1121 static void
1122 rhythmdb_query_model_insert_into_limited_list (RhythmDBQueryModel *model,
1123 RhythmDBEntry *entry)
1125 GSequencePtr ptr;
1127 /* take reference; released when removed from hash */
1128 rhythmdb_entry_ref (entry);
1130 if (model->priv->sort_func) {
1131 GCompareDataFunc sort_func;
1132 gpointer sort_data;
1133 struct ReverseSortData reverse_data;
1135 if (model->priv->sort_reverse) {
1136 sort_func = (GCompareDataFunc) _reverse_sorting_func;
1137 sort_data = &reverse_data;
1138 reverse_data.func = model->priv->sort_func;
1139 reverse_data.data = model->priv->sort_data;
1140 } else {
1141 sort_func = model->priv->sort_func;
1142 sort_data = model->priv->sort_data;
1145 ptr = g_sequence_insert_sorted (model->priv->limited_entries, entry,
1146 sort_func,
1147 sort_data);
1148 } else {
1149 ptr = g_sequence_get_end_ptr (model->priv->limited_entries);
1150 g_sequence_insert (ptr, entry);
1151 ptr = g_sequence_ptr_prev (ptr);
1154 /* the hash now owns this reference to the entry */
1155 g_hash_table_insert (model->priv->limited_reverse_map, entry, ptr);
1158 static void
1159 rhythmdb_query_model_remove_from_main_list (RhythmDBQueryModel *model,
1160 RhythmDBEntry *entry)
1162 GSequencePtr ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1163 int index;
1164 GtkTreePath *path;
1166 index = g_sequence_ptr_get_position (ptr);
1168 path = gtk_tree_path_new ();
1169 gtk_tree_path_append_index (path, index);
1171 gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1172 gtk_tree_path_free (path);
1174 model->priv->total_duration -= rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
1175 model->priv->total_size -= rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
1177 /* take temporary ref */
1178 rhythmdb_entry_ref (entry);
1180 g_sequence_remove (ptr);
1181 g_assert (g_hash_table_remove (model->priv->reverse_map, entry));
1183 g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[POST_ENTRY_DELETE], 0, entry);
1185 /* release temporary ref */
1186 rhythmdb_entry_unref (entry);
1189 static void
1190 rhythmdb_query_model_remove_from_limited_list (RhythmDBQueryModel *model,
1191 RhythmDBEntry *entry)
1193 GSequencePtr ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1195 /* take temporary ref */
1196 rhythmdb_entry_ref (entry);
1197 g_sequence_remove (ptr);
1198 g_hash_table_remove (model->priv->limited_reverse_map, entry);
1199 /* release temporary ref */
1200 rhythmdb_entry_unref (entry);
1203 static void
1204 rhythmdb_query_model_update_limited_entries (RhythmDBQueryModel *model)
1206 RhythmDBEntry *entry;
1207 GSequencePtr ptr;
1209 /* make it fit inside the limits */
1210 while (!rhythmdb_query_model_within_limit (model, NULL)) {
1211 ptr = g_sequence_ptr_prev (g_sequence_get_end_ptr (model->priv->entries));
1212 entry = (RhythmDBEntry*) g_sequence_ptr_get_data (ptr);
1214 /* take temporary ref */
1215 rhythmdb_entry_ref (entry);
1216 rhythmdb_query_model_remove_from_main_list (model, entry);
1217 rhythmdb_query_model_insert_into_limited_list (model, entry);
1218 /* release temporary ref */
1219 rhythmdb_entry_unref (entry);
1222 /* move entries that were previously limited, back to the main list */
1223 while (TRUE) {
1224 GtkTreePath *path;
1225 GtkTreeIter iter;
1227 ptr = g_sequence_get_begin_ptr (model->priv->limited_entries);
1228 if (!ptr || ptr == g_sequence_get_end_ptr (model->priv->limited_entries))
1229 break;
1230 entry = (RhythmDBEntry*) g_sequence_ptr_get_data (ptr);
1231 if (!entry)
1232 break;
1234 if (!rhythmdb_query_model_within_limit (model, entry))
1235 break;
1237 /* take temporary ref */
1238 rhythmdb_entry_ref (entry);
1239 rhythmdb_query_model_remove_from_limited_list (model, entry);
1240 rhythmdb_query_model_insert_into_main_list (model, entry, -1);
1241 /* release temporary ref */
1242 rhythmdb_entry_unref (entry);
1244 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1245 iter.stamp = model->priv->stamp;
1246 iter.user_data = ptr;
1247 path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1248 &iter);
1249 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
1250 path, &iter);
1251 gtk_tree_path_free (path);
1255 static gboolean
1256 rhythmdb_query_model_emit_reorder (RhythmDBQueryModel *model,
1257 gint old_pos,
1258 gint new_pos)
1260 int length, i;
1261 gint *reorder_map;
1262 GtkTreePath *path;
1263 GtkTreeIter iter;
1265 if (new_pos == old_pos) {
1266 /* it hasn't moved, don't emit a re-order */
1267 return FALSE;
1270 length = g_sequence_get_length (model->priv->entries);
1271 reorder_map = malloc (length * sizeof(gint));
1273 if (new_pos > old_pos) {
1274 /* it has mover further down the list */
1275 for (i = 0; i < old_pos; i++)
1276 reorder_map[i] = i;
1277 for (i = old_pos; i < new_pos; i++)
1278 reorder_map[i] = i + 1;
1279 reorder_map[new_pos] = old_pos;
1280 for (i = new_pos + 1; i < length; i++)
1281 reorder_map[i] = i;
1282 } else {
1283 /* it has moved up the list */
1284 for (i = 0; i < new_pos; i++)
1285 reorder_map[i] = i;
1286 reorder_map[new_pos] = old_pos;
1287 for (i = new_pos + 1; i < old_pos + 1; i++)
1288 reorder_map[i] = i - 1;
1289 for (i = old_pos + 1; i < length; i++)
1290 reorder_map[i] = i;
1293 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
1294 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1296 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
1297 path, &iter,
1298 reorder_map);
1299 gtk_tree_path_free (path);
1300 free (reorder_map);
1301 return TRUE;
1304 static gboolean
1305 rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model,
1306 RhythmDBEntry *entry)
1308 GSequencePtr ptr;
1309 int old_pos, new_pos;
1310 GtkTreePath *path;
1311 GtkTreeIter iter;
1312 GCompareDataFunc sort_func;
1313 gpointer sort_data;
1314 struct ReverseSortData reverse_data;
1316 if (model->priv->sort_func == NULL)
1317 return FALSE;
1319 if (model->priv->sort_reverse) {
1320 sort_func = (GCompareDataFunc) _reverse_sorting_func;
1321 sort_data = &reverse_data;
1322 reverse_data.func = model->priv->sort_func;
1323 reverse_data.data = model->priv->sort_data;
1324 } else {
1325 sort_func = model->priv->sort_func;
1326 sort_data = model->priv->sort_data;
1329 ptr = g_sequence_get_begin_ptr (model->priv->limited_entries);
1331 if (ptr != NULL && !g_sequence_ptr_is_end (ptr)) {
1332 RhythmDBEntry *first_limited = g_sequence_ptr_get_data (ptr);
1333 int cmp = (sort_func) (entry, first_limited, sort_data);
1335 if (cmp > 0) {
1336 /* the entry belongs in the limited list, so we don't need a re-order */
1337 /* take temporary ref */
1338 rhythmdb_entry_ref (entry);
1339 rhythmdb_query_model_remove_entry (model, entry);
1340 rhythmdb_query_model_do_insert (model, entry, -1);
1341 /* release temporary ref */
1342 rhythmdb_entry_unref (entry);
1343 return TRUE;
1347 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1348 iter.stamp = model->priv->stamp;
1349 iter.user_data = ptr;
1350 path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1351 &iter);
1352 gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
1353 path, &iter);
1354 gtk_tree_path_free (path);
1356 /* take a temporary ref */
1357 rhythmdb_entry_ref (entry);
1359 /* it may have moved, check for a re-order */
1360 g_hash_table_remove (model->priv->reverse_map, entry);
1361 old_pos = g_sequence_ptr_get_position (ptr);
1362 g_sequence_remove (ptr);
1364 ptr = g_sequence_insert_sorted (model->priv->entries, entry,
1365 sort_func,
1366 sort_data);
1367 new_pos = g_sequence_ptr_get_position (ptr);
1369 /* the hash now owns this reference to the entry */
1370 g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1372 return rhythmdb_query_model_emit_reorder (model, old_pos, new_pos);
1375 static void
1376 rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
1377 RhythmDBEntry *entry,
1378 gint index)
1380 GSequencePtr ptr;
1381 GtkTreePath *path;
1382 GtkTreeIter iter;
1384 g_assert (model->priv->show_hidden || !rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN));
1386 /* we check again if the entry already exists in the hash table */
1387 if (g_hash_table_lookup (model->priv->reverse_map, entry) != NULL)
1388 return;
1390 /* take temporary ref */
1391 rhythmdb_entry_ref (entry);
1393 ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1394 if (ptr != NULL) {
1395 rhythmdb_query_model_remove_from_limited_list (model, entry);
1398 rhythmdb_query_model_insert_into_main_list (model, entry, index);
1400 /* release temporary ref */
1401 rhythmdb_entry_unref (entry);
1403 /* it was added to the main list, send out the inserted signal */
1404 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1405 iter.stamp = model->priv->stamp;
1406 iter.user_data = ptr;
1407 path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1408 &iter);
1409 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
1410 path, &iter);
1411 gtk_tree_path_free (path);
1413 rhythmdb_query_model_update_limited_entries (model);
1416 static void
1417 rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
1418 RhythmDBEntry *entry)
1420 GSequencePtr ptr;
1422 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1423 if (ptr != NULL) {
1424 rhythmdb_query_model_remove_from_main_list (model, entry);
1425 rhythmdb_query_model_update_limited_entries (model);
1426 return;
1429 ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1430 if (ptr != NULL) {
1431 rhythmdb_query_model_remove_from_limited_list (model, entry);
1432 rhythmdb_query_model_update_limited_entries (model);
1433 return;
1437 void
1438 rhythmdb_query_model_move_entry (RhythmDBQueryModel *model,
1439 RhythmDBEntry *entry,
1440 gint index)
1442 GSequencePtr ptr;
1443 GSequencePtr nptr;
1444 gint old_pos;
1446 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1447 if (ptr == NULL)
1448 return;
1450 nptr = g_sequence_get_ptr_at_pos (model->priv->entries, index);
1451 if ((nptr == NULL) || (ptr == nptr))
1452 return;
1454 /* take temporary ref */
1455 rhythmdb_entry_ref (entry);
1457 /* remove from old position */
1458 old_pos = g_sequence_ptr_get_position (ptr);
1459 g_sequence_remove (ptr);
1460 g_hash_table_remove (model->priv->reverse_map, entry);
1462 /* insert into new position */
1463 g_sequence_insert (nptr, entry);
1464 ptr = g_sequence_ptr_prev (nptr);
1466 /* the hash now owns this reference to the entry */
1467 g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1469 rhythmdb_query_model_emit_reorder (model, old_pos, index);
1472 gboolean
1473 rhythmdb_query_model_remove_entry (RhythmDBQueryModel *model,
1474 RhythmDBEntry *entry)
1476 gboolean present = (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) ||
1477 (g_hash_table_lookup (model->priv->limited_reverse_map, entry) == NULL);
1478 g_return_val_if_fail (present, FALSE);
1480 if (model->priv->base_model != NULL)
1481 return rhythmdb_query_model_remove_entry (model->priv->base_model, entry);
1483 /* emit entry-removed, so listeners know the
1484 * entry has actually been removed, rather than filtered
1485 * out.
1487 g_signal_emit (G_OBJECT (model),
1488 rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
1489 entry);
1490 rhythmdb_query_model_filter_out_entry (model, entry);
1492 return TRUE;
1495 gboolean
1496 rhythmdb_query_model_entry_to_iter (RhythmDBQueryModel *model,
1497 RhythmDBEntry *entry,
1498 GtkTreeIter *iter)
1500 GSequencePtr ptr;
1502 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1504 if (G_UNLIKELY (ptr == NULL)) {
1505 /* Invalidate iterator so future uses break quickly. */
1506 iter->stamp = !(model->priv->stamp);
1507 return FALSE;
1510 iter->stamp = model->priv->stamp;
1511 iter->user_data = ptr;
1512 return TRUE;
1515 RhythmDBEntry *
1516 rhythmdb_query_model_tree_path_to_entry (RhythmDBQueryModel *model,
1517 GtkTreePath *path)
1519 GtkTreeIter entry_iter;
1521 g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &entry_iter, path));
1522 return rhythmdb_query_model_iter_to_entry (model, &entry_iter);
1525 RhythmDBEntry *
1526 rhythmdb_query_model_iter_to_entry (RhythmDBQueryModel *model,
1527 GtkTreeIter *entry_iter)
1529 RhythmDBEntry *entry;
1530 gtk_tree_model_get (GTK_TREE_MODEL (model), entry_iter, 0, &entry, -1);
1531 return entry;
1534 RhythmDBEntry *
1535 rhythmdb_query_model_get_next_from_entry (RhythmDBQueryModel *model,
1536 RhythmDBEntry *entry)
1538 GtkTreeIter iter;
1540 g_return_val_if_fail (entry != NULL, NULL);
1542 if (entry && rhythmdb_query_model_entry_to_iter (model, entry, &iter)) {
1543 if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
1544 return NULL;
1545 } else {
1546 /* If the entry isn't in the model, the "next" entry is the first. */
1547 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
1548 return NULL;
1551 return rhythmdb_query_model_iter_to_entry (model, &iter);
1554 RhythmDBEntry *
1555 rhythmdb_query_model_get_previous_from_entry (RhythmDBQueryModel *model,
1556 RhythmDBEntry *entry)
1558 GtkTreeIter iter;
1559 GtkTreePath *path;
1561 g_return_val_if_fail (entry != NULL, NULL);
1563 if (!rhythmdb_query_model_entry_to_iter (model, entry, &iter))
1564 return NULL;
1566 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1567 g_assert (path);
1568 if (!gtk_tree_path_prev (path)) {
1569 gtk_tree_path_free (path);
1570 return NULL;
1573 g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path));
1574 gtk_tree_path_free (path);
1576 return rhythmdb_query_model_iter_to_entry (model, &iter);
1579 static gboolean
1580 rhythmdb_query_model_row_draggable (RbTreeDragSource *dragsource,
1581 GList *paths)
1583 return TRUE;
1586 static gboolean
1587 rhythmdb_query_model_drag_data_delete (RbTreeDragSource *dragsource,
1588 GList *paths)
1590 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (dragsource);
1591 GtkTreeModel *treemodel = GTK_TREE_MODEL (model);
1593 /* we don't delete if it is a reorder drag and drop because the deletion already
1594 occured in rhythmdb_query_model_drag_data_received */
1595 if (model->priv->sort_func == NULL && !model->priv->reorder_drag_and_drop) {
1597 RhythmDBEntry *entry;
1598 GtkTreeIter iter;
1599 GtkTreePath *path;
1601 for (; paths; paths = paths->next) {
1603 path = gtk_tree_row_reference_get_path (paths->data);
1605 if (path) {
1606 if (rhythmdb_query_model_get_iter (treemodel, &iter, path)) {
1607 entry = g_sequence_ptr_get_data (iter.user_data);
1608 rhythmdb_query_model_remove_entry (model, entry);
1610 gtk_tree_path_free (path);
1615 model->priv->reorder_drag_and_drop = FALSE;
1616 return TRUE;
1620 static gboolean
1621 rhythmdb_query_model_drag_data_get (RbTreeDragSource *dragsource,
1622 GList *paths,
1623 GtkSelectionData *selection_data)
1625 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (dragsource);
1626 guint target;
1628 rb_debug ("getting drag data");
1630 if (gtk_target_list_find (rhythmdb_query_model_drag_target_list,
1631 selection_data->target, &target)) {
1632 RhythmDBEntry *entry;
1633 GList *tem;
1634 GString *data;
1635 gboolean need_newline = FALSE;
1637 data = g_string_new ("");
1639 for (tem = paths; tem; tem = tem->next) {
1640 GtkTreeIter iter;
1641 GtkTreePath *path;
1642 const char *location;
1644 path = gtk_tree_row_reference_get_path (tem->data);
1646 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1648 entry = g_sequence_ptr_get_data (iter.user_data);
1650 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
1651 if (location == NULL)
1652 continue;
1654 if (need_newline)
1655 g_string_append (data, "\r\n");
1657 g_string_append (data, location);
1658 need_newline = TRUE;
1661 gtk_selection_data_set (selection_data,
1662 selection_data->target,
1663 8, (guchar *) data->str,
1664 data->len);
1666 g_string_free (data, TRUE);
1668 return TRUE;
1671 return FALSE;
1674 static gboolean
1675 rhythmdb_query_model_drag_data_received (RbTreeDragDest *drag_dest,
1676 GtkTreePath *dest,
1677 GtkTreeViewDropPosition pos,
1678 GtkSelectionData *selection_data)
1680 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (drag_dest);
1682 if (model->priv->base_model) {
1683 GtkTreeIter base_iter;
1684 GtkTreePath *base_dest;
1685 RhythmDBEntry *entry;
1686 gboolean result;
1688 if (dest) {
1689 entry = rhythmdb_query_model_tree_path_to_entry (model, dest);
1690 g_assert (entry);
1691 rhythmdb_query_model_entry_to_iter (model->priv->base_model, entry, &base_iter);
1692 base_dest = gtk_tree_model_get_path (GTK_TREE_MODEL (model->priv->base_model), &base_iter);
1693 rhythmdb_entry_unref (entry);
1694 } else {
1695 base_dest = NULL;
1698 result = rhythmdb_query_model_drag_data_received ((RbTreeDragDest*)model->priv->base_model,
1699 base_dest, pos, selection_data);
1700 if (base_dest)
1701 gtk_tree_path_free (base_dest);
1703 return result;
1706 rb_debug ("drag received");
1708 if (model->priv->sort_func != NULL)
1709 return FALSE;
1711 if (selection_data->format == 8 && selection_data->length >= 0) {
1712 GtkTreeIter iter;
1713 GSequencePtr ptr;
1714 char **strv;
1715 RhythmDBEntry *entry;
1716 int i = 0;
1718 strv = g_strsplit ((char *) selection_data->data, "\r\n", -1);
1720 if (dest == NULL || !rhythmdb_query_model_get_iter (GTK_TREE_MODEL (model), &iter, dest))
1721 ptr = g_sequence_get_end_ptr (model->priv->entries);
1722 else
1723 ptr = iter.user_data;
1725 if (pos == GTK_TREE_VIEW_DROP_AFTER)
1726 ptr = g_sequence_ptr_next (ptr);
1728 for (; strv[i]; i++) {
1729 GSequencePtr tem_ptr;
1730 GtkTreeIter tem_iter;
1732 if (g_utf8_strlen (strv[i], -1) == 0)
1733 continue;
1735 entry = rhythmdb_entry_lookup_by_location (model->priv->db,
1736 strv[i]);
1738 if (entry == NULL) {
1739 int pos;
1741 if (g_sequence_ptr_is_end (ptr))
1742 pos = -1;
1743 else
1744 pos = g_sequence_ptr_get_position (ptr);
1746 g_signal_emit (G_OBJECT (model),
1747 rhythmdb_query_model_signals[NON_ENTRY_DROPPED],
1748 0, strv[i], pos);
1749 } else {
1750 GSequencePtr old_ptr;
1751 GtkTreePath *tem_path;
1752 gint old_pos = 0;
1753 gint new_pos;
1755 old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1756 /* trying to drag drop an entry on itself ! */
1757 if (old_ptr == ptr)
1758 continue;
1760 /* take temporary ref */
1761 rhythmdb_entry_ref (entry);
1763 /* the entry already exists it is either a reorder drag and drop
1764 (or a drag and drop from another application), so we delete
1765 the existing one before adding it again. */
1766 if (old_ptr) {
1767 model->priv->reorder_drag_and_drop = TRUE;
1769 old_pos = g_sequence_ptr_get_position (old_ptr);
1770 g_sequence_remove (old_ptr);
1771 g_assert (g_hash_table_remove (model->priv->reverse_map, entry));
1772 } else {
1773 model->priv->reorder_drag_and_drop = FALSE;
1776 g_sequence_insert (ptr, entry);
1778 tem_ptr = g_sequence_ptr_prev (ptr);
1779 new_pos = g_sequence_ptr_get_position (tem_ptr);
1781 tem_iter.stamp = model->priv->stamp;
1782 tem_iter.user_data = tem_ptr;
1783 /* the hash now owns this reference to the entry */
1784 g_hash_table_insert (model->priv->reverse_map, entry, tem_ptr);
1786 if (old_ptr) {
1787 rb_debug ("moving entry %p from %d to %d", entry, old_pos, new_pos);
1788 rhythmdb_query_model_emit_reorder (model, old_pos, new_pos);
1789 } else {
1790 tem_path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1791 &tem_iter);
1792 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
1793 tem_path, &tem_iter);
1794 gtk_tree_path_free (tem_path);
1799 g_strfreev (strv);
1800 return TRUE;
1802 return FALSE;
1805 static gboolean
1806 rhythmdb_query_model_row_drop_possible (RbTreeDragDest *drag_dest,
1807 GtkTreePath *dest,
1808 GtkTreeViewDropPosition pos,
1809 GtkSelectionData *selection_data)
1811 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (drag_dest);
1813 while (model) {
1814 if (model->priv->sort_func != NULL)
1815 return FALSE;
1817 model = model->priv->base_model;
1819 return TRUE;
1822 static gboolean
1823 rhythmdb_query_model_row_drop_position (RbTreeDragDest *drag_dest,
1824 GtkTreePath *dest_path,
1825 GList *targets,
1826 GtkTreeViewDropPosition *pos)
1828 return TRUE;
1831 static void
1832 rhythmdb_query_model_set_query (RhythmDBQueryResults *results, GPtrArray *query)
1834 g_object_set (G_OBJECT (results), "query", query, NULL);
1837 /* Threading: Called from the database query thread for async queries,
1838 * from the main thread for synchronous queries.
1840 static void
1841 rhythmdb_query_model_add_results (RhythmDBQueryResults *results,
1842 GPtrArray *entries)
1844 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (results);
1845 struct RhythmDBQueryModelUpdate *update;
1846 guint i;
1848 rb_debug ("adding %d entries", entries->len);
1850 update = g_new (struct RhythmDBQueryModelUpdate, 1);
1851 update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED;
1852 update->entrydata.entries = entries;
1853 update->model = model;
1855 /* take references; released in update idle */
1856 g_object_ref (model);
1858 for (i = 0; i < update->entrydata.entries->len; i++) {
1859 rhythmdb_entry_ref (g_ptr_array_index (update->entrydata.entries, i));
1862 rhythmdb_query_model_process_update (update);
1865 static void
1866 rhythmdb_query_model_query_complete (RhythmDBQueryResults *results)
1868 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (results);
1869 struct RhythmDBQueryModelUpdate *update;
1871 update = g_new0 (struct RhythmDBQueryModelUpdate, 1);
1872 update->type = RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE;
1873 update->model = model;
1875 /* take reference; released in update idle */
1876 g_object_ref (model);
1878 rhythmdb_query_model_process_update (update);
1881 static GtkTreeModelFlags
1882 rhythmdb_query_model_get_flags (GtkTreeModel *model)
1884 return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
1887 static gint
1888 rhythmdb_query_model_get_n_columns (GtkTreeModel *tree_model)
1890 return 2;
1893 static GType
1894 rhythmdb_query_model_get_column_type (GtkTreeModel *tree_model,
1895 int index)
1897 switch (index) {
1898 case 0:
1899 return RHYTHMDB_TYPE_ENTRY;
1900 case 1:
1901 return G_TYPE_INT;
1902 default:
1903 g_assert_not_reached ();
1904 return G_TYPE_INVALID;
1908 static gboolean
1909 rhythmdb_query_model_get_iter (GtkTreeModel *tree_model,
1910 GtkTreeIter *iter,
1911 GtkTreePath *path)
1913 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1914 guint index;
1915 GSequencePtr ptr;
1917 index = gtk_tree_path_get_indices (path)[0];
1919 if (index >= g_sequence_get_length (model->priv->entries))
1920 return FALSE;
1922 ptr = g_sequence_get_ptr_at_pos (model->priv->entries, index);
1923 g_assert (ptr);
1925 iter->stamp = model->priv->stamp;
1926 iter->user_data = ptr;
1928 return TRUE;
1931 static GtkTreePath *
1932 rhythmdb_query_model_get_path (GtkTreeModel *tree_model,
1933 GtkTreeIter *iter)
1935 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1936 GtkTreePath *path;
1938 g_return_val_if_fail (iter->stamp == model->priv->stamp, NULL);
1940 if (g_sequence_ptr_is_end (iter->user_data))
1941 return NULL;
1943 path = gtk_tree_path_new ();
1944 gtk_tree_path_append_index (path, g_sequence_ptr_get_position (iter->user_data));
1945 return path;
1948 static void
1949 rhythmdb_query_model_get_value (GtkTreeModel *tree_model,
1950 GtkTreeIter *iter,
1951 gint column,
1952 GValue *value)
1954 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1955 RhythmDBEntry *entry;
1957 g_return_if_fail (!g_sequence_ptr_is_end (iter->user_data));
1958 g_return_if_fail (model->priv->stamp == iter->stamp);
1960 entry = g_sequence_ptr_get_data (iter->user_data);
1962 switch (column) {
1963 case 0:
1964 g_value_init (value, RHYTHMDB_TYPE_ENTRY);
1965 g_value_set_boxed (value, entry);
1966 break;
1967 case 1:
1968 g_value_init (value, G_TYPE_INT);
1969 g_value_set_int (value, g_sequence_ptr_get_position (iter->user_data)+1);
1970 break;
1971 default:
1972 g_assert_not_reached ();
1976 static gboolean
1977 rhythmdb_query_model_iter_next (GtkTreeModel *tree_model,
1978 GtkTreeIter *iter)
1980 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1982 g_return_val_if_fail (iter->stamp == model->priv->stamp, FALSE);
1984 iter->user_data = g_sequence_ptr_next (iter->user_data);
1986 return !g_sequence_ptr_is_end (iter->user_data);
1989 static gboolean
1990 rhythmdb_query_model_iter_children (GtkTreeModel *tree_model,
1991 GtkTreeIter *iter,
1992 GtkTreeIter *parent)
1994 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
1996 if (parent != NULL)
1997 return FALSE;
1999 if (g_sequence_get_length (model->priv->entries) == 0)
2000 return FALSE;
2002 iter->stamp = model->priv->stamp;
2003 iter->user_data = g_sequence_get_begin_ptr (model->priv->entries);
2005 return TRUE;
2008 static gboolean
2009 rhythmdb_query_model_iter_has_child (GtkTreeModel *tree_model,
2010 GtkTreeIter *iter)
2012 return FALSE;
2015 static gint
2016 rhythmdb_query_model_iter_n_children (GtkTreeModel *tree_model,
2017 GtkTreeIter *iter)
2019 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2021 if (iter == NULL)
2022 return g_sequence_get_length (model->priv->entries);
2024 g_return_val_if_fail (model->priv->stamp == iter->stamp, -1);
2026 return 0;
2029 static gboolean
2030 rhythmdb_query_model_iter_nth_child (GtkTreeModel *tree_model,
2031 GtkTreeIter *iter,
2032 GtkTreeIter *parent,
2033 gint n)
2035 RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2036 GSequencePtr child;
2038 if (parent)
2039 return FALSE;
2041 child = g_sequence_get_ptr_at_pos (model->priv->entries, n);
2043 if (g_sequence_ptr_is_end (child))
2044 return FALSE;
2046 iter->stamp = model->priv->stamp;
2047 iter->user_data = child;
2049 return TRUE;
2052 static gboolean
2053 rhythmdb_query_model_iter_parent (GtkTreeModel *tree_model,
2054 GtkTreeIter *iter,
2055 GtkTreeIter *child)
2057 return FALSE;
2060 char *
2061 rhythmdb_query_model_compute_status_normal (RhythmDBQueryModel *model,
2062 const char *singular,
2063 const char *plural)
2065 return rhythmdb_compute_status_normal (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL),
2066 rhythmdb_query_model_get_duration (model),
2067 rhythmdb_query_model_get_size (model),
2068 singular,
2069 plural);
2072 static void
2073 apply_updated_entry_sequence (RhythmDBQueryModel *model,
2074 GSequence *new_entries)
2076 int *reorder_map;
2077 int length, i;
2078 GtkTreePath *path;
2079 GtkTreeIter iter;
2080 GSequencePtr ptr;
2082 length = g_sequence_get_length (new_entries);
2083 /* generate resort map and rebuild reverse map */
2084 reorder_map = malloc (length * sizeof(gint));
2086 ptr = g_sequence_get_begin_ptr (new_entries);
2087 for (i = 0; i < length; i++) {
2088 gpointer entry = g_sequence_ptr_get_data (ptr);
2089 GSequencePtr old_ptr;
2091 old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2092 reorder_map[i] = g_sequence_ptr_get_position (old_ptr);
2093 g_hash_table_replace (model->priv->reverse_map, rhythmdb_entry_ref (entry), ptr);
2095 ptr = g_sequence_ptr_next (ptr);
2097 g_sequence_free (model->priv->entries);
2098 model->priv->entries = new_entries;
2100 /* emit the re-order and clean up */
2101 gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
2102 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
2103 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
2104 path, &iter,
2105 reorder_map);
2107 gtk_tree_path_free (path);
2108 free (reorder_map);
2111 void
2112 rhythmdb_query_model_set_sort_order (RhythmDBQueryModel *model,
2113 GCompareDataFunc sort_func,
2114 gpointer sort_data,
2115 GDestroyNotify sort_data_destroy,
2116 gboolean sort_reverse)
2118 GSequence *new_entries;
2119 GSequencePtr ptr;
2120 int length, i;
2121 struct ReverseSortData reverse_data;
2123 if ((model->priv->sort_func == sort_func) &&
2124 (model->priv->sort_data == sort_data) &&
2125 (model->priv->sort_data_destroy == sort_data_destroy) &&
2126 (model->priv->sort_reverse == sort_reverse))
2127 return;
2129 g_return_if_fail ((model->priv->limit_type == RHYTHMDB_QUERY_MODEL_LIMIT_NONE) ||
2130 (model->priv->sort_func == NULL));
2131 if (model->priv->sort_func == NULL)
2132 g_assert (g_sequence_get_length (model->priv->limited_entries) == 0);
2134 if (model->priv->sort_data_destroy && model->priv->sort_data)
2135 model->priv->sort_data_destroy (model->priv->sort_data);
2137 model->priv->sort_func = sort_func;
2138 model->priv->sort_data = sort_data;
2139 model->priv->sort_data_destroy = sort_data_destroy;
2140 model->priv->sort_reverse = sort_reverse;
2142 if (model->priv->sort_reverse) {
2143 reverse_data.func = sort_func;
2144 reverse_data.data = sort_data;
2145 sort_func = (GCompareDataFunc) _reverse_sorting_func;
2146 sort_data = &reverse_data;
2149 /* create the new sorted entry sequence */
2150 length = g_sequence_get_length (model->priv->entries);
2151 if (length > 0) {
2152 new_entries = g_sequence_new (NULL);
2153 ptr = g_sequence_get_begin_ptr (model->priv->entries);
2154 for (i = 0; i < length; i++) {
2155 gpointer entry = g_sequence_ptr_get_data (ptr);
2157 g_sequence_insert_sorted (new_entries, entry,
2158 sort_func,
2159 sort_data);
2160 ptr = g_sequence_ptr_next (ptr);
2163 apply_updated_entry_sequence (model, new_entries);
2167 static int
2168 rhythmdb_query_model_child_index_to_base_index (RhythmDBQueryModel *model,
2169 int index)
2171 GSequencePtr ptr;
2172 RhythmDBEntry *entry;
2173 g_assert (model->priv->base_model);
2175 ptr = g_sequence_get_ptr_at_pos (model->priv->entries, index);
2176 if (ptr == NULL || g_sequence_ptr_is_end (ptr))
2177 return -1;
2178 entry = (RhythmDBEntry*)g_sequence_ptr_get_data (ptr);
2180 ptr = g_hash_table_lookup (model->priv->base_model->priv->reverse_map, entry);
2181 g_assert (ptr); /* all child model entries are in the base model */
2183 return g_sequence_ptr_get_position (ptr);
2186 /*static int
2187 rhythmdb_query_model_base_index_to_child_index (RhythmDBQueryModel *model, int index)
2189 GSequencePtr ptr;
2190 RhythmDBEntry *entry;
2191 int pos;
2193 g_assert (model->priv->base_model);
2194 if (index == -1)
2195 return -1;
2197 ptr = g_sequence_get_ptr_at_pos (model->priv->base_model->priv->entries, index);
2198 if (ptr == NULL || g_sequence_ptr_is_end (ptr))
2199 return -1;
2200 entry = (RhythmDBEntry*)g_sequence_ptr_get_data (ptr);
2202 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2203 if (ptr == NULL)
2204 return -1;
2206 pos = g_sequence_ptr_get_position (ptr);
2207 return pos;
2210 static int
2211 rhythmdb_query_model_get_entry_index (RhythmDBQueryModel *model,
2212 RhythmDBEntry *entry)
2214 GSequencePtr ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2216 if (ptr)
2217 return g_sequence_ptr_get_position (ptr);
2218 else
2219 return -1;
2222 static void
2223 rhythmdb_query_model_base_row_inserted (GtkTreeModel *tree_model,
2224 GtkTreePath *path,
2225 GtkTreeIter *iter,
2226 RhythmDBQueryModel *model)
2228 RhythmDBQueryModel *base_model = RHYTHMDB_QUERY_MODEL (tree_model);
2229 RhythmDBEntry *entry;
2230 RhythmDBEntry *prev_entry;
2231 int index;
2233 g_assert (base_model == model->priv->base_model);
2235 entry = rhythmdb_query_model_iter_to_entry (base_model, iter);
2237 if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
2238 goto out;
2240 if (rhythmdb_evaluate_query (model->priv->db, model->priv->query, entry)) {
2241 /* find the closest previous entry that is in the filter model, and it it after that */
2242 prev_entry = rhythmdb_query_model_get_previous_from_entry (base_model, entry);
2243 while (prev_entry && g_hash_table_lookup (model->priv->reverse_map, prev_entry) == NULL) {
2244 rhythmdb_entry_unref (prev_entry);
2245 prev_entry = rhythmdb_query_model_get_previous_from_entry (base_model, prev_entry);
2248 if (entry != NULL) {
2249 index = rhythmdb_query_model_get_entry_index (model, prev_entry) + 1;
2250 } else {
2251 index = 0;
2254 if (prev_entry != NULL) {
2255 rhythmdb_entry_unref (prev_entry);
2258 rb_debug ("inserting entry %p from base model %p to model %p in position %d", entry, base_model, model, index);
2259 rhythmdb_query_model_do_insert (model, entry, index);
2261 out:
2262 rhythmdb_entry_unref (entry);
2265 static void
2266 rhythmdb_query_model_base_row_deleted (GtkTreeModel *base_model,
2267 GtkTreePath *path,
2268 RhythmDBQueryModel *model)
2270 RhythmDBEntry *entry;
2272 entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (base_model), path);
2273 rb_debug ("deleting entry %p from base model %p to model %p", entry, base_model, model);
2275 rhythmdb_query_model_filter_out_entry (model, entry);
2276 rhythmdb_entry_unref (entry);
2279 static void
2280 rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel *base_model,
2281 const char *location,
2282 int position,
2283 RhythmDBQueryModel *model)
2285 g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[NON_ENTRY_DROPPED], 0,
2286 location, rhythmdb_query_model_child_index_to_base_index (model, position));
2289 static void
2290 rhythmdb_query_model_base_complete (GtkTreeModel *base_model,
2291 RhythmDBQueryModel *model)
2293 g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[COMPLETE], 0);
2296 static void
2297 rhythmdb_query_model_base_rows_reordered (GtkTreeModel *base_model,
2298 GtkTreePath *arg1,
2299 GtkTreeIter *arg2,
2300 gint *order_map,
2301 RhythmDBQueryModel *model)
2303 RhythmDBQueryModel *base_query_model = RHYTHMDB_QUERY_MODEL (base_model);
2304 GSequence *new_entries;
2305 GSequencePtr ptr;
2307 /* ignore, if this model sorts */
2308 if (model->priv->sort_func)
2309 return;
2311 new_entries = g_sequence_new (NULL);
2313 /* iterate over the entries in the base, and recreate the sequence */
2314 ptr = g_sequence_get_begin_ptr (base_query_model->priv->entries);
2315 while (!g_sequence_ptr_is_end (ptr)) {
2316 gpointer entry = g_sequence_ptr_get_data (ptr);
2318 if (g_hash_table_lookup (model->priv->reverse_map, entry))
2319 g_sequence_append (new_entries, entry);
2321 ptr = g_sequence_ptr_next (ptr);
2324 apply_updated_entry_sequence (model, new_entries);
2327 static void
2328 rhythmdb_query_model_base_entry_removed (RhythmDBQueryModel *base_model,
2329 RhythmDBEntry *entry,
2330 RhythmDBQueryModel *model)
2332 if (g_hash_table_lookup (model->priv->reverse_map, entry)) {
2333 /* propagate the signal out to any attached property models */
2334 g_signal_emit (G_OBJECT (model),
2335 rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
2336 entry);
2340 void
2341 rhythmdb_query_model_reapply_query (RhythmDBQueryModel *model,
2342 gboolean filter)
2344 GSequencePtr ptr;
2345 GSequencePtr next;
2346 RhythmDBEntry *entry;
2347 gboolean changed = FALSE;
2348 GList *remove = NULL;
2350 /* process limited list first, so entries that don't match can't sneak in
2351 * to the main list from there
2353 if (model->priv->limited_entries) {
2354 ptr = g_sequence_get_begin_ptr (model->priv->limited_entries);
2355 while (!g_sequence_ptr_is_end (ptr)) {
2356 next = g_sequence_ptr_next (ptr);
2357 entry = (RhythmDBEntry *)g_sequence_ptr_get_data (ptr);
2358 if (!rhythmdb_evaluate_query (model->priv->db,
2359 model->priv->query,
2360 entry)) {
2361 remove = g_list_prepend (remove, entry);
2364 ptr = next;
2367 if (remove) {
2368 GList *t;
2369 for (t = remove; t; t = t->next) {
2370 RhythmDBEntry *entry = t->data;
2371 rhythmdb_query_model_remove_from_limited_list (model, entry);
2374 changed = TRUE;
2375 g_list_free (remove);
2376 remove = NULL;
2380 if (model->priv->entries) {
2381 ptr = g_sequence_get_begin_ptr (model->priv->entries);
2382 while (!g_sequence_ptr_is_end (ptr)) {
2383 next = g_sequence_ptr_next (ptr);
2384 entry = (RhythmDBEntry *)g_sequence_ptr_get_data (ptr);
2385 if (!rhythmdb_evaluate_query (model->priv->db,
2386 model->priv->query,
2387 entry)) {
2388 remove = g_list_prepend (remove, entry);
2391 ptr = next;
2394 if (remove) {
2395 GList *t;
2396 for (t = remove; t; t = t->next) {
2397 RhythmDBEntry *entry = t->data;
2398 if (!filter) {
2399 g_signal_emit (G_OBJECT (model),
2400 rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
2401 entry);
2403 rhythmdb_query_model_remove_from_main_list (model, entry);
2406 changed = TRUE;
2407 g_list_free (remove);
2408 remove = NULL;
2412 if (changed)
2413 rhythmdb_query_model_update_limited_entries (model);
2416 static gint
2417 _reverse_sorting_func (gpointer a,
2418 gpointer b,
2419 struct ReverseSortData *reverse_data)
2421 return - reverse_data->func (a, b, reverse_data->data);
2424 gint
2425 rhythmdb_query_model_location_sort_func (RhythmDBEntry *a,
2426 RhythmDBEntry *b,
2427 gpointer data)
2429 const char *a_val;
2430 const char *b_val;
2432 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_LOCATION);
2433 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_LOCATION);
2435 if (a_val == NULL) {
2436 if (b_val == NULL)
2437 return 0;
2438 else
2439 return -1;
2440 } else if (b_val == NULL)
2441 return 1;
2442 else
2443 return strcmp (a_val, b_val);
2446 gint
2447 rhythmdb_query_model_title_sort_func (RhythmDBEntry *a,
2448 RhythmDBEntry *b,
2449 gpointer data)
2451 const char *a_val;
2452 const char *b_val;
2453 gint ret;
2455 /* Sort by album name */
2456 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE_SORT_KEY);
2457 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE_SORT_KEY);
2459 if (a_val == NULL) {
2460 if (b_val == NULL)
2461 ret = 0;
2462 else
2463 ret = -1;
2464 } else if (b_val == NULL)
2465 ret = 1;
2466 else
2467 ret = strcmp (a_val, b_val);
2469 if (ret != 0)
2470 return ret;
2471 else
2472 return rhythmdb_query_model_location_sort_func (a, b, data);
2475 gint
2476 rhythmdb_query_model_album_sort_func (RhythmDBEntry *a,
2477 RhythmDBEntry *b,
2478 gpointer data)
2480 const char *a_val;
2481 const char *b_val;
2482 gulong a_num;
2483 gulong b_num;
2484 gint ret;
2486 /* Sort by album name */
2487 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ALBUM_SORT_KEY);
2488 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ALBUM_SORT_KEY);
2490 if (a_val == NULL) {
2491 if (b_val == NULL)
2492 ret = 0;
2493 else
2494 ret = -1;
2495 } else if (b_val == NULL)
2496 ret = 1;
2497 else
2498 ret = strcmp (a_val, b_val);
2500 if (ret != 0)
2501 return ret;
2503 /* Then by disc number, */
2504 a_num = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_DISC_NUMBER);
2505 b_num = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_DISC_NUMBER);
2506 if (a_num != b_num)
2507 return (a_num < b_num ? -1 : 1);
2509 /* by track number */
2510 a_num = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_TRACK_NUMBER);
2511 b_num = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_TRACK_NUMBER);
2512 if (a_num != b_num)
2513 return (a_num < b_num ? -1 : 1);
2515 /* by title */
2516 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE_SORT_KEY);
2517 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE_SORT_KEY);
2519 if (a_val == NULL) {
2520 if (b_val == NULL)
2521 return 0;
2522 else
2523 return -1;
2524 } else if (b_val == NULL)
2525 return 1;
2526 else
2527 return rhythmdb_query_model_location_sort_func (a, b, data);
2530 gint
2531 rhythmdb_query_model_artist_sort_func (RhythmDBEntry *a,
2532 RhythmDBEntry *b,
2533 gpointer data)
2535 const char *a_val;
2536 const char *b_val;
2537 gint ret;
2539 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ARTIST_SORT_KEY);
2540 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ARTIST_SORT_KEY);
2542 if (a_val == NULL) {
2543 if (b_val == NULL)
2544 ret = 0;
2545 else
2546 ret = -1;
2547 } else if (b_val == NULL)
2548 ret = 1;
2549 else
2550 ret = strcmp (a_val, b_val);
2552 if (ret != 0)
2553 return ret;
2554 else
2555 return rhythmdb_query_model_album_sort_func (a, b, data);
2558 gint
2559 rhythmdb_query_model_genre_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
2560 gpointer data)
2562 const char *a_val;
2563 const char *b_val;
2564 gint ret;
2566 a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_GENRE_SORT_KEY);
2567 b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_GENRE_SORT_KEY);
2569 if (a_val == NULL) {
2570 if (b_val == NULL)
2571 ret = 0;
2572 else
2573 ret = -1;
2574 } else if (b_val == NULL)
2575 ret = 1;
2576 else
2577 ret = strcmp (a_val, b_val);
2579 if (ret != 0)
2580 return ret;
2581 else
2582 return rhythmdb_query_model_artist_sort_func (a, b, data);
2585 gint
2586 rhythmdb_query_model_track_sort_func (RhythmDBEntry *a,
2587 RhythmDBEntry *b,
2588 gpointer data)
2590 return rhythmdb_query_model_album_sort_func (a, b, data);
2593 gint
2594 rhythmdb_query_model_double_ceiling_sort_func (RhythmDBEntry *a,
2595 RhythmDBEntry *b,
2596 gpointer data)
2598 gdouble a_val, b_val;
2599 RhythmDBPropType prop_id;
2601 prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
2603 a_val = ceil (rhythmdb_entry_get_double (a, prop_id));
2604 b_val = ceil (rhythmdb_entry_get_double (b, prop_id));
2606 if (a_val != b_val)
2607 return (a_val > b_val ? 1 : -1);
2608 else
2609 return rhythmdb_query_model_location_sort_func (a, b, data);
2612 gint
2613 rhythmdb_query_model_ulong_sort_func (RhythmDBEntry *a,
2614 RhythmDBEntry *b,
2615 gpointer data)
2617 gulong a_val, b_val;
2618 RhythmDBPropType prop_id;
2620 prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
2621 a_val = rhythmdb_entry_get_ulong (a, prop_id);
2622 b_val = rhythmdb_entry_get_ulong (b, prop_id);
2624 if (a_val != b_val)
2625 return (a_val > b_val ? 1 : -1);
2626 else
2627 return rhythmdb_query_model_location_sort_func (a, b, data);
2630 gint
2631 rhythmdb_query_model_date_sort_func (RhythmDBEntry *a,
2632 RhythmDBEntry *b,
2633 gpointer data)
2636 gulong a_val, b_val;
2637 gint ret;
2639 a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_DATE);
2640 b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_DATE);
2642 ret = (a_val == b_val ? 0 : (a_val > b_val ? 1 : -1));
2643 if (a_val > b_val)
2644 return 1;
2645 else if (a_val < b_val)
2646 return -1;
2647 else
2648 return rhythmdb_query_model_album_sort_func (a, b, data);
2651 gint
2652 rhythmdb_query_model_string_sort_func (RhythmDBEntry *a,
2653 RhythmDBEntry *b,
2654 gpointer data)
2656 const char *a_val;
2657 const char *b_val;
2658 gint ret;
2659 RhythmDBPropType prop_id;
2661 prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
2662 a_val = rhythmdb_entry_get_string (a, prop_id);
2663 b_val = rhythmdb_entry_get_string (b, prop_id);
2665 if (a_val == NULL) {
2666 if (b_val == NULL)
2667 ret = 0;
2668 else
2669 ret = -1;
2670 } else if (b_val == NULL)
2671 ret = 1;
2672 else
2673 ret = strcmp (a_val, b_val);
2675 if (ret != 0)
2676 return ret;
2677 else
2678 return rhythmdb_query_model_location_sort_func (a, b, data);
2681 static gboolean
2682 rhythmdb_query_model_within_limit (RhythmDBQueryModel *model,
2683 RhythmDBEntry *entry)
2685 gboolean result = TRUE;
2687 switch (model->priv->limit_type) {
2688 case RHYTHMDB_QUERY_MODEL_LIMIT_NONE:
2689 result = TRUE;
2690 break;
2692 case RHYTHMDB_QUERY_MODEL_LIMIT_COUNT:
2694 gulong limit_count;
2695 gulong current_count;
2697 limit_count = g_value_get_ulong (g_value_array_get_nth (model->priv->limit_value, 0));
2698 current_count = g_hash_table_size (model->priv->reverse_map);
2700 if (entry)
2701 current_count++;
2703 result = (current_count <= limit_count);
2704 break;
2707 case RHYTHMDB_QUERY_MODEL_LIMIT_SIZE:
2709 guint64 limit_size;
2710 guint64 current_size;
2712 limit_size = g_value_get_uint64 (g_value_array_get_nth (model->priv->limit_value, 0));
2713 current_size = model->priv->total_size;
2715 if (entry)
2716 current_size += rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
2718 /* the limit is in MB */
2719 result = (current_size / (1024 * 1024) <= limit_size);
2720 break;
2723 case RHYTHMDB_QUERY_MODEL_LIMIT_TIME:
2725 gulong limit_time;
2726 gulong current_time;
2728 limit_time = g_value_get_ulong (g_value_array_get_nth (model->priv->limit_value, 0));
2729 current_time = model->priv->total_duration;
2731 if (entry)
2732 current_time += rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
2734 result = (current_time <= limit_time);
2735 break;
2739 return result;
2742 /* This should really be standard. */
2743 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
2745 GType
2746 rhythmdb_query_model_limit_type_get_type (void)
2748 static GType etype = 0;
2750 if (etype == 0)
2752 static const GEnumValue values[] =
2755 ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_NONE, "No limit"),
2756 ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_COUNT, "Limit by number of entries (count)"),
2757 ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_SIZE, "Limit by data size (Mb)"),
2758 ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_TIME, "Limit by duration (seconds)"),
2759 { 0, 0, 0 }
2762 etype = g_enum_register_static ("RhythmDBQueryModelLimitType", values);
2765 return etype;
2768 static gboolean
2769 rhythmdb_query_model_reapply_query_cb (RhythmDBQueryModel *model)
2771 GDK_THREADS_ENTER ();
2772 rhythmdb_query_model_reapply_query (model, FALSE);
2773 rhythmdb_do_full_query_async_parsed (model->priv->db,
2774 RHYTHMDB_QUERY_RESULTS (model),
2775 model->priv->original_query);
2776 GDK_THREADS_LEAVE ();
2777 return TRUE;