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