static JsonGenerator *
_model_to_generator (MexQueueModel *model)
{
  gint i;
  JsonArray *json_array;
  JsonNode *root;
  JsonGenerator *generator;

  json_array = json_array_sized_new (mex_model_get_length (MEX_MODEL (model)));

  for (i = 0; i < mex_model_get_length (MEX_MODEL (model)); i++)
    {
      MexContent *content;
      JsonNode *content_node;

      content = mex_model_get_content (MEX_MODEL (model), i);

      content_node = json_gobject_serialize (G_OBJECT (content));
      json_array_add_element (json_array, content_node);
    }

  generator = json_generator_new ();
  root = json_node_new (JSON_NODE_ARRAY);
  json_node_set_array (root, json_array);

  json_generator_set_root (generator, root);

  json_array_unref (json_array);
  json_node_free (root);

  return generator;
}
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
remove_model (gpointer key, gpointer value, gpointer user_data)
{
  MexBliptvPlugin *self = MEX_BLIPTV_PLUGIN (user_data);
  MexModel *model = MEX_MODEL (value);

  mex_model_manager_remove_model (self->priv->manager, model);
}
static void
remove_model (gpointer key, gpointer value, gpointer user_data)
{
  MexTrackerPlugin *self = MEX_TRACKER_PLUGIN (user_data);
  MexModel *model = MEX_MODEL (value);

  mex_model_manager_remove_model (self->priv->manager, model);
}
static void
_add_directory_query_complete_cb (MexFeed *feed,
                                  GParamSpec *spec,
                                  MexQueueButton *q_button)
{
  MexQueueButtonPrivate *priv = q_button->priv;

  _add_remove_recursive (priv->queue_model, MEX_MODEL (feed), TRUE);
}
Example #6
0
static void
mex_grilo_feed_open_default (MexGriloProgram *program, MexGriloFeed *feed)
{
  MexPlayer *player = mex_player_get_default ();

  mex_content_view_set_context (MEX_CONTENT_VIEW (player),
                                MEX_MODEL (feed));
  mex_content_view_set_content (MEX_CONTENT_VIEW (player),
                                MEX_CONTENT (program));
}
/* This function is synchronous! Blocking once at startup seems pretty
 * reasonable and allows us to avoid any complexity re. races
 */
