/** * rb_library_browser_get_property_views: * @widget: a #RBLibraryBrowser * * Return value: a GList containing the #RBPropertyView<!-- -->s * in the browser. */ GList* rb_library_browser_get_property_views (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); return rb_collate_hash_table_values (priv->property_views); }
/** * rb_library_browser_has_selection: * @widget: a #RBLibraryBrowser * * Return value: TRUE if any items in the browser are selected. */ gboolean rb_library_browser_has_selection (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); return (g_hash_table_size (priv->selections) > 0); }
static void restore_selection (RBLibraryBrowser *widget, gint property_index, gboolean query_pending) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); RBPropertyView *view; GList *selections; SelectionRestoreData *data; view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[property_index].type); selections = g_hash_table_lookup (priv->selections, (gpointer)browser_properties[property_index].type); if (query_pending) { g_object_ref (widget); data = g_new0 (SelectionRestoreData, 1); data->widget = widget; data->view = view; data->selections = selections; data->model = priv->input_model; data->handler_id = g_signal_connect_data (priv->input_model, "complete", G_CALLBACK (query_complete_cb), data, (GClosureNotify) selection_restore_data_destroy, 0); } else { ignore_selection_changes (widget, view, FALSE); rb_property_view_set_selection (view, selections); } }
static void rb_library_browser_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object); switch (prop_id) { case PROP_DB: g_value_set_object (value, priv->db); break; case PROP_INPUT_MODEL: g_value_set_object (value, priv->input_model); break; case PROP_OUTPUT_MODEL: g_value_set_object (value, priv->output_model); break; case PROP_ENTRY_TYPE: g_value_set_boxed (value, priv->entry_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void rb_library_browser_dispose (GObject *object) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object); if (priv->rebuild_data != NULL) { /* this looks a bit odd, but removing the idle handler cleans up the * data too. */ guint id = priv->rebuild_data->rebuild_idle_id; priv->rebuild_data = NULL; g_source_remove (id); } if (priv->db != NULL) { g_object_unref (priv->db); priv->db = NULL; } if (priv->input_model != NULL) { g_object_unref (priv->input_model); priv->input_model = NULL; } if (priv->output_model != NULL) { g_object_unref (priv->output_model); priv->output_model = NULL; } G_OBJECT_CLASS (rb_library_browser_parent_class)->dispose (object); }
/** * rb_library_browser_set_model: * @widget: a #RBLibraryBrowser * @model: the new input #RhythmDBQueryModel * @query_pending: if TRUE, the caller promises to run a * query to populate the input query model. * * Specifies a new input query model for the browser. * This should be the query model constructed from the * current search text, or the basic query model for the * source if there is no search text. */ void rb_library_browser_set_model (RBLibraryBrowser *widget, RhythmDBQueryModel *model, gboolean query_pending) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); RBPropertyView *view; RhythmDBPropertyModel *prop_model; if (priv->input_model != NULL) { g_object_unref (priv->input_model); } priv->input_model = model; if (priv->input_model != NULL) { g_object_ref (priv->input_model); } view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[0].type); ignore_selection_changes (widget, view, TRUE); prop_model = rb_property_view_get_model (view); g_object_set (prop_model, "query-model", priv->input_model, NULL); rebuild_child_model (widget, 0, query_pending); restore_selection (widget, 0, query_pending); }
static void rb_library_browser_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object); switch (prop_id) { case PROP_DB: if (priv->db != NULL) { g_object_unref (priv->db); } priv->db = g_value_get_object (value); if (priv->db != NULL) { g_object_ref (priv->db); } break; case PROP_ENTRY_TYPE: priv->entry_type = g_value_get_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean idle_rebuild_model (RBLibraryBrowserRebuildData *data) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (data->widget); priv->rebuild_data = NULL; rebuild_child_model (data->widget, data->rebuild_prop_index, FALSE); return FALSE; }
/** * rb_library_browser_get_property_view: * @widget: a #RBLibraryBrowser * @type: the property * * Return value: the #RBPropertyView for the specified property, or * NULL if there isn't one */ RBPropertyView * rb_library_browser_get_property_view (RBLibraryBrowser *widget, RhythmDBPropType type) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); RBPropertyView *view; view = g_hash_table_lookup (priv->property_views, GINT_TO_POINTER (type)); return view; }
static void rb_library_browser_finalize (GObject *object) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (object); g_hash_table_destroy (priv->property_views); g_hash_table_destroy (priv->selections); G_OBJECT_CLASS (rb_library_browser_parent_class)->finalize (object); }
static void rb_library_browser_init (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); gtk_box_set_spacing (GTK_BOX (widget), 5); priv->property_views = g_hash_table_new (g_direct_hash, g_direct_equal); priv->selections = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)rb_list_deep_free); }
/** * rb_library_browser_reset: * @widget: a #RBLibraryBrowser * * Clears all selections in the browser. * * Return value: TRUE if anything was changed */ gboolean rb_library_browser_reset (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); if (rb_library_browser_has_selection (widget)) { g_hash_table_foreach (priv->property_views, (GHFunc)reset_view_cb, widget); return TRUE; } else { return FALSE; } }
/** * rb_library_browser_set_selection: * @widget: a #RBLibraryBrowser * @type: the property for which to set the selection * @selection: a list of strings to select * * Replaces any current selection for the specified property. */ void rb_library_browser_set_selection (RBLibraryBrowser *widget, RhythmDBPropType type, GList *selection) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); GList *old_selection; RBPropertyView *view; int rebuild_index; RBLibraryBrowserRebuildData *rebuild_data; old_selection = g_hash_table_lookup (priv->selections, (gpointer)type); if (rb_string_list_equal (old_selection, selection)) return; if (selection) g_hash_table_insert (priv->selections, (gpointer)type, rb_string_list_copy (selection)); else g_hash_table_remove (priv->selections, (gpointer)type); rebuild_index = prop_to_index (type); if (priv->rebuild_data != NULL) { rebuild_data = priv->rebuild_data; if (rebuild_data->rebuild_prop_index <= rebuild_index) { /* already rebuilding a model further up the chain, * so we don't need to do anything for this one. */ return; } g_source_remove (rebuild_data->rebuild_idle_id); rebuild_data = NULL; } view = g_hash_table_lookup (priv->property_views, (gpointer)type); if (view) { ignore_selection_changes (widget, view, TRUE); } rebuild_data = g_new0 (RBLibraryBrowserRebuildData, 1); rebuild_data->widget = g_object_ref (widget); rebuild_data->rebuild_prop_index = rebuild_index; rebuild_data->rebuild_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) idle_rebuild_model, rebuild_data, (GDestroyNotify) destroy_idle_rebuild_model); priv->rebuild_data = rebuild_data; }
static void update_browser_views_visibility (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); GList *properties = NULL; if (strstr (priv->browser_views, "albums") != NULL) properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ALBUM); properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ARTIST); if (strstr (priv->browser_views, "genres") != NULL) properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_GENRE); g_hash_table_foreach (priv->property_views, (GHFunc)update_browser_property_visibilty, properties); g_list_free (properties); }
static void destroy_idle_rebuild_model (RBLibraryBrowserRebuildData *data) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (data->widget); RBPropertyView *view; RhythmDBPropType prop_type; prop_type = browser_properties[data->rebuild_prop_index].type; view = g_hash_table_lookup (priv->property_views, (gpointer)prop_type); if (view != NULL) { ignore_selection_changes (data->widget, view, FALSE); } priv->rebuild_data = NULL; g_object_unref (data->widget); g_free (data); }
static GObject * rb_library_browser_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { RBLibraryBrowserClass *klass; RBLibraryBrowser *browser; RBLibraryBrowserPrivate *priv; int i; klass = RB_LIBRARY_BROWSER_CLASS (g_type_class_peek (RB_TYPE_LIBRARY_BROWSER)); browser = RB_LIBRARY_BROWSER (G_OBJECT_CLASS (rb_library_browser_parent_class)-> constructor (type, n_construct_properties, construct_properties)); priv = RB_LIBRARY_BROWSER_GET_PRIVATE (browser); for (i = 0; i < num_browser_properties; i++) { RBPropertyView *view; view = rb_property_view_new (priv->db, browser_properties[i].type, _(browser_properties[i].name)); g_hash_table_insert (priv->property_views, (gpointer)(browser_properties[i].type), view); rb_property_view_set_selection_mode (view, GTK_SELECTION_MULTIPLE); g_signal_connect_object (G_OBJECT (view), "properties-selected", G_CALLBACK (view_property_selected_cb), browser, 0); g_signal_connect_object (G_OBJECT (view), "property-selection-reset", G_CALLBACK (view_selection_reset_cb), browser, 0); gtk_widget_show_all (GTK_WIDGET (view)); gtk_widget_set_no_show_all (GTK_WIDGET (view), TRUE); gtk_box_pack_start (GTK_BOX (browser), GTK_WIDGET (view), TRUE, TRUE, 0); } update_browser_views_visibility (browser); priv->browser_view_notify_id = eel_gconf_notification_add (CONF_UI_BROWSER_VIEWS, (GConfClientNotifyFunc) rb_library_browser_views_changed, browser); return G_OBJECT (browser); }
/** * rb_library_browser_construct_query: * @widget: a #RBLibraryBrowser * * Constructs a #RhythmDBQuery from the current selections in the browser. * * Return value: a #RhythmDBQuery constructed from the current selection. */ RhythmDBQuery * rb_library_browser_construct_query (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); RhythmDBQuery *query; ConstructQueryData *data; query = g_ptr_array_new (); data = g_new0 (ConstructQueryData, 1); data->widget = widget; data->db = priv->db; data->query = query; g_hash_table_foreach (priv->selections, (GHFunc)construct_query_cb, data); g_free (data); return query; }
static void update_browser_views_visibility (RBLibraryBrowser *widget) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); GList *properties = NULL; { int views = eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS); if (views == 0 || views == 2) properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ALBUM); properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_ARTIST); if (views == 1 || views == 2) properties = g_list_prepend (properties, (gpointer)RHYTHMDB_PROP_GENRE); } g_hash_table_foreach (priv->property_views, (GHFunc)update_browser_property_visibilty, properties); g_list_free (properties); }
static void rb_library_browser_constructed (GObject *object) { RBLibraryBrowser *browser; RBLibraryBrowserPrivate *priv; int i; RB_CHAIN_GOBJECT_METHOD (rb_library_browser_parent_class, constructed, object); browser = RB_LIBRARY_BROWSER (object); priv = RB_LIBRARY_BROWSER_GET_PRIVATE (browser); for (i = 0; i < num_browser_properties; i++) { RBPropertyView *view; view = rb_property_view_new (priv->db, browser_properties[i].type, _(browser_properties[i].name)); g_hash_table_insert (priv->property_views, (gpointer)(browser_properties[i].type), view); rb_property_view_set_selection_mode (view, GTK_SELECTION_MULTIPLE); g_signal_connect_object (G_OBJECT (view), "properties-selected", G_CALLBACK (view_property_selected_cb), browser, 0); g_signal_connect_object (G_OBJECT (view), "property-selection-reset", G_CALLBACK (view_selection_reset_cb), browser, 0); gtk_widget_show_all (GTK_WIDGET (view)); gtk_widget_set_no_show_all (GTK_WIDGET (view), TRUE); gtk_box_pack_start (GTK_BOX (browser), GTK_WIDGET (view), TRUE, TRUE, 0); } update_browser_views_visibility (browser); priv->browser_view_notify_id = eel_gconf_notification_add (CONF_UI_BROWSER_VIEWS, (GConfClientNotifyFunc) rb_library_browser_views_changed, browser); }
static void rebuild_child_model (RBLibraryBrowser *widget, gint property_index, gboolean query_pending) { RBLibraryBrowserPrivate *priv = RB_LIBRARY_BROWSER_GET_PRIVATE (widget); RhythmDBPropertyModel *prop_model; RhythmDBQueryModel *base_model, *child_model; RBPropertyView *view; RhythmDBQuery *query; GList *selections; g_assert (property_index >= 0); g_assert (property_index < num_browser_properties); /* get the query model for the previous property view */ view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[property_index].type); prop_model = rb_property_view_get_model (view); g_object_get (prop_model, "query-model", &base_model, NULL); selections = g_hash_table_lookup (priv->selections, (gpointer)browser_properties[property_index].type); if (selections != NULL) { /* create a new query model based on it, filtered by * the selections of the previous property view. * we need the entry type query criteria to allow the * backend to optimise the query. */ query = rhythmdb_query_parse (priv->db, RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, priv->entry_type, RHYTHMDB_QUERY_END); rhythmdb_query_append_prop_multiple (priv->db, query, browser_properties[property_index].type, selections); child_model = rhythmdb_query_model_new_empty (priv->db); if (query_pending) { rb_debug ("rebuilding child model for browser %d; query is pending", property_index); g_object_set (child_model, "query", query, "base-model", base_model, NULL); } else { rb_debug ("rebuilding child model for browser %d; running new query", property_index); rhythmdb_query_model_chain (child_model, base_model, FALSE); rhythmdb_do_full_query_parsed (priv->db, RHYTHMDB_QUERY_RESULTS (child_model), query); } rhythmdb_query_free (query); } else { rb_debug ("no selection for browser %d - reusing parent model", property_index); child_model = g_object_ref (base_model); } /* If this is the last property, use the child model as the output model * for the browser. Otherwise, use it as the input for the next property * view. */ if (property_index == num_browser_properties-1) { if (priv->output_model != NULL) { g_object_unref (priv->output_model); } priv->output_model = child_model; g_object_notify (G_OBJECT (widget), "output-model"); } else { view = g_hash_table_lookup (priv->property_views, (gpointer)browser_properties[property_index+1].type); ignore_selection_changes (widget, view, TRUE); prop_model = rb_property_view_get_model (view); g_object_set (prop_model, "query-model", child_model, NULL); g_object_unref (child_model); rebuild_child_model (widget, property_index + 1, query_pending); restore_selection (widget, property_index + 1, query_pending); } g_object_unref (base_model); }