1 /*
2 Copyright 2010 Canonical Ltd.
3 
4 Authors:
5     Conor Curran <conor.curran@canonical.com>
6     Mirco Müller <mirco.mueller@canonical.com>
7     Andrea Cimitan <andrea.cimitan@canonical.com>
8 
9 This program is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 3, as published
11 by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranties of
15 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16 PURPOSE.  See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License along
19 with this program.  If not, see <http://www.gnu.org/licenses/>.
20 
21 Uses code from ctk
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <math.h>
29 #include "transport-widget.h"
30 
31 
32 #define RECT_WIDTH 130.0f
33 #define Y 7.0f
34 #define X 70.0f
35 #define INNER_RADIUS 12.5
36 #define MIDDLE_RADIUS 13.0f
37 #define OUTER_RADIUS  14.5f
38 #define CIRCLE_RADIUS 21.0f
39 #define PREV_WIDTH  25.0f
40 #define PREV_HEIGHT 17.0f
41 #define NEXT_WIDTH  25.0f //PREV_WIDTH
42 #define NEXT_HEIGHT 17.0f //PREV_HEIGHT
43 #define TRI_WIDTH  11.0f
44 #define TRI_HEIGHT 13.0f
45 #define TRI_OFFSET  6.0f
46 #define PREV_X 68.0f
47 #define PREV_Y 13.0f
48 #define NEXT_X 146.0f
49 #define NEXT_Y 13.0f //prev_y
50 #define PAUSE_WIDTH 21.0f
51 #define PAUSE_HEIGHT 27.0f
52 #define BAR_WIDTH 4.5f
53 #define BAR_HEIGHT 24.0f
54 #define BAR_OFFSET 10.0f
55 #define PAUSE_X 111.0f
56 #define PAUSE_Y 7.0f
57 #define PLAY_WIDTH 28.0f
58 #define PLAY_HEIGHT 29.0f
59 #define PLAY_PADDING 5.0f
60 #define INNER_START_SHADE 0.98
61 #define INNER_END_SHADE 0.98
62 #define MIDDLE_START_SHADE 1.0
63 #define MIDDLE_END_SHADE 1.0
64 #define OUTER_START_SHADE 0.75
65 #define OUTER_END_SHADE 1.3
66 #define SHADOW_BUTTON_SHADE 0.8
67 #define OUTER_PLAY_START_SHADE 0.7
68 #define OUTER_PLAY_END_SHADE 1.38
69 #define BUTTON_START_SHADE 1.1
70 #define BUTTON_END_SHADE 0.9
71 #define BUTTON_SHADOW_SHADE 0.8
72 #define INNER_COMPRESSED_START_SHADE 1.0
73 #define INNER_COMPRESSED_END_SHADE 1.0
74 
75 typedef struct _TransportWidgetPrivate TransportWidgetPrivate;
76 
77 struct _TransportWidgetPrivate
78 {
79   TransportAction     current_command;
80   TransportAction     key_event;
81   TransportAction     motion_event;
82   TransportState      current_state;
83   GHashTable*         command_coordinates;
84   DbusmenuMenuitem*   twin_item;
85   gboolean            has_focus;
86   gint                hold_timer;
87   gint                skip_frequency;
88 };
89 
90 static GList *transport_widget_list = NULL;
91 static GtkStyleContext *spinner_style_context = NULL;
92 static GtkWidgetPath *spinner_widget_path = NULL;
93 
94 // TODO refactor the UI handlers, consolidate functionality between key press /release
95 // and button press / release.
96 #define TRANSPORT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRANSPORT_WIDGET_TYPE, TransportWidgetPrivate))
97 
98 /* Gobject boiler plate */
99 static void transport_widget_class_init (TransportWidgetClass *klass);
100 static void transport_widget_init       (TransportWidget *self);
101 static void transport_widget_dispose    (GObject *object);
102 static void transport_widget_finalize   (GObject *object);
103 G_DEFINE_TYPE (TransportWidget, transport_widget, GTK_TYPE_MENU_ITEM);
104 
105 /* essentials */
106 static void transport_widget_set_twin_item ( TransportWidget* self,
107                                              DbusmenuMenuitem* twin_item);
108 static gboolean draw (GtkWidget* button, cairo_t *cr);
109 
110 /* UI and dbusmenu callbacks */
111 static gboolean transport_widget_button_press_event   (GtkWidget      *menuitem,
112                                                       GdkEventButton  *event);
113 static gboolean transport_widget_button_release_event   (GtkWidget      *menuitem,
114                                                       GdkEventButton  *event);
115 static gboolean transport_widget_motion_notify_event    (GtkWidget      *menuitem,
116                                                       GdkEventMotion *event);
117 static gboolean transport_widget_leave_notify_event    (GtkWidget      *menuitem,
118                                                       GdkEventCrossing *event);
119 static void transport_widget_property_update ( DbusmenuMenuitem* item,
120                                                gchar * property,
121                                                GVariant * value,
122                                                gpointer userdata );
123 static void transport_widget_menu_hidden ( GtkWidget        *menu,
124                                            TransportWidget *transport);
125 static void transport_widget_notify ( GObject *item,
126                                       GParamSpec       *pspec,
127                                       gpointer          user_data );
128 static TransportAction transport_widget_determine_button_event ( TransportWidget* button,
129                                                                       GdkEventButton* event);
130 static TransportAction transport_widget_determine_motion_event ( TransportWidget* button,
131                                                                       GdkEventMotion* event);
132 static void transport_widget_react_to_button_release ( TransportWidget* button,
133                                                        TransportAction command);
134 static void transport_widget_toggle_play_pause ( TransportWidget* button,
135                                                  TransportState update);
136 static void transport_widget_select (GtkWidget* menu, gpointer Userdata);
137 static void transport_widget_deselect (GtkWidget* menu, gpointer Userdata);
138 static TransportAction transport_widget_collision_detection (gint x, gint y);
139 static void transport_widget_start_timing (TransportWidget* widget);
140 static gboolean transport_widget_trigger_seek (gpointer userdata);
141 static gboolean transport_widget_seek (gpointer userdata);
142 
143 
144 /// Init functions //////////////////////////////////////////////////////////
145 static void
transport_widget_class_init(TransportWidgetClass * klass)146 transport_widget_class_init (TransportWidgetClass *klass)
147 {
148   GObjectClass  *gobject_class = G_OBJECT_CLASS (klass);
149   GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
150 
151   g_type_class_add_private (klass, sizeof (TransportWidgetPrivate));
152 
153   widget_class->button_press_event = transport_widget_button_press_event;
154   widget_class->button_release_event = transport_widget_button_release_event;
155   widget_class->motion_notify_event = transport_widget_motion_notify_event;
156   widget_class->leave_notify_event = transport_widget_leave_notify_event;
157   widget_class->draw = draw;
158 
159   gobject_class->dispose = transport_widget_dispose;
160   gobject_class->finalize = transport_widget_finalize;
161 }
162 
163 static void
transport_widget_init(TransportWidget * self)164 transport_widget_init (TransportWidget *self)
165 {
166   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self);
167   if (transport_widget_list == NULL){
168     /* append the object to the static linked list. */
169     transport_widget_list = g_list_append (transport_widget_list, self);
170 
171     /* create widget path */
172     spinner_widget_path = gtk_widget_path_new();
173 
174     gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU);
175     gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU_ITEM);
176     gint pos = gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_SPINNER);
177     gtk_widget_path_iter_set_name (spinner_widget_path, pos, "IndicatorSoundSpinner");
178 
179     /* create style context and append path */
180     spinner_style_context = gtk_style_context_new();
181 
182     gtk_style_context_set_path (spinner_style_context, spinner_widget_path);
183     gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENU);
184     gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENUITEM);
185     gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_SPINNER);
186   }
187   priv->current_command = TRANSPORT_ACTION_NO_ACTION;
188   priv->current_state = TRANSPORT_STATE_PAUSED;
189   priv->key_event = TRANSPORT_ACTION_NO_ACTION;
190   priv->motion_event = TRANSPORT_ACTION_NO_ACTION;
191   priv->has_focus = FALSE;
192   priv->hold_timer = 0;
193   priv->skip_frequency = 0;
194   priv->command_coordinates =  g_hash_table_new_full(g_direct_hash,
195                                                       g_direct_equal,
196                                                       NULL,
197                                                       (GDestroyNotify)g_list_free);
198   GList* previous_list = NULL;
199   previous_list = g_list_insert(previous_list, GINT_TO_POINTER(15), 0);
200   previous_list = g_list_insert(previous_list, GINT_TO_POINTER(5), 1);
201   previous_list = g_list_insert(previous_list, GINT_TO_POINTER(60), 2);
202   previous_list = g_list_insert(previous_list, GINT_TO_POINTER(34), 3);
203   g_hash_table_insert(priv->command_coordinates,
204                       GINT_TO_POINTER(TRANSPORT_ACTION_PREVIOUS),
205                       previous_list);
206 
207   GList* play_list = NULL;
208   play_list = g_list_insert(play_list, GINT_TO_POINTER(58), 0);
209   play_list = g_list_insert(play_list, GINT_TO_POINTER(0), 1);
210   play_list = g_list_insert(play_list, GINT_TO_POINTER(50), 2);
211   play_list = g_list_insert(play_list, GINT_TO_POINTER(43), 3);
212 
213   g_hash_table_insert(priv->command_coordinates,
214                       GINT_TO_POINTER(TRANSPORT_ACTION_PLAY_PAUSE),
215                       play_list);
216 
217   GList* next_list = NULL;
218   next_list = g_list_insert(next_list, GINT_TO_POINTER(100), 0);
219   next_list = g_list_insert(next_list, GINT_TO_POINTER(5), 1);
220   next_list = g_list_insert(next_list, GINT_TO_POINTER(60), 2);
221   next_list = g_list_insert(next_list, GINT_TO_POINTER(34), 3);
222 
223   g_hash_table_insert(priv->command_coordinates,
224                       GINT_TO_POINTER(TRANSPORT_ACTION_NEXT),
225                       next_list);
226   gtk_widget_set_size_request(GTK_WIDGET(self), 200, 43);
227   g_signal_connect (G_OBJECT(self),
228                     "notify",
229                     G_CALLBACK (transport_widget_notify),
230                     NULL);
231   g_signal_connect (G_OBJECT(self),
232                     "select",
233                     G_CALLBACK (transport_widget_select),
234                     NULL);
235   g_signal_connect (G_OBJECT(self),
236                     "deselect",
237                     G_CALLBACK (transport_widget_deselect),
238                     NULL);
239   gtk_widget_realize ( GTK_WIDGET (self) );
240 
241 }
242 
243 static void
transport_widget_dispose(GObject * object)244 transport_widget_dispose (GObject *object)
245 {
246   transport_widget_list = g_list_remove (transport_widget_list, object);
247 
248   if (transport_widget_list == NULL){
249     if (spinner_widget_path != NULL){
250       gtk_widget_path_free (spinner_widget_path);
251       spinner_widget_path = NULL;
252     }
253 
254     if (spinner_style_context != NULL){
255       g_object_unref (spinner_style_context);
256       spinner_style_context = NULL;
257     }
258   }
259 
260   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(object);
261   if (priv->command_coordinates != NULL) {
262     g_hash_table_destroy (priv->command_coordinates);
263     priv->command_coordinates = NULL;
264   }
265 
266   G_OBJECT_CLASS (transport_widget_parent_class)->dispose (object);
267 }
268 
269 static void
transport_widget_finalize(GObject * object)270 transport_widget_finalize (GObject *object)
271 {
272 
273 
274   G_OBJECT_CLASS (transport_widget_parent_class)->finalize (object);
275 }
276 
277 gboolean
transport_widget_is_selected(TransportWidget * widget)278 transport_widget_is_selected ( TransportWidget* widget )
279 {
280   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(widget);
281   return priv->has_focus;
282 }
283 
284 static void
transport_widget_toggle_play_pause(TransportWidget * button,TransportState update)285 transport_widget_toggle_play_pause(TransportWidget* button,
286                                    TransportState update)
287 {
288   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
289   priv->current_state = update;
290   gtk_widget_queue_draw (GTK_WIDGET(button));
291 }
292 
293 static void
transport_widget_notify(GObject * item,GParamSpec * pspec,gpointer user_data)294 transport_widget_notify ( GObject *item,
295                           GParamSpec       *pspec,
296                           gpointer          user_data )
297 {
298   if (g_strcmp0 (pspec->name, "parent")){
299     GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (item));
300     if (parent){
301       g_signal_connect ( parent, "hide",
302                          G_CALLBACK (transport_widget_menu_hidden),
303                          item );
304     }
305   }
306 }
307 
308 static void
transport_widget_menu_hidden(GtkWidget * menu,TransportWidget * transport)309 transport_widget_menu_hidden ( GtkWidget        *menu,
310                                TransportWidget *transport)
311 {
312   g_return_if_fail(IS_TRANSPORT_WIDGET(transport));
313   transport_widget_react_to_button_release(transport, TRANSPORT_ACTION_NO_ACTION);
314 }
315 
316 static gboolean
transport_widget_motion_notify_event(GtkWidget * menuitem,GdkEventMotion * event)317 transport_widget_motion_notify_event (GtkWidget *menuitem,
318                                       GdkEventMotion *event)
319 {
320   //g_debug("transport_widget_motion_notify_event()");
321 
322   g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
323   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
324   TransportAction result = transport_widget_determine_motion_event ( TRANSPORT_WIDGET(menuitem),
325                                                                             event);
326   priv->motion_event = result;
327   gtk_widget_queue_draw (menuitem);
328   if (priv->hold_timer != 0){
329     g_source_remove (priv->hold_timer);
330     priv->hold_timer = 0;
331   }
332   if(priv->skip_frequency != 0){
333     g_source_remove (priv->skip_frequency);
334     priv->skip_frequency = 0;
335   }
336   return TRUE;
337 }
338 
339 static gboolean
transport_widget_leave_notify_event(GtkWidget * menuitem,GdkEventCrossing * event)340 transport_widget_leave_notify_event (GtkWidget *menuitem,
341                                      GdkEventCrossing *event)
342 {
343   //g_debug("transport_widget_leave_notify_event()");
344 
345   g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
346   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
347 
348   priv->motion_event = TRANSPORT_ACTION_NO_ACTION;
349   priv->current_command = TRANSPORT_ACTION_NO_ACTION;
350   gtk_widget_queue_draw (GTK_WIDGET(menuitem));
351 
352   return TRUE;
353 }
354 
355 static gboolean
transport_widget_button_press_event(GtkWidget * menuitem,GdkEventButton * event)356 transport_widget_button_press_event (GtkWidget *menuitem,
357                                      GdkEventButton *event)
358 {
359   //g_debug("transport_widget_button_press_event()");
360 
361   g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
362   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
363   TransportAction result = transport_widget_determine_button_event ( TRANSPORT_WIDGET(menuitem),
364                                                                             event);
365   if(result != TRANSPORT_ACTION_NO_ACTION){
366     priv->current_command = result;
367     gtk_widget_queue_draw (GTK_WIDGET(menuitem));
368     if (priv->current_command == TRANSPORT_ACTION_PREVIOUS ||
369         priv->current_command == TRANSPORT_ACTION_NEXT){
370       transport_widget_start_timing (TRANSPORT_WIDGET(menuitem));
371     }
372   }
373   return TRUE;
374 }
375 /**
376  * TODO rename or merge
377  * @param widget
378  */
379 static void
transport_widget_start_timing(TransportWidget * widget)380 transport_widget_start_timing (TransportWidget* widget)
381 {
382   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (widget);
383   if (priv->hold_timer == 0){
384     priv->hold_timer = g_timeout_add (800,
385                                       transport_widget_trigger_seek,
386                                       widget);
387   }
388 }
389 
390 static gboolean
transport_widget_trigger_seek(gpointer userdata)391 transport_widget_trigger_seek (gpointer userdata)
392 {
393   g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE );
394   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata));
395   if (priv->skip_frequency == 0){
396     priv->skip_frequency = g_timeout_add (100,
397                                           transport_widget_seek,
398                                           userdata);
399   }
400   priv->hold_timer = 0;
401   return FALSE;
402 }
403 
404 /**
405  * This will be called repeatedly until a key/button release is received
406  * @param userdata
407  * @return
408  */
409 static gboolean
transport_widget_seek(gpointer userdata)410 transport_widget_seek (gpointer userdata)
411 {
412   g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE );
413   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata));
414   GVariant* new_transport_state;
415   if(priv->current_command ==  TRANSPORT_ACTION_NEXT){
416     //g_debug ("we should be skipping forward");
417     new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_FORWIND);
418 
419     dbusmenu_menuitem_handle_event ( priv->twin_item,
420                                      "Transport state change",
421                                      new_transport_state,
422                                      0 );
423 
424   }
425   else if(priv->current_command ==  TRANSPORT_ACTION_PREVIOUS){
426     //g_debug ("we should be skipping back");
427     new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_REWIND);
428 
429     dbusmenu_menuitem_handle_event ( priv->twin_item,
430                                      "Transport state change",
431                                      new_transport_state,
432                                      0 );
433   }
434 
435   return TRUE;
436 }
437 
438 static gboolean
transport_widget_button_release_event(GtkWidget * menuitem,GdkEventButton * event)439 transport_widget_button_release_event (GtkWidget *menuitem,
440                                        GdkEventButton *event)
441 {
442   g_return_val_if_fail(IS_TRANSPORT_WIDGET(menuitem), FALSE);
443   TransportWidget* transport = TRANSPORT_WIDGET(menuitem);
444   TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
445   TransportAction result = transport_widget_determine_button_event ( transport,
446                                                                           event );
447   if (result != TRANSPORT_ACTION_NO_ACTION &&
448       priv->current_command == result &&
449       priv->skip_frequency == 0){
450     GVariant* new_transport_state = g_variant_new_int32 ((int)result);
451     dbusmenu_menuitem_handle_event ( priv->twin_item,
452                                      "Transport state change",
453                                      new_transport_state,
454                                      0 );
455   }
456   transport_widget_react_to_button_release ( transport,
457                                              result );
458   return TRUE;
459 }
460 
461 static void
transport_widget_select(GtkWidget * item,gpointer Userdata)462 transport_widget_select (GtkWidget* item, gpointer Userdata)
463 {
464   TransportWidget* transport = TRANSPORT_WIDGET(item);
465   TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
466   priv->has_focus = TRUE;
467 }
468 
469 static void
transport_widget_deselect(GtkWidget * item,gpointer Userdata)470 transport_widget_deselect (GtkWidget* item, gpointer Userdata)
471 {
472   TransportWidget* transport = TRANSPORT_WIDGET(item);
473   TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
474   priv->has_focus = FALSE;
475 }
476 
477 void
transport_widget_react_to_key_press_event(TransportWidget * transport,TransportAction transport_event)478 transport_widget_react_to_key_press_event ( TransportWidget* transport,
479                                             TransportAction transport_event )
480 {
481   if(transport_event != TRANSPORT_ACTION_NO_ACTION){
482     TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
483     priv->current_command = transport_event;
484     priv->key_event = transport_event;
485     gtk_widget_realize ( GTK_WIDGET(transport) );
486     gtk_widget_queue_draw (GTK_WIDGET(transport) );
487     if (priv->current_command == TRANSPORT_ACTION_PREVIOUS ||
488         priv->current_command == TRANSPORT_ACTION_NEXT){
489       transport_widget_start_timing (transport);
490     }
491   }
492 }
493 
494 void
transport_widget_react_to_key_release_event(TransportWidget * transport,TransportAction transport_event)495 transport_widget_react_to_key_release_event ( TransportWidget* transport,
496                                               TransportAction transport_event )
497 {
498   if(transport_event != TRANSPORT_ACTION_NO_ACTION){
499     TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
500     GVariant* new_transport_event = g_variant_new_int32((int)transport_event);
501     if (priv->skip_frequency == 0){
502       dbusmenu_menuitem_handle_event ( priv->twin_item,
503                                        "Transport state change",
504                                        new_transport_event,
505                                        0 );
506     }
507   }
508   transport_widget_react_to_button_release ( transport,
509                                              transport_event );
510 }
511 
512 void
transport_widget_focus_update(TransportWidget * transport,gboolean focus)513 transport_widget_focus_update ( TransportWidget* transport, gboolean focus )
514 {
515   TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
516   priv->has_focus = focus;
517 }
518 
519 static TransportAction
transport_widget_determine_button_event(TransportWidget * button,GdkEventButton * event)520 transport_widget_determine_button_event( TransportWidget* button,
521                                          GdkEventButton* event )
522 {
523   return transport_widget_collision_detection (event->x, event->y);
524 }
525 
526 static TransportAction
transport_widget_determine_motion_event(TransportWidget * button,GdkEventMotion * event)527 transport_widget_determine_motion_event( TransportWidget* button,
528                                          GdkEventMotion* event )
529 {
530   return transport_widget_collision_detection (event->x, event->y);
531 }
532 
533 static TransportAction
transport_widget_collision_detection(gint x,gint y)534 transport_widget_collision_detection ( gint x,
535                                        gint y )
536 {
537   TransportAction event = TRANSPORT_ACTION_NO_ACTION;
538 
539   if (x > 57 && x < 102
540       && y > 12 && y < 40){
541     event = TRANSPORT_ACTION_PREVIOUS;
542   }
543   else if (x > 101 && x < 143
544            && y > 5 && y < 47){
545     event = TRANSPORT_ACTION_PLAY_PAUSE;
546   }
547   else if (x > 142 && x < 187
548            && y > 12 && y < 40){
549     event = TRANSPORT_ACTION_NEXT;
550   }
551   return event;
552 }
553 
554 static void
transport_widget_react_to_button_release(TransportWidget * button,TransportAction command)555 transport_widget_react_to_button_release ( TransportWidget* button,
556                                            TransportAction command )
557 {
558   g_return_if_fail(IS_TRANSPORT_WIDGET(button));
559   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
560 
561   priv->current_command = TRANSPORT_ACTION_NO_ACTION;
562   priv->key_event = TRANSPORT_ACTION_NO_ACTION;
563 
564   gtk_widget_queue_draw (GTK_WIDGET(button));
565   if (priv->hold_timer != 0){
566     g_source_remove (priv->hold_timer);
567     priv->hold_timer = 0;
568   }
569   if(priv->skip_frequency != 0){
570     g_source_remove (priv->skip_frequency);
571     priv->skip_frequency = 0;
572   }
573 }
574 
575 /// internal helper functions //////////////////////////////////////////////////
576 
577 static void
draw_gradient(cairo_t * cr,double x,double y,double w,double r,double * rgba_start,double * rgba_end)578 draw_gradient (cairo_t* cr,
579                double   x,
580                double   y,
581                double   w,
582                double   r,
583                double*  rgba_start,
584                double*  rgba_end)
585 {
586   cairo_pattern_t* pattern = NULL;
587 
588   cairo_move_to (cr, x, y);
589   cairo_line_to (cr, x + w - 2.0f * r, y);
590   cairo_arc (cr,
591        x + w - 2.0f * r,
592        y + r,
593        r,
594        -90.0f * G_PI / 180.0f,
595        90.0f * G_PI / 180.0f);
596   cairo_line_to (cr, x, y + 2.0f * r);
597   cairo_arc (cr,
598        x,
599        y + r,
600        r,
601        90.0f * G_PI / 180.0f,
602        270.0f * G_PI / 180.0f);
603   cairo_close_path (cr);
604 
605   pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r);
606   cairo_pattern_add_color_stop_rgba (pattern,
607                                      0.0f,
608                                      rgba_start[0],
609                                      rgba_start[1],
610                                      rgba_start[2],
611                                      rgba_start[3]);
612   cairo_pattern_add_color_stop_rgba (pattern,
613                                      1.0f,
614                                      rgba_end[0],
615                                      rgba_end[1],
616                                      rgba_end[2],
617                                      rgba_end[3]);
618   cairo_set_source (cr, pattern);
619   cairo_fill (cr);
620   cairo_pattern_destroy (pattern);
621 }
622 
623 static void
draw_circle(cairo_t * cr,double x,double y,double r,double * rgba_start,double * rgba_end)624 draw_circle (cairo_t* cr,
625        double   x,
626        double   y,
627        double   r,
628        double*  rgba_start,
629        double*  rgba_end)
630 {
631   cairo_pattern_t* pattern = NULL;
632 
633   cairo_move_to (cr, x, y);
634   cairo_arc (cr,
635        x + r,
636        y + r,
637        r,
638        0.0f * G_PI / 180.0f,
639        360.0f * G_PI / 180.0f);
640 
641   pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r);
642   cairo_pattern_add_color_stop_rgba (pattern,
643                                      0.0f,
644                                      rgba_start[0],
645                                      rgba_start[1],
646                                      rgba_start[2],
647                                      rgba_start[3]);
648   cairo_pattern_add_color_stop_rgba (pattern,
649                                      1.0f,
650                                      rgba_end[0],
651                                      rgba_end[1],
652                                      rgba_end[2],
653                                      rgba_end[3]);
654   cairo_set_source (cr, pattern);
655   cairo_fill (cr);
656   cairo_pattern_destroy (pattern);
657 }
658 
659 static void
_setup(cairo_t ** cr,cairo_surface_t ** surf,gint width,gint height)660 _setup (cairo_t**         cr,
661   cairo_surface_t** surf,
662   gint              width,
663   gint              height)
664 {
665   if (!cr || !surf)
666     return;
667 
668   *surf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
669   *cr = cairo_create (*surf);
670   cairo_scale (*cr, 1.0f, 1.0f);
671   cairo_set_operator (*cr, CAIRO_OPERATOR_CLEAR);
672   cairo_paint (*cr);
673   cairo_set_operator (*cr, CAIRO_OPERATOR_OVER);
674 }
675 
676 static void
_mask_prev(cairo_t * cr,double x,double y,double tri_width,double tri_height,double tri_offset)677 _mask_prev (cairo_t* cr,
678       double   x,
679       double   y,
680       double   tri_width,
681       double   tri_height,
682       double   tri_offset)
683 {
684   if (!cr)
685     return;
686 
687   cairo_move_to (cr, x,             y + tri_height / 2.0f);
688   cairo_line_to (cr, x + tri_width, y);
689   cairo_line_to (cr, x + tri_width, y + tri_height);
690   x += tri_offset;
691   cairo_move_to (cr, x,             y + tri_height / 2.0f);
692   cairo_line_to (cr, x + tri_width, y);
693   cairo_line_to (cr, x + tri_width, y + tri_height);
694   x -= tri_offset;
695   cairo_rectangle (cr, x, y, 2.5f, tri_height);
696   cairo_close_path (cr);
697 }
698 
699 static void
_mask_next(cairo_t * cr,double x,double y,double tri_width,double tri_height,double tri_offset)700 _mask_next (cairo_t* cr,
701       double   x,
702       double   y,
703       double   tri_width,
704       double   tri_height,
705       double   tri_offset)
706 {
707   if (!cr)
708     return;
709 
710   cairo_move_to (cr, x,             y);
711   cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
712   cairo_line_to (cr, x,             y + tri_height);
713   x += tri_offset;
714   cairo_move_to (cr, x,             y);
715   cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
716   cairo_line_to (cr, x,             y + tri_height);
717   x -= tri_offset;
718   x += 2.0f * tri_width - tri_offset - 1.0f;
719   cairo_rectangle (cr, x, y, 2.5f, tri_height);
720 
721   cairo_close_path (cr);
722 }
723 
724 static void
_mask_pause(cairo_t * cr,double x,double y,double bar_width,double bar_height,double bar_offset)725 _mask_pause (cairo_t* cr,
726        double   x,
727        double   y,
728        double   bar_width,
729        double   bar_height,
730        double   bar_offset)
731 {
732   if (!cr)
733     return;
734 
735   cairo_set_line_width (cr, bar_width);
736   cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
737 
738   x += bar_width;
739   y += bar_width;
740   cairo_move_to (cr, x,              y);
741   cairo_line_to (cr, x,              y + bar_height);
742   cairo_move_to (cr, x + bar_offset, y);
743   cairo_line_to (cr, x + bar_offset, y + bar_height);
744 
745 }
746 
747 static void
_mask_play(cairo_t * cr,double x,double y,double tri_width,double tri_height)748 _mask_play (cairo_t* cr,
749        double   x,
750        double   y,
751        double   tri_width,
752        double   tri_height)
753 {
754   if (!cr)
755     return;
756 
757   cairo_move_to (cr, x,             y);
758   cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
759   cairo_line_to (cr, x,             y + tri_height);
760   cairo_close_path (cr);
761 
762 }
763 
764 static void
_fill(cairo_t * cr,double x_start,double y_start,double x_end,double y_end,double * rgba_start,double * rgba_end,gboolean stroke)765 _fill (cairo_t* cr,
766        double   x_start,
767        double   y_start,
768        double   x_end,
769        double   y_end,
770        double*  rgba_start,
771        double*  rgba_end,
772        gboolean stroke)
773 {
774   cairo_pattern_t* pattern = NULL;
775 
776   if (!cr || !rgba_start || !rgba_end)
777     return;
778 
779   pattern = cairo_pattern_create_linear (x_start, y_start, x_end, y_end);
780   cairo_pattern_add_color_stop_rgba (pattern,
781              0.0f,
782              rgba_start[0],
783              rgba_start[1],
784              rgba_start[2],
785              rgba_start[3]);
786   cairo_pattern_add_color_stop_rgba (pattern,
787              1.0f,
788              rgba_end[0],
789              rgba_end[1],
790              rgba_end[2],
791              rgba_end[3]);
792   cairo_set_source (cr, pattern);
793   if (stroke)
794     cairo_stroke (cr);
795   else
796     cairo_fill (cr);
797   cairo_pattern_destroy (pattern);
798 }
799 
800 static void
_finalize(cairo_t * cr,cairo_t ** cr_surf,cairo_surface_t ** surf,double x,double y)801 _finalize (cairo_t*          cr,
802      cairo_t**         cr_surf,
803      cairo_surface_t** surf,
804      double            x,
805      double            y)
806 {
807   if (!cr || !cr_surf || !surf)
808     return;
809 
810   cairo_set_source_surface (cr, *surf, x, y);
811   cairo_paint (cr);
812   cairo_surface_destroy (*surf);
813   cairo_destroy (*cr_surf);
814 }
815 
816 static void
_finalize_repaint(cairo_t * cr,cairo_t ** cr_surf,cairo_surface_t ** surf,double x,double y,int repaints)817 _finalize_repaint (cairo_t*          cr,
818              cairo_t**         cr_surf,
819              cairo_surface_t** surf,
820              double            x,
821              double            y,
822              int               repaints)
823 {
824   if (!cr || !cr_surf || !surf)
825     return;
826 
827   while (repaints > 0)
828   {
829     cairo_set_source_surface (cr, *surf, x, y);
830     cairo_paint (cr);
831     repaints--;
832   }
833 
834   cairo_surface_destroy (*surf);
835   cairo_destroy (*cr_surf);
836 }
837 
838 static void
_color_rgb_to_hls(gdouble * r,gdouble * g,gdouble * b)839 _color_rgb_to_hls (gdouble *r,
840                    gdouble *g,
841                    gdouble *b)
842 {
843   gdouble min;
844   gdouble max;
845   gdouble red;
846   gdouble green;
847   gdouble blue;
848   gdouble h = 0;
849   gdouble l;
850   gdouble s;
851   gdouble delta;
852 
853   red = *r;
854   green = *g;
855   blue = *b;
856 
857   if (red > green)
858   {
859     if (red > blue)
860       max = red;
861     else
862       max = blue;
863 
864     if (green < blue)
865       min = green;
866     else
867     min = blue;
868   }
869   else
870   {
871     if (green > blue)
872       max = green;
873     else
874     max = blue;
875 
876     if (red < blue)
877       min = red;
878     else
879       min = blue;
880   }
881   l = (max+min)/2;
882   if (fabs (max-min) < 0.0001)
883   {
884     h = 0;
885     s = 0;
886   }
887   else
888   {
889     if (l <= 0.5)
890     s = (max-min)/(max+min);
891     else
892     s = (max-min)/(2-max-min);
893 
894     delta = (max -min) != 0 ? (max -min) : 1;
895 
896     if(delta == 0)
897       delta = 1;
898     if (red == max)
899       h = (green-blue)/delta;
900     else if (green == max)
901       h = 2+(blue-red)/delta;
902     else if (blue == max)
903       h = 4+(red-green)/delta;
904 
905     h *= 60;
906     if (h < 0.0)
907       h += 360;
908   }
909 
910   *r = h;
911   *g = l;
912   *b = s;
913 }
914 
915 static void
_color_hls_to_rgb(gdouble * h,gdouble * l,gdouble * s)916 _color_hls_to_rgb (gdouble *h,
917                    gdouble *l,
918                    gdouble *s)
919 {
920   gdouble hue;
921   gdouble lightness;
922   gdouble saturation;
923   gdouble m1, m2;
924   gdouble r, g, b;
925 
926   lightness = *l;
927   saturation = *s;
928 
929   if (lightness <= 0.5)
930     m2 = lightness*(1+saturation);
931   else
932     m2 = lightness+saturation-lightness*saturation;
933 
934   m1 = 2*lightness-m2;
935 
936   if (saturation == 0)
937   {
938     *h = lightness;
939     *l = lightness;
940     *s = lightness;
941   }
942   else
943   {
944     hue = *h+120;
945     while (hue > 360)
946       hue -= 360;
947     while (hue < 0)
948       hue += 360;
949 
950     if (hue < 60)
951       r = m1+(m2-m1)*hue/60;
952     else if (hue < 180)
953       r = m2;
954     else if (hue < 240)
955       r = m1+(m2-m1)*(240-hue)/60;
956     else
957       r = m1;
958 
959     hue = *h;
960     while (hue > 360)
961       hue -= 360;
962     while (hue < 0)
963       hue += 360;
964 
965     if (hue < 60)
966       g = m1+(m2-m1)*hue/60;
967     else if (hue < 180)
968       g = m2;
969     else if (hue < 240)
970       g = m1+(m2-m1)*(240-hue)/60;
971     else
972       g = m1;
973 
974     hue = *h-120;
975     while (hue > 360)
976       hue -= 360;
977     while (hue < 0)
978       hue += 360;
979 
980     if (hue < 60)
981       b = m1+(m2-m1)*hue/60;
982     else if (hue < 180)
983       b = m2;
984     else if (hue < 240)
985       b = m1+(m2-m1)*(240-hue)/60;
986     else
987       b = m1;
988 
989     *h = r;
990     *l = g;
991     *s = b;
992   }
993 }
994 
995 void
_color_shade(const CairoColorRGB * a,float k,CairoColorRGB * b)996 _color_shade (const CairoColorRGB *a, float k, CairoColorRGB *b)
997 {
998   double red;
999   double green;
1000   double blue;
1001 
1002   red   = a->r;
1003   green = a->g;
1004   blue  = a->b;
1005 
1006   if (k == 1.0)
1007   {
1008     b->r = red;
1009     b->g = green;
1010     b->b = blue;
1011     return;
1012   }
1013 
1014   _color_rgb_to_hls (&red, &green, &blue);
1015 
1016   green *= k;
1017   if (green > 1.0)
1018     green = 1.0;
1019   else if (green < 0.0)
1020     green = 0.0;
1021 
1022   blue *= k;
1023   if (blue > 1.0)
1024     blue = 1.0;
1025   else if (blue < 0.0)
1026     blue = 0.0;
1027 
1028   _color_hls_to_rgb (&red, &green, &blue);
1029 
1030   b->r = red;
1031   b->g = green;
1032   b->b = blue;
1033 }
1034 
1035 static inline void
_blurinner(guchar * pixel,gint * zR,gint * zG,gint * zB,gint * zA,gint alpha,gint aprec,gint zprec)1036 _blurinner (guchar* pixel,
1037       gint*   zR,
1038       gint*   zG,
1039       gint*   zB,
1040       gint*   zA,
1041       gint    alpha,
1042       gint    aprec,
1043       gint    zprec)
1044 {
1045   gint R;
1046   gint G;
1047   gint B;
1048   guchar A;
1049 
1050   R = *pixel;
1051   G = *(pixel + 1);
1052   B = *(pixel + 2);
1053   A = *(pixel + 3);
1054 
1055   *zR += (alpha * ((R << zprec) - *zR)) >> aprec;
1056   *zG += (alpha * ((G << zprec) - *zG)) >> aprec;
1057   *zB += (alpha * ((B << zprec) - *zB)) >> aprec;
1058   *zA += (alpha * ((A << zprec) - *zA)) >> aprec;
1059 
1060   *pixel       = *zR >> zprec;
1061   *(pixel + 1) = *zG >> zprec;
1062   *(pixel + 2) = *zB >> zprec;
1063   *(pixel + 3) = *zA >> zprec;
1064 }
1065 
1066 static inline void
_blurrow(guchar * pixels,gint width,gint height,gint channels,gint line,gint alpha,gint aprec,gint zprec)1067 _blurrow (guchar* pixels,
1068     gint    width,
1069     gint    height,
1070     gint    channels,
1071     gint    line,
1072     gint    alpha,
1073     gint    aprec,
1074     gint    zprec)
1075 {
1076   gint    zR;
1077   gint    zG;
1078   gint    zB;
1079   gint    zA;
1080   gint    index;
1081   guchar* scanline;
1082 
1083   scanline = &(pixels[line * width * channels]);
1084 
1085   zR = *scanline << zprec;
1086   zG = *(scanline + 1) << zprec;
1087   zB = *(scanline + 2) << zprec;
1088   zA = *(scanline + 3) << zprec;
1089 
1090   for (index = 0; index < width; index ++)
1091     _blurinner (&scanline[index * channels],
1092           &zR,
1093           &zG,
1094           &zB,
1095           &zA,
1096           alpha,
1097           aprec,
1098           zprec);
1099 
1100   for (index = width - 2; index >= 0; index--)
1101     _blurinner (&scanline[index * channels],
1102           &zR,
1103           &zG,
1104           &zB,
1105           &zA,
1106           alpha,
1107           aprec,
1108           zprec);
1109 }
1110 
1111 static inline void
_blurcol(guchar * pixels,gint width,gint height,gint channels,gint x,gint alpha,gint aprec,gint zprec)1112 _blurcol (guchar* pixels,
1113     gint    width,
1114     gint    height,
1115     gint    channels,
1116     gint    x,
1117     gint    alpha,
1118     gint    aprec,
1119     gint    zprec)
1120 {
1121   gint zR;
1122   gint zG;
1123   gint zB;
1124   gint zA;
1125   gint index;
1126   guchar* ptr;
1127 
1128   ptr = pixels;
1129 
1130   ptr += x * channels;
1131 
1132   zR = *((guchar*) ptr    ) << zprec;
1133   zG = *((guchar*) ptr + 1) << zprec;
1134   zB = *((guchar*) ptr + 2) << zprec;
1135   zA = *((guchar*) ptr + 3) << zprec;
1136 
1137   for (index = width; index < (height - 1) * width; index += width)
1138     _blurinner ((guchar*) &ptr[index * channels],
1139           &zR,
1140           &zG,
1141           &zB,
1142           &zA,
1143           alpha,
1144           aprec,
1145           zprec);
1146 
1147   for (index = (height - 2) * width; index >= 0; index -= width)
1148     _blurinner ((guchar*) &ptr[index * channels],
1149           &zR,
1150           &zG,
1151           &zB,
1152           &zA,
1153           alpha,
1154           aprec,
1155           zprec);
1156 }
1157 
1158 void
_expblur(guchar * pixels,gint width,gint height,gint channels,gint radius,gint aprec,gint zprec)1159 _expblur (guchar* pixels,
1160     gint    width,
1161     gint    height,
1162     gint    channels,
1163     gint    radius,
1164     gint    aprec,
1165     gint    zprec)
1166 {
1167   gint alpha;
1168   gint row = 0;
1169   gint col = 0;
1170 
1171   if (radius < 1)
1172     return;
1173 
1174   // calculate the alpha such that 90% of
1175   // the kernel is within the radius.
1176   // (Kernel extends to infinity)
1177   alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
1178 
1179   for (; row < height; row++)
1180     _blurrow (pixels,
1181         width,
1182         height,
1183         channels,
1184         row,
1185         alpha,
1186         aprec,
1187         zprec);
1188 
1189   for(; col < width; col++)
1190     _blurcol (pixels,
1191         width,
1192         height,
1193         channels,
1194         col,
1195         alpha,
1196         aprec,
1197         zprec);
1198 
1199   return;
1200 }
1201 
1202 void
_surface_blur(cairo_surface_t * surface,guint radius)1203 _surface_blur (cairo_surface_t* surface,
1204                guint            radius)
1205 {
1206   guchar*        pixels;
1207   guint          width;
1208   guint          height;
1209   cairo_format_t format;
1210 
1211   // before we mess with the surface execute any pending drawing
1212   cairo_surface_flush (surface);
1213 
1214   pixels = cairo_image_surface_get_data (surface);
1215   width  = cairo_image_surface_get_width (surface);
1216   height = cairo_image_surface_get_height (surface);
1217   format = cairo_image_surface_get_format (surface);
1218 
1219   switch (format)
1220   {
1221     case CAIRO_FORMAT_ARGB32:
1222       _expblur (pixels, width, height, 4, radius, 16, 7);
1223     break;
1224 
1225     case CAIRO_FORMAT_RGB24:
1226       _expblur (pixels, width, height, 3, radius, 16, 7);
1227     break;
1228 
1229     case CAIRO_FORMAT_A8:
1230       _expblur (pixels, width, height, 1, radius, 16, 7);
1231     break;
1232 
1233     default :
1234       // do nothing
1235     break;
1236   }
1237 
1238   // inform cairo we altered the surfaces contents
1239   cairo_surface_mark_dirty (surface);
1240 }
1241 
1242 static gboolean
draw(GtkWidget * button,cairo_t * cr)1243 draw (GtkWidget* button, cairo_t *cr)
1244 {
1245   g_return_val_if_fail(IS_TRANSPORT_WIDGET(button), FALSE);
1246   g_return_val_if_fail(cr != NULL, FALSE);
1247   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
1248 
1249   //g_debug("transport-widget draw()");
1250 
1251   cairo_surface_t*  surf = NULL;
1252   cairo_t*       cr_surf = NULL;
1253 
1254 
1255   gtk_style_context_add_class (gtk_widget_get_style_context (button),
1256                                GTK_STYLE_CLASS_MENU);
1257   CairoColorRGB bg_color, fg_color, bg_selected, bg_prelight;
1258   CairoColorRGB color_middle[2], color_middle_prelight[2], color_outer[2], color_outer_prelight[2],
1259                 color_play_outer[2], color_play_outer_prelight[2],
1260                 color_button[4], color_button_shadow, color_inner[2], color_inner_compressed[2];
1261 
1262   GtkStyleContext *style = gtk_widget_get_style_context (button);
1263   gtk_style_context_get_background_color (style, 0, (GdkRGBA*)&bg_color);
1264   gtk_style_context_get_background_color (style, GTK_STATE_PRELIGHT, (GdkRGBA*)&bg_prelight);
1265   gtk_style_context_get_background_color (style, GTK_STATE_SELECTED, (GdkRGBA*)&bg_selected);
1266   gtk_style_context_get_color (style, 0, (GdkRGBA*)&fg_color);
1267 
1268   _color_shade (&bg_color,    MIDDLE_START_SHADE, &color_middle[0]);
1269   _color_shade (&bg_color,    MIDDLE_END_SHADE, &color_middle[1]);
1270   _color_shade (&bg_prelight, MIDDLE_START_SHADE, &color_middle_prelight[0]);
1271   _color_shade (&bg_prelight, MIDDLE_END_SHADE, &color_middle_prelight[1]);
1272   _color_shade (&bg_color,    OUTER_START_SHADE, &color_outer[0]);
1273   _color_shade (&bg_color,    OUTER_END_SHADE, &color_outer[1]);
1274   _color_shade (&bg_prelight, OUTER_START_SHADE, &color_outer_prelight[0]);
1275   _color_shade (&bg_prelight, OUTER_END_SHADE, &color_outer_prelight[1]);
1276   _color_shade (&bg_color,    OUTER_PLAY_START_SHADE, &color_play_outer[0]);
1277   _color_shade (&bg_color,    OUTER_PLAY_END_SHADE, &color_play_outer[1]);
1278   _color_shade (&bg_prelight, OUTER_PLAY_START_SHADE, &color_play_outer_prelight[0]);
1279   _color_shade (&bg_prelight, OUTER_PLAY_END_SHADE, &color_play_outer_prelight[1]);
1280   _color_shade (&bg_color, INNER_START_SHADE, &color_inner[0]);
1281   _color_shade (&bg_color, INNER_END_SHADE, &color_inner[1]);
1282   _color_shade (&fg_color, BUTTON_START_SHADE, &color_button[0]);
1283   _color_shade (&fg_color, BUTTON_END_SHADE, &color_button[1]);
1284   _color_shade (&bg_color, BUTTON_SHADOW_SHADE, &color_button[2]);
1285   _color_shade (&bg_color, SHADOW_BUTTON_SHADE, &color_button_shadow);
1286   _color_shade (&bg_selected, 1.0, &color_button[3]);
1287   _color_shade (&bg_color, INNER_COMPRESSED_START_SHADE, &color_inner_compressed[0]);
1288   _color_shade (&bg_color, INNER_COMPRESSED_END_SHADE, &color_inner_compressed[1]);
1289 
1290   double MIDDLE_END[]   = {color_middle[0].r, color_middle[0].g, color_middle[0].b, 1.0f};
1291   double MIDDLE_START[] = {color_middle[1].r, color_middle[1].g, color_middle[1].b, 1.0f};
1292   double MIDDLE_END_PRELIGHT[]   = {color_middle_prelight[0].r, color_middle_prelight[0].g, color_middle_prelight[0].b, 1.0f};
1293   double MIDDLE_START_PRELIGHT[] = {color_middle_prelight[1].r, color_middle_prelight[1].g, color_middle_prelight[1].b, 1.0f};
1294   double OUTER_END[]   = {color_outer[0].r, color_outer[0].g, color_outer[0].b, 1.0f};
1295   double OUTER_START[] = {color_outer[1].r, color_outer[1].g, color_outer[1].b, 1.0f};
1296   double OUTER_END_PRELIGHT[]   = {color_outer_prelight[0].r, color_outer_prelight[0].g, color_outer_prelight[0].b, 1.0f};
1297   double OUTER_START_PRELIGHT[] = {color_outer_prelight[1].r, color_outer_prelight[1].g, color_outer_prelight[1].b, 1.0f};
1298   double SHADOW_BUTTON[] = {color_button_shadow.r, color_button_shadow.g, color_button_shadow.b, 0.3f};
1299   double OUTER_PLAY_END[] = {color_play_outer[0].r, color_play_outer[0].g, color_play_outer[0].b, 1.0f};
1300   double OUTER_PLAY_START[] = {color_play_outer[1].r, color_play_outer[1].g, color_play_outer[1].b, 1.0f};
1301   double OUTER_PLAY_END_PRELIGHT[] = {color_play_outer_prelight[0].r, color_play_outer_prelight[0].g, color_play_outer_prelight[0].b, 1.0f};
1302   double OUTER_PLAY_START_PRELIGHT[] = {color_play_outer_prelight[1].r, color_play_outer_prelight[1].g, color_play_outer_prelight[1].b, 1.0f};
1303   double BUTTON_END[] = {color_button[0].r, color_button[0].g, color_button[0].b, 1.0f};
1304   double BUTTON_START[] = {color_button[1].r, color_button[1].g, color_button[1].b, 1.0f};
1305   double BUTTON_SHADOW[] = {color_button[2].r, color_button[2].g, color_button[2].b, 0.75f};
1306   double BUTTON_SHADOW_FOCUS[] = {color_button[3].r, color_button[3].g, color_button[3].b, 1.0f};
1307   double INNER_COMPRESSED_END[] = {color_inner_compressed[1].r, color_inner_compressed[1].g, color_inner_compressed[1].b, 1.0f};
1308   double INNER_COMPRESSED_START[] = {color_inner_compressed[0].r, color_inner_compressed[0].g, color_inner_compressed[0].b, 1.0f};
1309 
1310 
1311   draw_gradient (cr,
1312                  X,
1313                  Y,
1314                  RECT_WIDTH,
1315                  OUTER_RADIUS,
1316                  OUTER_START,
1317                  OUTER_END);
1318 
1319   draw_gradient (cr,
1320                  X,
1321                  Y + 1,
1322                  RECT_WIDTH - 2,
1323                  MIDDLE_RADIUS,
1324                  MIDDLE_START,
1325                  MIDDLE_END);
1326 
1327   draw_gradient (cr,
1328                  X,
1329                  Y + 2,
1330                  RECT_WIDTH - 4,
1331                  MIDDLE_RADIUS,
1332                  MIDDLE_START,
1333                  MIDDLE_END);
1334 
1335   //prev/next button
1336   if(priv->current_command == TRANSPORT_ACTION_PREVIOUS)
1337   {
1338     draw_gradient (cr,
1339                    X,
1340                    Y,
1341                    RECT_WIDTH/2,
1342                    OUTER_RADIUS,
1343                    OUTER_END,
1344                    OUTER_START);
1345 
1346     draw_gradient (cr,
1347                    X,
1348                    Y + 1,
1349                    RECT_WIDTH/2,
1350                    MIDDLE_RADIUS,
1351                    INNER_COMPRESSED_START,
1352                    INNER_COMPRESSED_END);
1353 
1354     draw_gradient (cr,
1355                    X,
1356                    Y + 2,
1357                    RECT_WIDTH/2,
1358                    MIDDLE_RADIUS,
1359                    INNER_COMPRESSED_START,
1360                    INNER_COMPRESSED_END);
1361   }
1362   else if(priv->current_command == TRANSPORT_ACTION_NEXT)
1363   {
1364     draw_gradient (cr,
1365                    RECT_WIDTH / 2 + X,
1366                    Y,
1367                    RECT_WIDTH/2,
1368                    OUTER_RADIUS,
1369                    OUTER_END,
1370                    OUTER_START);
1371 
1372     draw_gradient (cr,
1373                    RECT_WIDTH / 2 + X,
1374                    Y + 1,
1375                    (RECT_WIDTH - 4.5)/2,
1376                    MIDDLE_RADIUS,
1377                    INNER_COMPRESSED_START,
1378                    INNER_COMPRESSED_END);
1379 
1380     draw_gradient (cr,
1381                    RECT_WIDTH / 2 + X,
1382                    Y + 2,
1383                    (RECT_WIDTH - 7)/2,
1384                    MIDDLE_RADIUS,
1385                    INNER_COMPRESSED_START,
1386                    INNER_COMPRESSED_END);
1387   }
1388   else if (priv->motion_event == TRANSPORT_ACTION_PREVIOUS)
1389   {
1390     draw_gradient (cr,
1391                    X,
1392                    Y,
1393                    RECT_WIDTH/2,
1394                    OUTER_RADIUS,
1395                    OUTER_START_PRELIGHT,
1396                    OUTER_END_PRELIGHT);
1397 
1398     draw_gradient (cr,
1399                    X,
1400                    Y + 1,
1401                    RECT_WIDTH/2,
1402                    MIDDLE_RADIUS,
1403                    MIDDLE_START_PRELIGHT,
1404                    MIDDLE_END_PRELIGHT);
1405 
1406     draw_gradient (cr,
1407                    X,
1408                    Y + 2,
1409                    RECT_WIDTH/2,
1410                    MIDDLE_RADIUS,
1411                    MIDDLE_START_PRELIGHT,
1412                    MIDDLE_END_PRELIGHT);
1413   }
1414   else if (priv->motion_event == TRANSPORT_ACTION_NEXT)
1415   {
1416     draw_gradient (cr,
1417                    RECT_WIDTH / 2 + X,
1418                    Y,
1419                    RECT_WIDTH/2,
1420                    OUTER_RADIUS,
1421                    OUTER_START_PRELIGHT,
1422                    OUTER_END_PRELIGHT);
1423 
1424     draw_gradient (cr,
1425                    RECT_WIDTH / 2 + X,
1426                    Y + 1,
1427                    (RECT_WIDTH - 4.5)/2,
1428                    MIDDLE_RADIUS,
1429                    MIDDLE_START_PRELIGHT,
1430                    MIDDLE_END_PRELIGHT);
1431 
1432     draw_gradient (cr,
1433                    RECT_WIDTH / 2 + X,
1434                    Y + 2,
1435                    (RECT_WIDTH - 7)/2,
1436                    MIDDLE_RADIUS,
1437                    MIDDLE_START_PRELIGHT,
1438                    MIDDLE_END_PRELIGHT);
1439   }
1440 
1441   // play/pause shadow
1442   if(priv->current_command != TRANSPORT_ACTION_PLAY_PAUSE)
1443   {
1444     cairo_save (cr);
1445     cairo_rectangle (cr, X, Y, RECT_WIDTH, MIDDLE_RADIUS*2);
1446     cairo_clip (cr);
1447 
1448     draw_circle (cr,
1449                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f - 1.0f,
1450                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) - 1.0f,
1451                  CIRCLE_RADIUS + 1.0f,
1452                  SHADOW_BUTTON,
1453                  SHADOW_BUTTON);
1454 
1455     cairo_restore (cr);
1456   }
1457 
1458   // play/pause button
1459   if(priv->current_command == TRANSPORT_ACTION_PLAY_PAUSE)
1460   {
1461     draw_circle (cr,
1462                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f,
1463                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) ,
1464                  CIRCLE_RADIUS,
1465                  OUTER_PLAY_END,
1466                  OUTER_PLAY_START);
1467 
1468     draw_circle (cr,
1469                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
1470                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
1471                  CIRCLE_RADIUS - 1.25,
1472                  INNER_COMPRESSED_START,
1473                  INNER_COMPRESSED_END);
1474   }
1475   else if (priv->motion_event == TRANSPORT_ACTION_PLAY_PAUSE)
1476   {
1477     /* this subtle offset is to fix alpha borders, should be removed once this draw routine will be refactored */
1478     draw_circle (cr,
1479                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 0.1,
1480                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 0.1,
1481                  CIRCLE_RADIUS - 0.1,
1482                  OUTER_PLAY_START_PRELIGHT,
1483                  OUTER_PLAY_END_PRELIGHT);
1484 
1485     draw_circle (cr,
1486                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
1487                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
1488                  CIRCLE_RADIUS - 1.25,
1489                  MIDDLE_START_PRELIGHT,
1490                  MIDDLE_END_PRELIGHT);
1491   }
1492   else
1493   {
1494     draw_circle (cr,
1495                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f,
1496                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)),
1497                  CIRCLE_RADIUS,
1498                  OUTER_PLAY_START,
1499                  OUTER_PLAY_END);
1500 
1501     draw_circle (cr,
1502                  X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
1503                  Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
1504                  CIRCLE_RADIUS - 1.25,
1505                  MIDDLE_START,
1506                  MIDDLE_END);
1507   }
1508 
1509   // draw previous-button drop-shadow
1510   if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_PREVIOUS)
1511   {
1512     _setup (&cr_surf, &surf, PREV_WIDTH+6, PREV_HEIGHT+6);
1513     _mask_prev (cr_surf,
1514                 (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1515                 (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1516                 TRI_WIDTH,
1517                 TRI_HEIGHT,
1518                 TRI_OFFSET);
1519     _fill (cr_surf,
1520            (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1521            (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1522            (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1523            (double) TRI_HEIGHT,
1524            BUTTON_SHADOW_FOCUS,
1525            BUTTON_SHADOW_FOCUS,
1526            FALSE);
1527     _surface_blur (surf, 3);
1528     _finalize_repaint (cr, &cr_surf, &surf, PREV_X, PREV_Y + 0.5f, 3);
1529   }
1530   else
1531   {
1532     _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT);
1533     _mask_prev (cr_surf,
1534                 (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1535                 (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1536                 TRI_WIDTH,
1537                 TRI_HEIGHT,
1538                 TRI_OFFSET);
1539     _fill (cr_surf,
1540            (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1541            (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1542            (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1543            (double) TRI_HEIGHT,
1544            BUTTON_SHADOW,
1545            BUTTON_SHADOW,
1546            FALSE);
1547     _surface_blur (surf, 1);
1548     _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y + 1.0f);
1549   }
1550 
1551   // draw previous-button
1552   _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT);
1553   _mask_prev (cr_surf,
1554               (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1555               (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1556               TRI_WIDTH,
1557               TRI_HEIGHT,
1558               TRI_OFFSET);
1559   _fill (cr_surf,
1560        (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1561        (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
1562        (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1563        (double) TRI_HEIGHT,
1564        BUTTON_START,
1565        BUTTON_END,
1566        FALSE);
1567   _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y);
1568 
1569   // draw next-button drop-shadow
1570   if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_NEXT)
1571   {
1572     _setup (&cr_surf, &surf, NEXT_WIDTH+6, NEXT_HEIGHT+6);
1573     _mask_next (cr_surf,
1574                 (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1575                 (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1576                 TRI_WIDTH,
1577                 TRI_HEIGHT,
1578                 TRI_OFFSET);
1579     _fill (cr_surf,
1580            (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1581            (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1582            (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1583            (double) TRI_HEIGHT,
1584            BUTTON_SHADOW_FOCUS,
1585            BUTTON_SHADOW_FOCUS,
1586            FALSE);
1587     _surface_blur (surf, 3);
1588     _finalize_repaint (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 0.5f, 3);
1589   }
1590   else
1591   {
1592     _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT);
1593     _mask_next (cr_surf,
1594                 (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1595                 (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1596                 TRI_WIDTH,
1597                 TRI_HEIGHT,
1598                 TRI_OFFSET);
1599     _fill (cr_surf,
1600            (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1601            (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1602            (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1603            (double) TRI_HEIGHT,
1604            BUTTON_SHADOW,
1605            BUTTON_SHADOW,
1606            FALSE);
1607     _surface_blur (surf, 1);
1608     _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 1.0f);
1609   }
1610 
1611   // draw next-button
1612   _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT);
1613   _mask_next (cr_surf,
1614               (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1615               (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1616               TRI_WIDTH,
1617               TRI_HEIGHT,
1618               TRI_OFFSET);
1619   _fill (cr_surf,
1620          (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1621          (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
1622          (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
1623          (double) TRI_HEIGHT,
1624          BUTTON_START,
1625          BUTTON_END,
1626          FALSE);
1627   _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y);
1628 
1629   // draw pause-button drop-shadow
1630   if(priv->current_state == TRANSPORT_STATE_PLAYING)
1631   {
1632     if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION ||
1633         priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE))
1634     {
1635       _setup (&cr_surf, &surf, PAUSE_WIDTH+6, PAUSE_HEIGHT+6);
1636       _mask_pause (cr_surf,
1637                    (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1638                    (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1639                    BAR_WIDTH,
1640                    BAR_HEIGHT - 2.0f * BAR_WIDTH,
1641                    BAR_OFFSET);
1642       _fill (cr_surf,
1643              (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1644              (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1645              (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1646              (double) BAR_HEIGHT,
1647              BUTTON_SHADOW_FOCUS,
1648              BUTTON_SHADOW_FOCUS,
1649              TRUE);
1650       _surface_blur (surf, 3);
1651       _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 0.5f, 3);
1652     }
1653     else
1654     {
1655       _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT);
1656       _mask_pause (cr_surf,
1657                    (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1658                    (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1659                    BAR_WIDTH,
1660                    BAR_HEIGHT - 2.0f * BAR_WIDTH,
1661                    BAR_OFFSET);
1662       _fill (cr_surf,
1663              (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1664              (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1665              (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1666              (double) BAR_HEIGHT,
1667              BUTTON_SHADOW,
1668              BUTTON_SHADOW,
1669              TRUE);
1670       _surface_blur (surf, 1);
1671       _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 1.0f);
1672     }
1673 
1674     // draw pause-button
1675     _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT);
1676     _mask_pause (cr_surf,
1677                  (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1678                  (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1679                  BAR_WIDTH,
1680                  BAR_HEIGHT - 2.0f * BAR_WIDTH,
1681                  BAR_OFFSET);
1682     _fill (cr_surf,
1683            (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1684            (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
1685            (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
1686            (double) BAR_HEIGHT,
1687            BUTTON_START,
1688            BUTTON_END,
1689            TRUE);
1690     _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y);
1691   }
1692   else if(priv->current_state == TRANSPORT_STATE_PAUSED)
1693   {
1694     if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION ||
1695         priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE))
1696     {
1697       _setup (&cr_surf, &surf, PLAY_WIDTH+6, PLAY_HEIGHT+6);
1698       _mask_play (cr_surf,
1699                   PLAY_PADDING,
1700                   PLAY_PADDING,
1701                   PLAY_WIDTH - (2*PLAY_PADDING),
1702                   PLAY_HEIGHT - (2*PLAY_PADDING));
1703       _fill (cr_surf,
1704              PLAY_PADDING,
1705              PLAY_PADDING,
1706              PLAY_WIDTH - (2*PLAY_PADDING),
1707              PLAY_HEIGHT - (2*PLAY_PADDING),
1708              BUTTON_SHADOW_FOCUS,
1709              BUTTON_SHADOW_FOCUS,
1710              FALSE);
1711       _surface_blur (surf, 3);
1712       _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y + 0.5f, 3);
1713     }
1714     else
1715     {
1716       _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT);
1717       _mask_play (cr_surf,
1718                   PLAY_PADDING,
1719                   PLAY_PADDING,
1720                   PLAY_WIDTH - (2*PLAY_PADDING),
1721                   PLAY_HEIGHT - (2*PLAY_PADDING));
1722       _fill (cr_surf,
1723              PLAY_PADDING,
1724              PLAY_PADDING,
1725              PLAY_WIDTH - (2*PLAY_PADDING),
1726              PLAY_HEIGHT - (2*PLAY_PADDING),
1727              BUTTON_SHADOW,
1728              BUTTON_SHADOW,
1729              FALSE);
1730       _surface_blur (surf, 1);
1731       _finalize (cr, &cr_surf, &surf, PAUSE_X-0.75f, PAUSE_Y + 1.0f);
1732     }
1733 
1734     // draw play-button
1735     _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT);
1736     cairo_set_line_width (cr, 10.5);
1737     cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1738     cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
1739     _mask_play (cr_surf,
1740                 PLAY_PADDING,
1741                 PLAY_PADDING,
1742                 PLAY_WIDTH - (2*PLAY_PADDING),
1743                 PLAY_HEIGHT - (2*PLAY_PADDING));
1744     _fill (cr_surf,
1745            PLAY_PADDING,
1746            PLAY_PADDING,
1747            PLAY_WIDTH - (2*PLAY_PADDING),
1748            PLAY_HEIGHT - (2*PLAY_PADDING),
1749            BUTTON_START,
1750            BUTTON_END,
1751            FALSE);
1752     _finalize (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y);
1753   }
1754   else if(priv->current_state == TRANSPORT_STATE_LAUNCHING)
1755   {
1756     // the spinner is not aligned, why? because the play button has odd width/height numbers
1757     gtk_render_activity (spinner_style_context, cr, 106, 6, 30, 30);
1758   }
1759   return FALSE;
1760 }
1761 
1762 static void
transport_widget_set_twin_item(TransportWidget * self,DbusmenuMenuitem * twin_item)1763 transport_widget_set_twin_item(TransportWidget* self,
1764                            DbusmenuMenuitem* twin_item)
1765 {
1766     TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self);
1767     priv->twin_item = twin_item;
1768     g_signal_connect(G_OBJECT(priv->twin_item), "property-changed",
1769                               G_CALLBACK(transport_widget_property_update), self);
1770     gint initial_state = dbusmenu_menuitem_property_get_int (twin_item,
1771                                                              DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE );
1772     //g_debug("TRANSPORT WIDGET - INITIAL UPDATE = %i", initial_state);
1773     transport_widget_toggle_play_pause (self,
1774                                        (TransportState)initial_state);
1775 }
1776 
1777 /**
1778 * transport_widget_update_state()
1779 * Callback for updates from the other side of dbus
1780 **/
1781 static void
transport_widget_property_update(DbusmenuMenuitem * item,gchar * property,GVariant * value,gpointer userdata)1782 transport_widget_property_update(DbusmenuMenuitem* item, gchar* property,
1783                                  GVariant* value, gpointer userdata)
1784 {
1785   //g_debug("transport_widget_update_state - with property  %s", property);
1786   TransportWidget* bar = (TransportWidget*)userdata;
1787   g_return_if_fail(IS_TRANSPORT_WIDGET(bar));
1788   TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(bar);
1789 
1790   if(g_ascii_strcasecmp(DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE, property) == 0)
1791   {
1792     TransportState new_state = (TransportState)g_variant_get_int32(value);
1793     //g_debug("transport_widget_update_state - with value  %i", new_state);
1794     if (new_state == TRANSPORT_STATE_LAUNCHING){
1795       gtk_style_context_set_state (spinner_style_context, GTK_STATE_FLAG_ACTIVE);  // triggers the notification
1796 
1797       priv->current_state = TRANSPORT_STATE_LAUNCHING;
1798       g_debug("TransportWidget::toggle play state : %i", priv->current_state);
1799     }
1800     else{
1801       transport_widget_toggle_play_pause(bar, new_state);
1802     }
1803   }
1804 }
1805 
1806 
1807 /**
1808 * transport_widget_new:
1809 * @returns: a new #TransportWidget.
1810 **/
1811 GtkWidget*
transport_widget_new(DbusmenuMenuitem * item)1812 transport_widget_new ( DbusmenuMenuitem *item )
1813 {
1814   GtkWidget* widget = g_object_new(TRANSPORT_WIDGET_TYPE, NULL);
1815   gtk_widget_set_app_paintable (widget, TRUE);
1816   transport_widget_set_twin_item((TransportWidget*)widget, item);
1817   return widget;
1818 }
1819 
1820