/* * Copyright 2013 Canonical Ltd. * * 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 . * * Authors: * Conor Curran * Mirco Müller * Andrea Cimitan * Lars Uebernickel */ #include "idoplaybackmenuitem.h" #include #include #define RECT_WIDTH 130.0f #define Y 7.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 -2.0f #define PREV_Y 13.0f #define NEXT_X 76.0f //prev_y #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 41.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 enum { STATE_PAUSED, STATE_PLAYING, STATE_LAUNCHING } State; typedef enum { BUTTON_NONE, BUTTON_PREVIOUS, BUTTON_PLAYPAUSE, BUTTON_NEXT, N_BUTTONS } Button; typedef GtkMenuItemClass IdoPlaybackMenuItemClass; struct _IdoPlaybackMenuItem { GtkMenuItem parent; State current_state; Button cur_pushed_button; Button cur_hover_button; gboolean has_focus; gboolean keyboard_activated; /* TRUE if the current button was activated with a key */ GActionGroup *action_group; gchar *button_actions[N_BUTTONS]; }; G_DEFINE_TYPE (IdoPlaybackMenuItem, ido_playback_menu_item, GTK_TYPE_MENU_ITEM); static gboolean ido_playback_menu_item_draw (GtkWidget* button, cairo_t *cr); static void ido_playback_menu_item_dispose (GObject *object) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (object); if (item->action_group) { g_signal_handlers_disconnect_by_data (item->action_group, item); g_clear_object (&item->action_group); } G_OBJECT_CLASS (ido_playback_menu_item_parent_class)->dispose (object); } static void ido_playback_menu_item_finalize (GObject *object) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (object); gint i; for (i = 0; i < N_BUTTONS; i++) g_free (item->button_actions[i]); G_OBJECT_CLASS (ido_playback_menu_item_parent_class)->finalize (object); } static Button ido_playback_menu_item_get_button_at_pos (GtkWidget *item, gint x, gint y) { GtkAllocation alloc; gint left; /* 0 44 86 130 * 5 +------+ * 12 +-----+ +-----+ * |prev play next| * 40 +-----+ +-----+ * 47 +------+ */ gtk_widget_get_allocation (item, &alloc); left = alloc.x + (alloc.width - RECT_WIDTH) / 2; if (x > left && x < left + 44 && y > 12 && y < 40) return BUTTON_PREVIOUS; if (x > left + 44 && x < left + 86 && y > 5 && y < 47) return BUTTON_PLAYPAUSE; if (x > left + 86 && x < left + 130 && y > 12 && y < 40) return BUTTON_NEXT; return BUTTON_NONE; } static gboolean ido_playback_menu_item_parent_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { IdoPlaybackMenuItem *self = user_data; /* only listen to events when the playback menu item is selected */ if (!self->has_focus) return FALSE; switch (event->keyval) { case GDK_KEY_Left: self->cur_pushed_button = BUTTON_PREVIOUS; break; case GDK_KEY_Right: self->cur_pushed_button = BUTTON_NEXT; break; case GDK_KEY_space: if (self->cur_hover_button != BUTTON_NONE) self->cur_pushed_button = self->cur_hover_button; else self->cur_pushed_button = BUTTON_PLAYPAUSE; break; default: self->cur_pushed_button = BUTTON_NONE; } if (self->cur_pushed_button != BUTTON_NONE) { const gchar *action = self->button_actions[self->cur_pushed_button]; if (self->action_group && action) g_action_group_activate_action (self->action_group, action, NULL); self->keyboard_activated = TRUE; gtk_widget_queue_draw (widget); return TRUE; } return FALSE; } static gboolean ido_playback_menu_item_parent_key_release_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { IdoPlaybackMenuItem *self = user_data; switch (event->keyval) { case GDK_KEY_Left: case GDK_KEY_Right: case GDK_KEY_space: self->cur_pushed_button = BUTTON_NONE; self->keyboard_activated = FALSE; gtk_widget_queue_draw (widget); break; } return FALSE; } static void ido_playback_menu_item_parent_set (GtkWidget *widget, GtkWidget *old_parent) { GtkWidget *parent; /* Menus don't pass key events to their children. This works around * that by listening to key events on the parent widget. */ if (old_parent) { g_signal_handlers_disconnect_by_func (old_parent, ido_playback_menu_item_parent_key_press_event, widget); g_signal_handlers_disconnect_by_func (old_parent, ido_playback_menu_item_parent_key_release_event, widget); } parent = gtk_widget_get_parent (widget); if (parent) { g_signal_connect (parent, "key-press-event", G_CALLBACK (ido_playback_menu_item_parent_key_press_event), widget); g_signal_connect (parent, "key-release-event", G_CALLBACK (ido_playback_menu_item_parent_key_release_event), widget); } } static void ido_playback_menu_item_select (GtkMenuItem *item) { IdoPlaybackMenuItem *self = IDO_PLAYBACK_MENU_ITEM (item); self->has_focus = TRUE; GTK_MENU_ITEM_CLASS (ido_playback_menu_item_parent_class)->select (item); } static void ido_playback_menu_item_deselect (GtkMenuItem *item) { IdoPlaybackMenuItem *self = IDO_PLAYBACK_MENU_ITEM (item); self->has_focus = FALSE; GTK_MENU_ITEM_CLASS (ido_playback_menu_item_parent_class)->deselect (item); } static gboolean ido_playback_menu_item_button_press_event (GtkWidget *menuitem, GdkEventButton *event) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (menuitem); item->cur_pushed_button = ido_playback_menu_item_get_button_at_pos (menuitem, event->x, event->y); gtk_widget_queue_draw (menuitem); return TRUE; } static gboolean ido_playback_menu_item_button_release_event (GtkWidget *menuitem, GdkEventButton *event) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (menuitem); Button button; const gchar *action = action; button = ido_playback_menu_item_get_button_at_pos (menuitem, event->x, event->y); if (button != item->cur_pushed_button) button = BUTTON_NONE; action = item->button_actions[item->cur_pushed_button]; if (item->action_group && action) g_action_group_activate_action (item->action_group, action, NULL); item->cur_pushed_button = BUTTON_NONE; gtk_widget_queue_draw (menuitem); return TRUE; } static gboolean ido_playback_menu_item_motion_notify_event (GtkWidget *menuitem, GdkEventMotion *event) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (menuitem); item->cur_hover_button = ido_playback_menu_item_get_button_at_pos (menuitem, event->x, event->y); gtk_widget_queue_draw (menuitem); return TRUE; } static gboolean ido_playback_menu_item_leave_notify_event (GtkWidget *menuitem, GdkEventCrossing *event) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (menuitem); item->cur_pushed_button = BUTTON_NONE; item->cur_hover_button = BUTTON_NONE; gtk_widget_queue_draw (GTK_WIDGET(menuitem)); return TRUE; } static void ido_playback_menu_item_class_init (IdoPlaybackMenuItemClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkMenuItemClass *menuitem_class = GTK_MENU_ITEM_CLASS (klass); gobject_class->dispose = ido_playback_menu_item_dispose; gobject_class->finalize = ido_playback_menu_item_finalize; widget_class->button_press_event = ido_playback_menu_item_button_press_event; widget_class->button_release_event = ido_playback_menu_item_button_release_event; widget_class->motion_notify_event = ido_playback_menu_item_motion_notify_event; widget_class->leave_notify_event = ido_playback_menu_item_leave_notify_event; widget_class->parent_set = ido_playback_menu_item_parent_set; widget_class->draw = ido_playback_menu_item_draw; menuitem_class->select = ido_playback_menu_item_select; menuitem_class->deselect = ido_playback_menu_item_deselect; } static void ido_playback_menu_item_init (IdoPlaybackMenuItem *self) { gtk_widget_set_size_request (GTK_WIDGET (self), 200, 43); } static void ido_playback_menu_item_set_state (IdoPlaybackMenuItem *self, State state) { self->current_state = state; if (self->current_state == STATE_LAUNCHING) gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE, FALSE); else gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE); gtk_widget_queue_draw (GTK_WIDGET (self)); } static void ido_playback_menu_item_set_state_from_string (IdoPlaybackMenuItem *self, const gchar *state) { g_return_if_fail (state != NULL); if (g_str_equal (state, "Playing")) ido_playback_menu_item_set_state (self, STATE_PLAYING); else if (g_str_equal (state, "Launching")) ido_playback_menu_item_set_state (self, STATE_LAUNCHING); else /* "Paused" and fallback */ ido_playback_menu_item_set_state (self, STATE_PAUSED); } static void ido_playback_menu_item_action_added (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { IdoPlaybackMenuItem *self = user_data; const gchar *action; action = self->button_actions[BUTTON_PLAYPAUSE]; if (action && g_str_equal (action_name, action)) { GVariant *state; state = g_action_group_get_action_state (action_group, action); if (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING)) ido_playback_menu_item_set_state_from_string (self, g_variant_get_string (state, NULL)); g_variant_unref (state); } } static void ido_playback_menu_item_action_removed (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { IdoPlaybackMenuItem *self = user_data; const gchar *action; action = self->button_actions[BUTTON_PLAYPAUSE]; if (action && g_str_equal (action_name, action)) ido_playback_menu_item_set_state (self, STATE_PAUSED); } static void ido_playback_menu_item_action_state_changed (GActionGroup *action_group, const gchar *action_name, GVariant *value, gpointer user_data) { IdoPlaybackMenuItem *self = user_data; const gchar *action; g_return_if_fail (action_name != NULL); action = self->button_actions[BUTTON_PLAYPAUSE]; if (action && g_str_equal (action_name, action)) { if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) ido_playback_menu_item_set_state_from_string (self, g_variant_get_string (value, NULL)); } } GtkMenuItem * ido_playback_menu_item_new_from_model (GMenuItem *item, GActionGroup *actions) { IdoPlaybackMenuItem *widget; gchar *play_action; widget = g_object_new (IDO_TYPE_PLAYBACK_MENU_ITEM, NULL); widget->action_group = g_object_ref (actions); g_signal_connect (actions, "action-state-changed", G_CALLBACK (ido_playback_menu_item_action_state_changed), widget); g_signal_connect (actions, "action-added", G_CALLBACK (ido_playback_menu_item_action_added), widget); g_signal_connect (actions, "action-removed", G_CALLBACK (ido_playback_menu_item_action_removed), widget); g_menu_item_get_attribute (item, "x-ayatana-play-action", "s", &widget->button_actions[BUTTON_PLAYPAUSE]); g_menu_item_get_attribute (item, "x-ayatana-next-action", "s", &widget->button_actions[BUTTON_NEXT]); g_menu_item_get_attribute (item, "x-ayatana-previous-action", "s", &widget->button_actions[BUTTON_PREVIOUS]); play_action = widget->button_actions[BUTTON_PLAYPAUSE]; if (play_action && g_action_group_has_action (actions, play_action)) ido_playback_menu_item_action_added (actions, play_action, widget); return GTK_MENU_ITEM (widget); } /* * Drawing */ 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; } } static void _color_shade(const GdkRGBA *a, float k, GdkRGBA *b) { double red; double green; double blue; red = a->red; green = a->green; blue = a->blue; if (k == 1.0) { b->red = red; b->green = green; b->blue = 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->red = red; b->green = green; b->blue = 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); } static 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; } static 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 void get_colour(GtkStyleContext *pStyleContext, GtkStateFlags nState, const gchar *sColour, GdkRGBA *pRGBA) { GdkRGBA *pRGBATmp; gtk_style_context_get(pStyleContext, nState, sColour, &pRGBATmp, NULL); *pRGBA = *pRGBATmp; gdk_rgba_free(pRGBATmp); } static gboolean ido_playback_menu_item_draw (GtkWidget* button, cairo_t *cr) { IdoPlaybackMenuItem *item = IDO_PLAYBACK_MENU_ITEM (button); GtkAllocation alloc; gint X; gint abs_pause_x; gint abs_prev_x; gint abs_next_x; g_return_val_if_fail(IDO_IS_PLAYBACK_MENU_ITEM (button), FALSE); g_return_val_if_fail(cr != NULL, FALSE); cairo_surface_t* surf = NULL; cairo_t* cr_surf = NULL; GtkStyleContext *pStyleContext; GdkRGBA bg_color, fg_color, bg_selected, bg_prelight; GdkRGBA 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]; pStyleContext = gtk_widget_get_style_context(gtk_widget_get_parent(button)); // Some buggy themes don't define a fallback "background-color" - let's make this a button, then. gtk_style_context_add_class(pStyleContext, GTK_STYLE_CLASS_BUTTON); get_colour(pStyleContext, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bg_color); get_colour(pStyleContext, GTK_STATE_FLAG_PRELIGHT, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bg_prelight); get_colour(pStyleContext, GTK_STATE_FLAG_SELECTED, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bg_selected); get_colour(pStyleContext, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_COLOR, &fg_color); _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].red, color_middle[0].green, color_middle[0].blue, 1.0f}; double MIDDLE_START[] = {color_middle[1].red, color_middle[1].green, color_middle[1].blue, 1.0f}; double MIDDLE_END_PRELIGHT[] = {color_middle_prelight[0].red, color_middle_prelight[0].green, color_middle_prelight[0].blue, 1.0f}; double MIDDLE_START_PRELIGHT[] = {color_middle_prelight[1].red, color_middle_prelight[1].green, color_middle_prelight[1].blue, 1.0f}; double OUTER_END[] = {color_outer[0].red, color_outer[0].green, color_outer[0].blue, 1.0f}; double OUTER_START[] = {color_outer[1].red, color_outer[1].green, color_outer[1].blue, 1.0f}; double OUTER_END_PRELIGHT[] = {color_outer_prelight[0].red, color_outer_prelight[0].green, color_outer_prelight[0].blue, 1.0f}; double OUTER_START_PRELIGHT[] = {color_outer_prelight[1].red, color_outer_prelight[1].green, color_outer_prelight[1].blue, 1.0f}; double SHADOW_BUTTON[] = {color_button_shadow.red, color_button_shadow.green, color_button_shadow.blue, 0.3f}; double OUTER_PLAY_END[] = {color_play_outer[0].red, color_play_outer[0].green, color_play_outer[0].blue, 1.0f}; double OUTER_PLAY_START[] = {color_play_outer[1].red, color_play_outer[1].green, color_play_outer[1].blue, 1.0f}; double OUTER_PLAY_END_PRELIGHT[] = {color_play_outer_prelight[0].red, color_play_outer_prelight[0].green, color_play_outer_prelight[0].blue, 1.0f}; double OUTER_PLAY_START_PRELIGHT[] = {color_play_outer_prelight[1].red, color_play_outer_prelight[1].green, color_play_outer_prelight[1].blue, 1.0f}; double BUTTON_END[] = {color_button[0].red, color_button[0].green, color_button[0].blue, 1.0f}; double BUTTON_START[] = {color_button[1].red, color_button[1].green, color_button[1].blue, 1.0f}; double BUTTON_SHADOW[] = {color_button[2].red, color_button[2].green, color_button[2].blue, 0.75f}; double BUTTON_SHADOW_FOCUS[] = {color_button[3].red, color_button[3].green, color_button[3].blue, 1.0f}; double INNER_COMPRESSED_END[] = {color_inner_compressed[1].red, color_inner_compressed[1].green, color_inner_compressed[1].blue, 1.0f}; double INNER_COMPRESSED_START[] = {color_inner_compressed[0].red, color_inner_compressed[0].green, color_inner_compressed[0].blue, 1.0f}; gtk_widget_get_allocation (button, &alloc); X = alloc.x + (alloc.width - RECT_WIDTH) / 2 + OUTER_RADIUS; abs_pause_x = X + PAUSE_X; abs_prev_x = X + PREV_X; abs_next_x = X + NEXT_X; 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); if(item->cur_pushed_button == BUTTON_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(item->cur_pushed_button == BUTTON_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 (item->cur_hover_button == BUTTON_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 (item->cur_hover_button == BUTTON_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(item->cur_pushed_button != BUTTON_PLAYPAUSE) { 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(item->cur_pushed_button == BUTTON_PLAYPAUSE) { 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 (item->cur_hover_button == BUTTON_PLAYPAUSE) { /* 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 ((item->cur_pushed_button == BUTTON_PREVIOUS && item->keyboard_activated) || item->cur_hover_button == BUTTON_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, abs_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, abs_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, abs_prev_x, PREV_Y); // draw next-button drop-shadow if ((item->cur_pushed_button == BUTTON_NEXT && item->keyboard_activated) || item->cur_hover_button == BUTTON_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, abs_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, abs_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, abs_next_x, NEXT_Y); // draw pause-button drop-shadow if (item->current_state == STATE_PLAYING) { if (item->has_focus && (item->cur_hover_button == BUTTON_NONE || item->cur_hover_button == BUTTON_PLAYPAUSE) && (item->cur_pushed_button == BUTTON_NONE || item->cur_pushed_button == BUTTON_PLAYPAUSE)) { _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, abs_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, abs_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, abs_pause_x, PAUSE_Y); } else if (item->current_state == STATE_PAUSED) { if (item->has_focus && (item->cur_hover_button == BUTTON_NONE || item->cur_hover_button == BUTTON_PLAYPAUSE) && (item->cur_pushed_button == BUTTON_NONE || item->cur_pushed_button == BUTTON_PLAYPAUSE)) { _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, abs_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, abs_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, abs_pause_x-0.5f, PAUSE_Y); } else if (item->current_state == STATE_LAUNCHING) { gtk_render_activity (gtk_widget_get_style_context (button), cr, (alloc.width / 2) - 15, (alloc.height / 2) - 15, 30, 30); } return FALSE; }