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); }
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); }
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); }
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); }
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); }
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)); }
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"); }
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); }
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); } }
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); }
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; }
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); }