Don't fetch url. Only fetch just when we need it (to play the files).
[clutter-grilo-player:cgp.git] / src / cgp-library-group.c
1 #include "config.h"
2 #include <clutter/clutter.h>
3 #include <cgp-plugins-list-view.h>
4 #include <cgp-plugins-model.h>
5 #include <mx/mx.h>
6 #include <cgp-library-group.h>
7 #include <cgp-marshal.h>
8 #include <grilo.h>
9
10 enum
11 {
12         ACTIVATE_MEDIA,
13
14         LAST_SIGNAL
15 };
16 static guint library_group_signals[LAST_SIGNAL] = { 0, };
17
18 static void cgp_library_group_dispose (GObject *self);
19 static void on_error_banner_clicked (MxButton *button,
20                                      CgpLibraryGroup *self);
21 static void on_list_view_activated (CgpPluginsListView *list_view,
22                                     const gchar *id,
23                                     CgpLibraryGroup *self);
24 static void on_path_bar_notify_level (GObject    *gobject,
25                                       GParamSpec *pspec,
26                                       CgpLibraryGroup *self);
27 static void on_path_bar_entry_activated (ClutterText    *clutter_text,
28                                          CgpLibraryGroup *self);
29 static void on_path_bar_editable_changed (GObject    *gobject,
30                                           GParamSpec *pspec,
31                                           CgpLibraryGroup *self);
32
33 G_DEFINE_TYPE (CgpLibraryGroup,
34                cgp_library_group,
35                MX_TYPE_BOX_LAYOUT);
36
37 typedef struct _CgpLibraryGroupPrivate CgpLibraryGroupPrivate;
38 struct _CgpLibraryGroupPrivate {
39         ClutterActor *top_box;
40         ClutterActor *path_bar;
41         ClutterActor *plugins_list_view;
42         ClutterActor *notebook;
43         ClutterActor *banner;
44         GrlMedia *last_media;
45         gint page_count;
46 };
47
48 #define CGP_LIBRARY_GROUP_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
49                                                                            CGP_TYPE_LIBRARY_GROUP, \
50                                                                            CgpLibraryGroupPrivate))
51
52 static void
53 cgp_library_group_class_init (CgpLibraryGroupClass *klass)
54 {
55         GObjectClass *gobject_class;
56         ClutterActorClass *actor_class;
57         gobject_class = (GObjectClass*) klass;
58         actor_class = (ClutterActorClass *) klass;
59
60         gobject_class->dispose = cgp_library_group_dispose;
61
62         library_group_signals[ACTIVATE_MEDIA] =
63                 g_signal_new ("activate-media",
64                               G_TYPE_FROM_CLASS (klass),
65                               G_SIGNAL_RUN_LAST,
66                               G_STRUCT_OFFSET (CgpLibraryGroupClass, activate_media),
67                               NULL, NULL,
68                               _cgp_marshal_VOID__OBJECT,
69                               G_TYPE_NONE, 1, GRL_TYPE_MEDIA);
70         g_type_class_add_private (gobject_class, sizeof(CgpLibraryGroupPrivate));
71 }
72
73 static void
74 cgp_library_group_dispose (GObject *self)
75 {
76         CgpLibraryGroupPrivate *priv;
77
78         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
79         priv->last_media = NULL;
80
81         G_OBJECT_CLASS (cgp_library_group_parent_class)->dispose (G_OBJECT (self));
82 }
83
84 static void
85 cgp_library_group_init (CgpLibraryGroup *self)
86 {
87         CgpLibraryGroupPrivate *priv;
88         ClutterModel *plugins_model;
89         ClutterActor *blank;
90         ClutterColor blank_color;
91         ClutterActor *scroll_view;
92
93         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
94
95         priv->last_media = NULL;
96
97         priv->top_box = mx_table_new ();
98         clutter_actor_show (priv->top_box);
99         clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->top_box);
100         clutter_container_child_set (CLUTTER_CONTAINER (self), priv->top_box,
101                                      "expand", TRUE, NULL);
102
103         priv->path_bar = mx_path_bar_new ();
104         mx_path_bar_set_editable (MX_PATH_BAR (priv->path_bar), FALSE);
105         mx_path_bar_push (MX_PATH_BAR (priv->path_bar), "Providers");
106         clutter_actor_show (priv->path_bar);
107         mx_table_add_actor_with_properties (MX_TABLE (priv->top_box), priv->path_bar,
108                                             0, 0,
109                                             "row-span", 1, "column-span", 2,
110                                             "x-expand", TRUE, "y-expand", FALSE,
111                                             "x-fill", TRUE, "x-align", 0.0,
112                                             NULL);
113         g_signal_connect (G_OBJECT (priv->path_bar), "notify::level", G_CALLBACK (on_path_bar_notify_level), self);
114         g_signal_connect (G_OBJECT (priv->path_bar), "notify::editable", G_CALLBACK (on_path_bar_editable_changed), self);
115
116         blank = clutter_rectangle_new ();
117         clutter_color_from_string (&blank_color, "#0000");
118         clutter_rectangle_set_color (CLUTTER_RECTANGLE (blank), &blank_color);
119         mx_table_add_actor_with_properties (MX_TABLE (priv->top_box), blank,
120                                             1, 0,
121                                             "x-expand", TRUE, "y-expand", TRUE,
122                                             "x-fill", TRUE, "y-fill", TRUE,
123                                             NULL);
124         
125         priv->notebook = mx_notebook_new ();
126         mx_table_add_actor_with_properties (MX_TABLE (priv->top_box), priv->notebook,
127                                             1, 1,
128                                             "x-expand", TRUE, "y-expand", TRUE,
129                                             "x-fill", TRUE, "y-fill", TRUE,
130                                             NULL);
131
132         scroll_view = mx_scroll_view_new ();
133         mx_scroll_view_set_scroll_policy (MX_SCROLL_VIEW (scroll_view), MX_SCROLL_POLICY_VERTICAL);
134         clutter_container_add_actor (CLUTTER_CONTAINER (priv->notebook), scroll_view);
135         priv->page_count = 1;
136
137         plugins_model = cgp_plugins_model_new_from_registry (grl_plugin_registry_get_instance ());
138
139         priv->plugins_list_view = cgp_plugins_list_view_new (CGP_PLUGINS_MODEL (plugins_model));
140         g_signal_connect (G_OBJECT (priv->plugins_list_view), "activated", G_CALLBACK (on_list_view_activated), self);
141         g_object_unref (plugins_model);
142         clutter_container_add_actor (CLUTTER_CONTAINER (scroll_view), priv->plugins_list_view);
143
144         priv->banner = mx_button_new ();
145         clutter_actor_hide (priv->banner);
146         clutter_actor_set_name (priv->banner, "cgp-error-button");
147         g_signal_connect (priv->banner, "clicked", G_CALLBACK (on_error_banner_clicked), self);
148         mx_table_add_actor_with_properties (MX_TABLE (priv->top_box), priv->banner,
149                                             2, 0,
150                                             "x-expand", TRUE, "y-expand", FALSE,
151                                             "x-fill", TRUE, "y-fill", FALSE,
152                                             "row-span", 1, "column-span", 2,
153                                             NULL);
154
155
156         clutter_actor_show (priv->plugins_list_view);
157         clutter_actor_show (scroll_view);
158         clutter_actor_show (priv->notebook);
159         clutter_actor_show (blank);
160 }
161
162 ClutterActor *
163 cgp_library_group_new (void)
164 {
165         return g_object_new (CGP_TYPE_LIBRARY_GROUP, NULL);
166 }
167
168 static void
169 on_source_metadata_cb (GrlMediaSource *source,
170                        GrlMedia *media,
171                        gpointer user_data,
172                        const GError *error)
173 {
174         CgpPluginsListView *list_view = (CgpPluginsListView *) user_data;
175         CgpLibraryGroupPrivate *priv;
176
177         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (list_view);
178
179         if (media == priv->last_media) {
180                 const gchar *url;
181                 url = grl_media_get_url (GRL_MEDIA (media));
182                 if (url) {
183                         g_signal_emit (G_OBJECT (list_view),
184                                        library_group_signals[ACTIVATE_MEDIA],
185                                        0,
186                                        media);
187                 } else {
188                         mx_button_set_label (MX_BUTTON (priv->banner),
189                                              "Contents restricted");
190                         clutter_actor_show (priv->banner);
191                 }
192         }
193
194         g_object_unref (list_view);
195 }
196
197 static void
198 on_error_banner_clicked (MxButton *button,
199                          CgpLibraryGroup *self)
200 {
201         clutter_actor_hide (CLUTTER_ACTOR (button));
202 }
203
204 static void
205 on_list_view_activated (CgpPluginsListView *list_view,
206                         const gchar *id,
207                         CgpLibraryGroup *self)
208 {
209         CgpLibraryGroupPrivate *priv;
210         GObject *instance;
211         ClutterActor *new_view = NULL;
212         ClutterActor *scroll_view;
213
214         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
215
216         clutter_actor_hide (priv->banner);
217
218         instance = cgp_plugins_model_get_item (CGP_PLUGINS_MODEL (mx_list_view_get_model (MX_LIST_VIEW (list_view))), id);
219         if (!instance) return;
220
221         g_warning ("ACTIVATED ITEM %s has type %s", id?id:"(null)", instance?G_OBJECT_TYPE_NAME (instance):"(null)");
222
223         if (GRL_IS_MEDIA (instance) && !GRL_IS_MEDIA_BOX (instance)) {
224                 const gchar *url;
225                 url = grl_media_get_url (GRL_MEDIA (instance));
226                 priv->last_media = (GrlMedia *) instance;
227                 if (url) {
228                         g_signal_emit (G_OBJECT (self),
229                                        library_group_signals[ACTIVATE_MEDIA],
230                                        0,
231                                        instance);
232                 } else {
233                         ClutterModel *model;
234                         GObject *source;
235
236                         model = mx_list_view_get_model (MX_LIST_VIEW (list_view));
237                         g_object_get (model, "source", &source, NULL);
238                         
239                         grl_media_source_metadata (GRL_MEDIA_SOURCE (source), GRL_MEDIA (instance), 
240                                                    grl_metadata_key_list_new (GRL_METADATA_KEY_URL, NULL),
241                                                    GRL_RESOLVE_FULL, on_source_metadata_cb, g_object_ref (self));
242                 }
243         } else {
244                 priv->page_count++;
245                 if (GRL_IS_MEDIA_SOURCE (instance)) {
246                         GrlSupportedOps supported_ops;
247                         
248                         supported_ops = grl_metadata_source_supported_operations (GRL_METADATA_SOURCE (instance));
249                         if (supported_ops & GRL_OP_BROWSE) {
250                                 ClutterModel *model;
251                                 model = cgp_plugins_model_new_from_source (GRL_MEDIA_SOURCE (instance), NULL);
252                                 new_view = cgp_plugins_list_view_new (CGP_PLUGINS_MODEL (model));
253                                 g_object_unref (model);
254                         }
255                         mx_path_bar_push (MX_PATH_BAR (priv->path_bar), grl_metadata_source_get_name (GRL_METADATA_SOURCE (instance)));
256                         mx_path_bar_set_editable (MX_PATH_BAR (priv->path_bar), supported_ops & GRL_OP_SEARCH);
257                 } else if (GRL_IS_MEDIA_BOX (instance)) {
258                         ClutterModel *old_model, *model;
259                         old_model = mx_list_view_get_model (MX_LIST_VIEW (list_view));
260                         model = cgp_plugins_model_new_from_media_box (cgp_plugins_model_get_source (CGP_PLUGINS_MODEL (old_model)),
261                                                                       GRL_MEDIA_BOX (instance));
262                         new_view = cgp_plugins_list_view_new (CGP_PLUGINS_MODEL (model));
263                         g_object_unref (model);
264                         mx_path_bar_push (MX_PATH_BAR (priv->path_bar), grl_media_get_title (GRL_MEDIA (instance)));
265                 } else {
266                         new_view = clutter_rectangle_new ();
267                 }
268         }
269
270         if (new_view) {
271                 clutter_actor_show (new_view);
272                 if (MX_IS_SCROLLABLE (new_view)) {
273                         scroll_view = mx_scroll_view_new ();
274                         clutter_container_add_actor (CLUTTER_CONTAINER (scroll_view), new_view);
275                         clutter_actor_show (scroll_view);
276                         mx_scroll_view_set_scroll_policy (MX_SCROLL_VIEW (scroll_view), MX_SCROLL_POLICY_VERTICAL);
277                         clutter_container_add_actor (CLUTTER_CONTAINER (priv->notebook), scroll_view);
278                         mx_notebook_set_current_page (MX_NOTEBOOK (priv->notebook), scroll_view);
279                 } else {
280                         clutter_container_add_actor (CLUTTER_CONTAINER (priv->notebook), new_view);
281                         mx_notebook_set_current_page (MX_NOTEBOOK (priv->notebook), new_view);
282                 }
283                 g_signal_connect (G_OBJECT (new_view), "activated", G_CALLBACK (on_list_view_activated), self);
284         }
285
286         g_object_unref (instance);
287 }
288
289 static void
290 on_path_bar_entry_activated (ClutterText *entry_text,
291                              CgpLibraryGroup *self)
292 {
293         CgpLibraryGroupPrivate *priv;
294         gchar *text;
295         GList *notebook_children, *notebook_last;
296
297         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
298
299         text = clutter_text_get_chars (CLUTTER_TEXT (entry_text), 0, -1);
300
301         if (priv->page_count > 2) {
302                 while (mx_path_bar_get_level (MX_PATH_BAR (priv->path_bar)) > 2) {
303                         mx_path_bar_pop (MX_PATH_BAR (priv->path_bar));
304                 }
305         }
306
307         notebook_children = clutter_container_get_children (CLUTTER_CONTAINER (priv->notebook));
308         notebook_last = g_list_last (notebook_children);
309
310         if (MX_SCROLL_VIEW (notebook_last->data)) {
311                 ClutterActor *list_view;
312                 ClutterModel *model;
313                 GrlMediaSource *source;
314
315                 list_view = mx_bin_get_child (MX_BIN (notebook_last->data));
316
317                 model = mx_list_view_get_model (MX_LIST_VIEW (list_view));
318                 source = cgp_plugins_model_get_source (CGP_PLUGINS_MODEL (model));
319                 if (source) {
320                         ClutterModel *new_model;
321                         ClutterActor *scroll_view;
322                         ClutterActor *new_view;
323
324                         priv->page_count++;
325                 
326                         new_model = cgp_plugins_model_new_from_source (GRL_MEDIA_SOURCE (source), text);
327                         new_view = cgp_plugins_list_view_new (CGP_PLUGINS_MODEL (new_model));
328                         g_object_unref (new_model);
329
330                         mx_path_bar_push (MX_PATH_BAR (priv->path_bar), "Search");
331
332                         scroll_view = mx_scroll_view_new ();
333                         clutter_container_add_actor (CLUTTER_CONTAINER (scroll_view), new_view);
334                         clutter_actor_show (scroll_view);
335                         mx_scroll_view_set_scroll_policy (MX_SCROLL_VIEW (scroll_view), MX_SCROLL_POLICY_VERTICAL);
336                         clutter_container_add_actor (CLUTTER_CONTAINER (priv->notebook), scroll_view);
337                         g_signal_connect (G_OBJECT (new_view), "activated", G_CALLBACK (on_list_view_activated), self);
338                         mx_notebook_set_current_page (MX_NOTEBOOK (priv->notebook), scroll_view);
339                 }
340         }
341         
342         g_list_free (notebook_children);
343         g_free (text);
344 }
345
346 static void
347 on_path_bar_editable_changed (GObject    *gobject,
348                               GParamSpec *pspec,
349                               CgpLibraryGroup *self)
350 {
351         CgpLibraryGroupPrivate *priv;
352
353         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
354
355         if (mx_path_bar_get_editable (MX_PATH_BAR (priv->path_bar))) {
356                 ClutterActor *entry;
357                 ClutterActor *clutter_text;
358
359                 entry = (ClutterActor *) mx_path_bar_get_entry (MX_PATH_BAR (priv->path_bar));
360                 clutter_text = mx_entry_get_clutter_text (MX_ENTRY (entry));
361                 clutter_text_set_activatable (CLUTTER_TEXT (clutter_text), TRUE);
362                 
363                 g_signal_connect (G_OBJECT (clutter_text), "activate", G_CALLBACK (on_path_bar_entry_activated), self);
364         }
365 }
366
367 static void
368 on_path_bar_notify_level (GObject    *gobject,
369                           GParamSpec *pspec,
370                           CgpLibraryGroup *self)
371 {
372         MxPathBar *path_bar;
373         CgpLibraryGroupPrivate *priv;
374
375         path_bar = MX_PATH_BAR (gobject);
376         priv = CGP_LIBRARY_GROUP_GET_PRIVATE (self);
377
378         if (priv->page_count != mx_path_bar_get_level (path_bar)) {
379                 GList *children, *node;
380                 /* forced "back" */
381                 priv->page_count = mx_path_bar_get_level (path_bar);
382                 children = clutter_container_get_children (CLUTTER_CONTAINER (priv->notebook));
383                 node = g_list_nth (children, priv->page_count);
384                 while (node) {
385                         clutter_actor_destroy (node->data);
386                         node = g_list_next (node);
387                 }
388                 g_list_free (children);
389
390                 if (priv->page_count == 1) {
391                         mx_path_bar_set_editable (MX_PATH_BAR (priv->path_bar), FALSE);
392                 }
393         }
394
395 }