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.
31 #include <gtk/gtktreednd.h>
33 #include "rhythmdb-query-model.h"
35 #include "gsequence.h"
36 #include "rb-tree-dnd.h"
37 #include "rb-marshal.h"
40 struct ReverseSortData
42 GCompareDataFunc func
;
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
,
70 static void rhythmdb_query_model_get_property (GObject
*object
,
74 static void rhythmdb_query_model_do_insert (RhythmDBQueryModel
*model
,
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
,
90 GtkSelectionData
*selection_data
);
91 static gboolean
rhythmdb_query_model_drag_data_delete (RbTreeDragSource
*dragsource
,
93 static gboolean
rhythmdb_query_model_row_draggable (RbTreeDragSource
*dragsource
,
95 static gboolean
rhythmdb_query_model_drag_data_received (RbTreeDragDest
*drag_dest
,
97 GtkTreeViewDropPosition pos
,
98 GtkSelectionData
*selection_data
);
99 static gboolean
rhythmdb_query_model_row_drop_possible (RbTreeDragDest
*drag_dest
,
101 GtkTreeViewDropPosition pos
,
102 GtkSelectionData
*selection_data
);
103 static gboolean
rhythmdb_query_model_row_drop_position (RbTreeDragDest
*drag_dest
,
104 GtkTreePath
*dest_path
,
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
,
117 static GtkTreePath
* rhythmdb_query_model_get_path (GtkTreeModel
*tree_model
,
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
,
123 static gboolean
rhythmdb_query_model_iter_children (GtkTreeModel
*tree_model
,
125 GtkTreeIter
*parent
);
126 static gboolean
rhythmdb_query_model_iter_has_child (GtkTreeModel
*tree_model
,
128 static gint
rhythmdb_query_model_iter_n_children (GtkTreeModel
*tree_model
,
130 static gboolean
rhythmdb_query_model_iter_nth_child (GtkTreeModel
*tree_model
,
131 GtkTreeIter
*iter
, GtkTreeIter
*parent
,
133 static gboolean
rhythmdb_query_model_iter_parent (GtkTreeModel
*tree_model
,
137 static void rhythmdb_query_model_base_row_inserted (GtkTreeModel
*base_model
,
140 RhythmDBQueryModel
*model
);
141 static void rhythmdb_query_model_base_row_deleted (GtkTreeModel
*base_model
,
143 RhythmDBQueryModel
*model
);
144 static void rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel
*base_model
,
145 const char *location
,
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
,
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
,
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
;
175 RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED
,
176 RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX
,
177 RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE
,
182 RhythmDBEntry
*entry
;
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
201 RhythmDBQueryModel
*base_model
;
203 GCompareDataFunc sort_func
;
205 GDestroyNotify sort_data_destroy
;
206 gboolean sort_reverse
;
209 GPtrArray
*original_query
;
213 RhythmDBQueryModelLimitType limit_type
;
214 GValueArray
*limit_value
;
216 glong total_duration
;
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))
241 PROP_SORT_DATA_DESTROY
,
259 static guint rhythmdb_query_model_signals
[LAST_SIGNAL
] = { 0 };
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
,
280 g_param_spec_object ("db",
284 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
285 g_object_class_install_property (object_class
,
287 g_param_spec_pointer ("query",
291 g_object_class_install_property (object_class
,
293 g_param_spec_pointer ("sort-func",
297 g_object_class_install_property (object_class
,
299 g_param_spec_pointer ("sort-data",
303 g_object_class_install_property (object_class
,
304 PROP_SORT_DATA_DESTROY
,
305 g_param_spec_pointer ("sort-data-destroy",
307 "Sort data destroy function",
309 g_object_class_install_property (object_class
,
311 g_param_spec_boolean ("sort-reverse",
313 "Reverse sort order flag",
316 g_object_class_install_property (object_class
,
318 g_param_spec_enum ("limit-type",
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
,
326 g_param_spec_boxed ("limit-value",
330 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
331 g_object_class_install_property (object_class
,
333 g_param_spec_boolean ("show-hidden",
335 "maximum time (seconds)",
337 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
));
338 g_object_class_install_property (object_class
,
340 g_param_spec_object ("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
,
350 G_STRUCT_OFFSET (RhythmDBQueryModelClass
, entry_prop_changed
),
352 rb_marshal_VOID__BOXED_INT_POINTER_POINTER
,
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
,
359 G_STRUCT_OFFSET (RhythmDBQueryModelClass
, entry_removed
),
361 g_cclosure_marshal_VOID__BOXED
,
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
,
368 G_STRUCT_OFFSET (RhythmDBQueryModelClass
, non_entry_dropped
),
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
,
376 G_STRUCT_OFFSET (RhythmDBQueryModelClass
, complete
),
378 g_cclosure_marshal_VOID__VOID
,
380 rhythmdb_query_model_signals
[POST_ENTRY_DELETE
] =
381 g_signal_new ("post-entry-delete",
382 RHYTHMDB_TYPE_QUERY_MODEL
,
384 G_STRUCT_OFFSET (RhythmDBQueryModelClass
, post_entry_delete
),
386 g_cclosure_marshal_VOID__BOXED
,
388 1, RHYTHMDB_TYPE_ENTRY
);
390 g_type_class_add_private (klass
, sizeof (RhythmDBQueryModelPrivate
));
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
;
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
;
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
;
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
;
435 rhythmdb_query_model_set_query_internal (RhythmDBQueryModel
*model
,
438 if (query
== model
->priv
->original_query
)
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
,
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;
465 rhythmdb_query_model_set_property (GObject
*object
,
470 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (object
);
474 model
->priv
->db
= g_value_get_object (value
);
477 rhythmdb_query_model_set_query_internal (model
, g_value_get_pointer (value
));
480 model
->priv
->sort_func
= g_value_get_pointer (value
);
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
);
487 case PROP_SORT_DATA_DESTROY
:
488 model
->priv
->sort_data_destroy
= g_value_get_pointer (value
);
490 case PROP_SORT_REVERSE
:
491 model
->priv
->sort_reverse
= g_value_get_boolean (value
);
493 case PROP_LIMIT_TYPE
:
494 model
->priv
->limit_type
= g_value_get_enum (value
);
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
);
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 */
505 case PROP_BASE_MODEL
:
506 rhythmdb_query_model_chain (model
, g_value_get_object (value
), TRUE
);
509 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
515 rhythmdb_query_model_get_property (GObject
*object
,
520 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (object
);
524 g_value_set_object (value
, model
->priv
->db
);
527 g_value_set_pointer (value
, model
->priv
->original_query
);
530 g_value_set_pointer (value
, model
->priv
->sort_func
);
533 g_value_set_pointer (value
, model
->priv
->sort_data
);
535 case PROP_SORT_DATA_DESTROY
:
536 g_value_set_pointer (value
, model
->priv
->sort_data_destroy
);
538 case PROP_SORT_REVERSE
:
539 g_value_set_boolean (value
, model
->priv
->sort_reverse
);
541 case PROP_LIMIT_TYPE
:
542 g_value_set_enum (value
, model
->priv
->limit_type
);
544 case PROP_LIMIT_VALUE
:
545 g_value_set_boxed (value
, model
->priv
->limit_value
);
547 case PROP_SHOW_HIDDEN
:
548 g_value_set_boolean (value
, model
->priv
->show_hidden
);
550 case PROP_BASE_MODEL
:
551 g_value_set_object (value
, model
->priv
->base_model
);
554 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
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
,
569 (GDestroyNotify
)rhythmdb_entry_unref
,
572 model
->priv
->limited_entries
= g_sequence_new (NULL
);
573 model
->priv
->limited_reverse_map
= g_hash_table_new_full (g_direct_hash
,
575 (GDestroyNotify
)rhythmdb_entry_unref
,
578 model
->priv
->reorder_drag_and_drop
= FALSE
;
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
),
593 G_CALLBACK (rhythmdb_query_model_entry_added_cb
),
595 g_signal_connect_object (G_OBJECT (model
->priv
->db
),
597 G_CALLBACK (rhythmdb_query_model_entry_changed_cb
),
599 g_signal_connect_object (G_OBJECT (model
->priv
->db
),
601 G_CALLBACK (rhythmdb_query_model_entry_deleted_cb
),
604 return G_OBJECT (model
);
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
),
625 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
626 G_CALLBACK (rhythmdb_query_model_base_row_deleted
),
628 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
629 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped
),
631 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
632 G_CALLBACK (rhythmdb_query_model_base_complete
),
634 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
635 G_CALLBACK (rhythmdb_query_model_base_rows_reordered
),
637 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
638 G_CALLBACK (rhythmdb_query_model_base_entry_removed
),
640 g_signal_handlers_disconnect_by_func (G_OBJECT (model
->priv
->base_model
),
641 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed
),
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
);
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
);
688 rhythmdb_query_model_new (RhythmDB
*db
,
690 GCompareDataFunc sort_func
,
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
,
703 g_return_val_if_fail (model
->priv
!= NULL
, NULL
);
709 rhythmdb_query_model_new_empty (RhythmDB
*db
)
711 return g_object_new (RHYTHMDB_TYPE_QUERY_MODEL
,
716 rhythmdb_query_model_copy_contents (RhythmDBQueryModel
*dest
,
717 RhythmDBQueryModel
*src
)
719 GSequencePtr ptr
, next
;
720 RhythmDBEntry
*entry
;
722 if (src
->priv
->entries
== NULL
)
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);
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
),
750 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
751 G_CALLBACK (rhythmdb_query_model_base_row_deleted
),
753 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
754 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped
),
756 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
757 G_CALLBACK (rhythmdb_query_model_base_complete
),
759 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
760 G_CALLBACK (rhythmdb_query_model_base_rows_reordered
),
762 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
763 G_CALLBACK (rhythmdb_query_model_base_entry_removed
),
765 g_signal_handlers_disconnect_by_func (model
->priv
->base_model
,
766 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed
),
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
,
780 G_CALLBACK (rhythmdb_query_model_base_row_inserted
),
782 g_signal_connect_object (model
->priv
->base_model
,
784 G_CALLBACK (rhythmdb_query_model_base_row_deleted
),
786 g_signal_connect_object (model
->priv
->base_model
,
788 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped
),
790 g_signal_connect_object (model
->priv
->base_model
,
792 G_CALLBACK (rhythmdb_query_model_base_complete
),
794 g_signal_connect_object (model
->priv
->base_model
,
796 G_CALLBACK (rhythmdb_query_model_base_rows_reordered
),
798 g_signal_connect_object (model
->priv
->base_model
,
800 G_CALLBACK (rhythmdb_query_model_base_entry_removed
),
802 g_signal_connect_object (model
->priv
->base_model
,
803 "entry-prop-changed",
804 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed
),
808 rhythmdb_query_model_copy_contents (model
, model
->priv
->base_model
);
813 rhythmdb_query_model_has_pending_changes (RhythmDBQueryModel
*model
)
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
);
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
))
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
)
838 if (rhythmdb_evaluate_query (db
, model
->priv
->query
, entry
)) {
839 rhythmdb_query_model_do_insert (model
, entry
, -1);
845 rhythmdb_query_model_entry_changed_cb (RhythmDB
*db
,
846 RhythmDBEntry
*entry
,
848 RhythmDBQueryModel
*model
)
850 gboolean hidden
= FALSE
;
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
859 rhythmdb_query_model_entry_added_cb (db
, entry
, model
);
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
);
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
);
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 */
925 if (rhythmdb_query_model_entry_to_iter (model
, entry
, &iter
)) {
926 path
= rhythmdb_query_model_get_path (GTK_TREE_MODEL (model
),
928 gtk_tree_model_row_changed (GTK_TREE_MODEL (model
),
930 gtk_tree_path_free (path
);
936 rhythmdb_query_model_base_entry_prop_changed (RhythmDBQueryModel
*base_model
,
937 RhythmDBEntry
*entry
,
938 RhythmDBPropType prop
,
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
);
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
);
963 idle_process_update_idle (struct RhythmDBQueryModelUpdate
*update
)
965 GDK_THREADS_ENTER ();
966 idle_process_update (update
);
967 GDK_THREADS_LEAVE ();
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
);
978 g_idle_add ((GSourceFunc
) idle_process_update_idle
, update
);
982 idle_process_update (struct RhythmDBQueryModelUpdate
*update
)
984 switch (update
->type
) {
985 case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED
:
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
;
997 g_hash_table_lookup (base_model
->priv
->reverse_map
, entry
) == NULL
)
1000 rhythmdb_query_model_do_insert (update
->model
, entry
, -1);
1003 rhythmdb_entry_unref (entry
);
1006 g_ptr_array_free (update
->entrydata
.entries
, TRUE
);
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
);
1017 case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE
:
1018 g_signal_emit (G_OBJECT (update
->model
), rhythmdb_query_model_signals
[COMPLETE
], 0);
1022 g_atomic_int_add (&update
->model
->priv
->pending_update_count
, -1);
1023 g_object_unref (update
->model
);
1028 rhythmdb_query_model_add_entry (RhythmDBQueryModel
*model
,
1029 RhythmDBEntry
*entry
,
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");
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
));
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
);
1063 rhythmdb_query_model_get_size (RhythmDBQueryModel
*model
)
1065 return model
->priv
->total_size
;
1069 rhythmdb_query_model_get_duration (RhythmDBQueryModel
*model
)
1071 return model
->priv
->total_duration
;
1075 rhythmdb_query_model_insert_into_main_list (RhythmDBQueryModel
*model
,
1076 RhythmDBEntry
*entry
,
1081 /* take reference; released when removed from hash */
1082 rhythmdb_entry_ref (entry
);
1084 if (model
->priv
->sort_func
) {
1085 GCompareDataFunc sort_func
;
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
;
1095 sort_func
= model
->priv
->sort_func
;
1096 sort_data
= model
->priv
->sort_data
;
1099 ptr
= g_sequence_insert_sorted (model
->priv
->entries
,
1105 ptr
= g_sequence_get_end_ptr (model
->priv
->entries
);
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
);
1122 rhythmdb_query_model_insert_into_limited_list (RhythmDBQueryModel
*model
,
1123 RhythmDBEntry
*entry
)
1127 /* take reference; released when removed from hash */
1128 rhythmdb_entry_ref (entry
);
1130 if (model
->priv
->sort_func
) {
1131 GCompareDataFunc sort_func
;
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
;
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
,
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
);
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
);
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
);
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
);
1204 rhythmdb_query_model_update_limited_entries (RhythmDBQueryModel
*model
)
1206 RhythmDBEntry
*entry
;
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 */
1227 ptr
= g_sequence_get_begin_ptr (model
->priv
->limited_entries
);
1228 if (!ptr
|| ptr
== g_sequence_get_end_ptr (model
->priv
->limited_entries
))
1230 entry
= (RhythmDBEntry
*) g_sequence_ptr_get_data (ptr
);
1234 if (!rhythmdb_query_model_within_limit (model
, entry
))
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
),
1249 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
),
1251 gtk_tree_path_free (path
);
1256 rhythmdb_query_model_emit_reorder (RhythmDBQueryModel
*model
,
1265 if (new_pos
== old_pos
) {
1266 /* it hasn't moved, don't emit a re-order */
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
++)
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
++)
1283 /* it has moved up the list */
1284 for (i
= 0; i
< new_pos
; 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
++)
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
),
1299 gtk_tree_path_free (path
);
1305 rhythmdb_query_model_do_reorder (RhythmDBQueryModel
*model
,
1306 RhythmDBEntry
*entry
)
1309 int old_pos
, new_pos
;
1312 GCompareDataFunc sort_func
;
1314 struct ReverseSortData reverse_data
;
1316 if (model
->priv
->sort_func
== NULL
)
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
;
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
);
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
);
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
),
1352 gtk_tree_model_row_changed (GTK_TREE_MODEL (model
),
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
,
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
);
1376 rhythmdb_query_model_do_insert (RhythmDBQueryModel
*model
,
1377 RhythmDBEntry
*entry
,
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
)
1390 /* take temporary ref */
1391 rhythmdb_entry_ref (entry
);
1393 ptr
= g_hash_table_lookup (model
->priv
->limited_reverse_map
, entry
);
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
),
1409 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
),
1411 gtk_tree_path_free (path
);
1413 rhythmdb_query_model_update_limited_entries (model
);
1417 rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel
*model
,
1418 RhythmDBEntry
*entry
)
1422 ptr
= g_hash_table_lookup (model
->priv
->reverse_map
, entry
);
1424 rhythmdb_query_model_remove_from_main_list (model
, entry
);
1425 rhythmdb_query_model_update_limited_entries (model
);
1429 ptr
= g_hash_table_lookup (model
->priv
->limited_reverse_map
, entry
);
1431 rhythmdb_query_model_remove_from_limited_list (model
, entry
);
1432 rhythmdb_query_model_update_limited_entries (model
);
1438 rhythmdb_query_model_move_entry (RhythmDBQueryModel
*model
,
1439 RhythmDBEntry
*entry
,
1446 ptr
= g_hash_table_lookup (model
->priv
->reverse_map
, entry
);
1450 nptr
= g_sequence_get_ptr_at_pos (model
->priv
->entries
, index
);
1451 if ((nptr
== NULL
) || (ptr
== nptr
))
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
);
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
1487 g_signal_emit (G_OBJECT (model
),
1488 rhythmdb_query_model_signals
[ENTRY_REMOVED
], 0,
1490 rhythmdb_query_model_filter_out_entry (model
, entry
);
1496 rhythmdb_query_model_entry_to_iter (RhythmDBQueryModel
*model
,
1497 RhythmDBEntry
*entry
,
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
);
1510 iter
->stamp
= model
->priv
->stamp
;
1511 iter
->user_data
= ptr
;
1516 rhythmdb_query_model_tree_path_to_entry (RhythmDBQueryModel
*model
,
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
);
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);
1535 rhythmdb_query_model_get_next_from_entry (RhythmDBQueryModel
*model
,
1536 RhythmDBEntry
*entry
)
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
))
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
))
1551 return rhythmdb_query_model_iter_to_entry (model
, &iter
);
1555 rhythmdb_query_model_get_previous_from_entry (RhythmDBQueryModel
*model
,
1556 RhythmDBEntry
*entry
)
1561 g_return_val_if_fail (entry
!= NULL
, NULL
);
1563 if (!rhythmdb_query_model_entry_to_iter (model
, entry
, &iter
))
1566 path
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
), &iter
);
1568 if (!gtk_tree_path_prev (path
)) {
1569 gtk_tree_path_free (path
);
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
);
1580 rhythmdb_query_model_row_draggable (RbTreeDragSource
*dragsource
,
1587 rhythmdb_query_model_drag_data_delete (RbTreeDragSource
*dragsource
,
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
;
1601 for (; paths
; paths
= paths
->next
) {
1603 path
= gtk_tree_row_reference_get_path (paths
->data
);
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
;
1621 rhythmdb_query_model_drag_data_get (RbTreeDragSource
*dragsource
,
1623 GtkSelectionData
*selection_data
)
1625 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (dragsource
);
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
;
1635 gboolean need_newline
= FALSE
;
1637 data
= g_string_new ("");
1639 for (tem
= paths
; tem
; tem
= tem
->next
) {
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
)
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
,
1666 g_string_free (data
, TRUE
);
1675 rhythmdb_query_model_drag_data_received (RbTreeDragDest
*drag_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
;
1689 entry
= rhythmdb_query_model_tree_path_to_entry (model
, dest
);
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
);
1698 result
= rhythmdb_query_model_drag_data_received ((RbTreeDragDest
*)model
->priv
->base_model
,
1699 base_dest
, pos
, selection_data
);
1701 gtk_tree_path_free (base_dest
);
1706 rb_debug ("drag received");
1708 if (model
->priv
->sort_func
!= NULL
)
1711 if (selection_data
->format
== 8 && selection_data
->length
>= 0) {
1715 RhythmDBEntry
*entry
;
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
);
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)
1735 entry
= rhythmdb_entry_lookup_by_location (model
->priv
->db
,
1738 if (entry
== NULL
) {
1741 if (g_sequence_ptr_is_end (ptr
))
1744 pos
= g_sequence_ptr_get_position (ptr
);
1746 g_signal_emit (G_OBJECT (model
),
1747 rhythmdb_query_model_signals
[NON_ENTRY_DROPPED
],
1750 GSequencePtr old_ptr
;
1751 GtkTreePath
*tem_path
;
1755 old_ptr
= g_hash_table_lookup (model
->priv
->reverse_map
, entry
);
1756 /* trying to drag drop an entry on itself ! */
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. */
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
));
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
);
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
);
1790 tem_path
= rhythmdb_query_model_get_path (GTK_TREE_MODEL (model
),
1792 gtk_tree_model_row_inserted (GTK_TREE_MODEL (model
),
1793 tem_path
, &tem_iter
);
1794 gtk_tree_path_free (tem_path
);
1806 rhythmdb_query_model_row_drop_possible (RbTreeDragDest
*drag_dest
,
1808 GtkTreeViewDropPosition pos
,
1809 GtkSelectionData
*selection_data
)
1811 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (drag_dest
);
1814 if (model
->priv
->sort_func
!= NULL
)
1817 model
= model
->priv
->base_model
;
1823 rhythmdb_query_model_row_drop_position (RbTreeDragDest
*drag_dest
,
1824 GtkTreePath
*dest_path
,
1826 GtkTreeViewDropPosition
*pos
)
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.
1841 rhythmdb_query_model_add_results (RhythmDBQueryResults
*results
,
1844 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (results
);
1845 struct RhythmDBQueryModelUpdate
*update
;
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
);
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
;
1888 rhythmdb_query_model_get_n_columns (GtkTreeModel
*tree_model
)
1894 rhythmdb_query_model_get_column_type (GtkTreeModel
*tree_model
,
1899 return RHYTHMDB_TYPE_ENTRY
;
1903 g_assert_not_reached ();
1904 return G_TYPE_INVALID
;
1909 rhythmdb_query_model_get_iter (GtkTreeModel
*tree_model
,
1913 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (tree_model
);
1917 index
= gtk_tree_path_get_indices (path
)[0];
1919 if (index
>= g_sequence_get_length (model
->priv
->entries
))
1922 ptr
= g_sequence_get_ptr_at_pos (model
->priv
->entries
, index
);
1925 iter
->stamp
= model
->priv
->stamp
;
1926 iter
->user_data
= ptr
;
1931 static GtkTreePath
*
1932 rhythmdb_query_model_get_path (GtkTreeModel
*tree_model
,
1935 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (tree_model
);
1938 g_return_val_if_fail (iter
->stamp
== model
->priv
->stamp
, NULL
);
1940 if (g_sequence_ptr_is_end (iter
->user_data
))
1943 path
= gtk_tree_path_new ();
1944 gtk_tree_path_append_index (path
, g_sequence_ptr_get_position (iter
->user_data
));
1949 rhythmdb_query_model_get_value (GtkTreeModel
*tree_model
,
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
);
1964 g_value_init (value
, RHYTHMDB_TYPE_ENTRY
);
1965 g_value_set_boxed (value
, entry
);
1968 g_value_init (value
, G_TYPE_INT
);
1969 g_value_set_int (value
, g_sequence_ptr_get_position (iter
->user_data
)+1);
1972 g_assert_not_reached ();
1977 rhythmdb_query_model_iter_next (GtkTreeModel
*tree_model
,
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
);
1990 rhythmdb_query_model_iter_children (GtkTreeModel
*tree_model
,
1992 GtkTreeIter
*parent
)
1994 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (tree_model
);
1999 if (g_sequence_get_length (model
->priv
->entries
) == 0)
2002 iter
->stamp
= model
->priv
->stamp
;
2003 iter
->user_data
= g_sequence_get_begin_ptr (model
->priv
->entries
);
2009 rhythmdb_query_model_iter_has_child (GtkTreeModel
*tree_model
,
2016 rhythmdb_query_model_iter_n_children (GtkTreeModel
*tree_model
,
2019 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (tree_model
);
2022 return g_sequence_get_length (model
->priv
->entries
);
2024 g_return_val_if_fail (model
->priv
->stamp
== iter
->stamp
, -1);
2030 rhythmdb_query_model_iter_nth_child (GtkTreeModel
*tree_model
,
2032 GtkTreeIter
*parent
,
2035 RhythmDBQueryModel
*model
= RHYTHMDB_QUERY_MODEL (tree_model
);
2041 child
= g_sequence_get_ptr_at_pos (model
->priv
->entries
, n
);
2043 if (g_sequence_ptr_is_end (child
))
2046 iter
->stamp
= model
->priv
->stamp
;
2047 iter
->user_data
= child
;
2053 rhythmdb_query_model_iter_parent (GtkTreeModel
*tree_model
,
2061 rhythmdb_query_model_compute_status_normal (RhythmDBQueryModel
*model
,
2062 const char *singular
,
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
),
2073 apply_updated_entry_sequence (RhythmDBQueryModel
*model
,
2074 GSequence
*new_entries
)
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
),
2107 gtk_tree_path_free (path
);
2112 rhythmdb_query_model_set_sort_order (RhythmDBQueryModel
*model
,
2113 GCompareDataFunc sort_func
,
2115 GDestroyNotify sort_data_destroy
,
2116 gboolean sort_reverse
)
2118 GSequence
*new_entries
;
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
))
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
);
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
,
2160 ptr
= g_sequence_ptr_next (ptr
);
2163 apply_updated_entry_sequence (model
, new_entries
);
2168 rhythmdb_query_model_child_index_to_base_index (RhythmDBQueryModel
*model
,
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
))
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
);
2187 rhythmdb_query_model_base_index_to_child_index (RhythmDBQueryModel *model, int index)
2190 RhythmDBEntry *entry;
2193 g_assert (model->priv->base_model);
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))
2200 entry = (RhythmDBEntry*)g_sequence_ptr_get_data (ptr);
2202 ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2206 pos = g_sequence_ptr_get_position (ptr);
2211 rhythmdb_query_model_get_entry_index (RhythmDBQueryModel
*model
,
2212 RhythmDBEntry
*entry
)
2214 GSequencePtr ptr
= g_hash_table_lookup (model
->priv
->reverse_map
, entry
);
2217 return g_sequence_ptr_get_position (ptr
);
2223 rhythmdb_query_model_base_row_inserted (GtkTreeModel
*tree_model
,
2226 RhythmDBQueryModel
*model
)
2228 RhythmDBQueryModel
*base_model
= RHYTHMDB_QUERY_MODEL (tree_model
);
2229 RhythmDBEntry
*entry
;
2230 RhythmDBEntry
*prev_entry
;
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
))
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;
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
);
2262 rhythmdb_entry_unref (entry
);
2266 rhythmdb_query_model_base_row_deleted (GtkTreeModel
*base_model
,
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
);
2280 rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel
*base_model
,
2281 const char *location
,
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
));
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);
2297 rhythmdb_query_model_base_rows_reordered (GtkTreeModel
*base_model
,
2301 RhythmDBQueryModel
*model
)
2303 RhythmDBQueryModel
*base_query_model
= RHYTHMDB_QUERY_MODEL (base_model
);
2304 GSequence
*new_entries
;
2307 /* ignore, if this model sorts */
2308 if (model
->priv
->sort_func
)
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
);
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,
2341 rhythmdb_query_model_reapply_query (RhythmDBQueryModel
*model
,
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
,
2361 remove
= g_list_prepend (remove
, entry
);
2369 for (t
= remove
; t
; t
= t
->next
) {
2370 RhythmDBEntry
*entry
= t
->data
;
2371 rhythmdb_query_model_remove_from_limited_list (model
, entry
);
2375 g_list_free (remove
);
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
,
2388 remove
= g_list_prepend (remove
, entry
);
2396 for (t
= remove
; t
; t
= t
->next
) {
2397 RhythmDBEntry
*entry
= t
->data
;
2399 g_signal_emit (G_OBJECT (model
),
2400 rhythmdb_query_model_signals
[ENTRY_REMOVED
], 0,
2403 rhythmdb_query_model_remove_from_main_list (model
, entry
);
2407 g_list_free (remove
);
2413 rhythmdb_query_model_update_limited_entries (model
);
2417 _reverse_sorting_func (gpointer a
,
2419 struct ReverseSortData
*reverse_data
)
2421 return - reverse_data
->func (a
, b
, reverse_data
->data
);
2425 rhythmdb_query_model_location_sort_func (RhythmDBEntry
*a
,
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
) {
2440 } else if (b_val
== NULL
)
2443 return strcmp (a_val
, b_val
);
2447 rhythmdb_query_model_title_sort_func (RhythmDBEntry
*a
,
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
) {
2464 } else if (b_val
== NULL
)
2467 ret
= strcmp (a_val
, b_val
);
2472 return rhythmdb_query_model_location_sort_func (a
, b
, data
);
2476 rhythmdb_query_model_album_sort_func (RhythmDBEntry
*a
,
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
) {
2495 } else if (b_val
== NULL
)
2498 ret
= strcmp (a_val
, b_val
);
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
);
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
);
2513 return (a_num
< b_num
? -1 : 1);
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
) {
2524 } else if (b_val
== NULL
)
2527 return rhythmdb_query_model_location_sort_func (a
, b
, data
);
2531 rhythmdb_query_model_artist_sort_func (RhythmDBEntry
*a
,
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
) {
2547 } else if (b_val
== NULL
)
2550 ret
= strcmp (a_val
, b_val
);
2555 return rhythmdb_query_model_album_sort_func (a
, b
, data
);
2559 rhythmdb_query_model_genre_sort_func (RhythmDBEntry
*a
, RhythmDBEntry
*b
,
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
) {
2574 } else if (b_val
== NULL
)
2577 ret
= strcmp (a_val
, b_val
);
2582 return rhythmdb_query_model_artist_sort_func (a
, b
, data
);
2586 rhythmdb_query_model_track_sort_func (RhythmDBEntry
*a
,
2590 return rhythmdb_query_model_album_sort_func (a
, b
, data
);
2594 rhythmdb_query_model_double_ceiling_sort_func (RhythmDBEntry
*a
,
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
));
2607 return (a_val
> b_val
? 1 : -1);
2609 return rhythmdb_query_model_location_sort_func (a
, b
, data
);
2613 rhythmdb_query_model_ulong_sort_func (RhythmDBEntry
*a
,
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
);
2625 return (a_val
> b_val
? 1 : -1);
2627 return rhythmdb_query_model_location_sort_func (a
, b
, data
);
2631 rhythmdb_query_model_date_sort_func (RhythmDBEntry
*a
,
2636 gulong a_val
, b_val
;
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));
2645 else if (a_val
< b_val
)
2648 return rhythmdb_query_model_album_sort_func (a
, b
, data
);
2652 rhythmdb_query_model_string_sort_func (RhythmDBEntry
*a
,
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
) {
2670 } else if (b_val
== NULL
)
2673 ret
= strcmp (a_val
, b_val
);
2678 return rhythmdb_query_model_location_sort_func (a
, b
, data
);
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
:
2692 case RHYTHMDB_QUERY_MODEL_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
);
2703 result
= (current_count
<= limit_count
);
2707 case RHYTHMDB_QUERY_MODEL_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
;
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
);
2723 case RHYTHMDB_QUERY_MODEL_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
;
2732 current_time
+= rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_DURATION
);
2734 result
= (current_time
<= limit_time
);
2742 /* This should really be standard. */
2743 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
2746 rhythmdb_query_model_limit_type_get_type (void)
2748 static GType 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)"),
2762 etype
= g_enum_register_static ("RhythmDBQueryModelLimitType", values
);
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 ();