static void
mex_queue_model_load (MexQueueModel *model)
{
  JsonParser *parser;
  gchar *filename;
  GError *error = NULL;
  JsonNode *root;
  JsonArray *array;
  gint i = 0;

  filename = _queue_file_name ();

  if (!g_file_test (filename, G_FILE_TEST_EXISTS))
    {
      g_free (filename);

      return;
    }

  parser = json_parser_new ();
  if (!json_parser_load_from_file (parser, filename, &error))
    {
      g_warning (G_STRLOC ": error populating from file: %s",
                 error->message);
      g_clear_error (&error);
      goto out;
    }

  root = json_parser_get_root (parser);

  if (!JSON_NODE_HOLDS_ARRAY (root))
    {
      g_warning (G_STRLOC ": JSON data not of expected format!");

      goto out;
    }

  array = json_node_get_array (root);

  for (i = 0; i < json_array_get_length (array); i++)
    {
      MexContent *content;
      JsonNode *node;

      node = json_array_get_element (array, i);
      content = (MexContent *)json_gobject_deserialize (MEX_TYPE_PROGRAM,
                                                        node);

      mex_model_add_content (MEX_MODEL (model), content);
    }

out:
  g_free (filename);
  g_object_unref (parser);
}
Example #8
0
static gboolean
emit_media_added_finished (MexGriloFeed *feed)
{
  mex_model_add (MEX_MODEL (feed), feed->priv->items_to_add);

  g_list_free (feed->priv->items_to_add);
  feed->priv->items_to_add = NULL;

  g_object_unref (feed);

  return FALSE;
}
/* This function is asynchronous .. slightly worried about re-entrancy here */
static void
mex_queue_model_save (MexQueueModel *model)
{
  GFile *f;
  gchar *filename;
  JsonGenerator *generator;
  gchar *buf;
  gsize buf_len;

  filename = _queue_file_name ();
  f = g_file_new_for_path (filename);

  if (mex_model_get_length (MEX_MODEL (model)) == 0)
    {
      GError *error = NULL;

      if (!g_file_delete (f, NULL, &error))
        {
          g_warning (G_STRLOC ": Unable to delete file: %s",
                     error->message);
          g_clear_error (&error);
        }

      g_object_unref (f);
      g_free (filename);
      return;
    }

  generator = _model_to_generator (model);

  buf = json_generator_to_data (generator, &buf_len);

  g_file_replace_contents_async (f,
                                 buf,
                                 buf_len,
                                 NULL,
                                 FALSE,
                                 G_FILE_CREATE_REPLACE_DESTINATION,
                                 NULL,
                                 (GAsyncReadyCallback)_file_replaced_cb,
                                 buf);

  g_object_unref (f);
  g_free (filename);
  g_object_unref (generator);
}
Example #10
0
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
mex_queue_model_init (MexQueueModel *self)
{
  MexQueueModelPrivate *priv;

  self->priv = QUEUE_MODEL_PRIVATE (self);
  priv = self->priv;

  /* Load before setting up the controller otherwise .. BOOM! */
  mex_queue_model_load (self);

  priv->controller = mex_model_get_controller (MEX_MODEL (self));
  g_signal_connect (priv->controller,
                    "changed",
                    (GCallback)_controller_changed_cb,
                    self);

  g_object_set (self, "title", _("Queue"), NULL);
}
void
mex_aggregate_model_add_model (MexAggregateModel *aggregate,
                               MexModel          *model)
{
    gint i;
    MexContent *content;
    GController *controller;
    MexAggregateModelPrivate *priv;

    g_return_if_fail (MEX_IS_AGGREGATE_MODEL (aggregate));
    g_return_if_fail (MEX_IS_MODEL (model));

    priv = aggregate->priv;
    if (g_list_find (priv->models, model))
        return;

    /* Add a link back to the model from the controller */
    controller = mex_model_get_controller (model);
    g_hash_table_insert (priv->controller_to_model, controller, model);

    /* Add model to list */
    priv->models = g_list_insert_sorted (priv->models, model,
                                         (GCompareFunc) mex_aggregate_model_sort_func);

    /* Add existing items */
    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 (aggregate), content);
        i++;
    }

    /* Connect to the controller changed signal */
    g_signal_connect (controller, "changed",
                      G_CALLBACK (mex_aggregate_model_controller_changed_cb),
                      aggregate);

    /* Emit added signal */
    g_signal_emit (aggregate, signals[MODEL_ADDED], 0, model);
}
Example #13
0
void
mex_grilo_feed_browse (MexGriloFeed *feed,
                       int           offset,
                       int           limit)
{
  MexGriloFeedPrivate *priv;

  g_return_if_fail (MEX_IS_GRILO_FEED (feed));

  priv = feed->priv;

  mex_grilo_feed_init_op (feed);
  mex_model_clear (MEX_MODEL (feed));

  priv->op->type = MEX_GRILO_FEED_OPERATION_BROWSE;
  priv->op->offset = offset;
  priv->op->limit = limit;
  priv->op->count = 0;

  mex_grilo_feed_start_op (feed);
}
Example #14
0
void
mex_grilo_feed_query (MexGriloFeed *feed,
                      const char   *query,
                      int           offset,
                      int           limit)
{
  MexGriloFeedPrivate *priv;

  g_return_if_fail (MEX_IS_GRILO_FEED (feed));

  priv = feed->priv;

  mex_grilo_feed_init_op (feed);
  mex_model_clear (MEX_MODEL (feed));

  priv->op->type = MEX_GRILO_FEED_OPERATION_QUERY;
  priv->op->offset = offset;
  priv->op->limit = limit;
  priv->op->count = 0;
  priv->op->text = g_strdup (query);

  mex_grilo_feed_start_op (feed);
}
static void
add_model (MexBliptvPlugin *self,
           GrlSource *source)
{
  MexFeed *feed;
  GrlMedia *box;

  box = grl_media_video_new ();

  grl_media_set_id (GRL_MEDIA (box), NULL);
  feed = mex_grilo_feed_new (source,
                             self->priv->query_keys,
                             self->priv->video_keys, box);
  g_object_set (feed, "icon-name", "icon-panelheader-computer",
                "placeholder-text", "No videos found",
                "category", "videos",
                NULL);
  mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, 100);

  g_hash_table_insert (self->priv->video_models, source, feed);

  mex_model_manager_add_model (self->priv->manager, MEX_MODEL (feed));
}
static void
add_model (MexTrackerPlugin *self,
           GrlMediaPlugin *plugin,
           MexTrackerCategory category)
{
  GList *metadata_keys, *query_keys;
  MexFeed *feed, *dir_feed;
  GrlMedia *box;
  gchar *query, *cat_name;
  GHashTable *models;
  const gchar *source_name = grl_metadata_source_get_name (GRL_METADATA_SOURCE (plugin));
  gint priority;

  switch (category)
    {
    case MEX_TRACKER_CATEGORY_IMAGE:
      cat_name = "pictures";
      query = "?urn a nfo:FileDataObject . "
              "?urn tracker:available true . "
              "FILTER (fn:starts-with(nie:mimeType(?urn),'image/'))";
      models = self->priv->image_models;
      metadata_keys = self->priv->image_keys;
      query_keys = self->priv->query_image_keys;
      box = grl_media_image_new ();
      break;

    case MEX_TRACKER_CATEGORY_VIDEO:
      cat_name = "videos";
      query = "?urn a nfo:FileDataObject . "
              "?urn tracker:available true . "
              "FILTER (fn:starts-with(nie:mimeType(?urn),'video/'))";
      models = self->priv->video_models;
      metadata_keys = self->priv->video_keys;
      query_keys = self->priv->query_video_keys;
      box = grl_media_video_new ();
      break;

    case MEX_TRACKER_CATEGORY_MUSIC:
      cat_name = "music";
      query = "?urn a nfo:FileDataObject . "
              "?urn tracker:available true . "
              "FILTER (fn:starts-with(nie:mimeType(?urn),'audio/'))";
      models = self->priv->music_models;
      metadata_keys = self->priv->music_keys;
      query_keys = self->priv->query_music_keys;
      box = grl_media_audio_new ();
      break;
    }

  grl_media_set_id (GRL_MEDIA (box), NULL);
  feed = mex_grilo_tracker_feed_new (GRL_MEDIA_SOURCE (plugin),
                                     query_keys,
                                     metadata_keys,
                                     NULL, box);
  mex_model_set_sort_func (MEX_MODEL (feed),
                           mex_model_sort_time_cb,
                           GINT_TO_POINTER (TRUE));
  mex_grilo_feed_query (MEX_GRILO_FEED (feed), query, 0, MAX_TRACKER_RESULTS);

  g_hash_table_insert (models, plugin, feed);

  /* set the local files source to appear first */
  if (!g_strcmp0 (source_name, "Local files"))
    priority = -100;
  else
    priority = 0;

  dir_feed = mex_grilo_tracker_feed_new (GRL_MEDIA_SOURCE (plugin),
                                         query_keys,
                                         metadata_keys,
                                         query, NULL);
  mex_model_set_sort_func (MEX_MODEL (dir_feed),
                           mex_model_sort_alpha_cb,
                           GINT_TO_POINTER (FALSE));
  mex_grilo_feed_browse (MEX_GRILO_FEED (dir_feed), 0, G_MAXINT);

  g_object_set (G_OBJECT (feed),
                "category", cat_name,
                "priority", priority,
                "alt-model", dir_feed,
                "alt-model-string", _("Show Folders"),
                NULL);

  mex_model_manager_add_model (self->priv->manager, MEX_MODEL (feed));
}
Example #17
0
static void
mex_library_plugin_init (MexLibraryPlugin *self)
{
    GrlMediaPlugin *plugin;
    GrlPluginRegistry *registry;

    MexLibraryPluginPrivate *priv = self->priv =
                                        LIBRARY_PLUGIN_PRIVATE (self);

    registry = grl_plugin_registry_get_default ();

    plugin = grl_plugin_registry_lookup_source (registry, "grl-filesystem");
    if (plugin)
    {
        GList *query_keys;
        MexFeed *feed;
        GrlMedia *box;
        const gchar *path;
        MexModelInfo *video_info, *photo_info, *music_info;

        query_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_TITLE,
                                                GRL_METADATA_KEY_MIME,
                                                GRL_METADATA_KEY_URL,
                                                GRL_METADATA_KEY_DATE,
                                                NULL);

        /* Add the videos model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_DURATION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No videos found", NULL);


            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            video_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "videos", 0);
            priv->models = g_list_append (priv->models, video_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        /* Add the photos model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No pictures found", NULL);

            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            photo_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "pictures", 0);
            priv->models = g_list_append (priv->models, photo_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        /* Add the music model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           GRL_METADATA_KEY_ARTIST,
                                           GRL_METADATA_KEY_ALBUM,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No music found", NULL);

            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            music_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "music", 0);
            priv->models = g_list_append (priv->models, music_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        g_list_free (query_keys);
    }
    else
        g_warning ("Filesystem plugin not found");
}
Example #18
0
static void
mex_search_plugin_search (MexSearchPlugin *self,
                          const gchar     *search)
{
  GList *s, *sources;
  MexSearchPluginPrivate *priv = self->priv;

  /* Kill the last search */
  if (priv->search_model)
    {
      g_object_unref (priv->search_model);
      priv->search_model = NULL;
    }

  /* Create search model */
  priv->search_model = mex_aggregate_model_new ();
  g_object_set (G_OBJECT (priv->search_model), "title", "Search results", NULL);

  /* Iterate over searchable Grilo sources */
  sources = grl_plugin_registry_get_sources (grl_plugin_registry_get_default (),
                                             FALSE);

  /* find the local files source and place it first */
  for (s = sources; s; s = s->next)
    {
      GrlMetadataSource *meta_src = s->data;
      const gchar *name = grl_metadata_source_get_name (meta_src);

      if (!GRL_IS_METADATA_SOURCE (meta_src))
        continue;

      if (name && !strcmp (name, "Local files"))
        {
          sources = g_list_remove_link (sources, s);
          sources = g_list_concat (sources, s);
          break;
        }
    }


  for (s = sources; s; s = s->next)
    {
      const gchar *source_id;
      GrlSupportedOps supported;
      GrlMetadataSource *meta_src = s->data;

      if (!GRL_IS_METADATA_SOURCE (meta_src))
        continue;

      /* only search upnp and tracker sources */
      source_id = grl_media_plugin_get_id (GRL_MEDIA_PLUGIN (meta_src));

      supported = grl_metadata_source_supported_operations (meta_src);
      if ((supported & GRL_OP_SEARCH) || (supported & GRL_OP_QUERY))
        {
          MexFeed *feed;

          if (g_str_equal (source_id, "grl-tracker"))
            feed =
              mex_grilo_tracker_feed_new (GRL_MEDIA_SOURCE (meta_src), NULL, NULL, NULL, NULL);
          else
            feed =
              mex_grilo_feed_new (GRL_MEDIA_SOURCE (meta_src), NULL, NULL, NULL);
          g_object_set (G_OBJECT (feed), "placeholder-text",
                        _("No videos found"), NULL);
          GController *controller = mex_model_get_controller (MEX_MODEL (feed));

          /* Attach to the changed signal so that we can alter the
           * mime-type of content if necessary.
           */
          g_signal_connect (controller, "changed",
                            G_CALLBACK (mex_search_plugin_model_changed_cb),
                            feed);

          mex_aggregate_model_add_model (
            MEX_AGGREGATE_MODEL (priv->search_model), MEX_MODEL (feed));

          /* FIXME: Arbitrary 50 item limit... */
          mex_grilo_feed_search (MEX_GRILO_FEED (feed), search, 0, 50);
        }
    }
  g_list_free (sources);
}
Example #19
0
static void
mex_search_plugin_update_history (MexSearchPlugin *self,
                                  const gchar     *term)
{
  gint i;
  gsize length;
  gchar *contents, *current;

  MexSearchPluginPrivate *priv = self->priv;
  const gchar *base_dir =
    mex_settings_get_config_dir (mex_settings_get_default ());
  gchar *history_file = g_build_filename (base_dir, "search", "history", NULL);

  /* Read the history file contents */
  /* TODO: Make this less rubbish? */
  g_file_get_contents (history_file, &contents, &length, NULL);

  /* Prepend new search-term if appropriate */
  if (term)
    {
      gint terms;
      gchar *path;
      gsize new_length;

      gsize term_len = strlen (term);
      gchar *new_contents = g_malloc (length + term_len + 1);

      memcpy (new_contents, term, term_len);
      new_contents[term_len] = '\n';
      new_length = term_len + 1;

      /* Truncate list to 10 terms and remove duplicates */
      if (contents)
        {
          i = 0;
          terms = 1;
          do
            {
              gint cur_len;
              char *eos = strchr (contents + i, '\n');

              if (!eos)
                cur_len = strlen (contents + i);
              else
                cur_len = eos - (contents + i);

              if ((cur_len != term_len) ||
                  (strncmp (contents + i, term, term_len) != 0))
                {
                  memcpy (new_contents + new_length, contents + i, cur_len + 1);
                  new_length += cur_len + 1;

                  if (++terms >= 10)
                    break;
                }

              if (!eos)
                break;

              i += cur_len + 1;
            } while (i < length);
        }

      new_contents[new_length++] = '\0';

      /* Save new list */
      path = g_path_get_dirname (history_file);
      g_mkdir_with_parents (path, 0755);
      g_free (path);

      g_file_set_contents (history_file, new_contents, new_length, NULL);

      /* Replace content with new content */
      g_free (contents);
      contents = new_contents;
      length = new_length;
    }

  /* Empty current list */
  mex_model_clear (MEX_MODEL (priv->history_model));

  /* Populate with search history */
  if (contents)
    {
      current = contents;
      while (current < contents + length)
        {
          MexContent *content =
            MEX_CONTENT (mex_program_new (priv->history_model));
          gchar *end = g_utf8_strchr (current, -1, '\n');

          if (end)
            *end = '\0';

          if (*current)
            {
              mex_content_set_metadata (content,
                                        MEX_CONTENT_METADATA_TITLE,
                                        current);
              mex_content_set_metadata (content,
                                        MEX_CONTENT_METADATA_MIMETYPE,
                                        "x-mex/search");
              mex_model_add_content (MEX_MODEL (priv->history_model),
                                     content);
            }

          if (end)
            current = end + 1;
          else
            break;
        }

      g_free (contents);
    }
  else
    {
      /* Add a default search so the column isn't hidden.
       * TODO: Have a way of inserting 'stock' content rather than doing
       *       this, I suppose.
       */
      MexContent *content = MEX_CONTENT (mex_program_new (priv->history_model));
      mex_content_set_metadata (content, MEX_CONTENT_METADATA_TITLE, "MeeGo");
      mex_content_set_metadata (content, MEX_CONTENT_METADATA_MIMETYPE,
                                "x-mex/search");
      mex_model_add_content (MEX_MODEL (priv->history_model), content);
    }
}
Example #20
0
static void
mex_suggest_complete_cb (MexDownloadQueue *queue,
                         const gchar      *uri,
                         const gchar      *buffer,
                         gsize             count,
                         const GError     *error,
                         gpointer          userdata)
{
  RestXmlNode *root, *n;
  RestXmlParser *parser;
  MexSearchPlugin *self = userdata;
  MexSearchPluginPrivate *priv = self->priv;

  priv->suggest_id = NULL;

  /* hide spinner */
  mx_spinner_set_animating (MX_SPINNER (priv->spinner), FALSE);
  clutter_actor_hide (priv->spinner);

  if (error)
    {
      g_warning ("Error querying Google suggestions: %s",
                 error->message);
      return;
    }

  parser = rest_xml_parser_new ();
  root = rest_xml_parser_parse_from_data (parser, buffer, count);

  if (!root)
    {
      g_warning ("Unknown error parsing Google suggestions XML");
      g_object_unref (parser);
      return;
    }

  /* Clear model */
  mex_model_clear (MEX_MODEL (priv->suggest_model));

  /* Add new suggestions to model */
  n = rest_xml_node_find (root, "CompleteSuggestion");
  for (; n; n = n->next)
    {
      MexContent *content;
      const gchar *suggestion;

      RestXmlNode *node = rest_xml_node_find (n, "suggestion");

      if (!node)
        continue;

      suggestion = rest_xml_node_get_attr (node, "data");

      if (!suggestion)
        continue;

      content = MEX_CONTENT (mex_program_new (priv->suggest_model));
      mex_content_set_metadata (content, MEX_CONTENT_METADATA_TITLE,
                                suggestion);
      mex_content_set_metadata (content, MEX_CONTENT_METADATA_MIMETYPE,
                                "x-mex/search");
      mex_model_add_content (MEX_MODEL (priv->suggest_model), content);
    }

  /* Unref */
  rest_xml_node_unref (root);
  g_object_unref (parser);
}
Example #21
0
static void
mex_search_plugin_init (MexSearchPlugin *self)
{
  MexProxy *suggest_proxy;
  ClutterActor *icon, *header, *text, *frame, *box, *hbox;
  MexSearchPluginPrivate *priv = self->priv = SEARCH_PLUGIN_PRIVATE (self);

  /* Load style data */
  mx_style_load_from_file (mx_style_get_default (),
                           PLUGIN_DATA_DIR "/style.css", NULL);

  /* Create the history model and models list */
  priv->history_model = mex_feed_new (_("Search"), _("Search"));

  priv->model_info =
    mex_model_info_new_with_sort_funcs (MEX_MODEL (priv->history_model),
                                        "search", 0);
  g_object_unref (priv->history_model);
  priv->models = g_list_append (NULL, priv->model_info);

  /* Create the actions list */
  memset (&priv->action_info, 0, sizeof (MexActionInfo));
  priv->action_info.action =
    mx_action_new_full ("x-mex/search",
                        _("Search"),
                        G_CALLBACK (mex_search_plugin_history_cb),
                        self);
  priv->action_info.mime_types = (gchar **)search_mimetypes;
  priv->actions = g_list_append (NULL, &priv->action_info);

  /* Create the suggestions model */
  priv->suggest_model =
    mex_feed_new (_("Suggestions"), _("Google Suggestions"));

  /* Create the search page */

  /* Create header */
  icon = mx_icon_new ();
  mx_stylable_set_style_class (MX_STYLABLE (icon), "Search");

  header = mx_box_layout_new ();
  mx_box_layout_set_spacing (MX_BOX_LAYOUT (header), 5);
  clutter_actor_set_name (header, "search-header");

  /* Create search entry */
  frame = mx_table_new ();
  clutter_actor_set_name (frame, "search-entry-frame");
  priv->search_entry = mx_entry_new ();
  priv->spinner = mx_spinner_new ();

  mx_table_add_actor (MX_TABLE (frame), priv->search_entry, 0, 0);
  mx_table_add_actor (MX_TABLE (frame), priv->spinner, 0, 1);
  mx_table_child_set_x_fill (MX_TABLE (frame), priv->spinner, FALSE);
  mx_table_child_set_x_expand (MX_TABLE (frame), priv->spinner, FALSE);
  mx_table_child_set_y_fill (MX_TABLE (frame), priv->spinner, FALSE);

  mx_spinner_set_animating (MX_SPINNER (priv->spinner), FALSE);
  clutter_actor_hide (priv->spinner);

  clutter_container_add (CLUTTER_CONTAINER (header), icon, frame, NULL);
  clutter_container_child_set (CLUTTER_CONTAINER (header), icon,
                               "x-fill", FALSE, "y-fill", FALSE, NULL);
  clutter_container_child_set (CLUTTER_CONTAINER (header), frame,
                               "expand", TRUE, "x-fill", TRUE, NULL);

  text = mx_entry_get_clutter_text (MX_ENTRY (priv->search_entry));
  g_signal_connect_swapped (text, "activate",
                            G_CALLBACK (mex_search_plugin_search_cb), self);
  g_signal_connect (priv->search_entry, "notify::text",
                    G_CALLBACK (mex_search_text_changed_cb), self);
  g_signal_connect (priv->search_entry, "notify::style-pseudo-class",
                    G_CALLBACK (mex_search_text_style_changed), header);

  /* Create the suggestions column */
  priv->suggest_column = mx_box_layout_new ();
  clutter_actor_set_name (priv->suggest_column, "suggest-column");
  mx_box_layout_set_orientation (MX_BOX_LAYOUT (priv->suggest_column),
                                 MX_ORIENTATION_VERTICAL);
  suggest_proxy = mex_generic_proxy_new (MEX_MODEL (priv->suggest_model),
                                        MX_TYPE_BUTTON);
  mex_generic_proxy_bind (MEX_GENERIC_PROXY (suggest_proxy),
                          mex_enum_to_string (MEX_TYPE_CONTENT_METADATA,
                                              MEX_CONTENT_METADATA_TITLE),
                          "label");
  g_signal_connect (suggest_proxy, "object-created",
                    G_CALLBACK (mex_search_proxy_add_cb),
                    priv->suggest_column);
  g_signal_connect (suggest_proxy, "object-removed",
                    G_CALLBACK (mex_search_proxy_remove_cb),
                    priv->suggest_column);
  g_object_weak_ref (G_OBJECT (priv->suggest_column),
                     (GWeakNotify)g_object_unref, suggest_proxy);

  /* Pack the search page */
  priv->search_page = mx_frame_new ();
  clutter_actor_set_name (priv->search_page, "search-page");
  mx_bin_set_fill (MX_BIN (priv->search_page), FALSE, TRUE);
  mx_bin_set_alignment (MX_BIN (priv->search_page), MX_ALIGN_START,
                        MX_ALIGN_START);
  hbox = mex_resizing_hbox_new ();
  mex_resizing_hbox_set_resizing_enabled (MEX_RESIZING_HBOX (hbox), FALSE);
  box = mx_box_layout_new ();
  clutter_container_add_actor (CLUTTER_CONTAINER (priv->search_page), hbox);
  clutter_container_add_actor (CLUTTER_CONTAINER (hbox), box);
  mx_box_layout_set_orientation (MX_BOX_LAYOUT (box),
                                 MX_ORIENTATION_VERTICAL);
  clutter_container_add (CLUTTER_CONTAINER (box),
                         header, priv->suggest_column, NULL);
  mx_box_layout_child_set_expand (MX_BOX_LAYOUT (box),
                                  priv->suggest_column, TRUE);
  clutter_container_child_set (CLUTTER_CONTAINER (box), header,
                               "x-fill", TRUE,
                               "x-align", MX_ALIGN_START, NULL);
  clutter_container_child_set (CLUTTER_CONTAINER (box),
                               priv->suggest_column,
                               "x-fill", TRUE,
                               "x-align", MX_ALIGN_START, NULL);
  clutter_actor_set_width (box, 426.0);

  /* Update the history list */
  mex_search_plugin_update_history (self, NULL);

  /* Start the history list and suggestions proxy */
  mex_proxy_start (suggest_proxy);
}
static void
_controller_changed_cb (GController          *controller,
                        GControllerAction     action,
                        GControllerReference *ref,
                        MexQueueModel        *model)
{
  MexQueueModelPrivate *priv = model->priv;
  guint index_;
  MexContent *content;

  /*
   * MexGenericContent only does single items at a time so we have an
   * assumption here that our reference only contains a single item
   */

  if (action == G_CONTROLLER_ADD || action == G_CONTROLLER_REMOVE)
    {
      index_ = g_controller_reference_get_index_uint (ref, 0);
      content = mex_model_get_content (MEX_MODEL (model), index_);
    }

  if (action == G_CONTROLLER_ADD)
    {
      mex_content_set_metadata (content,
                                MEX_CONTENT_METADATA_QUEUED,
                                "yes");
    }
  else if (action == G_CONTROLLER_REMOVE)
    {
      mex_content_set_metadata (content,
                                MEX_CONTENT_METADATA_QUEUED,
                                NULL);
    }
  else if (action == G_CONTROLLER_CLEAR)
    {
      gint model_length;
      model_length = mex_model_get_length (MEX_MODEL (model));

      for (index_=0; index_ < model_length; index_++)
        {
          content = mex_model_get_content (MEX_MODEL (model), index_);

          mex_content_set_metadata (content,
                                    MEX_CONTENT_METADATA_QUEUED,
                                    NULL);
        }
    }
  else
    {
      GEnumClass *enum_class;

      enum_class = g_type_class_ref (G_TYPE_CONTROLLER_ACTION);

      g_critical (G_STRLOC ": Unexpected GController action: %s",
                  (g_enum_get_value (enum_class,
                                     action))->value_name);

      g_type_class_unref (enum_class);
    }

  if (priv->serialise_idle_id)
    return;

  /* Need to use a high priority idle here since we want to try and write
   * *after* the content has been removed from the model
   */
  priv->serialise_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT,
                                             (GSourceFunc)_serialise_idle_cb,
                                             g_object_ref (model),
                                             g_object_unref);
}
static void
mex_search_plugin_search (MexSearchPlugin *self,
                          const gchar     *search)
{
  GList *l, *list;
  MexSearchPluginPrivate *priv = self->priv;
  MexModelManager *manager = mex_model_manager_get_default ();
  gboolean have_tracker = FALSE;

  if (!priv->search_model)
    {
      /* Create search model */
      priv->search_model = mex_aggregate_model_new ();
      g_object_set (G_OBJECT (priv->search_model),
                    "title", _("Search results"), NULL);
    }

  /* Kill the last search */
  list = (GList *) mex_aggregate_model_get_models (
                       MEX_AGGREGATE_MODEL (priv->search_model));
  for (l = list; l; l = l->next)
    mex_model_manager_remove_model (manager, l->data);
  mex_aggregate_model_clear (MEX_AGGREGATE_MODEL (priv->search_model));

  /* Iterate over searchable Grilo sources */
  list = grl_plugin_registry_get_sources (grl_plugin_registry_get_default (),
                                             FALSE);

  /* find the local files source and place it first */
  for (l = list; l; l = l->next)
    {
      GrlMetadataSource *meta_src = l->data;
      const gchar *name = grl_metadata_source_get_name (meta_src);
      const gchar *source_id;

      if (!GRL_IS_METADATA_SOURCE (meta_src))
        continue;

      source_id = grl_media_plugin_get_id (GRL_MEDIA_PLUGIN (meta_src));

      if (source_id && g_str_equal (source_id, "grl-tracker"))
        have_tracker = TRUE;

      if (name && !strcmp (name, "Local files"))
        {
          list = g_list_remove_link (list, l);
          list = g_list_concat (list, l);
          break;
        }
    }

  /* prefer tracker over the filesystem plugin by removing it from the list if
   * tracker is available */
  if (have_tracker)
    {
      for (l = list; l; l = l->next)
        {
          GrlMetadataSource *meta_src = l->data;
          const gchar *source_id;

          if (!GRL_IS_METADATA_SOURCE (meta_src))
            continue;

          source_id = grl_media_plugin_get_id (GRL_MEDIA_PLUGIN (meta_src));

          if (source_id && g_str_equal (source_id, "grl-filesystem"))
            {
              list = g_list_delete_link (list, l);
              break;
            }
        }
    }


  for (l = list; l; l = l->next)
    {
      const gchar *source_id;
      GrlSupportedOps supported;
      GrlMetadataSource *meta_src = l->data;

      if (!GRL_IS_METADATA_SOURCE (meta_src))
        continue;

      /* only search upnp and tracker sources */
      source_id = grl_media_plugin_get_id (GRL_MEDIA_PLUGIN (meta_src));

      supported = grl_metadata_source_supported_operations (meta_src);
      if ((supported & GRL_OP_SEARCH) || (supported & GRL_OP_QUERY))
        {
          MexFeed *feed;
          GController *controller;

          if (g_str_equal (source_id, "grl-tracker"))
            feed = mex_grilo_tracker_feed_new (GRL_MEDIA_SOURCE (meta_src),
                                               NULL, NULL, NULL, NULL);
          else
            feed =
              mex_grilo_feed_new (GRL_MEDIA_SOURCE (meta_src), NULL, NULL, NULL);
          mex_model_set_sort_func (MEX_MODEL (feed),
                                   mex_model_sort_time_cb,
                                   GINT_TO_POINTER (TRUE));

          g_object_set (G_OBJECT (feed),
                        "category", "search-results",
                        "placeholder-text", _("No videos found"),
                        NULL);
          mex_model_manager_add_model (manager, MEX_MODEL (feed));

          controller = mex_model_get_controller (MEX_MODEL (feed));

          /* Attach to the changed signal so that we can alter the
           * mime-type of content if necessary.
           */
          g_signal_connect (controller, "changed",
                            G_CALLBACK (mex_search_plugin_model_changed_cb),
                            feed);

          mex_aggregate_model_add_model (
            MEX_AGGREGATE_MODEL (priv->search_model), MEX_MODEL (feed));

          /* FIXME: Arbitrary 50 item limit... */
          mex_grilo_feed_search (MEX_GRILO_FEED (feed), search, 0, 50);

          g_object_unref (G_OBJECT (feed));
        }
    }
  g_list_free (list);
}
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;
    }
}
static void
mex_media_controls_init (MexMediaControls *self)
{
    ClutterActor *actor;
    ClutterScript *script;
    GError *err = NULL;
    MxAdjustment *adjustment;
    ClutterActor *related_box;
    gchar *tmp;

    MexMediaControlsPrivate *priv = self->priv = MEDIA_CONTROLS_PRIVATE (self);

    priv->script = script = clutter_script_new ();

    tmp = g_build_filename (mex_get_data_dir (), "json", "media-controls.json",
                            NULL);
    clutter_script_load_from_file (script, tmp, &err);
    g_free (tmp);

    if (err)
        g_error ("Could not load media controls interface: %s", err->message);

    priv->vbox =
        (ClutterActor*) clutter_script_get_object (script, "media-controls");
    clutter_actor_set_parent (priv->vbox, CLUTTER_ACTOR (self));

    /* add shadow to media controls box */
    actor = (ClutterActor *) clutter_script_get_object (script,
            "media-controls-box");
    clutter_actor_add_effect (actor, CLUTTER_EFFECT (mex_shadow_new ()));


    /* vertical fade effect */
    priv->vertical_effect = mx_fade_effect_new ();
    clutter_actor_add_effect (priv->vbox, priv->vertical_effect);
    mx_scrollable_get_adjustments (MX_SCROLLABLE (mx_bin_get_child (MX_BIN (priv->vbox))),
                                   NULL, &adjustment);
    g_signal_connect (adjustment, "changed",
                      G_CALLBACK (notify_vertical_changed_cb), self);
    g_signal_connect (adjustment, "notify::value",
                      G_CALLBACK (notify_vertical_value_cb), self);

    /* horizontal fade effect */
    priv->horizontal_effect = mx_fade_effect_new ();
    related_box = (ClutterActor *) clutter_script_get_object (priv->script,
                  "related-box");
    clutter_actor_add_effect (related_box, priv->horizontal_effect);
    mx_scrollable_get_adjustments (MX_SCROLLABLE (related_box), &adjustment,
                                   NULL);
    g_signal_connect (adjustment, "changed",
                      G_CALLBACK (notify_horizontal_changed_cb), self);
    g_signal_connect (adjustment, "notify::value",
                      G_CALLBACK (notify_horizontal_value_cb), self);


    /* slider setup */
    priv->slider = (ClutterActor*) clutter_script_get_object (script, "slider");
    g_signal_connect (priv->slider, "notify::value",
                      G_CALLBACK (slider_value_changed_cb), self);
    g_signal_connect (priv->slider, "captured-event",
                      G_CALLBACK (slider_captured_event), self);

    priv->play_pause_action =
        (MxAction*) clutter_script_get_object (script, "play-pause-action");

    priv->stop_action =
        (MxAction*) clutter_script_get_object (script, "stop-action");

    priv->add_to_queue_action =
        (MxAction*) clutter_script_get_object (script, "add-to-queue-action");

    priv->queue_button =
        (ClutterActor *) clutter_script_get_object (script, "add-to-queue-button");

    g_signal_connect (priv->play_pause_action, "activated",
                      G_CALLBACK (mex_media_controls_play_cb), self);
    g_signal_connect (priv->stop_action, "activated",
                      G_CALLBACK (mex_media_controls_stop_cb), self);
#if 0
    g_signal_connect (priv->add_to_queue_action, "activated",
                      G_CALLBACK (mex_media_controls_add_to_queue_cb), self);
#endif

    /* proxy setup */

    priv->proxy_model = MEX_VIEW_MODEL (mex_view_model_new (NULL));
    /* FIXME: Set an arbitrary 200-item limit as we can't handle large
     *        amounts of actors without massive slow-down.
     */
    mex_view_model_set_limit (priv->proxy_model, 200);

    priv->proxy = mex_content_proxy_new (MEX_MODEL (priv->proxy_model),
                                         CLUTTER_CONTAINER (related_box),
                                         MEX_TYPE_CONTENT_TILE);

    g_signal_connect (priv->proxy, "object-created", G_CALLBACK (tile_created_cb),
                      self);

    priv->is_disabled = TRUE;
}
Example #26
0
static void
mex_view_model_controller_changed_cb (GController          *controller,
                                      GControllerAction     action,
                                      GControllerReference *ref,
                                      MexViewModel         *self)
{
  gint n_indices, i;

  MexViewModelPrivate *priv = self->priv;

  n_indices = g_controller_reference_get_n_indices (ref);

  switch (action)
    {
    case G_CONTROLLER_ADD:
      {
        guint view_length;

        /* increase the internal items array by the number of new items */
        view_length = mex_model_get_length (MEX_MODEL (self));
        g_ptr_array_set_size (priv->internal_items, view_length + n_indices);

        /* set the new items */
        while (n_indices-- > 0)
          {
            MexContent *content;
            guint idx;

            idx = g_controller_reference_get_index_uint (ref, n_indices);

            content = mex_model_get_content (priv->model, idx);

            g_signal_connect (content, "notify", G_CALLBACK (content_notify_cb),
                              self);

            priv->internal_items->pdata[view_length + n_indices] = content;
          }
      }
      break;

    case G_CONTROLLER_REMOVE:
      {
        while (n_indices-- > 0)
          {
            MexContent *content;
            gint idx;

            idx = g_controller_reference_get_index_int (ref, n_indices);

            content = mex_model_get_content (priv->model, idx);

            g_signal_handlers_disconnect_by_func (content,
                                                  G_CALLBACK (content_notify_cb),
                                                  self);

            g_ptr_array_remove_fast (priv->internal_items, content);
          }
      }
      break;

    case G_CONTROLLER_UPDATE:
      /* Should be no need for this, GBinding sorts it out for us :) */
      break;

    case G_CONTROLLER_CLEAR:
      for (i = 0; i < priv->external_items->len; i++)
        g_object_unref (g_ptr_array_index (priv->external_items, i));
      g_ptr_array_set_size (priv->external_items, 0);

      for (i = 0; i < priv->external_items->len; i++)
        g_signal_handlers_disconnect_by_func (g_ptr_array_index (priv->external_items, i),
                                              G_CALLBACK (content_notify_cb),
                                              self);
      g_ptr_array_set_size (priv->internal_items, 0);
      break;

    case G_CONTROLLER_REPLACE:
      g_warning (G_STRLOC ": G_CONTROLLER_REPLACE not implemented by MexViewModel");
      break;

    case G_CONTROLLER_INVALID_ACTION:
      g_warning (G_STRLOC ": View-model controller has issued an error");
      break;

    default:
      g_warning (G_STRLOC ": Unhandled action");
      break;
    }

  mex_view_model_refresh_external_items (self);
}