WIP
[gtkpod:phantomjinx-gtkpod-plugin.git] / plugins / clarity / clarity_cover.c
1 /*
2  |  Copyright (C) 2002-2011 Jorg Schuler <jcsjcs at users sourceforge net>
3  |                                             Paul Richardson <phantom_sf at users.sourceforge.net>
4  |  Part of the gtkpod project.
5  |
6  |  URL: http://www.gtkpod.org/
7  |  URL: http://gtkpod.sourceforge.net/
8  |
9  |  This program is free software; you can redistribute it and/or modify
10  |  it under the terms of the GNU General Public License as published by
11  |  the Free Software Foundation; either version 2 of the License, or
12  |  (at your option) any later version.
13  |
14  |  This program is distributed in the hope that it will be useful,
15  |  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  |  GNU General Public License for more details.
18  |
19  |  You should have received a copy of the GNU General Public License
20  |  along with this program; if not, write to the Free Software
21  |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  |
23  |  iTunes and iPod are trademarks of Apple
24  |
25  |  This product is not supported/written/published by Apple!
26  |
27  */
28
29 #include "clarity_utils.h"
30 #include "clarity_cover.h"
31
32 #define V_PADDING 4
33
34 /**
35  * SECTION:clarity-cover
36  * @short_description: Cover widget
37  *
38  * An artwork cover widget with support for a text label and background color.
39  */
40
41 G_DEFINE_TYPE( ClarityCover, clarity_cover, CLUTTER_TYPE_ACTOR);
42
43 /* macro for accessing the object's private structure */
44 #define CLARITY_COVER_GET_PRIVATE(obj) \
45   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLARITY_TYPE_COVER, ClarityCoverPrivate))
46
47 /* private structure - should only be accessed through the public API;
48  * this is used to store member variables whose properties
49  * need to be accessible from the implementation; for example, if we
50  * intend to create wrapper functions which modify properties on the
51  * actors composing an object, we should keep a reference to the actors
52  * here
53  *
54  * this is also the place where other state variables go:
55  * for example, you might record the current state of the button
56  * (toggled on or off) or a background image
57  */
58 struct _ClarityCoverPrivate {
59
60     ClutterContent *artwork;
61     int height;
62     int width;
63 //    ClutterActor *reflection;
64
65     gchar *title;
66     gchar *artist;
67 };
68
69 /* enumerates property identifiers for this class;
70  * note that property identifiers should be non-zero integers,
71  * so we add an unused PROP_0 to occupy the 0 position in the enum
72  */
73 enum {
74     PROP_0, PROP_TITLE, PROP_ARTIST
75 };
76
77 /*
78  * The finalize method finishes releasing the remaining
79  * resources just before the object itself will be freed from memory, and
80  * therefore it will only be called once.
81  */
82 static void clarity_cover_finalize(GObject *gobject) {
83     ClarityCoverPrivate *priv = CLARITY_COVER(gobject)->priv;
84
85     g_free(priv->title);
86     g_free(priv->artist);
87
88     /* call the parent class' finalize() method */
89     G_OBJECT_CLASS(clarity_cover_parent_class)->finalize(gobject);
90 }
91
92 /* ClutterActor implementation
93  *
94  * we only implement destroy(), get_preferred_height(), get_preferred_width(),
95  * allocate(), and paint(), as this is the minimum we can get away with
96  */
97
98 /* composite actors should implement destroy(), and inside their
99  * implementation destroy any actors they are composed from;
100  * in this case, we just destroy the child ClutterBox
101  */
102 void clarity_cover_destroy(ClutterActor *self) {
103     ClarityCoverPrivate *priv = CLARITY_COVER_GET_PRIVATE(self);
104
105     if (priv) {
106         if (CLUTTER_IS_IMAGE(priv->artwork)) {
107             g_object_unref(priv->artwork);
108             priv->artwork = NULL;
109         }
110
111 //        if (CLUTTER_IS_ACTOR(priv->reflection)) {
112 //            clutter_actor_destroy(priv->reflection);
113 //            priv->reflection = NULL;
114 //        }
115     }
116
117     /* chain up to destroy() on the parent ClutterActorClass;
118      * note that we check the parent class has a destroy() implementation
119      * before calling it
120      */
121     if (CLUTTER_ACTOR_CLASS(clarity_cover_parent_class)->destroy)
122         CLUTTER_ACTOR_CLASS(clarity_cover_parent_class)->destroy(self);
123 }
124
125 /* GObject class and instance initialization functions; note that
126  * these have been placed after the Clutter implementation, as
127  * they refer to the static function implementations above
128  */
129
130 /* class init: attach functions to superclasses, define properties
131  * and signals
132  */
133 static void clarity_cover_class_init(ClarityCoverClass *klass) {
134     ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS(klass);
135     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
136
137     gobject_class->finalize = clarity_cover_finalize;
138
139     actor_class->destroy = clarity_cover_destroy;
140
141     g_type_class_add_private(klass, sizeof(ClarityCoverPrivate));
142 }
143
144 /* object init: create a private structure and pack
145  * composed ClutterActors into it
146  */
147 static void clarity_cover_init(ClarityCover *self) {
148     ClarityCoverPrivate *priv;
149
150     priv = self->priv = CLARITY_COVER_GET_PRIVATE (self);
151
152     clutter_actor_set_reactive(CLUTTER_ACTOR(self), TRUE);
153
154     priv->title = NULL;
155     priv->artist = NULL;
156
157     priv->artwork = NULL;
158     priv->width = 0;
159     priv->height = 0;
160 //    priv->reflection = NULL;
161 }
162
163 static void _clone_paint_cb (ClutterActor *actor) {
164     ClutterActor *source;
165     ClutterActorBox box;
166     CoglTexture *texture;
167     gfloat width, height;
168     guint8 opacity;
169     CoglColor color_1, color_2;
170     CoglTextureVertex vertices[4];
171
172     /* if we don't have a source actor, don't paint */
173     source = clutter_clone_get_source (CLUTTER_CLONE (actor));
174     if (source == NULL)
175         goto out;
176
177     /* if the source texture does not have any content, don't paint */
178 //    texture = clutter_image_get_texture (CLUTTER_IMAGE(source));
179 //    if (texture == NULL)
180 //        goto out;
181
182     /* get the size of the reflection */
183     clutter_actor_get_allocation_box (actor, &box);
184     clutter_actor_box_get_size (&box, &width, &height);
185
186     /* get the composite opacity of the actor */
187     opacity = clutter_actor_get_paint_opacity (actor);
188
189     /* figure out the two colors for the reflection: the first is
190      * full color and the second is the same, but at 0 opacity
191      */
192     cogl_color_init_from_4f (&color_1, 1.0, 1.0, 1.0, opacity / 255.0);
193     cogl_color_premultiply (&color_1);
194     cogl_color_init_from_4f (&color_2, 1.0, 1.0, 1.0, 0.0);
195     cogl_color_premultiply (&color_2);
196
197     /* now describe the four vertices of the quad; since it has
198      * to be a reflection, we need to invert it as well
199      */
200     vertices[0].x = 0; vertices[0].y = 0; vertices[0].z = 0;
201     vertices[0].tx = 0.0; vertices[0].ty = 1.0;
202     vertices[0].color = color_1;
203
204     vertices[1].x = width; vertices[1].y = 0; vertices[1].z = 0;
205     vertices[1].tx = 1.0; vertices[1].ty = 1.0;
206     vertices[1].color = color_1;
207
208     vertices[2].x = width; vertices[2].y = height; vertices[2].z = 0;
209     vertices[2].tx = 1.0; vertices[2].ty = 0.0;
210     vertices[2].color = color_2;
211
212     vertices[3].x = 0; vertices[3].y = height; vertices[3].z = 0;
213     vertices[3].tx = 0.0; vertices[3].ty = 0.0;
214     vertices[3].color = color_2;
215
216     /* paint the same texture but with a different geometry */
217 //    cogl_set_source (material);
218 //    cogl_polygon (vertices, 4, TRUE);
219
220     clutter_content_invalidate(CLUTTER_IMAGE(source));
221
222     out:
223       /* prevent the default clone handler from running */
224       g_signal_stop_emission_by_name (actor, "paint");
225 }
226
227 void clarity_cover_set_album_item (ClarityCover *self, AlbumItem *item) {
228     g_return_if_fail(CLARITY_IS_COVER(self));
229
230     ClarityCoverPrivate *priv = CLARITY_COVER_GET_PRIVATE (self);
231     g_return_if_fail(priv);
232
233     GError *error = NULL;
234     gint y_offset;
235
236     if (!priv->artwork) {
237         priv->artwork = clutter_image_new();
238     }
239
240     priv->width = gdk_pixbuf_get_width (item->albumart);
241     priv->height = gdk_pixbuf_get_height (item->albumart);
242
243     if( priv->height > DEFAULT_IMG_SIZE) {
244         priv->width = priv->width * DEFAULT_IMG_SIZE / priv->height;
245         priv->height = DEFAULT_IMG_SIZE;
246     }
247
248     // Set the width and height
249 //    clutter_actor_set_width(priv->artwork_box, priv->width);
250 //    clutter_actor_set_height(priv->artwork_box, priv->height);
251     clutter_actor_set_width(CLUTTER_ACTOR(self), priv->width);
252     clutter_actor_set_height(CLUTTER_ACTOR(self), priv->height);
253
254     // Set cover artwork
255     clutter_image_set_data( CLUTTER_IMAGE (priv->artwork),
256                                               gdk_pixbuf_get_pixels (item->albumart),
257                                               gdk_pixbuf_get_has_alpha (item->albumart)
258                                                       ? COGL_PIXEL_FORMAT_RGBA_8888
259                                                       : COGL_PIXEL_FORMAT_RGB_888,
260                                               priv->width,
261                                               priv->height,
262                                               gdk_pixbuf_get_rowstride (item->albumart),
263                                               &error);
264     if (error) {
265         g_warning("%s", error->message);
266         g_error_free(error);
267         return;
268     }
269
270     clutter_actor_set_content (CLUTTER_ACTOR(self), priv->artwork);
271
272     // Add reflection
273 //    if (! priv->reflection) {
274 //        y_offset = clutter_actor_get_height (priv->texture) + V_PADDING;
275 //
276 //        priv->reflection = clutter_clone_new (priv->texture);
277 //        clutter_actor_add_constraint (priv->reflection, clutter_bind_constraint_new (priv->texture, CLUTTER_BIND_X, 0.0));
278 //        clutter_actor_add_constraint (priv->reflection, clutter_bind_constraint_new (priv->texture, CLUTTER_BIND_Y, y_offset));
279 //        clutter_actor_add_constraint (priv->reflection, clutter_bind_constraint_new (priv->texture, CLUTTER_BIND_WIDTH, 0.0));
280 //        clutter_actor_add_constraint (priv->reflection, clutter_bind_constraint_new (priv->texture, CLUTTER_BIND_HEIGHT, 0.0));
281 //        g_signal_connect (priv->reflection,
282 //                       "paint",
283 //                       G_CALLBACK (_clone_paint_cb),
284 //                       NULL);
285 //
286 //        clutter_actor_add_child(CLUTTER_ACTOR(self), priv->reflection);
287 //    }
288
289     // Add title / artist data
290     if (priv->title)
291         g_free(priv->title);
292
293     priv->title = g_strdup(item->albumname);
294
295     if (priv->artist)
296             g_free(priv->artist);
297
298     priv->artist = g_strdup(item->artist);
299 }
300
301 gchar *clarity_cover_get_title(ClarityCover *self) {
302     ClarityCoverPrivate *priv = self->priv;
303     return g_strdup(priv->title);
304 }
305
306 gchar *clarity_cover_get_artist(ClarityCover *self) {
307     ClarityCoverPrivate *priv = self->priv;
308     return g_strdup(priv->artist);
309 }
310
311 gfloat clarity_cover_get_artwork_height(ClarityCover *self) {
312     ClarityCoverPrivate *priv = self->priv;
313     return priv->height;
314 }
315
316 gfloat clarity_cover_get_artwork_width(ClarityCover *self) {
317     ClarityCoverPrivate *priv = self->priv;
318     return priv->width;
319 }
320
321 /**
322  * clarity_cover_new:
323  *
324  * Creates a new #ClarityCover instance
325  *
326  * Returns: a new #ClarityCover
327  */
328 ClarityCover *clarity_cover_new(void) {
329     return g_object_new(CLARITY_TYPE_COVER, NULL);
330 }
331