aboutsummaryrefslogtreecommitdiff
path: root/.pc/fix-864405.patch
diff options
context:
space:
mode:
authorOmer Akram <om26er@ubuntu.com>2011-11-29 04:03:20 +0500
committerPackage Import Robot <package-import@ubuntu.com>2011-11-29 04:03:20 +0500
commit3b0159dc8cbd1f4b9ae80655d575c81a77dc3db7 (patch)
tree04954d606f368cd062dd9978096189ce1d910b5d /.pc/fix-864405.patch
parent651e17daa69dd4491bbdc3f7a574373dd91a0541 (diff)
parent140cd5542edca8d6717294cd245ea2c148c666c2 (diff)
downloadayatana-indicator-sound-3b0159dc8cbd1f4b9ae80655d575c81a77dc3db7.tar.gz
ayatana-indicator-sound-3b0159dc8cbd1f4b9ae80655d575c81a77dc3db7.tar.bz2
ayatana-indicator-sound-3b0159dc8cbd1f4b9ae80655d575c81a77dc3db7.zip
* debian/patches/fix-864405.patch:
- Clicking on left 25% of "Pause/Play" circle clicks "Previous". (LP: #864405)
Diffstat (limited to '.pc/fix-864405.patch')
-rw-r--r--.pc/fix-864405.patch/src/transport-widget.c1874
1 files changed, 1874 insertions, 0 deletions
diff --git a/.pc/fix-864405.patch/src/transport-widget.c b/.pc/fix-864405.patch/src/transport-widget.c
new file mode 100644
index 0000000..c535e00
--- /dev/null
+++ b/.pc/fix-864405.patch/src/transport-widget.c
@@ -0,0 +1,1874 @@
+/*
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+ Mirco Müller <mirco.mueller@canonical.com>
+ Andrea Cimitan <andrea.cimitan@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Uses code from ctk
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include "transport-widget.h"
+
+
+#define RECT_WIDTH 130.0f
+#define Y 7.0f
+#define X 70.0f
+#define INNER_RADIUS 12.5
+#define MIDDLE_RADIUS 13.0f
+#define OUTER_RADIUS 14.5f
+#define CIRCLE_RADIUS 21.0f
+#define PREV_WIDTH 25.0f
+#define PREV_HEIGHT 17.0f
+#define NEXT_WIDTH 25.0f //PREV_WIDTH
+#define NEXT_HEIGHT 17.0f //PREV_HEIGHT
+#define TRI_WIDTH 11.0f
+#define TRI_HEIGHT 13.0f
+#define TRI_OFFSET 6.0f
+#define PREV_X 68.0f
+#define PREV_Y 13.0f
+#define NEXT_X 146.0f
+#define NEXT_Y 13.0f //prev_y
+#define PAUSE_WIDTH 21.0f
+#define PAUSE_HEIGHT 27.0f
+#define BAR_WIDTH 4.5f
+#define BAR_HEIGHT 24.0f
+#define BAR_OFFSET 10.0f
+#define PAUSE_X 111.0f
+#define PAUSE_Y 7.0f
+#define PLAY_WIDTH 28.0f
+#define PLAY_HEIGHT 29.0f
+#define PLAY_PADDING 5.0f
+#define INNER_START_SHADE 0.98
+#define INNER_END_SHADE 0.98
+#define MIDDLE_START_SHADE 1.0
+#define MIDDLE_END_SHADE 1.0
+#define OUTER_START_SHADE 0.75
+#define OUTER_END_SHADE 1.3
+#define SHADOW_BUTTON_SHADE 0.8
+#define OUTER_PLAY_START_SHADE 0.7
+#define OUTER_PLAY_END_SHADE 1.38
+#define BUTTON_START_SHADE 1.1
+#define BUTTON_END_SHADE 0.9
+#define BUTTON_SHADOW_SHADE 0.8
+#define INNER_COMPRESSED_START_SHADE 1.0
+#define INNER_COMPRESSED_END_SHADE 1.0
+
+typedef struct _TransportWidgetPrivate TransportWidgetPrivate;
+
+struct _TransportWidgetPrivate
+{
+ TransportAction current_command;
+ TransportAction key_event;
+ TransportAction motion_event;
+ TransportState current_state;
+ GHashTable* command_coordinates;
+ DbusmenuMenuitem* twin_item;
+ gboolean has_focus;
+ gint hold_timer;
+ gint skip_frequency;
+};
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+static GList *transport_widget_list = NULL;
+static GtkStyleContext *spinner_style_context = NULL;
+static GtkWidgetPath *spinner_widget_path = NULL;
+#endif
+
+// TODO refactor the UI handlers, consolidate functionality between key press /release
+// and button press / release.
+#define TRANSPORT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRANSPORT_WIDGET_TYPE, TransportWidgetPrivate))
+
+/* Gobject boiler plate */
+static void transport_widget_class_init (TransportWidgetClass *klass);
+static void transport_widget_init (TransportWidget *self);
+static void transport_widget_dispose (GObject *object);
+static void transport_widget_finalize (GObject *object);
+G_DEFINE_TYPE (TransportWidget, transport_widget, GTK_TYPE_MENU_ITEM);
+
+/* essentials */
+static void transport_widget_set_twin_item ( TransportWidget* self,
+ DbusmenuMenuitem* twin_item);
+#if ! GTK_CHECK_VERSION(3, 0, 0)
+static gboolean transport_widget_expose ( GtkWidget *button, GdkEventExpose *event);
+#endif
+static gboolean draw (GtkWidget* button, cairo_t *cr);
+
+/* UI and dbusmenu callbacks */
+static gboolean transport_widget_button_press_event (GtkWidget *menuitem,
+ GdkEventButton *event);
+static gboolean transport_widget_button_release_event (GtkWidget *menuitem,
+ GdkEventButton *event);
+static gboolean transport_widget_motion_notify_event (GtkWidget *menuitem,
+ GdkEventMotion *event);
+static gboolean transport_widget_leave_notify_event (GtkWidget *menuitem,
+ GdkEventCrossing *event);
+static void transport_widget_property_update ( DbusmenuMenuitem* item,
+ gchar * property,
+ GVariant * value,
+ gpointer userdata );
+static void transport_widget_menu_hidden ( GtkWidget *menu,
+ TransportWidget *transport);
+static void transport_widget_notify ( GObject *item,
+ GParamSpec *pspec,
+ gpointer user_data );
+static TransportAction transport_widget_determine_button_event ( TransportWidget* button,
+ GdkEventButton* event);
+static TransportAction transport_widget_determine_motion_event ( TransportWidget* button,
+ GdkEventMotion* event);
+static void transport_widget_react_to_button_release ( TransportWidget* button,
+ TransportAction command);
+static void transport_widget_toggle_play_pause ( TransportWidget* button,
+ TransportState update);
+static void transport_widget_select (GtkWidget* menu, gpointer Userdata);
+static void transport_widget_deselect (GtkWidget* menu, gpointer Userdata);
+static TransportAction transport_widget_collision_detection (gint x, gint y);
+static void transport_widget_start_timing (TransportWidget* widget);
+static gboolean transport_widget_trigger_seek (gpointer userdata);
+static gboolean transport_widget_seek (gpointer userdata);
+
+
+/// Init functions //////////////////////////////////////////////////////////
+static void
+transport_widget_class_init (TransportWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (TransportWidgetPrivate));
+
+ widget_class->button_press_event = transport_widget_button_press_event;
+ widget_class->button_release_event = transport_widget_button_release_event;
+ widget_class->motion_notify_event = transport_widget_motion_notify_event;
+ widget_class->leave_notify_event = transport_widget_leave_notify_event;
+#if GTK_CHECK_VERSION(3, 0, 0)
+ widget_class->draw = draw;
+#else
+ widget_class->expose_event = transport_widget_expose;
+#endif
+
+ gobject_class->dispose = transport_widget_dispose;
+ gobject_class->finalize = transport_widget_finalize;
+}
+
+static void
+transport_widget_init (TransportWidget *self)
+{
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self);
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ if (transport_widget_list == NULL){
+ /* append the object to the static linked list. */
+ transport_widget_list = g_list_append (transport_widget_list, self);
+
+ /* create widget path */
+ spinner_widget_path = gtk_widget_path_new();
+
+ gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU);
+ gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU_ITEM);
+ gint pos = gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_SPINNER);
+ gtk_widget_path_iter_set_name (spinner_widget_path, pos, "IndicatorSoundSpinner");
+
+ /* create style context and append path */
+ spinner_style_context = gtk_style_context_new();
+
+ gtk_style_context_set_path (spinner_style_context, spinner_widget_path);
+ gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENU);
+ gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENUITEM);
+ gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_SPINNER);
+ }
+ #endif
+ priv->current_command = TRANSPORT_ACTION_NO_ACTION;
+ priv->current_state = TRANSPORT_STATE_PAUSED;
+ priv->key_event = TRANSPORT_ACTION_NO_ACTION;
+ priv->motion_event = TRANSPORT_ACTION_NO_ACTION;
+ priv->has_focus = FALSE;
+ priv->hold_timer = 0;
+ priv->skip_frequency = 0;
+ priv->command_coordinates = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)g_list_free);
+ GList* previous_list = NULL;
+ previous_list = g_list_insert(previous_list, GINT_TO_POINTER(15), 0);
+ previous_list = g_list_insert(previous_list, GINT_TO_POINTER(5), 1);
+ previous_list = g_list_insert(previous_list, GINT_TO_POINTER(60), 2);
+ previous_list = g_list_insert(previous_list, GINT_TO_POINTER(34), 3);
+ g_hash_table_insert(priv->command_coordinates,
+ GINT_TO_POINTER(TRANSPORT_ACTION_PREVIOUS),
+ previous_list);
+
+ GList* play_list = NULL;
+ play_list = g_list_insert(play_list, GINT_TO_POINTER(58), 0);
+ play_list = g_list_insert(play_list, GINT_TO_POINTER(0), 1);
+ play_list = g_list_insert(play_list, GINT_TO_POINTER(50), 2);
+ play_list = g_list_insert(play_list, GINT_TO_POINTER(43), 3);
+
+ g_hash_table_insert(priv->command_coordinates,
+ GINT_TO_POINTER(TRANSPORT_ACTION_PLAY_PAUSE),
+ play_list);
+
+ GList* next_list = NULL;
+ next_list = g_list_insert(next_list, GINT_TO_POINTER(100), 0);
+ next_list = g_list_insert(next_list, GINT_TO_POINTER(5), 1);
+ next_list = g_list_insert(next_list, GINT_TO_POINTER(60), 2);
+ next_list = g_list_insert(next_list, GINT_TO_POINTER(34), 3);
+
+ g_hash_table_insert(priv->command_coordinates,
+ GINT_TO_POINTER(TRANSPORT_ACTION_NEXT),
+ next_list);
+ gtk_widget_set_size_request(GTK_WIDGET(self), 200, 43);
+ g_signal_connect (G_OBJECT(self),
+ "notify",
+ G_CALLBACK (transport_widget_notify),
+ NULL);
+ g_signal_connect (G_OBJECT(self),
+ "select",
+ G_CALLBACK (transport_widget_select),
+ NULL);
+ g_signal_connect (G_OBJECT(self),
+ "deselect",
+ G_CALLBACK (transport_widget_deselect),
+ NULL);
+ gtk_widget_realize ( GTK_WIDGET (self) );
+
+}
+
+static void
+transport_widget_dispose (GObject *object)
+{
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ transport_widget_list = g_list_remove (transport_widget_list, object);
+
+ if (transport_widget_list == NULL){
+ if (spinner_widget_path != NULL){
+ gtk_widget_path_free (spinner_widget_path);
+ spinner_widget_path = NULL;
+ }
+
+ if (spinner_style_context != NULL){
+ g_object_unref (spinner_style_context);
+ spinner_style_context = NULL;
+ }
+ }
+ #endif
+ G_OBJECT_CLASS (transport_widget_parent_class)->dispose (object);
+}
+
+static void
+transport_widget_finalize (GObject *object)
+{
+
+
+ G_OBJECT_CLASS (transport_widget_parent_class)->finalize (object);
+}
+
+#if ! GTK_CHECK_VERSION(3, 0, 0)
+static gboolean
+transport_widget_expose (GtkWidget *button, GdkEventExpose *event)
+{
+ cairo_t *cr;
+ cr = gdk_cairo_create (gtk_widget_get_window (button));
+
+ cairo_rectangle (cr,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ cairo_clip(cr);
+ draw (button, cr);
+
+ cairo_destroy (cr);
+ return FALSE;
+}
+#endif
+
+gboolean
+transport_widget_is_selected ( TransportWidget* widget )
+{
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(widget);
+ return priv->has_focus;
+}
+
+static void
+transport_widget_toggle_play_pause(TransportWidget* button,
+ TransportState update)
+{
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
+ priv->current_state = update;
+ gtk_widget_queue_draw (GTK_WIDGET(button));
+}
+
+static void
+transport_widget_notify ( GObject *item,
+ GParamSpec *pspec,
+ gpointer user_data )
+{
+ if (g_strcmp0 (pspec->name, "parent")){
+ GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (item));
+ if (parent){
+ g_signal_connect ( parent, "hide",
+ G_CALLBACK (transport_widget_menu_hidden),
+ item );
+ }
+ }
+}
+
+static void
+transport_widget_menu_hidden ( GtkWidget *menu,
+ TransportWidget *transport)
+{
+ g_return_if_fail(IS_TRANSPORT_WIDGET(transport));
+ transport_widget_react_to_button_release(transport, TRANSPORT_ACTION_NO_ACTION);
+}
+
+static gboolean
+transport_widget_motion_notify_event (GtkWidget *menuitem,
+ GdkEventMotion *event)
+{
+ //g_debug("transport_widget_motion_notify_event()");
+
+ g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
+ TransportAction result = transport_widget_determine_motion_event ( TRANSPORT_WIDGET(menuitem),
+ event);
+ priv->motion_event = result;
+ gtk_widget_queue_draw (menuitem);
+ if (priv->hold_timer != 0){
+ g_source_remove (priv->hold_timer);
+ priv->hold_timer = 0;
+ }
+ if(priv->skip_frequency != 0){
+ g_source_remove (priv->skip_frequency);
+ priv->skip_frequency = 0;
+ }
+ return TRUE;
+}
+
+static gboolean
+transport_widget_leave_notify_event (GtkWidget *menuitem,
+ GdkEventCrossing *event)
+{
+ //g_debug("transport_widget_leave_notify_event()");
+
+ g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
+
+ priv->motion_event = TRANSPORT_ACTION_NO_ACTION;
+ priv->current_command = TRANSPORT_ACTION_NO_ACTION;
+ gtk_widget_queue_draw (GTK_WIDGET(menuitem));
+
+ return TRUE;
+}
+
+static gboolean
+transport_widget_button_press_event (GtkWidget *menuitem,
+ GdkEventButton *event)
+{
+ //g_debug("transport_widget_button_press_event()");
+
+ g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE );
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) );
+ TransportAction result = transport_widget_determine_button_event ( TRANSPORT_WIDGET(menuitem),
+ event);
+ if(result != TRANSPORT_ACTION_NO_ACTION){
+ priv->current_command = result;
+ gtk_widget_queue_draw (GTK_WIDGET(menuitem));
+ if (priv->current_command == TRANSPORT_ACTION_PREVIOUS ||
+ priv->current_command == TRANSPORT_ACTION_NEXT){
+ transport_widget_start_timing (TRANSPORT_WIDGET(menuitem));
+ }
+ }
+ return TRUE;
+}
+/**
+ * TODO rename or merge
+ * @param widget
+ */
+static void
+transport_widget_start_timing (TransportWidget* widget)
+{
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (widget);
+ if (priv->hold_timer == 0){
+ priv->hold_timer = g_timeout_add (800,
+ transport_widget_trigger_seek,
+ widget);
+ }
+}
+
+static gboolean
+transport_widget_trigger_seek (gpointer userdata)
+{
+ g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE );
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata));
+ if (priv->skip_frequency == 0){
+ priv->skip_frequency = g_timeout_add (100,
+ transport_widget_seek,
+ userdata);
+ }
+ priv->hold_timer = 0;
+ return FALSE;
+}
+
+/**
+ * This will be called repeatedly until a key/button release is received
+ * @param userdata
+ * @return
+ */
+static gboolean
+transport_widget_seek (gpointer userdata)
+{
+ g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE );
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata));
+ GVariant* new_transport_state;
+ if(priv->current_command == TRANSPORT_ACTION_NEXT){
+ //g_debug ("we should be skipping forward");
+ new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_FORWIND);
+
+ dbusmenu_menuitem_handle_event ( priv->twin_item,
+ "Transport state change",
+ new_transport_state,
+ 0 );
+
+ }
+ else if(priv->current_command == TRANSPORT_ACTION_PREVIOUS){
+ //g_debug ("we should be skipping back");
+ new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_REWIND);
+
+ dbusmenu_menuitem_handle_event ( priv->twin_item,
+ "Transport state change",
+ new_transport_state,
+ 0 );
+ }
+
+ return TRUE;
+}
+
+static gboolean
+transport_widget_button_release_event (GtkWidget *menuitem,
+ GdkEventButton *event)
+{
+ g_return_val_if_fail(IS_TRANSPORT_WIDGET(menuitem), FALSE);
+ TransportWidget* transport = TRANSPORT_WIDGET(menuitem);
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ TransportAction result = transport_widget_determine_button_event ( transport,
+ event );
+ if (result != TRANSPORT_ACTION_NO_ACTION &&
+ priv->current_command == result &&
+ priv->skip_frequency == 0){
+ GVariant* new_transport_state = g_variant_new_int32 ((int)result);
+ dbusmenu_menuitem_handle_event ( priv->twin_item,
+ "Transport state change",
+ new_transport_state,
+ 0 );
+ }
+ transport_widget_react_to_button_release ( transport,
+ result );
+ return TRUE;
+}
+
+static void
+transport_widget_select (GtkWidget* item, gpointer Userdata)
+{
+ TransportWidget* transport = TRANSPORT_WIDGET(item);
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ priv->has_focus = TRUE;
+}
+
+static void
+transport_widget_deselect (GtkWidget* item, gpointer Userdata)
+{
+ TransportWidget* transport = TRANSPORT_WIDGET(item);
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ priv->has_focus = FALSE;
+}
+
+void
+transport_widget_react_to_key_press_event ( TransportWidget* transport,
+ TransportAction transport_event )
+{
+ if(transport_event != TRANSPORT_ACTION_NO_ACTION){
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ priv->current_command = transport_event;
+ priv->key_event = transport_event;
+ gtk_widget_realize ( GTK_WIDGET(transport) );
+ gtk_widget_queue_draw (GTK_WIDGET(transport) );
+ if (priv->current_command == TRANSPORT_ACTION_PREVIOUS ||
+ priv->current_command == TRANSPORT_ACTION_NEXT){
+ transport_widget_start_timing (transport);
+ }
+ }
+}
+
+void
+transport_widget_react_to_key_release_event ( TransportWidget* transport,
+ TransportAction transport_event )
+{
+ if(transport_event != TRANSPORT_ACTION_NO_ACTION){
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ GVariant* new_transport_event = g_variant_new_int32((int)transport_event);
+ if (priv->skip_frequency == 0){
+ dbusmenu_menuitem_handle_event ( priv->twin_item,
+ "Transport state change",
+ new_transport_event,
+ 0 );
+ }
+ }
+ transport_widget_react_to_button_release ( transport,
+ transport_event );
+}
+
+void
+transport_widget_focus_update ( TransportWidget* transport, gboolean focus )
+{
+ TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport );
+ priv->has_focus = focus;
+}
+
+static TransportAction
+transport_widget_determine_button_event( TransportWidget* button,
+ GdkEventButton* event )
+{
+ return transport_widget_collision_detection (event->x, event->y);
+}
+
+static TransportAction
+transport_widget_determine_motion_event( TransportWidget* button,
+ GdkEventMotion* event )
+{
+ return transport_widget_collision_detection (event->x, event->y);
+}
+
+static TransportAction
+transport_widget_collision_detection ( gint x,
+ gint y )
+{
+ TransportAction event = TRANSPORT_ACTION_NO_ACTION;
+
+ if (x > 67 && x < 112
+ && y > 12 && y < 40){
+ event = TRANSPORT_ACTION_PREVIOUS;
+ }
+ else if (x > 111 && x < 153
+ && y > 5 && y < 47){
+ event = TRANSPORT_ACTION_PLAY_PAUSE;
+ }
+ else if (x > 152 && x < 197
+ && y > 12 && y < 40){
+ event = TRANSPORT_ACTION_NEXT;
+ }
+ return event;
+}
+
+static void
+transport_widget_react_to_button_release ( TransportWidget* button,
+ TransportAction command )
+{
+ g_return_if_fail(IS_TRANSPORT_WIDGET(button));
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
+
+ priv->current_command = TRANSPORT_ACTION_NO_ACTION;
+ priv->key_event = TRANSPORT_ACTION_NO_ACTION;
+
+ gtk_widget_queue_draw (GTK_WIDGET(button));
+ if (priv->hold_timer != 0){
+ g_source_remove (priv->hold_timer);
+ priv->hold_timer = 0;
+ }
+ if(priv->skip_frequency != 0){
+ g_source_remove (priv->skip_frequency);
+ priv->skip_frequency = 0;
+ }
+}
+
+/// internal helper functions //////////////////////////////////////////////////
+
+static void
+draw_gradient (cairo_t* cr,
+ double x,
+ double y,
+ double w,
+ double r,
+ double* rgba_start,
+ double* rgba_end)
+{
+ cairo_pattern_t* pattern = NULL;
+
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + w - 2.0f * r, y);
+ cairo_arc (cr,
+ x + w - 2.0f * r,
+ y + r,
+ r,
+ -90.0f * G_PI / 180.0f,
+ 90.0f * G_PI / 180.0f);
+ cairo_line_to (cr, x, y + 2.0f * r);
+ cairo_arc (cr,
+ x,
+ y + r,
+ r,
+ 90.0f * G_PI / 180.0f,
+ 270.0f * G_PI / 180.0f);
+ cairo_close_path (cr);
+
+ pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 0.0f,
+ rgba_start[0],
+ rgba_start[1],
+ rgba_start[2],
+ rgba_start[3]);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 1.0f,
+ rgba_end[0],
+ rgba_end[1],
+ rgba_end[2],
+ rgba_end[3]);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+draw_circle (cairo_t* cr,
+ double x,
+ double y,
+ double r,
+ double* rgba_start,
+ double* rgba_end)
+{
+ cairo_pattern_t* pattern = NULL;
+
+ cairo_move_to (cr, x, y);
+ cairo_arc (cr,
+ x + r,
+ y + r,
+ r,
+ 0.0f * G_PI / 180.0f,
+ 360.0f * G_PI / 180.0f);
+
+ pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 0.0f,
+ rgba_start[0],
+ rgba_start[1],
+ rgba_start[2],
+ rgba_start[3]);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 1.0f,
+ rgba_end[0],
+ rgba_end[1],
+ rgba_end[2],
+ rgba_end[3]);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+_setup (cairo_t** cr,
+ cairo_surface_t** surf,
+ gint width,
+ gint height)
+{
+ if (!cr || !surf)
+ return;
+
+ *surf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ *cr = cairo_create (*surf);
+ cairo_scale (*cr, 1.0f, 1.0f);
+ cairo_set_operator (*cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (*cr);
+ cairo_set_operator (*cr, CAIRO_OPERATOR_OVER);
+}
+
+static void
+_mask_prev (cairo_t* cr,
+ double x,
+ double y,
+ double tri_width,
+ double tri_height,
+ double tri_offset)
+{
+ if (!cr)
+ return;
+
+ cairo_move_to (cr, x, y + tri_height / 2.0f);
+ cairo_line_to (cr, x + tri_width, y);
+ cairo_line_to (cr, x + tri_width, y + tri_height);
+ x += tri_offset;
+ cairo_move_to (cr, x, y + tri_height / 2.0f);
+ cairo_line_to (cr, x + tri_width, y);
+ cairo_line_to (cr, x + tri_width, y + tri_height);
+ x -= tri_offset;
+ cairo_rectangle (cr, x, y, 2.5f, tri_height);
+ cairo_close_path (cr);
+}
+
+static void
+_mask_next (cairo_t* cr,
+ double x,
+ double y,
+ double tri_width,
+ double tri_height,
+ double tri_offset)
+{
+ if (!cr)
+ return;
+
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
+ cairo_line_to (cr, x, y + tri_height);
+ x += tri_offset;
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
+ cairo_line_to (cr, x, y + tri_height);
+ x -= tri_offset;
+ x += 2.0f * tri_width - tri_offset - 1.0f;
+ cairo_rectangle (cr, x, y, 2.5f, tri_height);
+
+ cairo_close_path (cr);
+}
+
+static void
+_mask_pause (cairo_t* cr,
+ double x,
+ double y,
+ double bar_width,
+ double bar_height,
+ double bar_offset)
+{
+ if (!cr)
+ return;
+
+ cairo_set_line_width (cr, bar_width);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+ x += bar_width;
+ y += bar_width;
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x, y + bar_height);
+ cairo_move_to (cr, x + bar_offset, y);
+ cairo_line_to (cr, x + bar_offset, y + bar_height);
+
+}
+
+static void
+_mask_play (cairo_t* cr,
+ double x,
+ double y,
+ double tri_width,
+ double tri_height)
+{
+ if (!cr)
+ return;
+
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f);
+ cairo_line_to (cr, x, y + tri_height);
+ cairo_close_path (cr);
+
+}
+
+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)
+{
+ cairo_pattern_t* pattern = NULL;
+
+ if (!cr || !rgba_start || !rgba_end)
+ return;
+
+ pattern = cairo_pattern_create_linear (x_start, y_start, x_end, y_end);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 0.0f,
+ rgba_start[0],
+ rgba_start[1],
+ rgba_start[2],
+ rgba_start[3]);
+ cairo_pattern_add_color_stop_rgba (pattern,
+ 1.0f,
+ rgba_end[0],
+ rgba_end[1],
+ rgba_end[2],
+ rgba_end[3]);
+ cairo_set_source (cr, pattern);
+ if (stroke)
+ cairo_stroke (cr);
+ else
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+_finalize (cairo_t* cr,
+ cairo_t** cr_surf,
+ cairo_surface_t** surf,
+ double x,
+ double y)
+{
+ if (!cr || !cr_surf || !surf)
+ return;
+
+ cairo_set_source_surface (cr, *surf, x, y);
+ cairo_paint (cr);
+ cairo_surface_destroy (*surf);
+ cairo_destroy (*cr_surf);
+}
+
+static void
+_finalize_repaint (cairo_t* cr,
+ cairo_t** cr_surf,
+ cairo_surface_t** surf,
+ double x,
+ double y,
+ int repaints)
+{
+ if (!cr || !cr_surf || !surf)
+ return;
+
+ while (repaints > 0)
+ {
+ cairo_set_source_surface (cr, *surf, x, y);
+ cairo_paint (cr);
+ repaints--;
+ }
+
+ cairo_surface_destroy (*surf);
+ cairo_destroy (*cr_surf);
+}
+
+static void
+_color_rgb_to_hls (gdouble *r,
+ gdouble *g,
+ gdouble *b)
+{
+ gdouble min;
+ gdouble max;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+ gdouble h = 0;
+ gdouble l;
+ gdouble s;
+ gdouble delta;
+
+ red = *r;
+ green = *g;
+ blue = *b;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+ l = (max+min)/2;
+ if (fabs (max-min) < 0.0001)
+ {
+ h = 0;
+ s = 0;
+ }
+ else
+ {
+ if (l <= 0.5)
+ s = (max-min)/(max+min);
+ else
+ s = (max-min)/(2-max-min);
+
+ delta = (max -min) != 0 ? (max -min) : 1;
+
+ if(delta == 0)
+ delta = 1;
+ if (red == max)
+ h = (green-blue)/delta;
+ else if (green == max)
+ h = 2+(blue-red)/delta;
+ else if (blue == max)
+ h = 4+(red-green)/delta;
+
+ h *= 60;
+ if (h < 0.0)
+ h += 360;
+ }
+
+ *r = h;
+ *g = l;
+ *b = s;
+}
+
+static void
+_color_hls_to_rgb (gdouble *h,
+ gdouble *l,
+ gdouble *s)
+{
+ gdouble hue;
+ gdouble lightness;
+ gdouble saturation;
+ gdouble m1, m2;
+ gdouble r, g, b;
+
+ lightness = *l;
+ saturation = *s;
+
+ if (lightness <= 0.5)
+ m2 = lightness*(1+saturation);
+ else
+ m2 = lightness+saturation-lightness*saturation;
+
+ m1 = 2*lightness-m2;
+
+ if (saturation == 0)
+ {
+ *h = lightness;
+ *l = lightness;
+ *s = lightness;
+ }
+ else
+ {
+ hue = *h+120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ r = m1+(m2-m1)*hue/60;
+ else if (hue < 180)
+ r = m2;
+ else if (hue < 240)
+ r = m1+(m2-m1)*(240-hue)/60;
+ else
+ r = m1;
+
+ hue = *h;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ g = m1+(m2-m1)*hue/60;
+ else if (hue < 180)
+ g = m2;
+ else if (hue < 240)
+ g = m1+(m2-m1)*(240-hue)/60;
+ else
+ g = m1;
+
+ hue = *h-120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ b = m1+(m2-m1)*hue/60;
+ else if (hue < 180)
+ b = m2;
+ else if (hue < 240)
+ b = m1+(m2-m1)*(240-hue)/60;
+ else
+ b = m1;
+
+ *h = r;
+ *l = g;
+ *s = b;
+ }
+}
+
+void
+_color_shade (const CairoColorRGB *a, float k, CairoColorRGB *b)
+{
+ double red;
+ double green;
+ double blue;
+
+ red = a->r;
+ green = a->g;
+ blue = a->b;
+
+ if (k == 1.0)
+ {
+ b->r = red;
+ b->g = green;
+ b->b = blue;
+ return;
+ }
+
+ _color_rgb_to_hls (&red, &green, &blue);
+
+ green *= k;
+ if (green > 1.0)
+ green = 1.0;
+ else if (green < 0.0)
+ green = 0.0;
+
+ blue *= k;
+ if (blue > 1.0)
+ blue = 1.0;
+ else if (blue < 0.0)
+ blue = 0.0;
+
+ _color_hls_to_rgb (&red, &green, &blue);
+
+ b->r = red;
+ b->g = green;
+ b->b = blue;
+}
+
+static inline void
+_blurinner (guchar* pixel,
+ gint* zR,
+ gint* zG,
+ gint* zB,
+ gint* zA,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint R;
+ gint G;
+ gint B;
+ guchar A;
+
+ R = *pixel;
+ G = *(pixel + 1);
+ B = *(pixel + 2);
+ A = *(pixel + 3);
+
+ *zR += (alpha * ((R << zprec) - *zR)) >> aprec;
+ *zG += (alpha * ((G << zprec) - *zG)) >> aprec;
+ *zB += (alpha * ((B << zprec) - *zB)) >> aprec;
+ *zA += (alpha * ((A << zprec) - *zA)) >> aprec;
+
+ *pixel = *zR >> zprec;
+ *(pixel + 1) = *zG >> zprec;
+ *(pixel + 2) = *zB >> zprec;
+ *(pixel + 3) = *zA >> zprec;
+}
+
+static inline void
+_blurrow (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint line,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint zR;
+ gint zG;
+ gint zB;
+ gint zA;
+ gint index;
+ guchar* scanline;
+
+ scanline = &(pixels[line * width * channels]);
+
+ zR = *scanline << zprec;
+ zG = *(scanline + 1) << zprec;
+ zB = *(scanline + 2) << zprec;
+ zA = *(scanline + 3) << zprec;
+
+ for (index = 0; index < width; index ++)
+ _blurinner (&scanline[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+
+ for (index = width - 2; index >= 0; index--)
+ _blurinner (&scanline[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+}
+
+static inline void
+_blurcol (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint x,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint zR;
+ gint zG;
+ gint zB;
+ gint zA;
+ gint index;
+ guchar* ptr;
+
+ ptr = pixels;
+
+ ptr += x * channels;
+
+ zR = *((guchar*) ptr ) << zprec;
+ zG = *((guchar*) ptr + 1) << zprec;
+ zB = *((guchar*) ptr + 2) << zprec;
+ zA = *((guchar*) ptr + 3) << zprec;
+
+ for (index = width; index < (height - 1) * width; index += width)
+ _blurinner ((guchar*) &ptr[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+
+ for (index = (height - 2) * width; index >= 0; index -= width)
+ _blurinner ((guchar*) &ptr[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+}
+
+void
+_expblur (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint radius,
+ gint aprec,
+ gint zprec)
+{
+ gint alpha;
+ gint row = 0;
+ gint col = 0;
+
+ if (radius < 1)
+ return;
+
+ // calculate the alpha such that 90% of
+ // the kernel is within the radius.
+ // (Kernel extends to infinity)
+ alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
+
+ for (; row < height; row++)
+ _blurrow (pixels,
+ width,
+ height,
+ channels,
+ row,
+ alpha,
+ aprec,
+ zprec);
+
+ for(; col < width; col++)
+ _blurcol (pixels,
+ width,
+ height,
+ channels,
+ col,
+ alpha,
+ aprec,
+ zprec);
+
+ return;
+}
+
+void
+_surface_blur (cairo_surface_t* surface,
+ guint radius)
+{
+ guchar* pixels;
+ guint width;
+ guint height;
+ cairo_format_t format;
+
+ // before we mess with the surface execute any pending drawing
+ cairo_surface_flush (surface);
+
+ pixels = cairo_image_surface_get_data (surface);
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+ format = cairo_image_surface_get_format (surface);
+
+ switch (format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ _expblur (pixels, width, height, 4, radius, 16, 7);
+ break;
+
+ case CAIRO_FORMAT_RGB24:
+ _expblur (pixels, width, height, 3, radius, 16, 7);
+ break;
+
+ case CAIRO_FORMAT_A8:
+ _expblur (pixels, width, height, 1, radius, 16, 7);
+ break;
+
+ default :
+ // do nothing
+ break;
+ }
+
+ // inform cairo we altered the surfaces contents
+ cairo_surface_mark_dirty (surface);
+}
+
+static gboolean
+draw (GtkWidget* button, cairo_t *cr)
+{
+ g_return_val_if_fail(IS_TRANSPORT_WIDGET(button), FALSE);
+ g_return_val_if_fail(cr != NULL, FALSE);
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button);
+
+ //g_debug("transport-widget draw()");
+
+ cairo_surface_t* surf = NULL;
+ cairo_t* cr_surf = NULL;
+
+#if ! GTK_CHECK_VERSION(3, 0, 0)
+ GtkAllocation allocation;
+ gtk_widget_get_allocation (button, &allocation);
+ cairo_translate (cr, allocation.x, allocation.y);
+#endif
+
+ GtkStyle *style;
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+ gtk_style_context_add_class (gtk_widget_get_style_context (button),
+ GTK_STYLE_CLASS_MENU);
+#endif
+ CairoColorRGB bg_color, fg_color, bg_selected, bg_prelight;
+ CairoColorRGB color_middle[2], color_middle_prelight[2], color_outer[2], color_outer_prelight[2],
+ color_play_outer[2], color_play_outer_prelight[2],
+ color_button[4], color_button_shadow, color_inner[2], color_inner_compressed[2];
+
+ style = gtk_widget_get_style (button);
+
+ bg_color.r = style->bg[0].red/65535.0;
+ bg_color.g = style->bg[0].green/65535.0;
+ bg_color.b = style->bg[0].blue/65535.0;
+
+ bg_prelight.r = style->bg[GTK_STATE_PRELIGHT].red/65535.0;
+ bg_prelight.g = style->bg[GTK_STATE_PRELIGHT].green/65535.0;
+ bg_prelight.b = style->bg[GTK_STATE_PRELIGHT].blue/65535.0;
+
+ bg_selected.r = style->bg[GTK_STATE_SELECTED].red/65535.0;
+ bg_selected.g = style->bg[GTK_STATE_SELECTED].green/65535.0;
+ bg_selected.b = style->bg[GTK_STATE_SELECTED].blue/65535.0;
+
+ fg_color.r = style->fg[0].red/65535.0;
+ fg_color.g = style->fg[0].green/65535.0;
+ fg_color.b = style->fg[0].blue/65535.0;
+
+ _color_shade (&bg_color, MIDDLE_START_SHADE, &color_middle[0]);
+ _color_shade (&bg_color, MIDDLE_END_SHADE, &color_middle[1]);
+ _color_shade (&bg_prelight, MIDDLE_START_SHADE, &color_middle_prelight[0]);
+ _color_shade (&bg_prelight, MIDDLE_END_SHADE, &color_middle_prelight[1]);
+ _color_shade (&bg_color, OUTER_START_SHADE, &color_outer[0]);
+ _color_shade (&bg_color, OUTER_END_SHADE, &color_outer[1]);
+ _color_shade (&bg_prelight, OUTER_START_SHADE, &color_outer_prelight[0]);
+ _color_shade (&bg_prelight, OUTER_END_SHADE, &color_outer_prelight[1]);
+ _color_shade (&bg_color, OUTER_PLAY_START_SHADE, &color_play_outer[0]);
+ _color_shade (&bg_color, OUTER_PLAY_END_SHADE, &color_play_outer[1]);
+ _color_shade (&bg_prelight, OUTER_PLAY_START_SHADE, &color_play_outer_prelight[0]);
+ _color_shade (&bg_prelight, OUTER_PLAY_END_SHADE, &color_play_outer_prelight[1]);
+ _color_shade (&bg_color, INNER_START_SHADE, &color_inner[0]);
+ _color_shade (&bg_color, INNER_END_SHADE, &color_inner[1]);
+ _color_shade (&fg_color, BUTTON_START_SHADE, &color_button[0]);
+ _color_shade (&fg_color, BUTTON_END_SHADE, &color_button[1]);
+ _color_shade (&bg_color, BUTTON_SHADOW_SHADE, &color_button[2]);
+ _color_shade (&bg_color, SHADOW_BUTTON_SHADE, &color_button_shadow);
+ _color_shade (&bg_selected, 1.0, &color_button[3]);
+ _color_shade (&bg_color, INNER_COMPRESSED_START_SHADE, &color_inner_compressed[0]);
+ _color_shade (&bg_color, INNER_COMPRESSED_END_SHADE, &color_inner_compressed[1]);
+
+ double MIDDLE_END[] = {color_middle[0].r, color_middle[0].g, color_middle[0].b, 1.0f};
+ double MIDDLE_START[] = {color_middle[1].r, color_middle[1].g, color_middle[1].b, 1.0f};
+ double MIDDLE_END_PRELIGHT[] = {color_middle_prelight[0].r, color_middle_prelight[0].g, color_middle_prelight[0].b, 1.0f};
+ double MIDDLE_START_PRELIGHT[] = {color_middle_prelight[1].r, color_middle_prelight[1].g, color_middle_prelight[1].b, 1.0f};
+ double OUTER_END[] = {color_outer[0].r, color_outer[0].g, color_outer[0].b, 1.0f};
+ double OUTER_START[] = {color_outer[1].r, color_outer[1].g, color_outer[1].b, 1.0f};
+ double OUTER_END_PRELIGHT[] = {color_outer_prelight[0].r, color_outer_prelight[0].g, color_outer_prelight[0].b, 1.0f};
+ double OUTER_START_PRELIGHT[] = {color_outer_prelight[1].r, color_outer_prelight[1].g, color_outer_prelight[1].b, 1.0f};
+ double SHADOW_BUTTON[] = {color_button_shadow.r, color_button_shadow.g, color_button_shadow.b, 0.3f};
+ double OUTER_PLAY_END[] = {color_play_outer[0].r, color_play_outer[0].g, color_play_outer[0].b, 1.0f};
+ double OUTER_PLAY_START[] = {color_play_outer[1].r, color_play_outer[1].g, color_play_outer[1].b, 1.0f};
+ 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};
+ 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};
+ double BUTTON_END[] = {color_button[0].r, color_button[0].g, color_button[0].b, 1.0f};
+ double BUTTON_START[] = {color_button[1].r, color_button[1].g, color_button[1].b, 1.0f};
+ double BUTTON_SHADOW[] = {color_button[2].r, color_button[2].g, color_button[2].b, 0.75f};
+ double BUTTON_SHADOW_FOCUS[] = {color_button[3].r, color_button[3].g, color_button[3].b, 1.0f};
+ double INNER_COMPRESSED_END[] = {color_inner_compressed[1].r, color_inner_compressed[1].g, color_inner_compressed[1].b, 1.0f};
+ double INNER_COMPRESSED_START[] = {color_inner_compressed[0].r, color_inner_compressed[0].g, color_inner_compressed[0].b, 1.0f};
+
+
+ draw_gradient (cr,
+ X,
+ Y,
+ RECT_WIDTH,
+ OUTER_RADIUS,
+ OUTER_START,
+ OUTER_END);
+
+ draw_gradient (cr,
+ X,
+ Y + 1,
+ RECT_WIDTH - 2,
+ MIDDLE_RADIUS,
+ MIDDLE_START,
+ MIDDLE_END);
+
+ draw_gradient (cr,
+ X,
+ Y + 2,
+ RECT_WIDTH - 4,
+ MIDDLE_RADIUS,
+ MIDDLE_START,
+ MIDDLE_END);
+
+ //prev/next button
+ if(priv->current_command == TRANSPORT_ACTION_PREVIOUS)
+ {
+ draw_gradient (cr,
+ X,
+ Y,
+ RECT_WIDTH/2,
+ OUTER_RADIUS,
+ OUTER_END,
+ OUTER_START);
+
+ draw_gradient (cr,
+ X,
+ Y + 1,
+ RECT_WIDTH/2,
+ MIDDLE_RADIUS,
+ INNER_COMPRESSED_START,
+ INNER_COMPRESSED_END);
+
+ draw_gradient (cr,
+ X,
+ Y + 2,
+ RECT_WIDTH/2,
+ MIDDLE_RADIUS,
+ INNER_COMPRESSED_START,
+ INNER_COMPRESSED_END);
+ }
+ else if(priv->current_command == TRANSPORT_ACTION_NEXT)
+ {
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y,
+ RECT_WIDTH/2,
+ OUTER_RADIUS,
+ OUTER_END,
+ OUTER_START);
+
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y + 1,
+ (RECT_WIDTH - 4.5)/2,
+ MIDDLE_RADIUS,
+ INNER_COMPRESSED_START,
+ INNER_COMPRESSED_END);
+
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y + 2,
+ (RECT_WIDTH - 7)/2,
+ MIDDLE_RADIUS,
+ INNER_COMPRESSED_START,
+ INNER_COMPRESSED_END);
+ }
+ else if (priv->motion_event == TRANSPORT_ACTION_PREVIOUS)
+ {
+ draw_gradient (cr,
+ X,
+ Y,
+ RECT_WIDTH/2,
+ OUTER_RADIUS,
+ OUTER_START_PRELIGHT,
+ OUTER_END_PRELIGHT);
+
+ draw_gradient (cr,
+ X,
+ Y + 1,
+ RECT_WIDTH/2,
+ MIDDLE_RADIUS,
+ MIDDLE_START_PRELIGHT,
+ MIDDLE_END_PRELIGHT);
+
+ draw_gradient (cr,
+ X,
+ Y + 2,
+ RECT_WIDTH/2,
+ MIDDLE_RADIUS,
+ MIDDLE_START_PRELIGHT,
+ MIDDLE_END_PRELIGHT);
+ }
+ else if (priv->motion_event == TRANSPORT_ACTION_NEXT)
+ {
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y,
+ RECT_WIDTH/2,
+ OUTER_RADIUS,
+ OUTER_START_PRELIGHT,
+ OUTER_END_PRELIGHT);
+
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y + 1,
+ (RECT_WIDTH - 4.5)/2,
+ MIDDLE_RADIUS,
+ MIDDLE_START_PRELIGHT,
+ MIDDLE_END_PRELIGHT);
+
+ draw_gradient (cr,
+ RECT_WIDTH / 2 + X,
+ Y + 2,
+ (RECT_WIDTH - 7)/2,
+ MIDDLE_RADIUS,
+ MIDDLE_START_PRELIGHT,
+ MIDDLE_END_PRELIGHT);
+ }
+
+ // play/pause shadow
+ if(priv->current_command != TRANSPORT_ACTION_PLAY_PAUSE)
+ {
+ cairo_save (cr);
+ cairo_rectangle (cr, X, Y, RECT_WIDTH, MIDDLE_RADIUS*2);
+ cairo_clip (cr);
+
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f - 1.0f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) - 1.0f,
+ CIRCLE_RADIUS + 1.0f,
+ SHADOW_BUTTON,
+ SHADOW_BUTTON);
+
+ cairo_restore (cr);
+ }
+
+ // play/pause button
+ if(priv->current_command == TRANSPORT_ACTION_PLAY_PAUSE)
+ {
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) ,
+ CIRCLE_RADIUS,
+ OUTER_PLAY_END,
+ OUTER_PLAY_START);
+
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
+ CIRCLE_RADIUS - 1.25,
+ INNER_COMPRESSED_START,
+ INNER_COMPRESSED_END);
+ }
+ else if (priv->motion_event == TRANSPORT_ACTION_PLAY_PAUSE)
+ {
+ /* this subtle offset is to fix alpha borders, should be removed once this draw routine will be refactored */
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 0.1,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 0.1,
+ CIRCLE_RADIUS - 0.1,
+ OUTER_PLAY_START_PRELIGHT,
+ OUTER_PLAY_END_PRELIGHT);
+
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
+ CIRCLE_RADIUS - 1.25,
+ MIDDLE_START_PRELIGHT,
+ MIDDLE_END_PRELIGHT);
+ }
+ else
+ {
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)),
+ CIRCLE_RADIUS,
+ OUTER_PLAY_START,
+ OUTER_PLAY_END);
+
+ draw_circle (cr,
+ X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f,
+ Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f,
+ CIRCLE_RADIUS - 1.25,
+ MIDDLE_START,
+ MIDDLE_END);
+ }
+
+ // draw previous-button drop-shadow
+ if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_PREVIOUS)
+ {
+ _setup (&cr_surf, &surf, PREV_WIDTH+6, PREV_HEIGHT+6);
+ _mask_prev (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_SHADOW_FOCUS,
+ BUTTON_SHADOW_FOCUS,
+ FALSE);
+ _surface_blur (surf, 3);
+ _finalize_repaint (cr, &cr_surf, &surf, PREV_X, PREV_Y + 0.5f, 3);
+ }
+ else
+ {
+ _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT);
+ _mask_prev (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_SHADOW,
+ BUTTON_SHADOW,
+ FALSE);
+ _surface_blur (surf, 1);
+ _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y + 1.0f);
+ }
+
+ // draw previous-button
+ _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT);
+ _mask_prev (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (PREV_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_START,
+ BUTTON_END,
+ FALSE);
+ _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y);
+
+ // draw next-button drop-shadow
+ if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_NEXT)
+ {
+ _setup (&cr_surf, &surf, NEXT_WIDTH+6, NEXT_HEIGHT+6);
+ _mask_next (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_SHADOW_FOCUS,
+ BUTTON_SHADOW_FOCUS,
+ FALSE);
+ _surface_blur (surf, 3);
+ _finalize_repaint (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 0.5f, 3);
+ }
+ else
+ {
+ _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT);
+ _mask_next (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_SHADOW,
+ BUTTON_SHADOW,
+ FALSE);
+ _surface_blur (surf, 1);
+ _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 1.0f);
+ }
+
+ // draw next-button
+ _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT);
+ _mask_next (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ TRI_WIDTH,
+ TRI_HEIGHT,
+ TRI_OFFSET);
+ _fill (cr_surf,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f,
+ (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f,
+ (double) TRI_HEIGHT,
+ BUTTON_START,
+ BUTTON_END,
+ FALSE);
+ _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y);
+
+ // draw pause-button drop-shadow
+ if(priv->current_state == TRANSPORT_STATE_PLAYING)
+ {
+ if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION ||
+ priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE))
+ {
+ _setup (&cr_surf, &surf, PAUSE_WIDTH+6, PAUSE_HEIGHT+6);
+ _mask_pause (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ BAR_WIDTH,
+ BAR_HEIGHT - 2.0f * BAR_WIDTH,
+ BAR_OFFSET);
+ _fill (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (double) BAR_HEIGHT,
+ BUTTON_SHADOW_FOCUS,
+ BUTTON_SHADOW_FOCUS,
+ TRUE);
+ _surface_blur (surf, 3);
+ _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 0.5f, 3);
+ }
+ else
+ {
+ _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT);
+ _mask_pause (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ BAR_WIDTH,
+ BAR_HEIGHT - 2.0f * BAR_WIDTH,
+ BAR_OFFSET);
+ _fill (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (double) BAR_HEIGHT,
+ BUTTON_SHADOW,
+ BUTTON_SHADOW,
+ TRUE);
+ _surface_blur (surf, 1);
+ _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 1.0f);
+ }
+
+ // draw pause-button
+ _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT);
+ _mask_pause (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ BAR_WIDTH,
+ BAR_HEIGHT - 2.0f * BAR_WIDTH,
+ BAR_OFFSET);
+ _fill (cr_surf,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f,
+ (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f,
+ (double) BAR_HEIGHT,
+ BUTTON_START,
+ BUTTON_END,
+ TRUE);
+ _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y);
+ }
+ else if(priv->current_state == TRANSPORT_STATE_PAUSED)
+ {
+ if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION ||
+ priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE))
+ {
+ _setup (&cr_surf, &surf, PLAY_WIDTH+6, PLAY_HEIGHT+6);
+ _mask_play (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING));
+ _fill (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING),
+ BUTTON_SHADOW_FOCUS,
+ BUTTON_SHADOW_FOCUS,
+ FALSE);
+ _surface_blur (surf, 3);
+ _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y + 0.5f, 3);
+ }
+ else
+ {
+ _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT);
+ _mask_play (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING));
+ _fill (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING),
+ BUTTON_SHADOW,
+ BUTTON_SHADOW,
+ FALSE);
+ _surface_blur (surf, 1);
+ _finalize (cr, &cr_surf, &surf, PAUSE_X-0.75f, PAUSE_Y + 1.0f);
+ }
+
+ // draw play-button
+ _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT);
+ cairo_set_line_width (cr, 10.5);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
+ _mask_play (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING));
+ _fill (cr_surf,
+ PLAY_PADDING,
+ PLAY_PADDING,
+ PLAY_WIDTH - (2*PLAY_PADDING),
+ PLAY_HEIGHT - (2*PLAY_PADDING),
+ BUTTON_START,
+ BUTTON_END,
+ FALSE);
+ _finalize (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y);
+ }
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ else if(priv->current_state == TRANSPORT_STATE_LAUNCHING)
+ {
+ // the spinner is not aligned, why? because the play button has odd width/height numbers
+ gtk_render_activity (spinner_style_context, cr, 106, 6, 30, 30);
+ }
+ #endif
+ return FALSE;
+}
+
+static void
+transport_widget_set_twin_item(TransportWidget* self,
+ DbusmenuMenuitem* twin_item)
+{
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self);
+ priv->twin_item = twin_item;
+ g_signal_connect(G_OBJECT(priv->twin_item), "property-changed",
+ G_CALLBACK(transport_widget_property_update), self);
+ gint initial_state = dbusmenu_menuitem_property_get_int (twin_item,
+ DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE );
+ //g_debug("TRANSPORT WIDGET - INITIAL UPDATE = %i", initial_state);
+ transport_widget_toggle_play_pause (self,
+ (TransportState)initial_state);
+}
+
+/**
+* transport_widget_update_state()
+* Callback for updates from the other side of dbus
+**/
+static void
+transport_widget_property_update(DbusmenuMenuitem* item, gchar* property,
+ GVariant* value, gpointer userdata)
+{
+ //g_debug("transport_widget_update_state - with property %s", property);
+ TransportWidget* bar = (TransportWidget*)userdata;
+ g_return_if_fail(IS_TRANSPORT_WIDGET(bar));
+ TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(bar);
+
+ if(g_ascii_strcasecmp(DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE, property) == 0)
+ {
+ TransportState new_state = (TransportState)g_variant_get_int32(value);
+ //g_debug("transport_widget_update_state - with value %i", new_state);
+ if (new_state == TRANSPORT_STATE_LAUNCHING){
+ #if GTK_CHECK_VERSION(3, 0, 0)
+ gtk_style_context_notify_state_change (spinner_style_context,
+ gtk_widget_get_window ( GTK_WIDGET(userdata)),
+ NULL,
+ GTK_STATE_FLAG_ACTIVE,
+ TRUE);
+ gtk_style_context_set_state (spinner_style_context, GTK_STATE_FLAG_ACTIVE);
+ #endif
+
+ priv->current_state = TRANSPORT_STATE_LAUNCHING;
+ g_debug("TransportWidget::toggle play state : %i", priv->current_state);
+ }
+ else{
+ transport_widget_toggle_play_pause(bar, new_state);
+ }
+ }
+}
+
+
+/**
+* transport_widget_new:
+* @returns: a new #TransportWidget.
+**/
+GtkWidget*
+transport_widget_new ( DbusmenuMenuitem *item )
+{
+ GtkWidget* widget = g_object_new(TRANSPORT_WIDGET_TYPE, NULL);
+ gtk_widget_set_app_paintable (widget, TRUE);
+ transport_widget_set_twin_item((TransportWidget*)widget, item);
+ return widget;
+}
+