Initial Import
[profile/ivi/clutter-toys.git] / attic / astro-desktop / applications / music / astro-music-window.c
1 /*
2  * Copyright (C) 2007 OpenedHand Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Neil Jagdish Patel <njp@o-hand.com>
20  */
21
22
23 #include "astro-music-window.h"
24
25 #include <math.h>
26 #include <string.h>
27 #include <libastro-desktop/astro-defines.h>
28 #include <libastro-desktop/astro-application.h>
29 #include <libastro-desktop/astro-window.h>
30 #include <libastro-desktop/astro-behave.h>
31 #include <libastro-desktop/astro-utils.h>
32
33 #include "astro-reflection.h"
34
35 G_DEFINE_TYPE (AstroMusicWindow, astro_music_window, ASTRO_TYPE_WINDOW);
36
37 #define ASTRO_MUSIC_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
38         ASTRO_TYPE_MUSIC_WINDOW, AstroMusicWindowPrivate))
39
40 #define ALBUM_DIR PKGDATADIR"/albums"
41
42 struct _AstroMusicWindowPrivate
43 {
44   GList *covers;
45
46   ClutterActor *albums;
47   ClutterActor *label;
48
49   ClutterActor *player;
50
51   gint active;
52   gboolean activated;
53
54   ClutterTimeline  *timeline;
55   ClutterAlpha     *alpha;
56   ClutterBehaviour *behave;
57 };
58
59 /* Public Functions */
60
61 /* Private functions */
62 typedef struct
63 {
64   gint x;
65   gfloat scale;
66
67 } CoverTrans;
68
69 static void
70 ensure_layout (AstroMusicWindow *window)
71 {
72   AstroMusicWindowPrivate *priv;
73   GList *c;
74   gint i = 0;
75
76   priv = window->priv;
77
78   c = priv->covers;
79   for (c=c; c; c = c->next)
80     {
81       ClutterActor *cover = c->data;
82       CoverTrans *trans = g_object_get_data (G_OBJECT (cover), "trans");
83
84       if (i == priv->active)
85         {
86           trans->x = CSW ()/2;
87           trans->scale = 1.0;
88         }
89       else if (i > priv->active)
90         {
91           gint diff;
92
93           diff = i - priv->active;
94           trans->x = (CSW()/2) + ((CSW()/4)*diff);
95           if (diff > 3)
96             trans->scale = 0.4;
97           else
98             trans->scale = 0.4 + (0.4 * (3-diff)/3);
99         }
100       else
101         {
102           gint diff;
103
104           diff = priv->active - i;
105           trans->x = (CSW()/2) - ((CSW()/4)*diff);
106           if (diff > 3)
107             trans->scale = 0.4;
108           else
109             trans->scale = 0.4 + (0.4 * (3-diff)/3);        
110         }
111
112       i++;
113     }
114 }
115
116 static void
117 astro_music_window_advance (AstroMusicWindow *window, gint n)
118 {
119   AstroMusicWindowPrivate *priv;
120   gint new_active;
121
122   g_return_if_fail (ASTRO_IS_MUSIC_WINDOW (window));
123   priv = window->priv;
124   
125   new_active = priv->active + n;
126   if (new_active < 0 || 
127    new_active > (clutter_group_get_n_children (CLUTTER_GROUP (priv->albums))-1))
128     return;
129
130   priv->active += n;
131   ensure_layout (window);
132
133   if (clutter_timeline_is_playing (priv->timeline))
134     clutter_timeline_rewind (priv->timeline);
135   else
136     clutter_timeline_start (priv->timeline);
137
138 }
139
140 static void
141 on_cover_active_completed (ClutterTimeline *timeline,
142                            AstroReflection *reflection)
143 {
144   astro_reflection_set_active (reflection, TRUE);
145
146   g_signal_handlers_disconnect_by_func (timeline, 
147                                         on_cover_active_completed,
148                                         reflection);
149 }
150
151 static void
152 on_cover_activated (AstroMusicWindow *window)
153 {
154   AstroMusicWindowPrivate *priv;
155   ClutterActor *cover;
156   GList *children;
157   CoverTrans *trans;
158
159   g_return_if_fail (ASTRO_IS_MUSIC_WINDOW (window));
160   priv = window->priv;
161
162   children = priv->covers;
163   cover = g_list_nth_data (children, priv->active);
164
165   if (!CLUTTER_IS_ACTOR (cover))
166     return;
167
168   trans = g_object_get_data (G_OBJECT (cover), "trans");
169   if (!trans)
170     return;
171
172   priv->activated = TRUE;
173
174   trans->scale = ALBUM_SCALE;
175   trans->x = (CSW()/2) - ((ALBUM_SIZE * ALBUM_SCALE) * 0.5);
176
177   clutter_actor_raise_top (cover);
178
179   if (clutter_timeline_is_playing (priv->timeline))
180     clutter_timeline_rewind (priv->timeline);
181   else
182     clutter_timeline_start (priv->timeline);
183
184   g_signal_connect (priv->timeline, "completed",
185                     G_CALLBACK (on_cover_active_completed), cover);
186 }
187
188 static gboolean
189 on_cover_clicked (ClutterActor      *cover, 
190                   ClutterEvent      *event,
191                   AstroMusicWindow  *window)
192 {
193   AstroMusicWindowPrivate *priv;
194   GList *children;
195   gint n;
196
197   g_return_val_if_fail (ASTRO_IS_MUSIC_WINDOW (window), FALSE);
198   priv = window->priv;
199
200   children = priv->covers;
201   n = g_list_index (children, cover);
202
203   if (priv->activated)
204     {
205       if (event->button.x > CSW()/2)
206         return FALSE;
207       astro_reflection_set_active (g_list_nth_data (priv->covers,
208                                    priv->active), FALSE);
209       priv->activated = FALSE;
210       
211       astro_music_window_advance (window, 0);
212       return FALSE;
213     }
214
215   if (n == priv->active)
216     on_cover_activated (window);
217   else
218     {
219       gint diff;
220       if (n > priv->active)
221         diff = (n-priv->active);
222       else
223         diff = (priv->active - n) * -1;
224       astro_music_window_advance (window, diff);
225     }
226
227   return FALSE;
228 }
229
230 static ClutterActor *
231 make_cover (const gchar *filename)
232 {
233   GdkPixbuf *pixbuf;
234   ClutterActor *texture;
235
236   pixbuf = gdk_pixbuf_new_from_file_at_size (filename,
237                                              ALBUM_SIZE, ALBUM_SIZE,
238                                              NULL);
239   if (!pixbuf)
240     return NULL;
241
242   texture = astro_reflection_new (pixbuf);
243
244   g_object_set_data (G_OBJECT (texture), "trans", g_new0 (CoverTrans, 1));
245   return texture;
246 }
247
248 static void
249 load_details (ClutterActor *cover, const gchar *leaf)
250 {
251   gchar *details;
252   gint i;
253
254   details = g_strndup (leaf, strlen (leaf)-4);
255
256   for (i = 0; i < strlen (details); i++)
257     if (details[i] == '_') details[i] = ' ';
258
259   clutter_actor_set_name (cover, details);
260   g_free (details);
261 }
262
263 static void
264 load_albums (AstroMusicWindow *window)
265 {
266   AstroMusicWindowPrivate *priv;
267   GDir *dir;
268   const gchar *leaf;
269   GError *error = NULL;
270   gint offset = CSW()*2;
271
272   priv = window->priv;
273
274   dir = g_dir_open (ALBUM_DIR, 0, &error);
275   if (error)
276     {
277       g_warning ("Cannot load albums: %s", error->message);
278       g_error_free (error);
279       return;
280     }
281   
282   while ((leaf = g_dir_read_name (dir)))
283     {
284       ClutterActor *cover;
285       gchar *filename;
286
287       if (!g_str_has_suffix (leaf, ".jpg"))
288         continue;
289
290       filename = g_build_filename (ALBUM_DIR, leaf, NULL);
291       cover = make_cover (filename);
292
293       if (!CLUTTER_IS_ACTOR (cover))
294         {
295           g_free (filename);
296           continue;
297         }
298       load_details (cover, leaf);
299       clutter_container_add_actor (CLUTTER_CONTAINER (priv->albums), cover);
300       clutter_actor_set_position (cover, offset, 0);
301       clutter_actor_show_all (cover);
302       clutter_actor_set_reactive (cover, TRUE);
303       g_signal_connect (cover, "button-release-event",
304                         G_CALLBACK (on_cover_clicked), window);
305
306       priv->covers = g_list_append (priv->covers, cover);
307
308       g_free (filename);
309
310       offset += ALBUM_SIZE * 0.9;
311     }
312 }
313
314 static void
315 astro_music_alpha (ClutterBehaviour *behave,
316                    guint32           alpha_value,
317                    AstroMusicWindow *window)
318 {
319   AstroMusicWindowPrivate *priv;
320   GList *c;
321
322   g_return_if_fail (ASTRO_IS_MUSIC_WINDOW (window));
323   priv = window->priv;
324
325   c = priv->covers;
326   for (c=c; c; c = c->next)
327     {
328       ClutterActor *cover = c->data;
329       CoverTrans *trans = g_object_get_data (G_OBJECT (cover), "trans");
330       gdouble cscale, dscale;
331       gint currentx, diffx;
332       
333       currentx = clutter_actor_get_x (cover);
334       if (currentx > trans->x)
335         diffx = (currentx - trans->x) * -1;
336       else
337         diffx = trans->x - currentx;
338
339       clutter_actor_set_x (cover, currentx 
340         + (gint)((diffx*alpha_value)/CLUTTER_ALPHA_MAX_ALPHA));
341
342       clutter_actor_get_scale (cover, &cscale, &cscale);
343       if (cscale > trans->scale)
344         dscale = (cscale - trans->scale) * -1;
345       else
346         dscale = trans->scale - cscale;
347
348       clutter_actor_set_scale (cover, 
349         cscale + ((dscale*alpha_value)/CLUTTER_ALPHA_MAX_ALPHA),
350         cscale + ((dscale*alpha_value)/CLUTTER_ALPHA_MAX_ALPHA));
351     }
352 }
353
354 static void
355 on_main_timeline_completed (ClutterTimeline  *timeline,
356                             AstroMusicWindow *window)
357 {
358   AstroMusicWindowPrivate *priv;
359   const gchar *details;
360   GList *children;
361
362   g_return_if_fail (ASTRO_MUSIC_WINDOW (window));
363   priv = window->priv;
364
365   children = priv->covers;
366   details = clutter_actor_get_name (g_list_nth_data (children, priv->active));
367
368   clutter_label_set_text (CLUTTER_LABEL (priv->label), details);
369 }
370
371 static gboolean
372 on_key_release_event (ClutterActor     *actor, 
373                       ClutterEvent     *event,
374                       AstroMusicWindow *window)
375 {
376   AstroMusicWindowPrivate *priv;
377   
378   g_return_val_if_fail (ASTRO_IS_WINDOW (window), FALSE);
379   priv = window->priv;
380
381   switch (event->key.keyval)
382     {
383       case CLUTTER_Return:
384       case CLUTTER_KP_Enter:
385       case CLUTTER_ISO_Enter:
386         on_cover_activated (window);
387         break;
388       case CLUTTER_Left:
389       case CLUTTER_KP_Left:
390         if (priv->activated)
391           {
392             astro_reflection_set_active (g_list_nth_data (priv->covers,
393                                                           priv->active), FALSE);  
394             priv->activated = FALSE;
395           }
396         astro_music_window_advance (window, -1);
397         break;
398       case CLUTTER_Right:
399       case CLUTTER_KP_Right:
400         if (priv->activated)
401           {
402             astro_reflection_set_active (g_list_nth_data (priv->covers,
403                                                         priv->active), FALSE);
404             priv->activated = FALSE;
405           }
406           astro_music_window_advance (window, 1);
407         break;
408       default:
409         ;
410     }
411
412   return FALSE;
413 }
414
415 /* GObject stuff */
416 static void
417 astro_music_window_class_init (AstroMusicWindowClass *klass)
418 {
419   GObjectClass        *gobject_class = G_OBJECT_CLASS (klass);
420
421   g_type_class_add_private (gobject_class, sizeof (AstroMusicWindowPrivate));
422 }
423
424 static void
425 astro_music_window_init (AstroMusicWindow *window)
426 {
427   AstroMusicWindowPrivate *priv;
428   ClutterColor white = { 0xff, 0xff, 0xff, 0xff };
429   gchar *font = NULL;
430
431   priv = window->priv = ASTRO_MUSIC_WINDOW_GET_PRIVATE (window);
432
433   priv->covers = NULL;
434   priv->active = 0;
435   priv->activated = FALSE;
436
437   priv->albums = clutter_group_new ();
438   clutter_container_add_actor (CLUTTER_CONTAINER (window), priv->albums);
439   clutter_actor_set_anchor_point_from_gravity (priv->albums, 
440                                                CLUTTER_GRAVITY_WEST);
441   clutter_actor_set_position (priv->albums, 0, CSH() * 0.5);
442
443   load_albums (window);
444   
445   font = g_strdup_printf ("Sans %d", CSH()/30);
446   priv->label = clutter_label_new_full (font, 
447                                         "Jay Z - American Gangster",
448                                         &white);
449   clutter_label_set_line_wrap (CLUTTER_LABEL (priv->label), FALSE);
450   clutter_label_set_alignment (CLUTTER_LABEL (priv->label),
451                                PANGO_ALIGN_CENTER);
452   clutter_container_add_actor (CLUTTER_CONTAINER (window), priv->label);
453   clutter_actor_set_size (priv->label, CSW(), CSH()/10);
454   clutter_actor_set_anchor_point_from_gravity (priv->label, 
455                                                CLUTTER_GRAVITY_CENTER);
456   clutter_actor_set_position (priv->label, CSW()/2, CSH()*0.95);
457   g_free (font);
458
459   ensure_layout (window);
460
461   priv->timeline = clutter_timeline_new_for_duration (1200);
462   priv->alpha = clutter_alpha_new_full (priv->timeline,
463                                         clutter_sine_inc_func,
464                                         NULL, NULL);
465   priv->behave = astro_behave_new (priv->alpha,
466                                    (AstroBehaveAlphaFunc)astro_music_alpha,
467                                    window);
468
469   g_signal_connect (priv->timeline, "completed",
470                     G_CALLBACK (on_main_timeline_completed), window);
471
472   clutter_timeline_start (priv->timeline);
473
474   g_signal_connect (window, "key-release-event",
475                     G_CALLBACK (on_key_release_event), window);
476   clutter_grab_keyboard (CLUTTER_ACTOR (window));
477
478
479   clutter_actor_set_position (CLUTTER_ACTOR (window), 0, 0);
480   clutter_actor_show_all (CLUTTER_ACTOR (window));
481 }
482
483 AstroWindow * 
484 astro_music_window_new (void)
485 {
486   AstroWindow *music_window =  g_object_new (ASTRO_TYPE_MUSIC_WINDOW,
487                                                                                                NULL);
488
489   return music_window;
490 }
491