hythmbox-2.98/rhythmdb/rhythmdb-query-model.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rhythmdb-query-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found rhythmdb-query-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2003 Colin Walters <walters@gnome.org>
   4  *
   5  *  This program is free software; you can redistribute it and/or modify
   6  *  it under the terms of the GNU General Public License as published by
   7  *  the Free Software Foundation; either version 2 of the License, or
   8  *  (at your option) any later version.
   9  *
  10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  11  *  GStreamer plugins to be used and distributed together with GStreamer
  12  *  and Rhythmbox. This permission is above and beyond the permissions granted
  13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  14  *  you may extend this exception to your version of the code, but you are not
  15  *  obligated to do so. If you do not wish to do so, delete this exception
  16  *  statement from your version.
  17  *
  18  *  This program is distributed in the hope that it will be useful,
  19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21  *  GNU General Public License for more details.
  22  *
  23  *  You should have received a copy of the GNU General Public License
  24  *  along with this program; if not, write to the Free Software
  25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  26  *
  27  */
  28 
  29 #include "config.h"
  30 
  31 #include <unistd.h>
  32 #include <stdlib.h>
  33 #include <string.h>
  34 #include <stdio.h>
  35 #include <math.h>
  36 #include <glib.h>
  37 
  38 #include <gtk/gtk.h>
  39 
  40 #include "rhythmdb-query-model.h"
  41 #include "rb-debug.h"
  42 #include "rb-tree-dnd.h"
  43 #include "rb-marshal.h"
  44 #include "rb-util.h"
  45 
  46 struct ReverseSortData
  47 {
  48 	GCompareDataFunc	func;
  49 	gpointer		data;
  50 };
  51 
  52 static void rhythmdb_query_model_query_results_init (RhythmDBQueryResultsIface *iface);
  53 static void rhythmdb_query_model_tree_model_init (GtkTreeModelIface *iface);
  54 static void rhythmdb_query_model_drag_source_init (RbTreeDragSourceIface *iface);
  55 static void rhythmdb_query_model_drag_dest_init (RbTreeDragDestIface *iface);
  56 
  57 G_DEFINE_TYPE_WITH_CODE(RhythmDBQueryModel, rhythmdb_query_model, G_TYPE_OBJECT,
  58 			G_IMPLEMENT_INTERFACE(RHYTHMDB_TYPE_QUERY_RESULTS,
  59 					      rhythmdb_query_model_query_results_init)
  60 			G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
  61 					      rhythmdb_query_model_tree_model_init)
  62 			G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_SOURCE,
  63 					      rhythmdb_query_model_drag_source_init)
  64 			G_IMPLEMENT_INTERFACE(RB_TYPE_TREE_DRAG_DEST,
  65 					      rhythmdb_query_model_drag_dest_init))
  66 
  67 static void rhythmdb_query_model_init (RhythmDBQueryModel *shell_player);
  68 static void rhythmdb_query_model_constructed (GObject *object);
  69 static void rhythmdb_query_model_dispose (GObject *object);
  70 static void rhythmdb_query_model_finalize (GObject *object);
  71 static void rhythmdb_query_model_set_property (GObject *object,
  72 					       guint prop_id,
  73 					       const GValue *value,
  74 					       GParamSpec *pspec);
  75 static void rhythmdb_query_model_get_property (GObject *object,
  76 					       guint prop_id,
  77 					       GValue *value,
  78 					       GParamSpec *pspec);
  79 static void rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
  80 					    RhythmDBEntry *entry,
  81 					    gint index);
  82 static void rhythmdb_query_model_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
  83 						 RhythmDBQueryModel *model);
  84 static void rhythmdb_query_model_entry_changed_cb (RhythmDB *db, RhythmDBEntry *entry,
  85 						   GArray *changes, RhythmDBQueryModel *model);
  86 static void rhythmdb_query_model_entry_deleted_cb (RhythmDB *db, RhythmDBEntry *entry,
  87 						   RhythmDBQueryModel *model);
  88 
  89 static void rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
  90 						   RhythmDBEntry *entry);
  91 static gboolean rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model, RhythmDBEntry *entry);
  92 static gboolean rhythmdb_query_model_emit_reorder (RhythmDBQueryModel *model, gint old_pos, gint new_pos);
  93 static gboolean rhythmdb_query_model_drag_data_get (RbTreeDragSource *dragsource,
  94 							  GList *paths,
  95 							  GtkSelectionData *selection_data);
  96 static gboolean rhythmdb_query_model_drag_data_delete (RbTreeDragSource *dragsource,
  97 							     GList *paths);
  98 static gboolean rhythmdb_query_model_row_draggable (RbTreeDragSource *dragsource,
  99 							  GList *paths);
 100 static gboolean rhythmdb_query_model_drag_data_received (RbTreeDragDest *drag_dest,
 101 							 GtkTreePath *dest,
 102 							 GtkTreeViewDropPosition pos,
 103 							 GtkSelectionData  *selection_data);
 104 static gboolean rhythmdb_query_model_row_drop_possible (RbTreeDragDest *drag_dest,
 105 							GtkTreePath *dest,
 106 							GtkTreeViewDropPosition pos,
 107 							GtkSelectionData  *selection_data);
 108 static gboolean rhythmdb_query_model_row_drop_position (RbTreeDragDest   *drag_dest,
 109 							GtkTreePath       *dest_path,
 110 							GList *targets,
 111 							GtkTreeViewDropPosition *pos);
 112 
 113 static void rhythmdb_query_model_set_query (RhythmDBQueryResults *results, GPtrArray *query);
 114 static void rhythmdb_query_model_add_results (RhythmDBQueryResults *results, GPtrArray *entries);
 115 static void rhythmdb_query_model_query_complete (RhythmDBQueryResults *results);
 116 
 117 static GtkTreeModelFlags rhythmdb_query_model_get_flags (GtkTreeModel *model);
 118 static gint rhythmdb_query_model_get_n_columns (GtkTreeModel *tree_model);
 119 static GType rhythmdb_query_model_get_column_type (GtkTreeModel *tree_model, int index);
 120 static gboolean rhythmdb_query_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter,
 121 					       GtkTreePath  *path);
 122 static GtkTreePath * rhythmdb_query_model_get_path (GtkTreeModel *tree_model,
 123 						    GtkTreeIter  *iter);
 124 static void rhythmdb_query_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter,
 125 					    gint column, GValue *value);
 126 static gboolean rhythmdb_query_model_iter_next (GtkTreeModel  *tree_model,
 127 						GtkTreeIter   *iter);
 128 static gboolean rhythmdb_query_model_iter_children (GtkTreeModel *tree_model,
 129 						    GtkTreeIter  *iter,
 130 						    GtkTreeIter  *parent);
 131 static gboolean rhythmdb_query_model_iter_has_child (GtkTreeModel *tree_model,
 132 						     GtkTreeIter  *iter);
 133 static gint rhythmdb_query_model_iter_n_children (GtkTreeModel *tree_model,
 134 						  GtkTreeIter  *iter);
 135 static gboolean rhythmdb_query_model_iter_nth_child (GtkTreeModel *tree_model,
 136 						     GtkTreeIter *iter, GtkTreeIter *parent,
 137 						     gint n);
 138 static gboolean rhythmdb_query_model_iter_parent (GtkTreeModel *tree_model,
 139 						  GtkTreeIter  *iter,
 140 						  GtkTreeIter  *child);
 141 
 142 static void rhythmdb_query_model_base_row_inserted (GtkTreeModel *base_model,
 143 						    GtkTreePath *path,
 144 						    GtkTreeIter *iter,
 145 						    RhythmDBQueryModel *model);
 146 static void rhythmdb_query_model_base_row_deleted (GtkTreeModel *base_model,
 147 						   GtkTreePath *path,
 148 						   RhythmDBQueryModel *model);
 149 static void rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel *base_model,
 150 							 const char *location,
 151 							 int position,
 152 							 RhythmDBQueryModel *model);
 153 static void rhythmdb_query_model_base_complete (GtkTreeModel *base_model,
 154 						RhythmDBQueryModel *model);
 155 static void rhythmdb_query_model_base_rows_reordered (GtkTreeModel *base_model,
 156 						      GtkTreePath *arg1,
 157 						      GtkTreeIter *arg2,
 158 						      gint *order_map,
 159 						      RhythmDBQueryModel *model);
 160 static void rhythmdb_query_model_base_entry_removed (RhythmDBQueryModel *base_model,
 161 						     RhythmDBEntry *entry,
 162 						     RhythmDBQueryModel *model);
 163 static void rhythmdb_query_model_base_entry_prop_changed (RhythmDBQueryModel *base_model,
 164 							  RhythmDBEntry *entry,
 165 							  RhythmDBPropType prop,
 166 							  const GValue *old,
 167 							  const GValue *new_value,
 168 							  RhythmDBQueryModel *model);
 169 static int rhythmdb_query_model_child_index_to_base_index (RhythmDBQueryModel *model, int index);
 170 
 171 static gint _reverse_sorting_func (gpointer a, gpointer b, struct ReverseSortData *model);
 172 static gboolean rhythmdb_query_model_within_limit (RhythmDBQueryModel *model,
 173 						   RhythmDBEntry *entry);
 174 static gboolean rhythmdb_query_model_reapply_query_cb (RhythmDBQueryModel *model);
 175 
 176 struct RhythmDBQueryModelUpdate
 177 {
 178 	RhythmDBQueryModel *model;
 179 	enum {
 180 		RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED,
 181 		RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX,
 182 		RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE,
 183 	} type;
 184 
 185 	union {
 186 		struct {
 187 			RhythmDBEntry *entry;
 188 			gint index;
 189 		} data;
 190 		GPtrArray *entries;
 191 	} entrydata;
 192 };
 193 
 194 static void rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update);
 195 
 196 static void idle_process_update (struct RhythmDBQueryModelUpdate *update);
 197 
 198 enum {
 199 	TARGET_ENTRIES,
 200 	TARGET_URIS
 201 };
 202 
 203 static const GtkTargetEntry rhythmdb_query_model_drag_types[] = {
 204 	{ "application/x-rhythmbox-entry", 0, TARGET_ENTRIES },
 205 	{ "text/uri-list", 0, TARGET_URIS },
 206 };
 207 
 208 static GtkTargetList *rhythmdb_query_model_drag_target_list = NULL;
 209 
 210 struct _RhythmDBQueryModelPrivate
 211 {
 212 	RhythmDB *db;
 213 
 214 	RhythmDBQueryModel *base_model;
 215 
 216 	GCompareDataFunc sort_func;
 217 	gpointer sort_data;
 218 	GDestroyNotify sort_data_destroy;
 219 	gboolean sort_reverse;
 220 
 221 	GPtrArray *query;
 222 	GPtrArray *original_query;
 223 
 224 	guint stamp;
 225 
 226 	RhythmDBQueryModelLimitType limit_type;
 227 	GArray *limit_value;
 228 
 229 	glong total_duration;
 230 	guint64 total_size;
 231 
 232 	GSequence *entries;
 233 	GHashTable *reverse_map;
 234 	GSequence *limited_entries;
 235 	GHashTable *limited_reverse_map;
 236 	GHashTable *hidden_entry_map;
 237 
 238 	gint pending_update_count;
 239 
 240 	gboolean reorder_drag_and_drop;
 241 	gboolean show_hidden;
 242 
 243 	gint query_reapply_timeout_id;
 244 };
 245 
 246 #define RHYTHMDB_QUERY_MODEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RHYTHMDB_TYPE_QUERY_MODEL, RhythmDBQueryModelPrivate))
 247 
 248 enum
 249 {
 250 	PROP_0,
 251 	PROP_RHYTHMDB,
 252 	PROP_QUERY,
 253 	PROP_SORT_FUNC,
 254 	PROP_SORT_DATA,
 255 	PROP_SORT_DATA_DESTROY,
 256 	PROP_SORT_REVERSE,
 257 	PROP_LIMIT_TYPE,
 258 	PROP_LIMIT_VALUE,
 259 	PROP_SHOW_HIDDEN,
 260 	PROP_BASE_MODEL,
 261 };
 262 
 263 enum
 264 {
 265 	COMPLETE,
 266 	ENTRY_PROP_CHANGED,
 267 	ENTRY_REMOVED,
 268 	NON_ENTRY_DROPPED,
 269 	POST_ENTRY_DELETE,
 270 	FILTER_ENTRY_DROP,
 271 	LAST_SIGNAL
 272 };
 273 
 274 static guint rhythmdb_query_model_signals[LAST_SIGNAL] = { 0 };
 275 
 276 /**
 277  * SECTION:rhythmdb-query-model
 278  * @short_description: a #GtkTreeModel containing #RhythmDBEntry items
 279  *
 280  * A RhythmDBQueryModel contains an ordered set of #RhythmDBEntry items,
 281  * either generated by running a query against the database, or populated
 282  * by adding individual entries.
 283  *
 284  * All sources use a #RhythmDBQueryModel to provide their track listing.
 285  * Since most sources provide a search or filtering capacity, there is
 286  * usually a distinction between the source's base query model, which contains
 287  * all entries for the source, and its current query model, which reflects
 288  * the current search terms and filter selections.
 289  *
 290  * A RhythmDBQueryModel can be populated directly from the #RhythmDB, or it
 291  * can be chained to another query model.  Chained query models inherit the
 292  * set of entries from the query model they chain to and then restrict the
 293  * set using a query.
 294  *
 295  * The query model consists of a #GSequence, which provides ordering of entries,
 296  * and a #GHashTable, which allows efficient checks to see if a given entry
 297  * is in the model.  A side effect of this is that an entry can only be placed
 298  * into a query model in one location.
 299  *
 300  * In addition to the query, a #RhythmDBQueryModel can have a sort order and
 301  * a limit, specified in terms of file size, duration, or number of entries.
 302  * A query model can only have a limit if it also has a sort order, as the
 303  * sort order is required to determine which entries fall inside the limit.
 304  * When a limit is applied, entries that match the query but fall outside the
 305  * limit are maintained in a separate #GSequence and #GHashTable inside the
 306  * query model.
 307  */
 308 
 309 static void
 310 rhythmdb_query_model_class_init (RhythmDBQueryModelClass *klass)
 311 {
 312 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 313 
 314 	if (!rhythmdb_query_model_drag_target_list)
 315 		rhythmdb_query_model_drag_target_list
 316 			= gtk_target_list_new (rhythmdb_query_model_drag_types,
 317 					       G_N_ELEMENTS (rhythmdb_query_model_drag_types));
 318 
 319 	object_class->set_property = rhythmdb_query_model_set_property;
 320 	object_class->get_property = rhythmdb_query_model_get_property;
 321 
 322 	object_class->dispose = rhythmdb_query_model_dispose;
 323 	object_class->finalize = rhythmdb_query_model_finalize;
 324 	object_class->constructed = rhythmdb_query_model_constructed;
 325 
 326 	g_object_class_install_property (object_class,
 327 					 PROP_RHYTHMDB,
 328 					 g_param_spec_object ("db",
 329 							      "RhythmDB",
 330 							      "RhythmDB object",
 331 							      RHYTHMDB_TYPE,
 332 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 333 	g_object_class_install_property (object_class,
 334 					 PROP_QUERY,
 335 					 g_param_spec_pointer ("query",
 336 							      "Query",
 337 							      "RhythmDBQuery",
 338 							      G_PARAM_READWRITE));
 339 	g_object_class_install_property (object_class,
 340 					 PROP_SORT_FUNC,
 341 					 g_param_spec_pointer ("sort-func",
 342 							      "SortFunc",
 343 							      "Sort function",
 344 							       G_PARAM_READWRITE));
 345 	g_object_class_install_property (object_class,
 346 					 PROP_SORT_DATA,
 347 					 g_param_spec_pointer ("sort-data",
 348 							       "Sort data",
 349 							       "Sort data",
 350 							       G_PARAM_READWRITE));
 351 	g_object_class_install_property (object_class,
 352 					 PROP_SORT_DATA_DESTROY,
 353 					 g_param_spec_pointer ("sort-data-destroy",
 354 							       "Sort data destroy",
 355 							       "Sort data destroy function",
 356 							       G_PARAM_READWRITE));
 357 	g_object_class_install_property (object_class,
 358 					 PROP_SORT_REVERSE,
 359 					 g_param_spec_boolean ("sort-reverse",
 360 							      "sort-reverse",
 361 							      "Reverse sort order flag",
 362 							      FALSE,
 363 							      G_PARAM_READWRITE));
 364 	g_object_class_install_property (object_class,
 365 					 PROP_LIMIT_TYPE,
 366 					 g_param_spec_enum ("limit-type",
 367 							    "limit-type",
 368 							    "type of limit",
 369 							    RHYTHMDB_TYPE_QUERY_MODEL_LIMIT_TYPE,
 370 							    RHYTHMDB_QUERY_MODEL_LIMIT_NONE,
 371 							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 372 	g_object_class_install_property (object_class,
 373 					 PROP_LIMIT_VALUE,
 374 					 g_param_spec_boxed ("limit-value",
 375 							     "limit-value",
 376 							     "value of limit",
 377 							     G_TYPE_ARRAY,
 378 							     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 379 	g_object_class_install_property (object_class,
 380 					 PROP_SHOW_HIDDEN,
 381 					 g_param_spec_boolean ("show-hidden",
 382 							       "show hidden",
 383 							       "if TRUE, include entries that are ordinarily hidden",
 384 							       FALSE,
 385 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 386 	g_object_class_install_property (object_class,
 387 					 PROP_BASE_MODEL,
 388 					 g_param_spec_object ("base-model",
 389 						 	      "base-model",
 390 							      "base RhythmDBQueryModel",
 391 							      RHYTHMDB_TYPE_QUERY_MODEL,
 392 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 393 
 394 	/**
 395 	 * RhythmDBQueryModel::entry-prop-changed:
 396 	 * @model: the #RhythmDBQueryModel
 397 	 * @entry: the #RhythmDBEntry that changed
 398 	 * @prop: the #RhythmDBPropType that was changed
 399 	 * @old: the previous value for the property
 400 	 * @new_value: the new value for the property
 401 	 *
 402 	 * Emitted when an entry in the query model is changed.  When multiple
 403 	 * properties are changed, the entry-prop-changed signals will be emitted
 404 	 * in the order that the changes were made.  At the point that the
 405 	 * signal is emitted, all changes have already been applied to the
 406 	 * #RhythmDBEntry.
 407 	 */
 408 	rhythmdb_query_model_signals[ENTRY_PROP_CHANGED] =
 409 		g_signal_new ("entry-prop-changed",
 410 			      RHYTHMDB_TYPE_QUERY_MODEL,
 411 			      G_SIGNAL_RUN_LAST,
 412 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_prop_changed),
 413 			      NULL, NULL,
 414 			      rb_marshal_VOID__BOXED_INT_POINTER_POINTER,
 415 			      G_TYPE_NONE,
 416 			      4, RHYTHMDB_TYPE_ENTRY, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER);
 417 	/**
 418 	 * RhythmDBQueryModel::entry-removed:
 419 	 * @model: the #RhythmDBQueryModel
 420 	 * @entry: the #RhythmDBEntry that was removed
 421 	 *
 422 	 * Emitted when an entry is removed from the model.  There is some
 423 	 * difference between this and the #GtkTreeModel row-removed signal
 424 	 * but I don't know what it is right now.
 425 	 */
 426 	rhythmdb_query_model_signals[ENTRY_REMOVED] =
 427 		g_signal_new ("entry-removed",
 428 			      RHYTHMDB_TYPE_QUERY_MODEL,
 429 			      G_SIGNAL_RUN_LAST,
 430 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, entry_removed),
 431 			      NULL, NULL,
 432 			      g_cclosure_marshal_VOID__BOXED,
 433 			      G_TYPE_NONE,
 434 			      1, RHYTHMDB_TYPE_ENTRY);
 435 	/**
 436 	 * RhythmDBQueryModel::non-entry-dropped:
 437 	 * @model: the #RhythmDBQueryModel
 438 	 * @uri: the URI that was dropped
 439 	 * @position: the position in the query model at which it was dropped
 440 	 *
 441 	 * Emitted when a URI that does not match an existing #RhythmDBEntry
 442 	 * is dropped into the query model.
 443 	 */
 444 	rhythmdb_query_model_signals[NON_ENTRY_DROPPED] =
 445 		g_signal_new ("non-entry-dropped",
 446 			      RHYTHMDB_TYPE_QUERY_MODEL,
 447 			      G_SIGNAL_RUN_LAST,
 448 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, non_entry_dropped),
 449 			      NULL, NULL,
 450 			      rb_marshal_VOID__POINTER_INT,
 451 			      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
 452 	/**
 453 	 * RhythmDBQueryModel::complete:
 454 	 * @model: the #RhythmDBQueryModel
 455 	 *
 456 	 * Emitted when the database query populating the model is complete.
 457 	 */
 458 	rhythmdb_query_model_signals[COMPLETE] =
 459 		g_signal_new ("complete",
 460 			      RHYTHMDB_TYPE_QUERY_MODEL,
 461 			      G_SIGNAL_RUN_LAST,
 462 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, complete),
 463 			      NULL, NULL,
 464 			      g_cclosure_marshal_VOID__VOID,
 465 			      G_TYPE_NONE, 0);
 466 	/**
 467 	 * RhythmDBQueryModel::post-entry-delete:
 468 	 * @model: the #RhythmDBQueryModel
 469 	 * @entry: the #RhythmDBEntry that was removed
 470 	 *
 471 	 * Emitted after an entry has been removed from the model.
 472 	 */
 473 	rhythmdb_query_model_signals[POST_ENTRY_DELETE] =
 474 		g_signal_new ("post-entry-delete",
 475 			      RHYTHMDB_TYPE_QUERY_MODEL,
 476 			      G_SIGNAL_RUN_LAST,
 477 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, post_entry_delete),
 478 			      NULL, NULL,
 479 			      g_cclosure_marshal_VOID__BOXED,
 480 			      G_TYPE_NONE,
 481 			      1, RHYTHMDB_TYPE_ENTRY);
 482 	/**
 483 	 * RhythmDBQueryModel::filter-entry-drop:
 484 	 * @model: the #RhythmDBQueryModel
 485 	 * @entry: the #RhythmDBEntry being dropped
 486 	 *
 487 	 * Emitted when an entry is being added to a model by drag-and-drop.
 488 	 * This allows the owner of the model to filter out entries that should
 489 	 * not be added to the model (based on entry type, for instance).
 490 	 * If the signal handler returns %FALSE, the entry will not be added.
 491 	 */
 492 	rhythmdb_query_model_signals[FILTER_ENTRY_DROP] =
 493 		g_signal_new ("filter-entry-drop",
 494 			      RHYTHMDB_TYPE_QUERY_MODEL,
 495 			      G_SIGNAL_RUN_LAST,
 496 			      G_STRUCT_OFFSET (RhythmDBQueryModelClass, filter_entry_drop),
 497 			      NULL, NULL,
 498 			      rb_marshal_BOOLEAN__BOXED,
 499 			      G_TYPE_BOOLEAN,
 500 			      1, RHYTHMDB_TYPE_ENTRY);
 501 
 502 	g_type_class_add_private (klass, sizeof (RhythmDBQueryModelPrivate));
 503 }
 504 
 505 static void
 506 rhythmdb_query_model_query_results_init (RhythmDBQueryResultsIface *iface)
 507 {
 508 	iface->set_query = rhythmdb_query_model_set_query;
 509 	iface->add_results = rhythmdb_query_model_add_results;
 510 	iface->query_complete = rhythmdb_query_model_query_complete;
 511 }
 512 
 513 static void
 514 rhythmdb_query_model_tree_model_init (GtkTreeModelIface *iface)
 515 {
 516 	iface->get_flags = rhythmdb_query_model_get_flags;
 517 	iface->get_n_columns = rhythmdb_query_model_get_n_columns;
 518 	iface->get_column_type = rhythmdb_query_model_get_column_type;
 519 	iface->get_iter = rhythmdb_query_model_get_iter;
 520 	iface->get_path = rhythmdb_query_model_get_path;
 521 	iface->get_value = rhythmdb_query_model_get_value;
 522 	iface->iter_next = rhythmdb_query_model_iter_next;
 523 	iface->iter_children = rhythmdb_query_model_iter_children;
 524 	iface->iter_has_child = rhythmdb_query_model_iter_has_child;
 525 	iface->iter_n_children = rhythmdb_query_model_iter_n_children;
 526 	iface->iter_nth_child = rhythmdb_query_model_iter_nth_child;
 527 	iface->iter_parent = rhythmdb_query_model_iter_parent;
 528 }
 529 
 530 static void
 531 rhythmdb_query_model_drag_source_init (RbTreeDragSourceIface *iface)
 532 {
 533 	iface->rb_row_draggable = rhythmdb_query_model_row_draggable;
 534 	iface->rb_drag_data_delete = rhythmdb_query_model_drag_data_delete;
 535 	iface->rb_drag_data_get = rhythmdb_query_model_drag_data_get;
 536 }
 537 
 538 static void
 539 rhythmdb_query_model_drag_dest_init (RbTreeDragDestIface *iface)
 540 {
 541 	iface->rb_drag_data_received = rhythmdb_query_model_drag_data_received;
 542 	iface->rb_row_drop_possible = rhythmdb_query_model_row_drop_possible;
 543 	iface->rb_row_drop_position = rhythmdb_query_model_row_drop_position;
 544 }
 545 
 546 static void
 547 rhythmdb_query_model_set_query_internal (RhythmDBQueryModel *model,
 548 					GPtrArray          *query)
 549 {
 550 	if (query == model->priv->original_query)
 551 		return;
 552 
 553 	rhythmdb_query_free (model->priv->query);
 554 	rhythmdb_query_free (model->priv->original_query);
 555 
 556 	model->priv->query = rhythmdb_query_copy (query);
 557 	model->priv->original_query = rhythmdb_query_copy (model->priv->query);
 558 	rhythmdb_query_preprocess (model->priv->db, model->priv->query);
 559 
 560 	/* if the query contains time-relative criteria, re-run it periodically.
 561 	 * currently it's just every minute, but perhaps it could be smarter.
 562 	 */
 563 	if (rhythmdb_query_is_time_relative (model->priv->db, model->priv->query)) {
 564 		if (model->priv->query_reapply_timeout_id == 0) {
 565 			model->priv->query_reapply_timeout_id =
 566 				g_timeout_add_seconds (60, (GSourceFunc) rhythmdb_query_model_reapply_query_cb, model);
 567 		}
 568 	} else if (model->priv->query_reapply_timeout_id) {
 569 		g_source_remove (model->priv->query_reapply_timeout_id);
 570 		model->priv->query_reapply_timeout_id = 0;
 571 	}
 572 }
 573 
 574 static void
 575 rhythmdb_query_model_set_property (GObject *object,
 576 				   guint prop_id,
 577 				   const GValue *value,
 578 				   GParamSpec *pspec)
 579 {
 580 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (object);
 581 
 582 	switch (prop_id) {
 583 	case PROP_RHYTHMDB:
 584 		model->priv->db = g_value_get_object (value);
 585 		break;
 586 	case PROP_QUERY:
 587 		rhythmdb_query_model_set_query_internal (model, g_value_get_pointer (value));
 588 		break;
 589 	case PROP_SORT_FUNC:
 590 		model->priv->sort_func = g_value_get_pointer (value);
 591 		break;
 592 	case PROP_SORT_DATA:
 593 		if (model->priv->sort_data_destroy && model->priv->sort_data)
 594 			model->priv->sort_data_destroy (model->priv->sort_data);
 595 		model->priv->sort_data = g_value_get_pointer (value);
 596 		break;
 597 	case PROP_SORT_DATA_DESTROY:
 598 		model->priv->sort_data_destroy = g_value_get_pointer (value);
 599 		break;
 600 	case PROP_SORT_REVERSE:
 601 		model->priv->sort_reverse  = g_value_get_boolean (value);
 602 		break;
 603 	case PROP_LIMIT_TYPE:
 604 		model->priv->limit_type = g_value_get_enum (value);
 605 		break;
 606 	case PROP_LIMIT_VALUE:
 607 		if (model->priv->limit_value)
 608 			g_array_unref (model->priv->limit_value);
 609 		model->priv->limit_value = (GArray*)g_value_dup_boxed (value);
 610 		break;
 611 	case PROP_SHOW_HIDDEN:
 612 		model->priv->show_hidden = g_value_get_boolean (value);
 613 		/* FIXME: this will have funky issues if this is changed after entries are added */
 614 		break;
 615 	case PROP_BASE_MODEL:
 616 		rhythmdb_query_model_chain (model, g_value_get_object (value), TRUE);
 617 		break;
 618 	default:
 619 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 620 		break;
 621 	}
 622 }
 623 
 624 static void
 625 rhythmdb_query_model_get_property (GObject *object,
 626 				   guint prop_id,
 627 				   GValue *value,
 628 				   GParamSpec *pspec)
 629 {
 630 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (object);
 631 
 632 	switch (prop_id) {
 633 	case PROP_RHYTHMDB:
 634 		g_value_set_object (value, model->priv->db);
 635 		break;
 636 	case PROP_QUERY:
 637 		g_value_set_pointer (value, model->priv->original_query);
 638 		break;
 639 	case PROP_SORT_FUNC:
 640 		g_value_set_pointer (value, model->priv->sort_func);
 641 		break;
 642 	case PROP_SORT_DATA:
 643 		g_value_set_pointer (value, model->priv->sort_data);
 644 		break;
 645 	case PROP_SORT_DATA_DESTROY:
 646 		g_value_set_pointer (value, model->priv->sort_data_destroy);
 647 		break;
 648 	case PROP_SORT_REVERSE:
 649 		g_value_set_boolean (value, model->priv->sort_reverse);
 650 		break;
 651 	case PROP_LIMIT_TYPE:
 652 		g_value_set_enum (value, model->priv->limit_type);
 653 		break;
 654 	case PROP_LIMIT_VALUE:
 655 		g_value_set_boxed (value, model->priv->limit_value);
 656 		break;
 657 	case PROP_SHOW_HIDDEN:
 658 		g_value_set_boolean (value, model->priv->show_hidden);
 659 		break;
 660 	case PROP_BASE_MODEL:
 661 		g_value_set_object (value, model->priv->base_model);
 662 		break;
 663 	default:
 664 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 665 		break;
 666 	}
 667 }
 668 
 669 static void
 670 rhythmdb_query_model_init (RhythmDBQueryModel *model)
 671 {
 672 	model->priv = RHYTHMDB_QUERY_MODEL_GET_PRIVATE (model);
 673 
 674 	model->priv->stamp = g_random_int ();
 675 
 676 	model->priv->entries = g_sequence_new (NULL);
 677 	model->priv->reverse_map = g_hash_table_new_full (g_direct_hash,
 678 							  g_direct_equal,
 679 							  (GDestroyNotify)rhythmdb_entry_unref,
 680 							  NULL);
 681 
 682 	model->priv->limited_entries = g_sequence_new (NULL);
 683 	model->priv->limited_reverse_map = g_hash_table_new_full (g_direct_hash,
 684 								  g_direct_equal,
 685 								  (GDestroyNotify)rhythmdb_entry_unref,
 686 								  NULL);
 687 
 688 	model->priv->hidden_entry_map = g_hash_table_new_full (g_direct_hash,
 689 							       g_direct_equal,
 690 							       (GDestroyNotify)rhythmdb_entry_unref,
 691 							       NULL);
 692 
 693 	model->priv->reorder_drag_and_drop = FALSE;
 694 }
 695 
 696 static void
 697 rhythmdb_query_model_constructed (GObject *object)
 698 {
 699 	RhythmDBQueryModel *model;
 700 
 701 	RB_CHAIN_GOBJECT_METHOD (rhythmdb_query_model_parent_class, constructed, object);
 702 	model = RHYTHMDB_QUERY_MODEL (object);
 703 
 704 	g_signal_connect_object (G_OBJECT (model->priv->db),
 705 				 "entry_added",
 706 				 G_CALLBACK (rhythmdb_query_model_entry_added_cb),
 707 				 model, 0);
 708 	g_signal_connect_object (G_OBJECT (model->priv->db),
 709 				 "entry_changed",
 710 				 G_CALLBACK (rhythmdb_query_model_entry_changed_cb),
 711 				 model, 0);
 712 	g_signal_connect_object (G_OBJECT (model->priv->db),
 713 				 "entry_deleted",
 714 				 G_CALLBACK (rhythmdb_query_model_entry_deleted_cb),
 715 				 model, 0);
 716 }
 717 
 718 static void
 719 rhythmdb_query_model_dispose (GObject *object)
 720 {
 721 	RhythmDBQueryModel *model;
 722 
 723 	g_return_if_fail (object != NULL);
 724 	g_return_if_fail (RHYTHMDB_IS_QUERY_MODEL (object));
 725 
 726 	model = RHYTHMDB_QUERY_MODEL (object);
 727 
 728 	g_return_if_fail (model->priv != NULL);
 729 
 730 	rb_debug ("disposing query model %p", object);
 731 
 732 	if (model->priv->base_model) {
 733 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 734 						      G_CALLBACK (rhythmdb_query_model_base_row_inserted),
 735 						      model);
 736 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 737 						      G_CALLBACK (rhythmdb_query_model_base_row_deleted),
 738 						      model);
 739 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 740 						      G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
 741 						      model);
 742 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 743 						      G_CALLBACK (rhythmdb_query_model_base_complete),
 744 						      model);
 745 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 746 						      G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
 747 						      model);
 748 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 749 						      G_CALLBACK (rhythmdb_query_model_base_entry_removed),
 750 						      model);
 751 		g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->base_model),
 752 						      G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
 753 						      model);
 754 		g_object_unref (model->priv->base_model);
 755 		model->priv->base_model = NULL;
 756 	}
 757 
 758 	if (model->priv->query_reapply_timeout_id != 0) {
 759 		g_source_remove (model->priv->query_reapply_timeout_id);
 760 		model->priv->query_reapply_timeout_id = 0;
 761 	}
 762 
 763 	G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->dispose (object);
 764 }
 765 
 766 static void
 767 rhythmdb_query_model_finalize (GObject *object)
 768 {
 769 	RhythmDBQueryModel *model;
 770 
 771 	g_return_if_fail (object != NULL);
 772 	g_return_if_fail (RHYTHMDB_IS_QUERY_MODEL (object));
 773 
 774 	model = RHYTHMDB_QUERY_MODEL (object);
 775 
 776 	g_return_if_fail (model->priv != NULL);
 777 
 778 	rb_debug ("finalizing query model %p", object);
 779 
 780 	g_hash_table_destroy (model->priv->reverse_map);
 781 	g_sequence_free (model->priv->entries);
 782 
 783 	g_hash_table_destroy (model->priv->limited_reverse_map);
 784 	g_sequence_free (model->priv->limited_entries);
 785 
 786 	g_hash_table_destroy (model->priv->hidden_entry_map);
 787 
 788 	if (model->priv->query)
 789 		rhythmdb_query_free (model->priv->query);
 790 	if (model->priv->original_query)
 791 		rhythmdb_query_free (model->priv->original_query);
 792 
 793 	if (model->priv->sort_data_destroy && model->priv->sort_data)
 794 		model->priv->sort_data_destroy (model->priv->sort_data);
 795 
 796 	if (model->priv->limit_value)
 797 		g_array_unref (model->priv->limit_value);
 798 
 799 	G_OBJECT_CLASS (rhythmdb_query_model_parent_class)->finalize (object);
 800 }
 801 
 802 /**
 803  * rhythmdb_query_model_new:
 804  * @db: the #RhythmDB
 805  * @query: the query for the new model
 806  * @sort_func: the sort function for the new model
 807  * @sort_data: data to pass to the sort function
 808  * @sort_data_destroy: function to call when destroying the sort data
 809  * @sort_reverse: if %TRUE, reverse the sort order
 810  *
 811  * Constructs a new #RhythmDBQueryModel with the specified query and sorting
 812  * parameters.
 813  *
 814  * Return value: the newly constructed query model
 815  */
 816 RhythmDBQueryModel *
 817 rhythmdb_query_model_new (RhythmDB *db,
 818 			  GPtrArray *query,
 819 			  GCompareDataFunc sort_func,
 820 			  gpointer sort_data,
 821 			  GDestroyNotify sort_data_destroy,
 822 			  gboolean sort_reverse)
 823 {
 824 	RhythmDBQueryModel *model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
 825 						  "db", db, "query", query,
 826 						  "sort-func", sort_func,
 827 						  "sort-data", sort_data,
 828 						  "sort-data-destroy", sort_data_destroy,
 829 						  "sort-reverse", sort_reverse,
 830 						  NULL);
 831 
 832 	g_return_val_if_fail (model->priv != NULL, NULL);
 833 
 834 	return model;
 835 }
 836 
 837 /**
 838  * rhythmdb_query_model_new_empty:
 839  * @db: the #RhythmDB
 840  *
 841  * Constructs a new empty query model with no query or sorting parameters.
 842  * This should only be used when the query model will be populated by
 843  * explicitly adding entries.
 844  *
 845  * Return value: the newly constructed query model
 846  */
 847 RhythmDBQueryModel *
 848 rhythmdb_query_model_new_empty (RhythmDB *db)
 849 {
 850 	return g_object_new (RHYTHMDB_TYPE_QUERY_MODEL,
 851 			     "db", db, NULL);
 852 }
 853 
 854 static void
 855 _copy_contents_foreach_cb (RhythmDBEntry *entry, RhythmDBQueryModel *dest)
 856 {
 857 	if (dest->priv->query == NULL ||
 858 	    rhythmdb_evaluate_query (dest->priv->db, dest->priv->query, entry)) {
 859 		if (dest->priv->show_hidden || (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN) == FALSE))
 860 			rhythmdb_query_model_do_insert (dest, entry, -1);
 861 	}
 862 }
 863 
 864 /**
 865  * rhythmdb_query_model_copy_contents:
 866  * @dest: destination #RhythmDBQueryModel
 867  * @src: source #RhythmDBQueryModel
 868  *
 869  * Copies all entries from @src to @dest.
 870  */
 871 void
 872 rhythmdb_query_model_copy_contents (RhythmDBQueryModel *dest,
 873 				    RhythmDBQueryModel *src)
 874 {
 875 	if (src->priv->entries == NULL)
 876 		return;
 877 
 878 	g_sequence_foreach (src->priv->entries, (GFunc)_copy_contents_foreach_cb, dest);
 879 }
 880 
 881 /**
 882  * rhythmdb_query_model_chain:
 883  * @model: the #RhythmDBQueryModel to chain
 884  * @base: the #RhythmDBQueryModel to chain it to
 885  * @import_entries: if %TRUE, copy all existing entries from @base to @model
 886  *
 887  * Chains @model to @base.  All changes made to the base model will be reflected in
 888  * the child model, and all changes made to the child model will be passed on to the
 889  * base model.
 890  */
 891 void
 892 rhythmdb_query_model_chain (RhythmDBQueryModel *model,
 893 			    RhythmDBQueryModel *base,
 894 			    gboolean import_entries)
 895 {
 896 	rb_debug ("query model %p chaining to base model %p", model, base);
 897 
 898 	if (model->priv->base_model != NULL) {
 899 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 900 						      G_CALLBACK (rhythmdb_query_model_base_row_inserted),
 901 						      model);
 902 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 903 						      G_CALLBACK (rhythmdb_query_model_base_row_deleted),
 904 						      model);
 905 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 906 						      G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
 907 						      model);
 908 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 909 						      G_CALLBACK (rhythmdb_query_model_base_complete),
 910 						      model);
 911 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 912 						      G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
 913 						      model);
 914 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 915 						      G_CALLBACK (rhythmdb_query_model_base_entry_removed),
 916 						      model);
 917 		g_signal_handlers_disconnect_by_func (model->priv->base_model,
 918 						      G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
 919 						      model);
 920 		g_object_unref (model->priv->base_model);
 921 	}
 922 
 923 	model->priv->base_model = base;
 924 
 925 	if (model->priv->base_model != NULL) {
 926 		g_object_ref (model->priv->base_model);
 927 
 928 		g_assert (model->priv->base_model->priv->db == model->priv->db);
 929 
 930 		g_signal_connect_object (model->priv->base_model,
 931 					 "row-inserted",
 932 					 G_CALLBACK (rhythmdb_query_model_base_row_inserted),
 933 					 model, 0);
 934 		g_signal_connect_object (model->priv->base_model,
 935 					 "row-deleted",
 936 					 G_CALLBACK (rhythmdb_query_model_base_row_deleted),
 937 					 model, 0);
 938 		g_signal_connect_object (model->priv->base_model,
 939 					 "non-entry-dropped",
 940 					 G_CALLBACK (rhythmdb_query_model_base_non_entry_dropped),
 941 					 model, 0);
 942 		g_signal_connect_object (model->priv->base_model,
 943 					 "complete",
 944 					 G_CALLBACK (rhythmdb_query_model_base_complete),
 945 					 model, 0);
 946 		g_signal_connect_object (model->priv->base_model,
 947 					 "rows-reordered",
 948 					 G_CALLBACK (rhythmdb_query_model_base_rows_reordered),
 949 					 model, 0);
 950 		g_signal_connect_object (model->priv->base_model,
 951 					 "entry-removed",
 952 					 G_CALLBACK (rhythmdb_query_model_base_entry_removed),
 953 					 model, 0);
 954 		g_signal_connect_object (model->priv->base_model,
 955 					 "entry-prop-changed",
 956 					 G_CALLBACK (rhythmdb_query_model_base_entry_prop_changed),
 957 					 model, 0);
 958 
 959 		if (import_entries)
 960 			rhythmdb_query_model_copy_contents (model, model->priv->base_model);
 961 	}
 962 }
 963 
 964 /**
 965  * rhythmdb_query_model_has_pending_changes:
 966  * @model: a #RhythmDBQueryModel
 967  *
 968  * Checks if a #RhythmDBQueryModel has any outstanding changes that are
 969  * yet to be processed.  This is not very useful.
 970  *
 971  * Return value: %TRUE if there are outstanding changes to the model
 972  */
 973 gboolean
 974 rhythmdb_query_model_has_pending_changes (RhythmDBQueryModel *model)
 975 {
 976 	gboolean result;
 977 
 978 	result = g_atomic_int_get (&model->priv->pending_update_count) > 0;
 979 	if (model->priv->base_model)
 980 		result |= rhythmdb_query_model_has_pending_changes (model->priv->base_model);
 981 
 982 	return result;
 983 }
 984 
 985 static void
 986 rhythmdb_query_model_entry_added_cb (RhythmDB *db,
 987 				     RhythmDBEntry *entry,
 988 				     RhythmDBQueryModel *model)
 989 {
 990 	int index = -1;
 991 	gboolean insert = FALSE;
 992 	if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) {
 993 		return;
 994 	}
 995 
 996 	/* check if it's in the base model */
 997 	if (model->priv->base_model) {
 998 	       if (g_hash_table_lookup (model->priv->base_model->priv->reverse_map, entry) == NULL) {
 999 		       return;
1000 	       }
1001 	}
1002 
1003 	if (model->priv->query != NULL) {
1004 		insert = rhythmdb_evaluate_query (db, model->priv->query, entry);
1005 	} else {
1006 		index = GPOINTER_TO_INT (g_hash_table_lookup (model->priv->hidden_entry_map, entry));
1007 		insert = g_hash_table_remove (model->priv->hidden_entry_map, entry);
1008 		if (insert)
1009 			rb_debug ("adding unhidden entry at index %d", index);
1010 	}
1011 
1012 	if (insert) {
1013 		rhythmdb_query_model_do_insert (model, entry, index);
1014 	}
1015 }
1016 
1017 static void
1018 rhythmdb_query_model_entry_changed_cb (RhythmDB *db,
1019 				       RhythmDBEntry *entry,
1020 				       GArray *changes,
1021 				       RhythmDBQueryModel *model)
1022 {
1023 	gboolean hidden = FALSE;
1024 	int i;
1025 
1026 	hidden = (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN));
1027 
1028 	if (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) {
1029 		if (hidden == FALSE) {
1030 			/* the changed entry may now satisfy the query
1031 			 * so we test it */
1032 			rhythmdb_query_model_entry_added_cb (db, entry, model);
1033 		}
1034 		return;
1035 	}
1036 
1037 	if (hidden) {
1038 		/* emit an entry-prop-changed signal so property models
1039 		 * can be updated correctly.  if we have a base model,
1040 		 * we'll propagate the parent's signal instead.
1041 		 */
1042 		if (model->priv->base_model == NULL) {
1043 			GValue true_val = { 0, };
1044 			GValue false_val = { 0, };
1045 
1046 			g_value_init (&true_val, G_TYPE_BOOLEAN);
1047 			g_value_set_boolean (&true_val, TRUE);
1048 			g_value_init (&false_val, G_TYPE_BOOLEAN);
1049 			g_value_set_boolean (&false_val, FALSE);
1050 
1051 			rb_debug ("emitting hidden-removal notification for %s",
1052 				  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
1053 			g_signal_emit (G_OBJECT (model),
1054 				       rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
1055 				       entry, RHYTHMDB_PROP_HIDDEN, &false_val, &true_val);
1056 			g_value_unset (&true_val);
1057 			g_value_unset (&false_val);
1058 		}
1059 
1060 		/* if we don't have a query to help us decide, we need to
1061 		 * track hidden entries that were in this query model,
1062 		 * so we can add them back in if they become visible again.
1063 		 * if we have a query, any entry that matches will be added.
1064 		 */
1065 		if (model->priv->query == NULL) {
1066 			GtkTreeIter iter;
1067 			GtkTreePath *path;
1068 			gint index;
1069 
1070 			/* find the entry's position so we can restore it there
1071 			 * if it reappears
1072 			 */
1073 			g_assert (rhythmdb_query_model_entry_to_iter (model, entry, &iter));
1074 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1075 			index = gtk_tree_path_get_indices (path)[0];
1076 			gtk_tree_path_free (path);
1077 			rb_debug ("adding hidden entry to map with index %d", index);
1078 
1079 			g_hash_table_insert (model->priv->hidden_entry_map,
1080 					     rhythmdb_entry_ref (entry),
1081 					     GINT_TO_POINTER (index));
1082 		}
1083 
1084 		rhythmdb_query_model_filter_out_entry (model, entry);
1085 		return;
1086 	}
1087 
1088 	/* emit separate change signals for each property
1089 	 * unless this is a chained query model, in which
1090 	 * case we propagate the parent model's signals instead.
1091 	 */
1092 	for (i = 0; i < changes->len; i++) {
1093 		GValue *v = &g_array_index (changes, GValue, i);
1094 		RhythmDBEntryChange *change = g_value_get_boxed (v);
1095 
1096 		if (model->priv->base_model == NULL) {
1097 			g_signal_emit (G_OBJECT (model),
1098 				       rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
1099 				       entry, change->prop, &change->old, &change->new);
1100 		}
1101 
1102 		if (change->prop == RHYTHMDB_PROP_DURATION) {
1103 			model->priv->total_duration -= g_value_get_ulong (&change->old);
1104 			model->priv->total_duration += g_value_get_ulong (&change->new);
1105 		} else if (change->prop == RHYTHMDB_PROP_FILE_SIZE) {
1106 			model->priv->total_size -= g_value_get_uint64 (&change->old);
1107 			model->priv->total_size += g_value_get_uint64 (&change->new);
1108 		}
1109 	}
1110 
1111 	if (model->priv->query &&
1112 	    !rhythmdb_evaluate_query (db, model->priv->query, entry)) {
1113 		rhythmdb_query_model_filter_out_entry (model, entry);
1114 		return;
1115 	}
1116 
1117 	/* it may have moved, so we can't just emit a changed entry */
1118 	if (!rhythmdb_query_model_do_reorder (model, entry)) {
1119 		/* but if it didn't, we can */
1120 		GtkTreeIter iter;
1121 		GtkTreePath *path;
1122 
1123 		if (rhythmdb_query_model_entry_to_iter (model, entry, &iter)) {
1124 			path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1125 							      &iter);
1126 			gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
1127 						     path, &iter);
1128 			gtk_tree_path_free (path);
1129 		}
1130 	}
1131 }
1132 
1133 static void
1134 rhythmdb_query_model_base_entry_prop_changed (RhythmDBQueryModel *base_model,
1135 					      RhythmDBEntry *entry,
1136 					      RhythmDBPropType prop,
1137 					      const GValue *old,
1138 					      const GValue *new_value,
1139 					      RhythmDBQueryModel *model)
1140 {
1141 	if (g_hash_table_lookup (model->priv->reverse_map, entry)) {
1142 		/* propagate the signal */
1143 		g_signal_emit (G_OBJECT (model),
1144 			       rhythmdb_query_model_signals[ENTRY_PROP_CHANGED], 0,
1145 			       entry, prop, old, new_value);
1146 	}
1147 }
1148 
1149 static void
1150 rhythmdb_query_model_entry_deleted_cb (RhythmDB *db,
1151 				       RhythmDBEntry *entry,
1152 				       RhythmDBQueryModel *model)
1153 {
1154 
1155 	if (g_hash_table_lookup (model->priv->reverse_map, entry) ||
1156 	    g_hash_table_lookup (model->priv->limited_reverse_map, entry))
1157 		rhythmdb_query_model_remove_entry (model, entry);
1158 }
1159 
1160 static gboolean
1161 idle_process_update_idle (struct RhythmDBQueryModelUpdate *update)
1162 {
1163 	GDK_THREADS_ENTER ();
1164 	idle_process_update (update);
1165 	GDK_THREADS_LEAVE ();
1166 	return FALSE;
1167 }
1168 
1169 static void
1170 rhythmdb_query_model_process_update (struct RhythmDBQueryModelUpdate *update)
1171 {
1172 	g_atomic_int_inc (&update->model->priv->pending_update_count);
1173 	if (rb_is_main_thread ())
1174 		idle_process_update (update);
1175 	else
1176 		g_idle_add ((GSourceFunc) idle_process_update_idle, update);
1177 }
1178 
1179 static void
1180 idle_process_update (struct RhythmDBQueryModelUpdate *update)
1181 {
1182 	switch (update->type) {
1183 	case RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED:
1184 	{
1185 		guint i;
1186 
1187 		rb_debug ("inserting %d rows", update->entrydata.entries->len);
1188 
1189 		for (i = 0; i < update->entrydata.entries->len; i++ ) {
1190 			RhythmDBEntry *entry = g_ptr_array_index (update->entrydata.entries, i);
1191 
1192 			if (update->model->priv->show_hidden || !rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) {
1193 				RhythmDBQueryModel *base_model = update->model->priv->base_model;
1194 				if (base_model &&
1195 				    g_hash_table_lookup (base_model->priv->reverse_map, entry) == NULL)
1196 					       continue;
1197 
1198 				rhythmdb_query_model_do_insert (update->model, entry, -1);
1199 			}
1200 
1201 			rhythmdb_entry_unref (entry);
1202 		}
1203 
1204 		g_ptr_array_free (update->entrydata.entries, TRUE);
1205 
1206 		break;
1207 	}
1208 	case RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX:
1209 	{
1210 		rb_debug ("inserting row at index %d", update->entrydata.data.index);
1211 		rhythmdb_query_model_do_insert (update->model, update->entrydata.data.entry, update->entrydata.data.index);
1212 		rhythmdb_entry_unref (update->entrydata.data.entry);
1213 		break;
1214 	}
1215 	case RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE:
1216 		g_signal_emit (G_OBJECT (update->model), rhythmdb_query_model_signals[COMPLETE], 0);
1217 		break;
1218 	}
1219 
1220 	g_atomic_int_add (&update->model->priv->pending_update_count, -1);
1221 	g_object_unref (update->model);
1222 	g_free (update);
1223 }
1224 
1225 /**
1226  * rhythmdb_query_model_add_entry:
1227  * @model: a #RhythmDBQueryModel
1228  * @entry: a #RhythmDBEntry to add
1229  * @index: position at which to add it, or -1 to add at the end
1230  *
1231  * Adds an entry to the query model at the specified position.  Does not check
1232  * if the entry matches the query (if any).
1233  */
1234 void
1235 rhythmdb_query_model_add_entry (RhythmDBQueryModel *model,
1236 				RhythmDBEntry *entry,
1237 				gint index)
1238 {
1239 	struct RhythmDBQueryModelUpdate *update;
1240 
1241 	if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN)) {
1242 		rb_debug ("attempting to add hidden entry");
1243 		return;
1244 	}
1245 
1246 	if (model->priv->base_model) {
1247 		/* add it to the base model, which will cause it to be added to this one */
1248 		rhythmdb_query_model_add_entry (model->priv->base_model, entry,
1249 						rhythmdb_query_model_child_index_to_base_index (model, index));
1250 		return;
1251 	}
1252 
1253 	rb_debug ("inserting entry %p at index %d", entry, index);
1254 
1255 	update = g_new (struct RhythmDBQueryModelUpdate, 1);
1256 	update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROW_INSERTED_INDEX;
1257 	update->entrydata.data.entry = entry;
1258 	update->entrydata.data.index = index;
1259 	update->model = model;
1260 
1261 	/* take references; released in update idle */
1262 	g_object_ref (model);
1263 
1264 	rhythmdb_entry_ref (entry);
1265 
1266 	rhythmdb_query_model_process_update (update);
1267 }
1268 
1269 /**
1270  * rhythmdb_query_model_get_size:
1271  * @model: the #RhythmDBQueryModel
1272  *
1273  * Returns the total size of all entries in the model.
1274  *
1275  * Return value: the total size (in bytes) of all entries in the model
1276  */
1277 guint64
1278 rhythmdb_query_model_get_size (RhythmDBQueryModel *model)
1279 {
1280 	return model->priv->total_size;
1281 }
1282 
1283 /**
1284  * rhythmdb_query_model_get_duration:
1285  * @model: the #RhythmDBQueryModel
1286  *
1287  * Returns the total duration of all entries in the model.
1288  *
1289  * Return value: the total duration (in seconds) of all entries in the model
1290  */
1291 long
1292 rhythmdb_query_model_get_duration (RhythmDBQueryModel *model)
1293 {
1294 	return model->priv->total_duration;
1295 }
1296 
1297 static void
1298 rhythmdb_query_model_insert_into_main_list (RhythmDBQueryModel *model,
1299 					    RhythmDBEntry *entry,
1300 					    gint index)
1301 {
1302 	GSequenceIter *ptr;
1303 
1304 	/* take reference; released when removed from hash */
1305 	rhythmdb_entry_ref (entry);
1306 
1307 	if (model->priv->sort_func) {
1308 		GCompareDataFunc sort_func;
1309 		gpointer sort_data;
1310 		struct ReverseSortData reverse_data;
1311 
1312 		if (model->priv->sort_reverse) {
1313 			sort_func = (GCompareDataFunc) _reverse_sorting_func;
1314 			sort_data = &reverse_data;
1315 			reverse_data.func = model->priv->sort_func;
1316 			reverse_data.data = model->priv->sort_data;
1317 		} else {
1318 			sort_func = model->priv->sort_func;
1319 			sort_data = model->priv->sort_data;
1320 		}
1321 
1322 		ptr = g_sequence_insert_sorted (model->priv->entries,
1323 						entry,
1324 						sort_func,
1325 						sort_data);
1326 	} else {
1327 		if (index == -1) {
1328 			ptr = g_sequence_get_end_iter (model->priv->entries);
1329 		} else {
1330 			ptr = g_sequence_get_iter_at_pos (model->priv->entries, index);
1331 		}
1332 
1333 		g_sequence_insert_before (ptr, entry);
1334 		ptr = g_sequence_iter_prev (ptr);
1335 	}
1336 
1337 	/* the hash now owns this reference to the entry */
1338 	g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1339 
1340 	model->priv->total_duration += rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
1341 	model->priv->total_size += rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
1342 }
1343 
1344 static void
1345 rhythmdb_query_model_insert_into_limited_list (RhythmDBQueryModel *model,
1346 					       RhythmDBEntry *entry)
1347 {
1348 	GSequenceIter *ptr;
1349 
1350 	/* take reference; released when removed from hash */
1351 	rhythmdb_entry_ref (entry);
1352 
1353 	if (model->priv->sort_func) {
1354 		GCompareDataFunc sort_func;
1355 		gpointer sort_data;
1356 		struct ReverseSortData reverse_data;
1357 
1358 		if (model->priv->sort_reverse) {
1359 			sort_func = (GCompareDataFunc) _reverse_sorting_func;
1360 			sort_data = &reverse_data;
1361 			reverse_data.func = model->priv->sort_func;
1362 			reverse_data.data = model->priv->sort_data;
1363 		} else {
1364 			sort_func = model->priv->sort_func;
1365 			sort_data = model->priv->sort_data;
1366 		}
1367 
1368 		ptr = g_sequence_insert_sorted (model->priv->limited_entries, entry,
1369 						sort_func,
1370 						sort_data);
1371 	} else {
1372 		ptr = g_sequence_get_end_iter (model->priv->limited_entries);
1373 		g_sequence_insert_before (ptr, entry);
1374 		ptr = g_sequence_iter_prev (ptr);
1375 	}
1376 
1377 	/* the hash now owns this reference to the entry */
1378 	g_hash_table_insert (model->priv->limited_reverse_map, entry, ptr);
1379 }
1380 
1381 static void
1382 rhythmdb_query_model_remove_from_main_list (RhythmDBQueryModel *model,
1383 					    RhythmDBEntry *entry)
1384 {
1385 	GSequenceIter *ptr;
1386 	int index;
1387 	GtkTreePath *path;
1388 
1389 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1390 	index = g_sequence_iter_get_position (ptr);
1391 
1392 	path = gtk_tree_path_new ();
1393 	gtk_tree_path_append_index (path, index);
1394 
1395 	gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
1396 	gtk_tree_path_free (path);
1397 
1398 	model->priv->total_duration -= rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
1399 	model->priv->total_size -= rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
1400 
1401 	/* take temporary ref */
1402 	rhythmdb_entry_ref (entry);
1403 
1404 	/* find the sequence pointer again in case a row-deleted
1405 	 * signal handler moved it.
1406 	 */
1407 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1408 	g_sequence_remove (ptr);
1409 	g_assert (g_hash_table_remove (model->priv->reverse_map, entry));
1410 
1411 	g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[POST_ENTRY_DELETE], 0, entry);
1412 
1413 	/* release temporary ref */
1414 	rhythmdb_entry_unref (entry);
1415 }
1416 
1417 static void
1418 rhythmdb_query_model_remove_from_limited_list (RhythmDBQueryModel *model,
1419 					       RhythmDBEntry *entry)
1420 {
1421 	GSequenceIter *ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1422 
1423 	/* take temporary ref */
1424 	rhythmdb_entry_ref (entry);
1425 	g_sequence_remove (ptr);
1426 	g_hash_table_remove (model->priv->limited_reverse_map, entry);
1427 	/* release temporary ref */
1428 	rhythmdb_entry_unref (entry);
1429 }
1430 
1431 static void
1432 rhythmdb_query_model_update_limited_entries (RhythmDBQueryModel *model)
1433 {
1434 	RhythmDBEntry *entry;
1435 	GSequenceIter *ptr;
1436 
1437 	/* make it fit inside the limits */
1438 	while (!rhythmdb_query_model_within_limit (model, NULL)) {
1439 		ptr = g_sequence_iter_prev (g_sequence_get_end_iter (model->priv->entries));
1440 		entry = (RhythmDBEntry*) g_sequence_get (ptr);
1441 
1442 		/* take temporary ref */
1443 		rhythmdb_entry_ref (entry);
1444 		rhythmdb_query_model_remove_from_main_list (model, entry);
1445 		rhythmdb_query_model_insert_into_limited_list (model, entry);
1446 		/* release temporary ref */
1447 		rhythmdb_entry_unref (entry);
1448 	}
1449 
1450 	/* move entries that were previously limited, back to the main list */
1451 	while (TRUE) {
1452 		GtkTreePath *path;
1453 		GtkTreeIter iter;
1454 
1455 		ptr = g_sequence_get_begin_iter (model->priv->limited_entries);
1456 		if (!ptr || ptr == g_sequence_get_end_iter (model->priv->limited_entries))
1457 			break;
1458 		entry = (RhythmDBEntry*) g_sequence_get (ptr);
1459 		if (!entry)
1460 			break;
1461 
1462 		if (!rhythmdb_query_model_within_limit (model, entry))
1463 			break;
1464 
1465 		/* take temporary ref */
1466 		rhythmdb_entry_ref (entry);
1467 		rhythmdb_query_model_remove_from_limited_list (model, entry);
1468 		rhythmdb_query_model_insert_into_main_list (model, entry, -1);
1469 		/* release temporary ref */
1470 		rhythmdb_entry_unref (entry);
1471 
1472 		ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1473 		iter.stamp = model->priv->stamp;
1474 		iter.user_data = ptr;
1475 		path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1476 						      &iter);
1477 		gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
1478 					     path, &iter);
1479 		gtk_tree_path_free (path);
1480 	}
1481 }
1482 
1483 static gboolean
1484 rhythmdb_query_model_emit_reorder (RhythmDBQueryModel *model,
1485 				   gint old_pos,
1486 				   gint new_pos)
1487 {
1488 	int length, i;
1489 	gint *reorder_map;
1490 	GtkTreePath *path;
1491 	GtkTreeIter iter;
1492 
1493 	if (new_pos == old_pos) {
1494 		/* it hasn't moved, don't emit a re-order */
1495 		return FALSE;
1496 	}
1497 
1498 	length = g_sequence_get_length (model->priv->entries);
1499 	reorder_map = g_malloc (length * sizeof(gint));
1500 
1501 	if (new_pos > old_pos) {
1502 		/* it has mover further down the list */
1503 		for (i = 0; i < old_pos; i++)
1504 			reorder_map[i] = i;
1505 		for (i = old_pos; i < new_pos; i++)
1506 			reorder_map[i] = i + 1;
1507 		reorder_map[new_pos] = old_pos;
1508 		for (i = new_pos + 1; i < length; i++)
1509 			reorder_map[i] = i;
1510 	} else {
1511 		/* it has moved up the list */
1512 		for (i = 0; i < new_pos; i++)
1513 			reorder_map[i] = i;
1514 		reorder_map[new_pos] = old_pos;
1515 		for (i = new_pos + 1; i < old_pos + 1; i++)
1516 			reorder_map[i] = i - 1;
1517 		for (i = old_pos + 1; i < length; i++)
1518 			reorder_map[i] = i;
1519 	}
1520 
1521 	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
1522 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1523 
1524 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
1525 				       path, &iter,
1526 				       reorder_map);
1527 	gtk_tree_path_free (path);
1528 	g_free (reorder_map);
1529 	return TRUE;
1530 }
1531 
1532 static gboolean
1533 rhythmdb_query_model_do_reorder (RhythmDBQueryModel *model,
1534 				 RhythmDBEntry *entry)
1535 {
1536 	GSequenceIter *ptr;
1537 	int old_pos, new_pos;
1538 	GtkTreePath *path;
1539 	GtkTreeIter iter;
1540 	GCompareDataFunc sort_func;
1541 	gpointer sort_data;
1542 	struct ReverseSortData reverse_data;
1543 
1544 	if (model->priv->sort_func == NULL)
1545 		return FALSE;
1546 
1547 	if (model->priv->sort_reverse) {
1548 		sort_func = (GCompareDataFunc) _reverse_sorting_func;
1549 		sort_data = &reverse_data;
1550 		reverse_data.func = model->priv->sort_func;
1551 		reverse_data.data = model->priv->sort_data;
1552 	} else {
1553 		sort_func = model->priv->sort_func;
1554 		sort_data = model->priv->sort_data;
1555 	}
1556 
1557 	ptr = g_sequence_get_begin_iter (model->priv->limited_entries);
1558 
1559 	if (ptr != NULL && !g_sequence_iter_is_end (ptr)) {
1560 		RhythmDBEntry *first_limited = g_sequence_get (ptr);
1561 		int cmp = (sort_func) (entry, first_limited, sort_data);
1562 
1563 		if (cmp > 0) {
1564 			/* the entry belongs in the limited list, so we don't need a re-order */
1565 			/* take temporary ref */
1566 			rhythmdb_entry_ref (entry);
1567 			rhythmdb_query_model_remove_entry (model, entry);
1568 			rhythmdb_query_model_do_insert (model, entry, -1);
1569 			/* release temporary ref */
1570 			rhythmdb_entry_unref (entry);
1571 			return TRUE;
1572 		}
1573 	}
1574 
1575 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1576 	iter.stamp = model->priv->stamp;
1577 	iter.user_data = ptr;
1578 	path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1579 					      &iter);
1580 	gtk_tree_model_row_changed (GTK_TREE_MODEL (model),
1581 				     path, &iter);
1582 	gtk_tree_path_free (path);
1583 
1584 	/* take a temporary ref */
1585 	rhythmdb_entry_ref (entry);
1586 
1587 	/* it may have moved, check for a re-order */
1588 	g_hash_table_remove (model->priv->reverse_map, entry);
1589 	old_pos = g_sequence_iter_get_position (ptr);
1590 	g_sequence_remove (ptr);
1591 
1592 	ptr = g_sequence_insert_sorted (model->priv->entries, entry,
1593 					sort_func,
1594 					sort_data);
1595 	new_pos = g_sequence_iter_get_position (ptr);
1596 
1597 	/* the hash now owns this reference to the entry */
1598 	g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1599 
1600 	return rhythmdb_query_model_emit_reorder (model, old_pos, new_pos);
1601 }
1602 
1603 static void
1604 rhythmdb_query_model_do_insert (RhythmDBQueryModel *model,
1605 				RhythmDBEntry *entry,
1606 				gint index)
1607 {
1608 	GSequenceIter *ptr;
1609 	GtkTreePath *path;
1610 	GtkTreeIter iter;
1611 
1612 	g_assert (model->priv->show_hidden || !rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN));
1613 
1614 	/* we check again if the entry already exists in the hash table */
1615 	if (g_hash_table_lookup (model->priv->reverse_map, entry) != NULL)
1616 		return;
1617 
1618 	/* take temporary ref */
1619 	rhythmdb_entry_ref (entry);
1620 
1621 	ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1622 	if (ptr != NULL) {
1623 		rhythmdb_query_model_remove_from_limited_list (model, entry);
1624 	}
1625 
1626 	rhythmdb_query_model_insert_into_main_list (model, entry, index);
1627 
1628 	/* release temporary ref */
1629 	rhythmdb_entry_unref (entry);
1630 
1631 	/* it was added to the main list, send out the inserted signal */
1632 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1633 	iter.stamp = model->priv->stamp;
1634 	iter.user_data = ptr;
1635 	path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
1636 					      &iter);
1637 	gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
1638 				     path, &iter);
1639 	gtk_tree_path_free (path);
1640 
1641 	rhythmdb_query_model_update_limited_entries (model);
1642 }
1643 
1644 static void
1645 rhythmdb_query_model_filter_out_entry (RhythmDBQueryModel *model,
1646 				       RhythmDBEntry *entry)
1647 {
1648 	GSequenceIter *ptr;
1649 
1650 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1651 	if (ptr != NULL) {
1652 		rhythmdb_query_model_remove_from_main_list (model, entry);
1653 		rhythmdb_query_model_update_limited_entries (model);
1654 		return;
1655 	}
1656 
1657 	ptr = g_hash_table_lookup (model->priv->limited_reverse_map, entry);
1658 	if (ptr != NULL) {
1659 		rhythmdb_query_model_remove_from_limited_list (model, entry);
1660 		rhythmdb_query_model_update_limited_entries (model);
1661 		return;
1662 	}
1663 }
1664 
1665 /**
1666  * rhythmdb_query_model_shuffle_entries:
1667  * @model: a #RhythmDBQueryModel
1668  *
1669  * Shuffles the entries in the model into a new random order.
1670  */
1671 void
1672 rhythmdb_query_model_shuffle_entries (RhythmDBQueryModel *model)
1673 {
1674 	RhythmDBEntry **entries;
1675 	int listsize;
1676 	int i;
1677 	int swapwith;
1678 	RhythmDBEntry *entry;
1679 	GSequenceIter *iter;
1680 	GtkTreePath *path;
1681 	GtkTreeIter tree_iter;
1682 	int *map_new_old;
1683 
1684 	/* Convert the entries list to an array, for fast lookups. */
1685 	listsize = g_sequence_get_length (model->priv->entries);
1686 	entries = (RhythmDBEntry **)g_malloc (sizeof(RhythmDBEntry *) * listsize);
1687 	map_new_old = (int *)g_malloc (sizeof(int) * listsize);
1688 
1689 	iter = g_sequence_get_begin_iter (model->priv->entries);
1690 	i = 0;
1691 	while (!g_sequence_iter_is_end (iter)) {
1692 		entries[i++] = g_sequence_get (iter);
1693 		iter = g_sequence_iter_next (iter);
1694 	}
1695 
1696 	/* Shuffle the array. */
1697 	for (i=0; i<listsize; i++) {
1698 		swapwith = g_random_int_range (i, listsize);
1699 		map_new_old[swapwith] = i;
1700 		entry = entries[swapwith];
1701 		entries[swapwith] = entries[i];
1702 		entries[i] = entry;
1703 	}
1704 
1705 	/* Convert the array back into a sequence, rebuilding the reverse map. */
1706 	iter = g_sequence_get_begin_iter (model->priv->entries);
1707 	i = 0;
1708 	while (!g_sequence_iter_is_end (iter)) {
1709 		g_sequence_set (iter, (gpointer)entries[i]);
1710 		rhythmdb_entry_ref (entries[i]);
1711 		g_hash_table_remove (model->priv->reverse_map, entries[i]);
1712 		g_hash_table_insert (model->priv->reverse_map, entries[i], iter);
1713 
1714 		iter = g_sequence_iter_next (iter);
1715 		i++;
1716 	}
1717 
1718 	/* Emit reorder signals. */
1719 	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tree_iter);
1720 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &tree_iter);
1721 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), path, &tree_iter, map_new_old);
1722 	gtk_tree_path_free (path);
1723 
1724 	g_free (map_new_old);
1725 	g_free (entries);
1726 }
1727 
1728 /**
1729  * rhythmdb_query_model_move_entry:
1730  * @model: a #RhythmDBQueryModel
1731  * @entry: the #RhythmDBEntry to move
1732  * @index: position to move to
1733  *
1734  * Moves an entry to a new position in the query model.
1735  * The position must be a between 0 and the number of entries in the model.
1736  * Specifying -1 does not move the entry to the end of the model.
1737  */
1738 void
1739 rhythmdb_query_model_move_entry (RhythmDBQueryModel *model,
1740 				 RhythmDBEntry *entry,
1741 				 gint index)
1742 {
1743 	GSequenceIter *ptr;
1744 	GSequenceIter *nptr;
1745 	gint old_pos;
1746 
1747 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1748 	if (ptr == NULL)
1749 		return;
1750 
1751 	nptr = g_sequence_get_iter_at_pos (model->priv->entries, index);
1752 	if ((nptr == NULL) || (ptr == nptr))
1753 		return;
1754 
1755 	/* take temporary ref */
1756 	rhythmdb_entry_ref (entry);
1757 
1758 	/* remove from old position */
1759 	old_pos = g_sequence_iter_get_position (ptr);
1760 	g_sequence_remove (ptr);
1761 	g_hash_table_remove (model->priv->reverse_map, entry);
1762 
1763 	/* insert into new position */
1764 	g_sequence_insert_before (nptr, entry);
1765 	ptr = g_sequence_iter_prev (nptr);
1766 
1767 	/* the hash now owns this reference to the entry */
1768 	g_hash_table_insert (model->priv->reverse_map, entry, ptr);
1769 
1770 	rhythmdb_query_model_emit_reorder (model, old_pos, index);
1771 }
1772 
1773 /**
1774  * rhythmdb_query_model_remove_entry:
1775  * @model: a #RhythmDBQueryModel
1776  * @entry: the #RhythmDBEntry to remove
1777  *
1778  * Removes an entry from the query model.
1779  *
1780  * Return value: %TRUE if the entry was removed
1781  */
1782 gboolean
1783 rhythmdb_query_model_remove_entry (RhythmDBQueryModel *model,
1784 				   RhythmDBEntry *entry)
1785 {
1786 	gboolean present = (g_hash_table_lookup (model->priv->reverse_map, entry) == NULL) ||
1787 			    (g_hash_table_lookup (model->priv->limited_reverse_map, entry) == NULL);
1788 	g_return_val_if_fail (present, FALSE);
1789 
1790 	if (model->priv->base_model != NULL)
1791 		return rhythmdb_query_model_remove_entry (model->priv->base_model, entry);
1792 
1793 	/* emit entry-removed, so listeners know the
1794 	 * entry has actually been removed, rather than filtered
1795 	 * out.
1796 	 */
1797 	g_signal_emit (G_OBJECT (model),
1798 		       rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
1799 		       entry);
1800 	rhythmdb_query_model_filter_out_entry (model, entry);
1801 
1802 	return TRUE;
1803 }
1804 
1805 /**
1806  * rhythmdb_query_model_entry_to_iter:
1807  * @model: a #RhythmDBQueryModel
1808  * @entry: the #RhythmDBEntry to look up
1809  * @iter: holds the returned #GtkTreeIter
1810  *
1811  * Creates a #GtkTreeIter pointing to the specified entry in the model.
1812  *
1813  * Return value: %TRUE if the iterator now points to the entry
1814  */
1815 gboolean
1816 rhythmdb_query_model_entry_to_iter (RhythmDBQueryModel *model,
1817 				    RhythmDBEntry *entry,
1818 				    GtkTreeIter *iter)
1819 {
1820 	GSequenceIter *ptr;
1821 
1822 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
1823 
1824 	if (G_UNLIKELY (ptr == NULL)) {
1825 		/* Invalidate iterator so future uses break quickly. */
1826 		iter->stamp = !(model->priv->stamp);
1827 		return FALSE;
1828 	}
1829 
1830 	iter->stamp = model->priv->stamp;
1831 	iter->user_data = ptr;
1832 	return TRUE;
1833 }
1834 
1835 /**
1836  * rhythmdb_query_model_tree_path_to_entry:
1837  * @model: a #RhythmDBQueryModel
1838  * @path: a #GtkTreePath
1839  *
1840  * Locates the #RhythmDBEntry identified by the specified path in the model.
1841  * The caller owns a reference to the returned entry.
1842  *
1843  * Return value: the #RhythmDBEntry, if any
1844  */
1845 RhythmDBEntry *
1846 rhythmdb_query_model_tree_path_to_entry (RhythmDBQueryModel *model,
1847 					 GtkTreePath *path)
1848 {
1849 	GtkTreeIter entry_iter;
1850 
1851 	g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &entry_iter, path));
1852 	return rhythmdb_query_model_iter_to_entry (model, &entry_iter);
1853 }
1854 
1855 /**
1856  * rhythmdb_query_model_iter_to_entry:
1857  * @model: a #RhythmDBQueryModel
1858  * @entry_iter: a #GtkTreeIter to dereference
1859  *
1860  * Locates and returns the #RhythmDBEntry pointed to by the specified iterator
1861  * in the model.  The caller owns a reference to the returned entry.
1862  *
1863  * Return value: the #RhythmDBEntry, if any
1864  */
1865 RhythmDBEntry *
1866 rhythmdb_query_model_iter_to_entry (RhythmDBQueryModel *model,
1867 				    GtkTreeIter *entry_iter)
1868 {
1869 	RhythmDBEntry *entry;
1870 	gtk_tree_model_get (GTK_TREE_MODEL (model), entry_iter, 0, &entry, -1);
1871 	return entry;
1872 }
1873 
1874 /**
1875  * rhythmdb_query_model_get_next_from_entry:
1876  * @model: a #RhythmDBQueryModel
1877  * @entry: a #RhythmDBEntry
1878  *
1879  * Locates and returns the next #RhythmDBEntry in the model after the specified
1880  * entry.  The caller owns a reference to the returned entry.
1881  *
1882  * Return value: the next #RhythmDBEntry in the model, if any
1883  */
1884 RhythmDBEntry *
1885 rhythmdb_query_model_get_next_from_entry (RhythmDBQueryModel *model,
1886 					  RhythmDBEntry *entry)
1887 {
1888 	GtkTreeIter iter;
1889 
1890 	g_return_val_if_fail (entry != NULL, NULL);
1891 
1892 	if (entry && rhythmdb_query_model_entry_to_iter (model, entry, &iter)) {
1893 		if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
1894 			return NULL;
1895 	} else {
1896 		/* If the entry isn't in the model, the "next" entry is the first. */
1897 		if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
1898 			return NULL;
1899 	}
1900 
1901 	return rhythmdb_query_model_iter_to_entry (model, &iter);
1902 }
1903 
1904 /**
1905  * rhythmdb_query_model_get_previous_from_entry:
1906  * @model: a #RhythmDBQueryModel
1907  * @entry: a #RhythmDBEntry
1908  *
1909  * Locates and returns the  #RhythmDBEntry in the model before the specified
1910  * entry.  The caller owns a reference to the returned entry.
1911  *
1912  * Return value: the previous #RhythmDBEntry in the model, if any
1913  */
1914 RhythmDBEntry *
1915 rhythmdb_query_model_get_previous_from_entry (RhythmDBQueryModel *model,
1916 					      RhythmDBEntry *entry)
1917 {
1918 	GtkTreeIter iter;
1919 	GtkTreePath *path;
1920 
1921 	g_return_val_if_fail (entry != NULL, NULL);
1922 
1923 	if (!rhythmdb_query_model_entry_to_iter (model, entry, &iter))
1924 		return NULL;
1925 
1926 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1927 	g_assert (path);
1928 	if (!gtk_tree_path_prev (path)) {
1929 		gtk_tree_path_free (path);
1930 		return NULL;
1931 	}
1932 
1933 	g_assert (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path));
1934 	gtk_tree_path_free (path);
1935 
1936 	return rhythmdb_query_model_iter_to_entry (model, &iter);
1937 }
1938 
1939 static gboolean
1940 rhythmdb_query_model_row_draggable (RbTreeDragSource *dragsource,
1941 				    GList *paths)
1942 {
1943 	return TRUE;
1944 }
1945 
1946 static gboolean
1947 rhythmdb_query_model_drag_data_delete (RbTreeDragSource *dragsource,
1948 				       GList *paths)
1949 {
1950 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (dragsource);
1951 	GtkTreeModel *treemodel = GTK_TREE_MODEL (model);
1952 
1953 	/* we don't delete if it is a reorder drag and drop because the deletion already
1954 	   occured in rhythmdb_query_model_drag_data_received */
1955 	if (model->priv->sort_func == NULL && !model->priv->reorder_drag_and_drop) {
1956 
1957 		RhythmDBEntry *entry;
1958 		GtkTreeIter iter;
1959 		GtkTreePath *path;
1960 
1961 		for (; paths; paths = paths->next) {
1962 
1963 			path = gtk_tree_row_reference_get_path (paths->data);
1964 
1965 			if (path) {
1966 				if (rhythmdb_query_model_get_iter (treemodel, &iter, path)) {
1967 					entry = g_sequence_get (iter.user_data);
1968 					rhythmdb_query_model_remove_entry (model, entry);
1969 				}
1970 				gtk_tree_path_free (path);
1971 			}
1972 		}
1973 	}
1974 
1975 	model->priv->reorder_drag_and_drop = FALSE;
1976 	return TRUE;
1977 
1978 }
1979 
1980 static gboolean
1981 rhythmdb_query_model_drag_data_get (RbTreeDragSource *dragsource,
1982 				    GList *paths,
1983 				    GtkSelectionData *selection_data)
1984 {
1985 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (dragsource);
1986 	RhythmDBEntry *entry;
1987 	GdkAtom selection_data_target;
1988 	GString *data;
1989 	guint target;
1990 	GList *tem;
1991 	gboolean need_newline = FALSE;
1992 
1993 	rb_debug ("getting drag data");
1994 
1995 	selection_data_target = gtk_selection_data_get_target (selection_data);
1996 	if (!gtk_target_list_find (rhythmdb_query_model_drag_target_list,
1997 				   selection_data_target, &target)) {
1998 		return FALSE;
1999 	}
2000 
2001 
2002 	data = g_string_new ("");
2003 
2004 	for (tem = paths; tem; tem = tem->next) {
2005 		GtkTreeIter iter;
2006 		GtkTreePath *path;
2007 
2008 		path = gtk_tree_row_reference_get_path (tem->data);
2009 
2010 		gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
2011 
2012 		entry = g_sequence_get (iter.user_data);
2013 
2014 		if (need_newline)
2015 			g_string_append (data, "\r\n");
2016 
2017 		if (target == TARGET_URIS) {
2018 			char *location;
2019 
2020 			location = rhythmdb_entry_get_playback_uri (entry);
2021 			if (location == NULL) {
2022 				need_newline = FALSE;
2023 				continue;
2024 			}
2025 
2026 			g_string_append (data, location);
2027 			g_free (location);
2028 		} else if (target == TARGET_ENTRIES) {
2029 			g_string_append_printf (data,
2030 						"%lu",
2031 						rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_ENTRY_ID));
2032 		}
2033 		need_newline = TRUE;
2034 	}
2035 
2036 	gtk_selection_data_set (selection_data,
2037 				selection_data_target,
2038 				8, (guchar *) data->str,
2039 				data->len);
2040 
2041 	g_string_free (data, TRUE);
2042 
2043 	return TRUE;
2044 }
2045 
2046 static gboolean
2047 rhythmdb_query_model_drag_data_received (RbTreeDragDest *drag_dest,
2048 					 GtkTreePath *dest,
2049 					 GtkTreeViewDropPosition pos,
2050 					 GtkSelectionData *selection_data)
2051 {
2052 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (drag_dest);
2053 
2054 	if (model->priv->base_model) {
2055 		GtkTreeIter base_iter;
2056 		GtkTreePath *base_dest;
2057 		RhythmDBEntry *entry;
2058 		gboolean result;
2059 
2060 		if (dest) {
2061 			entry = rhythmdb_query_model_tree_path_to_entry (model, dest);
2062 			g_assert (entry);
2063 			rhythmdb_query_model_entry_to_iter (model->priv->base_model, entry, &base_iter);
2064 			base_dest = gtk_tree_model_get_path (GTK_TREE_MODEL (model->priv->base_model), &base_iter);
2065 			rhythmdb_entry_unref (entry);
2066 		} else {
2067 			base_dest = NULL;
2068 		}
2069 
2070 		result = rhythmdb_query_model_drag_data_received ((RbTreeDragDest*)model->priv->base_model,
2071 								  base_dest, pos, selection_data);
2072 		if (base_dest)
2073 			gtk_tree_path_free (base_dest);
2074 
2075 		return result;
2076 	}
2077 
2078 	rb_debug ("drag received");
2079 
2080 	if (model->priv->sort_func != NULL)
2081 		return FALSE;
2082 
2083 	if ((gtk_selection_data_get_format (selection_data) == 8) &&
2084 	    (gtk_selection_data_get_length (selection_data) >= 0)) {
2085 		GtkTreeIter iter;
2086 		GSequenceIter *ptr;
2087 		char **strv;
2088 		RhythmDBEntry *entry;
2089 		gboolean uri_list;
2090 		int i = 0;
2091 
2092 		uri_list = (gtk_selection_data_get_data_type (selection_data) == gdk_atom_intern ("text/uri-list", TRUE));
2093 
2094 		strv = g_strsplit ((char *) gtk_selection_data_get_data (selection_data), "\r\n", -1);
2095 
2096 		if (dest == NULL || !rhythmdb_query_model_get_iter (GTK_TREE_MODEL (model), &iter, dest))
2097 			ptr = g_sequence_get_end_iter (model->priv->entries);
2098 		else
2099 			ptr = iter.user_data;
2100 
2101 		if (pos == GTK_TREE_VIEW_DROP_AFTER)
2102 			ptr = g_sequence_iter_next (ptr);
2103 
2104 		for (; strv[i]; i++) {
2105 			GSequenceIter *tem_ptr;
2106 			GtkTreeIter tem_iter;
2107 
2108 			if (g_utf8_strlen (strv[i], -1) == 0)
2109 				continue;
2110 
2111 			entry = rhythmdb_entry_lookup_from_string (model->priv->db, strv[i], !uri_list);
2112 			if (entry == NULL) {
2113 				int pos;
2114 
2115 				if (uri_list) {
2116 					if (g_sequence_iter_is_end (ptr))
2117 						pos = -1;
2118 					else
2119 						pos = g_sequence_iter_get_position (ptr);
2120 
2121 					g_signal_emit (G_OBJECT (model),
2122 						       rhythmdb_query_model_signals[NON_ENTRY_DROPPED],
2123 						       0, strv[i], pos);
2124 				} else {
2125 					rb_debug ("got drop with entry id %s, but can't find the entry", strv[i]);
2126 				}
2127 			} else {
2128 				GSequenceIter *old_ptr;
2129 				GtkTreePath *tem_path;
2130 				gint old_pos = 0;
2131 				gint new_pos;
2132 
2133 				old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2134 				/* trying to drag drop an entry on itself ! */
2135 				if (old_ptr == ptr) {
2136 					continue;
2137 				} else if (old_ptr == NULL) {
2138 					gboolean allow;
2139 
2140 					g_signal_emit (G_OBJECT (model),
2141 						       rhythmdb_query_model_signals[FILTER_ENTRY_DROP],
2142 						       0, entry, &allow);
2143 					if (allow == FALSE) {
2144 						rb_debug ("dropping of entry %s disallowed by filter",
2145 							  rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
2146 						continue;
2147 					}
2148 				}
2149 
2150 				/* take temporary ref */
2151 				rhythmdb_entry_ref (entry);
2152 
2153 				/* the entry already exists it is either a reorder drag and drop
2154 				   (or a drag and drop from another application), so we delete
2155 				   the existing one before adding it again. */
2156 				if (old_ptr) {
2157 					model->priv->reorder_drag_and_drop = TRUE;
2158 
2159 					old_pos = g_sequence_iter_get_position (old_ptr);
2160 					g_sequence_remove (old_ptr);
2161 					g_assert (g_hash_table_remove (model->priv->reverse_map, entry));
2162 				} else {
2163 					model->priv->reorder_drag_and_drop = FALSE;
2164 				}
2165 
2166 				g_sequence_insert_before (ptr, entry);
2167 
2168 				tem_ptr = g_sequence_iter_prev (ptr);
2169 				new_pos = g_sequence_iter_get_position (tem_ptr);
2170 
2171 				tem_iter.stamp = model->priv->stamp;
2172 				tem_iter.user_data = tem_ptr;
2173 				/* the hash now owns this reference to the entry */
2174 				g_hash_table_insert (model->priv->reverse_map, entry, tem_ptr);
2175 
2176 				if (old_ptr) {
2177 					rb_debug ("moving entry %p from %d to %d", entry, old_pos, new_pos);
2178 					rhythmdb_query_model_emit_reorder (model, old_pos, new_pos);
2179 				} else {
2180 					tem_path = rhythmdb_query_model_get_path (GTK_TREE_MODEL (model),
2181 										  &tem_iter);
2182 					gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
2183 								     tem_path, &tem_iter);
2184 					gtk_tree_path_free (tem_path);
2185 				}
2186 			}
2187 		}
2188 
2189 		g_strfreev (strv);
2190 		return TRUE;
2191 	}
2192 	return FALSE;
2193 }
2194 
2195 /* 
2196  * determines whether reordering is possible by checking up
2197  * the chain for a model with a sort function set.
2198  */
2199 static gboolean
2200 query_model_chain_can_reorder (RhythmDBQueryModel *model)
2201 {
2202 	while (model) {
2203 		if (model->priv->sort_func != NULL)
2204 			return FALSE;
2205 
2206 		model = model->priv->base_model;
2207 	}
2208 	return TRUE;
2209 }
2210 
2211 static gboolean
2212 rhythmdb_query_model_row_drop_possible (RbTreeDragDest *drag_dest,
2213 					GtkTreePath *dest,
2214 					GtkTreeViewDropPosition pos,
2215 					GtkSelectionData *selection_data)
2216 {
2217 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (drag_dest);
2218 	return query_model_chain_can_reorder (model);
2219 }
2220 
2221 static gboolean
2222 rhythmdb_query_model_row_drop_position (RbTreeDragDest *drag_dest,
2223 					GtkTreePath *dest_path,
2224 					GList *targets,
2225 					GtkTreeViewDropPosition *pos)
2226 {
2227 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (drag_dest);
2228 	return query_model_chain_can_reorder (model);
2229 }
2230 
2231 static void
2232 rhythmdb_query_model_set_query (RhythmDBQueryResults *results, GPtrArray *query)
2233 {
2234 	g_object_set (G_OBJECT (results), "query", query, NULL);
2235 }
2236 
2237 /* Threading: Called from the database query thread for async queries,
2238  *  from the main thread for synchronous queries.
2239  */
2240 static void
2241 rhythmdb_query_model_add_results (RhythmDBQueryResults *results,
2242 				  GPtrArray *entries)
2243 {
2244 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (results);
2245 	struct RhythmDBQueryModelUpdate *update;
2246 	guint i;
2247 
2248 	rb_debug ("adding %d entries", entries->len);
2249 
2250 	update = g_new (struct RhythmDBQueryModelUpdate, 1);
2251 	update->type = RHYTHMDB_QUERY_MODEL_UPDATE_ROWS_INSERTED;
2252 	update->entrydata.entries = entries;
2253 	update->model = model;
2254 
2255 	/* take references; released in update idle */
2256 	g_object_ref (model);
2257 
2258 	for (i = 0; i < update->entrydata.entries->len; i++) {
2259 		rhythmdb_entry_ref (g_ptr_array_index (update->entrydata.entries, i));
2260 	}
2261 
2262 	rhythmdb_query_model_process_update (update);
2263 }
2264 
2265 static void
2266 rhythmdb_query_model_query_complete (RhythmDBQueryResults *results)
2267 {
2268 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (results);
2269 	struct RhythmDBQueryModelUpdate *update;
2270 
2271 	update = g_new0 (struct RhythmDBQueryModelUpdate, 1);
2272 	update->type = RHYTHMDB_QUERY_MODEL_UPDATE_QUERY_COMPLETE;
2273 	update->model = model;
2274 
2275 	/* take reference; released in update idle */
2276 	g_object_ref (model);
2277 
2278 	rhythmdb_query_model_process_update (update);
2279 }
2280 
2281 static GtkTreeModelFlags
2282 rhythmdb_query_model_get_flags (GtkTreeModel *model)
2283 {
2284 	return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
2285 }
2286 
2287 static gint
2288 rhythmdb_query_model_get_n_columns (GtkTreeModel *tree_model)
2289 {
2290 	return 2;
2291 }
2292 
2293 static GType
2294 rhythmdb_query_model_get_column_type (GtkTreeModel *tree_model,
2295 				      int index)
2296 {
2297 	switch (index) {
2298 	case 0:
2299 		return RHYTHMDB_TYPE_ENTRY;
2300 	case 1:
2301 		return G_TYPE_INT;
2302 	default:
2303 		g_assert_not_reached ();
2304 		return G_TYPE_INVALID;
2305 	}
2306 }
2307 
2308 static gboolean
2309 rhythmdb_query_model_get_iter (GtkTreeModel *tree_model,
2310 			       GtkTreeIter *iter,
2311 			       GtkTreePath *path)
2312 {
2313 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2314 	guint index;
2315 	GSequenceIter *ptr;
2316 
2317 	index = gtk_tree_path_get_indices (path)[0];
2318 
2319 	if (index >= g_sequence_get_length (model->priv->entries))
2320 		return FALSE;
2321 
2322 	ptr = g_sequence_get_iter_at_pos (model->priv->entries, index);
2323 	g_assert (ptr);
2324 
2325 	iter->stamp = model->priv->stamp;
2326 	iter->user_data = ptr;
2327 
2328 	return TRUE;
2329 }
2330 
2331 static GtkTreePath *
2332 rhythmdb_query_model_get_path (GtkTreeModel *tree_model,
2333 			       GtkTreeIter  *iter)
2334 {
2335 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2336 	GtkTreePath *path;
2337 
2338 	g_return_val_if_fail (iter->stamp == model->priv->stamp, NULL);
2339 
2340 	if (g_sequence_iter_is_end (iter->user_data))
2341 		return NULL;
2342 
2343 	path = gtk_tree_path_new ();
2344 	gtk_tree_path_append_index (path, g_sequence_iter_get_position (iter->user_data));
2345 	return path;
2346 }
2347 
2348 static void
2349 rhythmdb_query_model_get_value (GtkTreeModel *tree_model,
2350 				GtkTreeIter *iter,
2351 				gint column,
2352 				GValue *value)
2353 {
2354 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2355 	RhythmDBEntry *entry;
2356 
2357 	/* this is done internally by eggsequence anyway
2358 	g_return_if_fail (!g_sequence_iter_is_end (iter->user_data));*/
2359 	g_return_if_fail (model->priv->stamp == iter->stamp);
2360 
2361 	entry = g_sequence_get (iter->user_data);
2362 
2363 	switch (column) {
2364 	case 0:
2365 		g_value_init (value, RHYTHMDB_TYPE_ENTRY);
2366 		g_value_set_boxed (value, entry);
2367 		break;
2368 	case 1:
2369 		g_value_init (value, G_TYPE_INT);
2370 		g_value_set_int (value, g_sequence_iter_get_position (iter->user_data)+1);
2371 		break;
2372 	default:
2373 		g_assert_not_reached ();
2374 	}
2375 }
2376 
2377 static gboolean
2378 rhythmdb_query_model_iter_next (GtkTreeModel  *tree_model,
2379 				GtkTreeIter   *iter)
2380 {
2381 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2382 
2383 	g_return_val_if_fail (iter->stamp == model->priv->stamp, FALSE);
2384 
2385 	iter->user_data = g_sequence_iter_next (iter->user_data);
2386 
2387 	return !g_sequence_iter_is_end (iter->user_data);
2388 }
2389 
2390 static gboolean
2391 rhythmdb_query_model_iter_children (GtkTreeModel *tree_model,
2392 				    GtkTreeIter  *iter,
2393 				    GtkTreeIter  *parent)
2394 {
2395 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2396 
2397 	if (parent != NULL)
2398 		return FALSE;
2399 
2400 	if (g_sequence_get_length (model->priv->entries) == 0)
2401 		return FALSE;
2402 
2403 	iter->stamp = model->priv->stamp;
2404 	iter->user_data = g_sequence_get_begin_iter (model->priv->entries);
2405 
2406 	return TRUE;
2407 }
2408 
2409 static gboolean
2410 rhythmdb_query_model_iter_has_child (GtkTreeModel *tree_model,
2411 				     GtkTreeIter  *iter)
2412 {
2413 	return FALSE;
2414 }
2415 
2416 static gint
2417 rhythmdb_query_model_iter_n_children (GtkTreeModel *tree_model,
2418 				      GtkTreeIter  *iter)
2419 {
2420 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2421 
2422 	if (iter == NULL)
2423 		return g_sequence_get_length (model->priv->entries);
2424 
2425 	g_return_val_if_fail (model->priv->stamp == iter->stamp, -1);
2426 
2427 	return 0;
2428 }
2429 
2430 static gboolean
2431 rhythmdb_query_model_iter_nth_child (GtkTreeModel *tree_model,
2432 				     GtkTreeIter *iter,
2433 				     GtkTreeIter *parent,
2434 				     gint n)
2435 {
2436 	RhythmDBQueryModel *model = RHYTHMDB_QUERY_MODEL (tree_model);
2437 	GSequenceIter *child;
2438 
2439 	if (parent)
2440 		return FALSE;
2441 
2442 	child = g_sequence_get_iter_at_pos (model->priv->entries, n);
2443 
2444 	if (g_sequence_iter_is_end (child))
2445 		return FALSE;
2446 
2447 	iter->stamp = model->priv->stamp;
2448 	iter->user_data = child;
2449 
2450 	return TRUE;
2451 }
2452 
2453 static gboolean
2454 rhythmdb_query_model_iter_parent (GtkTreeModel *tree_model,
2455 				  GtkTreeIter  *iter,
2456 				  GtkTreeIter  *child)
2457 {
2458 	return FALSE;
2459 }
2460 
2461 /**
2462  * rhythmdb_query_model_compute_status_normal:
2463  * @model: a #RhythmDBQueryModel
2464  * @singular: singular form of the pattern describing the number of entries ("%d song")
2465  * @plural: plural form of the pattern describing the number of entries ("%d songs")
2466  *
2467  * Constructs a status string describing the contents of the model.
2468  *
2469  * Return value: allocated status string, to be freed by the caller.
2470  */
2471 char *
2472 rhythmdb_query_model_compute_status_normal (RhythmDBQueryModel *model,
2473 					    const char *singular,
2474 					    const char *plural)
2475 {
2476 	return rhythmdb_compute_status_normal (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL),
2477 					       rhythmdb_query_model_get_duration (model),
2478 					       rhythmdb_query_model_get_size (model),
2479 					       singular,
2480 					       plural);
2481 }
2482 
2483 static void
2484 apply_updated_entry_sequence (RhythmDBQueryModel *model,
2485 			      GSequence *new_entries)
2486 {
2487 	int *reorder_map;
2488 	int length, i;
2489 	GtkTreePath *path;
2490 	GtkTreeIter iter;
2491 	GSequenceIter *ptr;
2492 
2493 	length = g_sequence_get_length (new_entries);
2494 	/* generate resort map and rebuild reverse map */
2495 	reorder_map = g_malloc (length * sizeof(gint));
2496 
2497 	ptr = g_sequence_get_begin_iter (new_entries);
2498 	for (i = 0; i < length; i++) {
2499 		gpointer entry = g_sequence_get (ptr);
2500 		GSequenceIter *old_ptr;
2501 
2502 		old_ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2503 		reorder_map[i] = g_sequence_iter_get_position (old_ptr);
2504 		g_hash_table_replace (model->priv->reverse_map, rhythmdb_entry_ref (entry), ptr);
2505 
2506 		ptr = g_sequence_iter_next (ptr);
2507 	}
2508 	g_sequence_free (model->priv->entries);
2509 	model->priv->entries = new_entries;
2510 
2511 	/* emit the re-order and clean up */
2512 	gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
2513 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
2514 	gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
2515 				       path, &iter,
2516 				       reorder_map);
2517 
2518 	gtk_tree_path_free (path);
2519 	g_free (reorder_map);
2520 }
2521 
2522 /**
2523  * rhythmdb_query_model_set_sort_order:
2524  * @model: a #RhythmDBQueryModel
2525  * @sort_func: new sort function
2526  * @sort_data: data to pass to the new sort function
2527  * @sort_data_destroy: function to call to free the sort data
2528  * @sort_reverse: if %TRUE, reverse the sort order
2529  *
2530  * Sets a new sort order on the model.  This reorders the entries
2531  * in the model to match the new sort order.
2532  */
2533 void
2534 rhythmdb_query_model_set_sort_order (RhythmDBQueryModel *model,
2535 				     GCompareDataFunc sort_func,
2536 				     gpointer sort_data,
2537 				     GDestroyNotify sort_data_destroy,
2538 				     gboolean sort_reverse)
2539 {
2540 	GSequence *new_entries;
2541 	GSequenceIter *ptr;
2542 	int length, i;
2543 	struct ReverseSortData reverse_data;
2544 
2545 	if ((model->priv->sort_func == sort_func) &&
2546 	    (model->priv->sort_data == sort_data) &&
2547 	    (model->priv->sort_data_destroy == sort_data_destroy) &&
2548 	    (model->priv->sort_reverse == sort_reverse))
2549 		return;
2550 
2551 	g_return_if_fail ((model->priv->limit_type == RHYTHMDB_QUERY_MODEL_LIMIT_NONE) ||
2552 			  (model->priv->sort_func == NULL));
2553 	if (model->priv->sort_func == NULL)
2554 		g_assert (g_sequence_get_length (model->priv->limited_entries) == 0);
2555 
2556 	if (model->priv->sort_data_destroy && model->priv->sort_data)
2557 		model->priv->sort_data_destroy (model->priv->sort_data);
2558 
2559 	model->priv->sort_func = sort_func;
2560 	model->priv->sort_data = sort_data;
2561 	model->priv->sort_data_destroy = sort_data_destroy;
2562 	model->priv->sort_reverse = sort_reverse;
2563 
2564 	if (model->priv->sort_reverse) {
2565 		reverse_data.func = sort_func;
2566 		reverse_data.data = sort_data;
2567 		sort_func = (GCompareDataFunc) _reverse_sorting_func;
2568 		sort_data = &reverse_data;
2569 	}
2570 
2571 	/* create the new sorted entry sequence */
2572 	length = g_sequence_get_length (model->priv->entries);
2573 	if (length > 0) {
2574 		new_entries = g_sequence_new (NULL);
2575 		ptr = g_sequence_get_begin_iter (model->priv->entries);
2576 		for (i = 0; i < length; i++) {
2577 			gpointer entry = g_sequence_get (ptr);
2578 
2579 			g_sequence_insert_sorted (new_entries, entry,
2580 						  sort_func,
2581 						  sort_data);
2582 			ptr = g_sequence_iter_next (ptr);
2583 		}
2584 
2585 		apply_updated_entry_sequence (model, new_entries);
2586 	}
2587 }
2588 
2589 static int
2590 rhythmdb_query_model_child_index_to_base_index (RhythmDBQueryModel *model,
2591 						int index)
2592 {
2593 	GSequenceIter *ptr;
2594 	RhythmDBEntry *entry;
2595 	g_assert (model->priv->base_model);
2596 
2597 	ptr = g_sequence_get_iter_at_pos (model->priv->entries, index);
2598 	if (ptr == NULL || g_sequence_iter_is_end (ptr))
2599 		return -1;
2600 	entry = (RhythmDBEntry*)g_sequence_get (ptr);
2601 
2602 	ptr = g_hash_table_lookup (model->priv->base_model->priv->reverse_map, entry);
2603 	g_assert (ptr); /* all child model entries are in the base model */
2604 
2605 	return g_sequence_iter_get_position (ptr);
2606 }
2607 
2608 /*static int
2609 rhythmdb_query_model_base_index_to_child_index (RhythmDBQueryModel *model, int index)
2610 {
2611 	GSequenceIter *ptr;
2612 	RhythmDBEntry *entry;
2613 	int pos;
2614 
2615 	g_assert (model->priv->base_model);
2616 	if (index == -1)
2617 		return -1;
2618 
2619 	ptr = g_sequence_get_iter_at_pos (model->priv->base_model->priv->entries, index);
2620 	if (ptr == NULL || g_sequence_iter_is_end (ptr))
2621 		return -1;
2622 	entry = (RhythmDBEntry*)g_sequence_get (ptr);
2623 
2624 	ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2625 	if (ptr == NULL)
2626 		return -1;
2627 
2628 	pos = g_sequence_iter_get_position (ptr);
2629 	return pos;
2630 }*/
2631 
2632 static int
2633 rhythmdb_query_model_get_entry_index (RhythmDBQueryModel *model,
2634 				      RhythmDBEntry *entry)
2635 {
2636 	GSequenceIter *ptr = g_hash_table_lookup (model->priv->reverse_map, entry);
2637 
2638 	if (ptr)
2639 		return g_sequence_iter_get_position (ptr);
2640 	else
2641 		return -1;
2642 }
2643 
2644 static void
2645 rhythmdb_query_model_base_row_inserted (GtkTreeModel *tree_model,
2646 					GtkTreePath *path,
2647 					GtkTreeIter *iter,
2648 					RhythmDBQueryModel *model)
2649 {
2650 	RhythmDBQueryModel *base_model = RHYTHMDB_QUERY_MODEL (tree_model);
2651 	RhythmDBEntry *entry;
2652 	RhythmDBEntry *prev_entry;
2653 	int index;
2654 
2655 	g_assert (base_model == model->priv->base_model);
2656 
2657 	entry = rhythmdb_query_model_iter_to_entry (base_model, iter);
2658 
2659 	if (!model->priv->show_hidden && rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
2660 		goto out;
2661 
2662 	if (rhythmdb_evaluate_query (model->priv->db, model->priv->query, entry)) {
2663 		/* find the closest previous entry that is in the filter model, and it it after that */
2664 		prev_entry = rhythmdb_query_model_get_previous_from_entry (base_model, entry);
2665 		while (prev_entry && g_hash_table_lookup (model->priv->reverse_map, prev_entry) == NULL) {
2666 			rhythmdb_entry_unref (prev_entry);
2667 			prev_entry = rhythmdb_query_model_get_previous_from_entry (base_model, prev_entry);
2668 		}
2669 
2670 		if (entry != NULL) {
2671 			index = rhythmdb_query_model_get_entry_index (model, prev_entry) + 1;
2672 		} else {
2673 			index = 0;
2674 		}
2675 
2676 		if (prev_entry != NULL) {
2677 			rhythmdb_entry_unref (prev_entry);
2678 		}
2679 
2680 		rb_debug ("inserting entry %p from base model %p to model %p in position %d", entry, base_model, model, index);
2681 		rhythmdb_query_model_do_insert (model, entry, index);
2682 	}
2683  out:
2684 	rhythmdb_entry_unref (entry);
2685 }
2686 
2687 static void
2688 rhythmdb_query_model_base_row_deleted (GtkTreeModel *base_model,
2689 				       GtkTreePath *path,
2690 				       RhythmDBQueryModel *model)
2691 {
2692 	RhythmDBEntry *entry;
2693 
2694 	entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (base_model), path);
2695 	rb_debug ("deleting entry %p from base model %p to model %p", entry, base_model, model);
2696 
2697 	rhythmdb_query_model_filter_out_entry (model, entry);
2698 	rhythmdb_entry_unref (entry);
2699 }
2700 
2701 static void
2702 rhythmdb_query_model_base_non_entry_dropped (GtkTreeModel *base_model,
2703 					     const char *location,
2704 					     int position,
2705 					     RhythmDBQueryModel *model)
2706 {
2707 	g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[NON_ENTRY_DROPPED], 0,
2708 		       location, rhythmdb_query_model_child_index_to_base_index (model, position));
2709 }
2710 
2711 static void
2712 rhythmdb_query_model_base_complete (GtkTreeModel *base_model,
2713 				    RhythmDBQueryModel *model)
2714 {
2715 	g_signal_emit (G_OBJECT (model), rhythmdb_query_model_signals[COMPLETE], 0);
2716 }
2717 
2718 typedef struct {
2719 	RhythmDBQueryModel *model;
2720 	GSequence *new_entries;
2721 } _BaseRowsReorderedData;
2722 
2723 static void
2724 _base_rows_reordered_foreach_cb (RhythmDBEntry *entry, _BaseRowsReorderedData *data)
2725 {
2726 	if (g_hash_table_lookup (data->model->priv->reverse_map, entry))
2727 		g_sequence_append (data->new_entries, entry);
2728 }
2729 
2730 static void
2731 rhythmdb_query_model_base_rows_reordered (GtkTreeModel *base_model,
2732 					  GtkTreePath *arg1,
2733 					  GtkTreeIter *arg2,
2734 					  gint *order_map,
2735 					  RhythmDBQueryModel *model)
2736 {
2737 	RhythmDBQueryModel *base_query_model = RHYTHMDB_QUERY_MODEL (base_model);
2738 	_BaseRowsReorderedData data;
2739 
2740 	/* ignore, if this model sorts */
2741 	if (model->priv->sort_func)
2742 		return;
2743 
2744 	data.new_entries = g_sequence_new (NULL);
2745 	data.model = model;
2746 	g_sequence_foreach (base_query_model->priv->entries, (GFunc)_base_rows_reordered_foreach_cb, &data);
2747 	apply_updated_entry_sequence (model, data.new_entries);
2748 }
2749 
2750 static void
2751 rhythmdb_query_model_base_entry_removed (RhythmDBQueryModel *base_model,
2752 					 RhythmDBEntry *entry,
2753 					 RhythmDBQueryModel *model)
2754 {
2755 	if (g_hash_table_lookup (model->priv->reverse_map, entry)) {
2756 		/* propagate the signal out to any attached property models */
2757 		g_signal_emit (G_OBJECT (model),
2758 			       rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
2759 			       entry);
2760 	}
2761 }
2762 
2763 typedef struct {
2764 	RhythmDBQueryModel *model;
2765 	GList *remove;
2766 } _ReapplyQueryForeachData;
2767 
2768 static void
2769 _reapply_query_foreach_cb (RhythmDBEntry *entry, _ReapplyQueryForeachData *data)
2770 {
2771 	if (!rhythmdb_evaluate_query (data->model->priv->db,
2772 				      data->model->priv->query,
2773 				      entry)) {
2774 		data->remove = g_list_prepend (data->remove, entry);
2775 	}
2776 }
2777 
2778 /**
2779  * rhythmdb_query_model_reapply_query:
2780  * @model: a #RhythmDBQueryModel
2781  * @filter: if %FALSE, emit entry-removed signals
2782  *
2783  * Reapplies the existing query to the entries in the model.  This
2784  * is mostly useful when the query contains relative time criteria
2785  * (such as 'not played in the last hour').  This will only remove
2786  * entries that are already in the model, it will not find entries
2787  * that previously did not match the query.
2788  *
2789  * The 'filter' parameter should be set to TRUE when the query is
2790  * being used as a filter, rather than to define a base set of entries.
2791  */
2792 void
2793 rhythmdb_query_model_reapply_query (RhythmDBQueryModel *model,
2794 				    gboolean filter)
2795 {
2796 	gboolean changed = FALSE;
2797 	_ReapplyQueryForeachData data;
2798 	GList *t;
2799 
2800 	data.model = model;
2801 	data.remove = NULL;
2802 
2803 	/* process limited list first, so entries that don't match can't sneak in
2804 	 * to the main list from there
2805 	 */
2806 	if (model->priv->limited_entries)
2807 		g_sequence_foreach (model->priv->limited_entries, (GFunc)_reapply_query_foreach_cb, &data);
2808 
2809 	for (t = data.remove; t; t = t->next)
2810 		rhythmdb_query_model_remove_from_limited_list (model, (RhythmDBEntry*)t->data);
2811 
2812 	changed |= (data.remove != NULL);
2813 	g_list_free (data.remove);
2814 	data.remove = NULL;
2815 
2816 
2817 	if (model->priv->entries)
2818 		g_sequence_foreach (model->priv->entries, (GFunc)_reapply_query_foreach_cb, &data);
2819 
2820 	for (t = data.remove; t; t = t->next) {
2821 		RhythmDBEntry *entry = t->data;
2822 		if (!filter) {
2823 			g_signal_emit (G_OBJECT (model),
2824 				       rhythmdb_query_model_signals[ENTRY_REMOVED], 0,
2825 				       entry);
2826 		}
2827 		rhythmdb_query_model_remove_from_main_list (model, entry);
2828 	}
2829 
2830 	changed |= (data.remove != NULL);
2831 	g_list_free (data.remove);
2832 	data.remove = NULL;
2833 
2834 	if (changed)
2835 		rhythmdb_query_model_update_limited_entries (model);
2836 }
2837 
2838 static gint
2839 _reverse_sorting_func (gpointer a,
2840 		       gpointer b,
2841 		       struct ReverseSortData *reverse_data)
2842 {
2843 	return - reverse_data->func (a, b, reverse_data->data);
2844 }
2845 
2846 /**
2847  * rhythmdb_query_model_location_sort_func:
2848  * @a: a #RhythmDBEntry
2849  * @b: a #RhythmDBEntry
2850  * @data: nothing
2851  *
2852  * Sort function for sorting by location.
2853  *
2854  * Returns: result of sort comparison between a and b.
2855  */
2856 gint
2857 rhythmdb_query_model_location_sort_func (RhythmDBEntry *a,
2858 					 RhythmDBEntry *b,
2859 					 gpointer data)
2860 {
2861 	const char *a_val;
2862 	const char *b_val;
2863 
2864 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_LOCATION);
2865 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_LOCATION);
2866 
2867 	if (a_val == NULL) {
2868 		if (b_val == NULL)
2869 			return 0;
2870 		else
2871 			return -1;
2872 	} else if (b_val == NULL)
2873 		return 1;
2874 	else
2875 		return strcmp (a_val, b_val);
2876 }
2877 
2878 /**
2879  * rhythmdb_query_model_title_sort_func:
2880  * @a: a #RhythmDBEntry
2881  * @b: a #RhythmDBEntry
2882  * @data: nothing
2883  *
2884  * Sort function for sorting by title.  Falls back to sorting
2885  * by location if the titles are the same.
2886  *
2887  * Returns: result of sort comparison between a and b.
2888  */
2889 gint
2890 rhythmdb_query_model_title_sort_func (RhythmDBEntry *a,
2891 				      RhythmDBEntry *b,
2892 				      gpointer data)
2893 {
2894 	const char *a_val;
2895 	const char *b_val;
2896 	gint ret;
2897 
2898 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE_SORT_KEY);
2899 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE_SORT_KEY);
2900 
2901 	if (a_val == NULL) {
2902 		if (b_val == NULL)
2903 			ret = 0;
2904 		else
2905 			ret = -1;
2906 	} else if (b_val == NULL)
2907 		ret = 1;
2908 	else
2909 		ret = strcmp (a_val, b_val);
2910 
2911 	if (ret != 0)
2912 		return ret;
2913 	else
2914 		return rhythmdb_query_model_location_sort_func (a, b, data);
2915 }
2916 
2917 /**
2918  * rhythmdb_query_model_album_sort_func:
2919  * @a: a #RhythmDBEntry
2920  * @b: a #RhythmDBEntry
2921  * @data: nothing
2922  *
2923  * Sort function for sorting by album.  Sorts by album, then
2924  * disc number, then track number, then title.
2925  *
2926  * Returns: result of sort comparison between a and b.
2927  */
2928 gint
2929 rhythmdb_query_model_album_sort_func (RhythmDBEntry *a,
2930 				      RhythmDBEntry *b,
2931 				      gpointer data)
2932 {
2933 	const char *a_val;
2934 	const char *b_val;
2935 	gulong a_num;
2936 	gulong b_num;
2937 	gint ret;
2938 
2939 	/* Sort by album name */
2940 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ALBUM_SORTNAME_SORT_KEY);
2941 	if (a_val[0] == '\0') {
2942 		a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ALBUM_SORT_KEY);
2943 	}
2944 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ALBUM_SORTNAME_SORT_KEY);
2945 	if (b_val[0] == '\0') {
2946 		b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ALBUM_SORT_KEY);
2947 	}
2948 
2949 	if (a_val == NULL) {
2950 		if (b_val == NULL)
2951 			ret = 0;
2952 		else
2953 			ret = -1;
2954 	} else if (b_val == NULL)
2955 		ret = 1;
2956 	else
2957 		ret = strcmp (a_val, b_val);
2958 
2959 	if (ret != 0)
2960 		return ret;
2961 
2962 	/* Then by disc number (assume 1 if non-existent) */
2963 	a_num = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_DISC_NUMBER);
2964 	b_num = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_DISC_NUMBER);
2965 	a_num = (a_num ? a_num : 1);
2966 	b_num = (b_num ? b_num : 1);
2967 	if (a_num != b_num)
2968 		return (a_num < b_num ? -1 : 1);
2969 
2970 	/* by track number (assume 0 if non-existent) */
2971 	a_num = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_TRACK_NUMBER);
2972 	b_num = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_TRACK_NUMBER);
2973 	if (a_num != b_num)
2974 		return (a_num < b_num ? -1 : 1);
2975 
2976 	/*  by title */
2977 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_TITLE_SORT_KEY);
2978 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_TITLE_SORT_KEY);
2979 
2980 	if (a_val == NULL) {
2981 		if (b_val == NULL)
2982 			return 0;
2983 		else
2984 			return -1;
2985 	} else if (b_val == NULL)
2986 		return 1;
2987 	else
2988 		return rhythmdb_query_model_location_sort_func (a, b, data);
2989 }
2990 
2991 /**
2992  * rhythmdb_query_model_artist_sort_func:
2993  * @a: a #RhythmDBEntry
2994  * @b: a #RhythmDBEntry
2995  * @data: nothing
2996  *
2997  * Sort function for sorting by artist.  Sorts by artist, then
2998  * album, then disc number, then track number, then title.
2999  *
3000  * Returns: result of sort comparison between a and b.
3001  */
3002 gint
3003 rhythmdb_query_model_artist_sort_func (RhythmDBEntry *a,
3004 				       RhythmDBEntry *b,
3005 				       gpointer data)
3006 {
3007 	const char *a_val;
3008 	const char *b_val;
3009 	gint ret;
3010 
3011 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ARTIST_SORTNAME_SORT_KEY);
3012 	if (a_val[0] == '\0') {
3013 		a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_ARTIST_SORT_KEY);
3014 	}
3015 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ARTIST_SORTNAME_SORT_KEY);
3016 	if (b_val[0] == '\0') {
3017 		b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_ARTIST_SORT_KEY);
3018 	}
3019 
3020 	if (a_val == NULL) {
3021 		if (b_val == NULL)
3022 			ret = 0;
3023 		else
3024 			ret = -1;
3025 	} else if (b_val == NULL)
3026 		ret = 1;
3027 	else
3028 		ret = strcmp (a_val, b_val);
3029 
3030 	if (ret != 0)
3031 		return ret;
3032 	else
3033 		return rhythmdb_query_model_album_sort_func (a, b, data);
3034 }
3035 
3036 /**
3037  * rhythmdb_query_model_genre_sort_func:
3038  * @a: a #RhythmDBEntry
3039  * @b: a #RhythmDBEntry
3040  * @data: nothing
3041  *
3042  * Sort function for sorting by genre.  Sorts by genre, then artist,
3043  * then album, then disc number, then track number, then title.
3044  *
3045  * Returns: result of sort comparison between a and b.
3046  */
3047 gint
3048 rhythmdb_query_model_genre_sort_func (RhythmDBEntry *a, RhythmDBEntry *b,
3049 				      gpointer data)
3050 {
3051 	const char *a_val;
3052 	const char *b_val;
3053 	gint ret;
3054 
3055 	a_val = rhythmdb_entry_get_string (a, RHYTHMDB_PROP_GENRE_SORT_KEY);
3056 	b_val = rhythmdb_entry_get_string (b, RHYTHMDB_PROP_GENRE_SORT_KEY);
3057 
3058 	if (a_val == NULL) {
3059 		if (b_val == NULL)
3060 			ret = 0;
3061 		else
3062 			ret = -1;
3063 	} else if (b_val == NULL)
3064 		ret = 1;
3065 	else
3066 		ret = strcmp (a_val, b_val);
3067 
3068 	if (ret != 0)
3069 		return ret;
3070 	else
3071 		return rhythmdb_query_model_artist_sort_func (a, b, data);
3072 }
3073 
3074 /**
3075  * rhythmdb_query_model_track_sort_func:
3076  * @a: a #RhythmDBEntry
3077  * @b: a #RhythmDBEntry
3078  * @data: nothing
3079  *
3080  * Sort function for sorting by track.  Sorts by artist,
3081  * then album, then disc number, then track number, then title.
3082  *
3083  * Returns: result of sort comparison between a and b.
3084  */
3085 gint
3086 rhythmdb_query_model_track_sort_func (RhythmDBEntry *a,
3087 				      RhythmDBEntry *b,
3088 				      gpointer data)
3089 {
3090 	return rhythmdb_query_model_album_sort_func (a, b, data);
3091 }
3092 
3093 /**
3094  * rhythmdb_query_model_double_ceiling_sort_func:
3095  * @a: a #RhythmDBEntry
3096  * @b: a #RhythmDBEntry
3097  * @data: property to sort on
3098  *
3099  * Sort function for sorting by a rounded floating point value.
3100  * The property value is rounded up to an integer value for sorting.
3101  * If the values are the same, falls back to sorting by location.
3102  *
3103  * Returns: result of sort comparison between a and b.
3104  */
3105 gint
3106 rhythmdb_query_model_double_ceiling_sort_func (RhythmDBEntry *a,
3107 					       RhythmDBEntry *b,
3108 					       gpointer data)
3109 {
3110 	gdouble a_val, b_val;
3111 	RhythmDBPropType prop_id;
3112 
3113 	prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
3114 
3115 	a_val = ceil (rhythmdb_entry_get_double (a, prop_id));
3116 	b_val = ceil (rhythmdb_entry_get_double (b, prop_id));
3117 
3118 	if (a_val != b_val)
3119 		return (a_val > b_val ? 1 : -1);
3120 	else
3121 		return rhythmdb_query_model_location_sort_func (a, b, data);
3122 }
3123 
3124 /**
3125  * rhythmdb_query_model_ulong_sort_func:
3126  * @a: a #RhythmDBEntry
3127  * @b: a #RhythmDBEntry
3128  * @data: property to sort on
3129  *
3130  * Sort function for sorting by an unsigned integer property value.
3131  * If the values are the same, falls back to sorting by location.
3132  *
3133  * Returns: result of sort comparison between a and b.
3134  */
3135 gint
3136 rhythmdb_query_model_ulong_sort_func (RhythmDBEntry *a,
3137 				      RhythmDBEntry *b,
3138 				      gpointer data)
3139 {
3140 	gulong a_val, b_val;
3141 	RhythmDBPropType prop_id;
3142 
3143 	prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
3144 	a_val = rhythmdb_entry_get_ulong (a, prop_id);
3145 	b_val = rhythmdb_entry_get_ulong (b, prop_id);
3146 
3147 	if (a_val != b_val)
3148 		return (a_val > b_val ? 1 : -1);
3149 	else
3150 		return rhythmdb_query_model_location_sort_func (a, b, data);
3151 }
3152 
3153 /**
3154  * rhythmdb_query_model_bitrate_sort_func:
3155  * @a: a #RhythmDBEntry
3156  * @b: a #RhythmDBEntry
3157  * @data: nothing
3158  *
3159  * Sort function for sorting by bitrate.  Lossless encodings (as identified
3160  * by media type) are considered to have the highest possible bitrate.
3161  * Falls back to sorting by location for equal bitrates.
3162  *
3163  * Returns: result of sort comparison between a and b.
3164  */
3165 gint
3166 rhythmdb_query_model_bitrate_sort_func (RhythmDBEntry *a,
3167 					RhythmDBEntry *b,
3168 					gpointer data)
3169 {
3170 	gulong a_val, b_val;
3171 	
3172 	if (rhythmdb_entry_is_lossless (a)) {
3173 		if (rhythmdb_entry_is_lossless (b))
3174 			return rhythmdb_query_model_location_sort_func (a, b, data);
3175 		else
3176 			return 1;
3177 	} else {
3178 		if (rhythmdb_entry_is_lossless (b))
3179 			return -1;
3180 	}
3181 
3182 	a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_BITRATE);
3183 	b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_BITRATE);
3184 
3185 	if (a_val != b_val)
3186 		return (a_val > b_val ? 1 : -1);
3187 	else
3188 		return rhythmdb_query_model_location_sort_func (a, b, data);
3189 }
3190 
3191 /**
3192  * rhythmdb_query_model_date_sort_func:
3193  * @a: a #RhythmDBEntry
3194  * @b: a #RhythmDBEntry
3195  * @data: nothing
3196  *
3197  * Sort function for sorting by release date.
3198  * Falls back to album sort order for equal dates.
3199  *
3200  * Returns: result of sort comparison between a and b.
3201  */
3202 gint
3203 rhythmdb_query_model_date_sort_func (RhythmDBEntry *a,
3204 				     RhythmDBEntry *b,
3205 				     gpointer data)
3206 
3207 {
3208 	gulong a_val, b_val;
3209 
3210 	a_val = rhythmdb_entry_get_ulong (a, RHYTHMDB_PROP_DATE);
3211 	b_val = rhythmdb_entry_get_ulong (b, RHYTHMDB_PROP_DATE);
3212 
3213 	if (a_val > b_val)
3214 		return 1;
3215 	else if (a_val < b_val)
3216 		return -1;
3217 	else
3218 		return rhythmdb_query_model_album_sort_func (a, b, data);
3219 }
3220 
3221 /**
3222  * rhythmdb_query_model_string_sort_func:
3223  * @a: a #RhythmDBEntry
3224  * @b: a #RhythmDBEntry
3225  * @data: property to sort on
3226  *
3227  * Sort function for sorting by a single string property
3228  * Falls back to location sort order if the strings are equal.
3229  *
3230  * Returns: result of sort comparison between a and b.
3231  */
3232 gint
3233 rhythmdb_query_model_string_sort_func (RhythmDBEntry *a,
3234 				       RhythmDBEntry *b,
3235 				       gpointer data)
3236 {
3237 	const char *a_val;
3238 	const char *b_val;
3239 	gint ret;
3240 	RhythmDBPropType prop_id;
3241 
3242 	prop_id = (RhythmDBPropType) GPOINTER_TO_INT (data);
3243 	a_val = rhythmdb_entry_get_string (a, prop_id);
3244 	b_val = rhythmdb_entry_get_string (b, prop_id);
3245 
3246 	if (a_val == NULL) {
3247 		if (b_val == NULL)
3248 			ret = 0;
3249 		else
3250 			ret = -1;
3251 	} else if (b_val == NULL)
3252 		ret = 1;
3253 	else
3254 		ret = strcmp (a_val, b_val);
3255 
3256 	if (ret != 0)
3257 		return ret;
3258 	else
3259 		return rhythmdb_query_model_location_sort_func (a, b, data);
3260 }
3261 
3262 static gboolean
3263 rhythmdb_query_model_within_limit (RhythmDBQueryModel *model,
3264 				   RhythmDBEntry *entry)
3265 {
3266 	gboolean result = TRUE;
3267 
3268 	switch (model->priv->limit_type) {
3269 	case RHYTHMDB_QUERY_MODEL_LIMIT_NONE:
3270 		result = TRUE;
3271 		break;
3272 
3273 	case RHYTHMDB_QUERY_MODEL_LIMIT_COUNT:
3274 		{
3275 			gulong limit_count;
3276 			gulong current_count;
3277 
3278 			limit_count = g_value_get_ulong (&g_array_index (model->priv->limit_value, GValue, 0));
3279 			current_count = g_hash_table_size (model->priv->reverse_map);
3280 
3281 			if (entry)
3282 				current_count++;
3283 
3284 			result = (current_count <= limit_count);
3285 			break;
3286 		}
3287 
3288 	case RHYTHMDB_QUERY_MODEL_LIMIT_SIZE:
3289 		{
3290 			guint64 limit_size;
3291 			guint64 current_size;
3292 
3293 			limit_size = g_value_get_uint64 (&g_array_index (model->priv->limit_value, GValue, 0));
3294 			current_size = model->priv->total_size;
3295 
3296 			if (entry)
3297 				current_size += rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
3298 
3299 			/* the limit is in MB */
3300 			result = (current_size / (1024 * 1024) <= limit_size);
3301 			break;
3302 		}
3303 
3304 	case RHYTHMDB_QUERY_MODEL_LIMIT_TIME:
3305 		{
3306 			gulong limit_time;
3307 			gulong current_time;
3308 
3309 			limit_time = g_value_get_ulong (&g_array_index (model->priv->limit_value, GValue, 0));
3310 			current_time = model->priv->total_duration;
3311 
3312 			if (entry)
3313 				current_time += rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
3314 
3315 			result = (current_time <= limit_time);
3316 			break;
3317 		}
3318 	}
3319 
3320 	return result;
3321 }
3322 
3323 /* This should really be standard. */
3324 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
3325 
3326 GType
3327 rhythmdb_query_model_limit_type_get_type (void)
3328 {
3329 	static GType etype = 0;
3330 
3331 	if (etype == 0)
3332 	{
3333 		static const GEnumValue values[] =
3334 		{
3335 
3336 			ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_NONE, "no-limit"),
3337 			ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_COUNT, "limit-count"),
3338 			ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_SIZE, "limit-size"),
3339 			ENUM_ENTRY (RHYTHMDB_QUERY_MODEL_LIMIT_TIME, "limit-duration"),
3340 			{ 0, 0, 0 }
3341 		};
3342 
3343 		etype = g_enum_register_static ("RhythmDBQueryModelLimitType", values);
3344 	}
3345 
3346 	return etype;
3347 }
3348 
3349 static gboolean
3350 rhythmdb_query_model_reapply_query_cb (RhythmDBQueryModel *model)
3351 {
3352 	GDK_THREADS_ENTER ();
3353 	rhythmdb_query_model_reapply_query (model, FALSE);
3354 	rhythmdb_do_full_query_async_parsed (model->priv->db,
3355 					     RHYTHMDB_QUERY_RESULTS (model),
3356 					     model->priv->original_query);
3357 	GDK_THREADS_LEAVE ();
3358 	return TRUE;
3359 }