static void
infobar_response_cb (GtkInfoBar *infobar, gint response, RBImportErrorsSource *source)
{
	char **details = NULL;
	GtkTreeIter iter;
	GClosure *closure;
	int i;

	/* gather plugin installer detail strings */
	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (source->priv->missing_plugin_model), &iter) == FALSE) {
		return;
	}

	i = 0;
	do {
		RhythmDBEntry *entry;
		char **bits;
		int j;

		entry = rhythmdb_query_model_iter_to_entry (source->priv->missing_plugin_model, &iter);
		bits = g_strsplit (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_COMMENT), "\n", 0);

		for (j = 0; bits[j] != NULL; j++) {
			if (rb_str_in_strv (bits[j], (const char **)details) == FALSE) {
				details = g_realloc (details, sizeof (char *) * i+2);
				details[i++] = g_strdup (bits[j]);
				details[i] = NULL;
			}
		}

		g_strfreev (bits);
	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->missing_plugin_model), &iter));

	/* run the installer */
	closure = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
				  g_object_ref (source),
				  (GClosureNotify) missing_plugins_retry_cleanup);
	g_closure_set_marshal (closure, g_cclosure_marshal_VOID__BOOLEAN);
	if (rb_missing_plugins_install ((const char **)details, TRUE, closure) == TRUE) {
		/* disable the button while the installer is running */
		gtk_info_bar_set_response_sensitive (infobar, response, FALSE);
	}
	g_closure_sink (closure);

	g_strfreev (details);
}
static gboolean
emit_status_changed (RhythmDBImportJob *job)
{
	g_mutex_lock (&job->priv->lock);
	job->priv->status_changed_id = 0;

	rb_debug ("emitting status changed: %d/%d", job->priv->imported, job->priv->total);
	g_signal_emit (job, signals[STATUS_CHANGED], 0, job->priv->total, job->priv->imported);

	/* temporary ref while emitting this signal as we're expecting the caller
	 * to release the final reference there.
	 */
	g_object_ref (job);
	if (job->priv->scan_complete && job->priv->imported >= job->priv->total) {

		if (job->priv->retry_entries != NULL && job->priv->retried == FALSE) {
			gboolean processing = FALSE;
			char **details = NULL;
			GClosure *retry;
			GSList *l;
			int i;

			/* gather missing plugin details etc. */
			i = 0;
			for (l = job->priv->retry_entries; l != NULL; l = l->next) {
				RhythmDBEntry *entry;
				char **bits;
				int j;

				entry = (RhythmDBEntry *)l->data;
				bits = g_strsplit (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_COMMENT), "\n", 0);
				for (j = 0; bits[j] != NULL; j++) {
					if (rb_str_in_strv (bits[j], (const char **)details) == FALSE) {
						details = g_realloc (details, sizeof (char *) * (i+2));
						details[i++] = g_strdup (bits[j]);
						details[i] = NULL;
					}
				}
				g_strfreev (bits);
			}

			retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
						g_object_ref (job),
						(GClosureNotify)g_object_unref);
			g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN);

			processing = rb_missing_plugins_install ((const char **)details, FALSE, retry);
			g_strfreev (details);
			if (processing) {
				rb_debug ("plugin installation is in progress");
			} else {
				rb_debug ("no plugin installation attempted; job complete");
				g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
			}
			g_closure_sink (retry);
		} else {
			rb_debug ("emitting job complete");
			g_signal_emit (job, signals[COMPLETE], 0, job->priv->total);
		}
	}
	g_mutex_unlock (&job->priv->lock);
	g_object_unref (job);

	return FALSE;
}
static void
missing_encoder_response_cb (GtkDialog *dialog, gint response, RBTrackTransferQueue *queue)
{
	GClosure *retry;
	GstEncodingTarget *target;
	GPtrArray *details;
	GList *profiles;
	const GList *l;
	RBEncoder *encoder;

	switch (response) {
	case GTK_RESPONSE_YES:
		/* 'continue' -> start the batch */
		rb_debug ("starting batch regardless of missing plugins");
		actually_start_batch (queue);
		break;

	case GTK_RESPONSE_CANCEL:
	case GTK_RESPONSE_DELETE_EVENT:
		/* 'cancel' -> cancel the batch and start the next one */
		rb_debug ("cancelling batch");
		_rb_track_transfer_batch_cancel (queue->priv->current);
		g_object_unref (queue->priv->current);
		queue->priv->current = NULL;

		start_next_batch (queue);
		break;

	case GTK_RESPONSE_ACCEPT:
		/* 'install plugins' -> try to install encoder/muxer */

		/* get profiles that need plugins installed */
		profiles = NULL;
		encoder = rb_encoder_new ();
		g_object_get (queue->priv->current, "encoding-target", &target, NULL);
		for (l = gst_encoding_target_get_profiles (target); l != NULL; l = l->next) {
			GstEncodingProfile *profile = GST_ENCODING_PROFILE (l->data);
			char *profile_media_type;
			profile_media_type = rb_gst_encoding_profile_get_media_type (profile);
			if (profile_media_type != NULL &&
			    (rb_gst_media_type_is_lossless (profile_media_type) == FALSE) &&
			    rb_encoder_get_missing_plugins (encoder, profile, NULL, NULL)) {
				profiles = g_list_append (profiles, profile);
			}
			g_free (profile_media_type);
		}
		g_object_unref (encoder);
		g_object_unref (target);

		if (profiles == NULL) {
			rb_debug ("apparently we don't need any plugins any more");
			actually_start_batch (queue);
			break;
		}

		rb_debug ("attempting plugin installation");
		details = get_missing_plugin_strings (profiles, FALSE);
		retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
					g_object_ref (queue),
					(GClosureNotify) g_object_unref);
		g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN);
		if (rb_missing_plugins_install ((const char **)details->pdata, FALSE, retry)) {
			rb_debug ("attempting to install missing plugins for transcoding");
		} else {
			rb_debug ("proceeding without the missing plugins for transcoding");
			actually_start_batch (queue);
		}

		g_closure_sink (retry);
		g_ptr_array_free (details, TRUE);
		g_list_free (profiles);
		break;

	default:
		g_assert_not_reached ();
	}

	gtk_widget_destroy (GTK_WIDGET (dialog));
}