static void
mex_aggregate_model_clear_model (MexAggregateModel *self,
                                 MexModel          *model)
{
    gint i;
    GList *c, *remove;
    MexContent *content;

    MexAggregateModelPrivate *priv = self->priv;

    i = 0;
    remove = NULL;
    while ((content = mex_model_get_content (MEX_MODEL (self), i++)))
    {
        MexModel *parent = g_hash_table_lookup (priv->content_to_model, content);

        if (parent == model)
        {
            g_hash_table_remove (priv->content_to_model, content);
            remove = g_list_prepend (remove, content);
        }
    }

    /* Remove the contents after the loop, to avoid modifying what we're
     * iterating over.
     */
    for (c = remove; c; c = c->next)
        mex_model_remove_content (MEX_MODEL (self), MEX_CONTENT (c->data));

    g_list_free (remove);
}
static void
_add_remove_recursive (MexModel *queue_model, MexModel *model, gboolean add)
{
  gint len, i;

  len = mex_model_get_length (model);

  for (i=0; i < len; i++)
    {
      MexContent *content;
      const gchar *mimetype;

      content = mex_model_get_content (model, i);

      mimetype = mex_content_get_metadata (content,
                                           MEX_CONTENT_METADATA_MIMETYPE);

      /* Don't accidentally add a directory or group to the queue */
      if (!g_strcmp0 (mimetype, "x-grl/box")
          || !g_strcmp0 (mimetype, "x-mex/group"))
        continue;

      if (add)
        mex_model_add_content (queue_model, content);
      else
        mex_model_remove_content (queue_model, content);
    }
  g_object_unref (model);
}
static void
_mex_grilo_feed_content_updated (GrlSource *source,
                                 GPtrArray *changed_medias,
                                 GrlSourceChangeType change_type,
                                 gboolean known_location,
                                 MexGriloFeed *feed)
{
  gint i;
  GrlMedia *media;
  const gchar *id;
  MexGriloProgram *program;

  for (i = 0 ; i < changed_medias->len ; i++)
    {
      media =  g_ptr_array_index (changed_medias, i);
      id = grl_media_get_id (media);
      switch (change_type)
        {
        case GRL_CONTENT_CHANGED:
          program = MEX_GRILO_PROGRAM (mex_feed_lookup (MEX_FEED (feed), id));
          /* The policy might be slightly different here... */
          if (program != NULL) {
            mex_grilo_program_set_grilo_media (program, media);
          }
          break;

        case GRL_CONTENT_ADDED:
          program = MEX_GRILO_PROGRAM (mex_feed_lookup (MEX_FEED (feed), id));
          if (program != NULL) {
            mex_grilo_program_set_grilo_media (program, media);
          } else {
            emit_media_added (feed, media);
          }
          break;

        case GRL_CONTENT_REMOVED:
          program = MEX_GRILO_PROGRAM (mex_feed_lookup (MEX_FEED (feed), id));
          if (program != NULL) {
            mex_model_remove_content (MEX_MODEL (feed), MEX_CONTENT (program));
          }
          break;
        }
    }
}
static void
_queue_button_notify_toggled_cb (MxButton       *button,
                                 GParamSpec     *pspec,
                                 MexQueueButton *q_button)
{
  MexQueueButtonPrivate *priv = q_button->priv;
  const gchar *mimetype;

  gboolean directory;

  mimetype = mex_content_get_metadata (priv->content,
                                       MEX_CONTENT_METADATA_MIMETYPE);
  directory = !g_strcmp0 (mimetype, "x-grl/box")
    || !g_strcmp0 (mimetype, "x-mex/group");

  /* Triggers a train of actions that makes content have it's queued
   * property set which then runs the notify cb which calls
   * mex_queue_button_update so we don't need to run it directly
   */

  if (mx_button_get_toggled (button))
    {
      mex_queue_button_set_animated (q_button, TRUE);

      if (directory)
        _add_from_directory (q_button, TRUE);
      else
        mex_model_add_content (priv->queue_model, priv->content);
    }
  else
    {
      mex_queue_button_set_animated (q_button, FALSE);

      if (directory)
          _add_from_directory (q_button, FALSE);
      else
        mex_model_remove_content (priv->queue_model, priv->content);
    }
}
static void
mex_aggregate_model_controller_changed_cb (GController          *controller,
        GControllerAction     action,
        GControllerReference *ref,
        MexAggregateModel    *self)
{
    gint i;

    gint n_indices = 0;
    MexAggregateModelPrivate *priv = self->priv;
    MexModel *model = g_hash_table_lookup (priv->controller_to_model, controller);

    if (!model)
    {
        g_warning (G_STRLOC ": Signal from unknown controller");
        return;
    }

    if (ref)
        n_indices = g_controller_reference_get_n_indices (ref);

    switch (action)
    {
    case G_CONTROLLER_ADD:
        for (i = 0; i < n_indices; i++)
        {
            MexContent *content;

            gint content_index = g_controller_reference_get_index_uint (ref, i);
            content = mex_model_get_content (model, content_index);
            g_hash_table_insert (priv->content_to_model, content, model);

            mex_model_add_content (MEX_MODEL (self), content);
        }
        break;

    case G_CONTROLLER_REMOVE:
        for (i = 0; i < n_indices; i++)
        {
            MexContent *content;

            gint content_index = g_controller_reference_get_index_uint (ref, i);

            content = mex_model_get_content (model, content_index);
            g_hash_table_remove (priv->content_to_model, content);

            mex_model_remove_content (MEX_MODEL (self), content);
        }
        break;

    case G_CONTROLLER_UPDATE:
        break;

    case G_CONTROLLER_CLEAR:
        mex_aggregate_model_clear_model (self, model);
        break;

    case G_CONTROLLER_REPLACE:
    {
        MexContent *content;

        mex_aggregate_model_clear_model (self, model);
        i = 0;
        while ((content = mex_model_get_content (model, i++)))
        {
            g_hash_table_insert (priv->content_to_model, content, model);
            mex_model_add_content (MEX_MODEL (self), content);
        }
    }
    break;

    case G_CONTROLLER_INVALID_ACTION:
        g_warning (G_STRLOC ": Proxy controller has issued an error");
        break;

    default:
        break;
    }
}