/**
 * 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;
	}
}
Beispiel #5
0
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;
}
Beispiel #14
0
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);
}