--- gtk/gtkmenu.c.orig Mon Jun 27 13:36:34 2005 +++ gtk/gtkmenu.c Mon Jul 4 22:18:27 2005 @@ -51,6 +51,7 @@ #define DEFAULT_POPUP_DELAY 225 #define DEFAULT_POPDOWN_DELAY 1000 +#define DEFAULT_SHADOW_DELAY 50 #define NAVIGATION_REGION_OVERSHOOT 50 /* How much the navigation region * extends below the submenu @@ -93,6 +94,11 @@ gboolean have_layout; gint n_rows; gint n_columns; + + /* Shadow patch addon */ + GdkPixbuf *east, *south; + GdkWindow *east_shadow, *south_shadow; + guint32 timeout_id; }; typedef struct @@ -126,6 +132,60 @@ CHILD_PROP_BOTTOM_ATTACH }; +enum side { + EAST_SIDE, + SOUTH_SIDE +}; + +const double shadow_strip_l[5] = { + .937, .831, .670, .478, .180 +}; + +const double bottom_left_corner[25] = { + 1.00, .682, .423, .333, .258, + 1.00, .898, .800, .682, .584, + 1.00, .937, .874, .800, .737, + 1.00, .968, .937, .898, .866, + 1.00, .988, .976, .960, .945 +}; + +const double bottom_right_corner[25] = { + .258, .584, .737, .866, .945, + .584, .682, .800, .898, .960, + .737, .800, .874, .937, .976, + .866, .898, .937, .968, .988, + .945, .960, .976, .988, .996 +}; + +const double top_right_corner[25] = { + 1.00, 1.00, 1.00, 1.00, 1.00, + .686, .898, .937, .968, .988, + .423, .803, .874, .937, .976, + .333, .686, .800, .898, .960, + .258, .584, .737, .866, .945 +}; + +const double top_left_corner[25] = { + .988, .968, .937, .898, .498, + .976, .937, .874, .803, .423, + .960, .898, .800, .686, .333, + .945, .866, .737, .584, .258, + .941, .847, .698, .521, .215 +}; + +static GdkPixbuf *get_pixbuf (GtkMenu *menu, + int x, + int y, + int width, + int height); +static void shadow_paint (GtkWidget *widget, + GdkRectangle *area, + enum side shadow); +static void pixbuf_add_shadow (GdkPixbuf *pb, + enum side shadow); +static gboolean map_shadow_windows (gpointer data); +static void shadow_add_timeout (GtkWidget *widget); +static void shadow_remove_timeout (GtkWidget *widget); static void gtk_menu_class_init (GtkMenuClass *klass); static void gtk_menu_init (GtkMenu *menu); static void gtk_menu_set_property (GObject *object, @@ -246,6 +306,9 @@ { GtkMenuPrivate *priv = (GtkMenuPrivate *)data; + if (priv->timeout_id > 0) + g_source_remove (priv->timeout_id); + g_free (priv->heights); g_free (priv); @@ -466,6 +529,307 @@ } +static GdkPixbuf * +get_pixbuf (GtkMenu *menu, + int x, + int y, + int width, + int height) +{ + GdkPixbuf *dest, *src; + GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(menu)); + GdkWindow *root = gdk_screen_get_root_window (screen); + gint screen_height = gdk_screen_get_height (screen); + gint screen_width = gdk_screen_get_width (screen); + gint original_width = width; + gint original_height = height; + + if (x < 0) + { + width += x; + x = 0; + } + + if (y < 0) + { + height += y; + y = 0; + } + + if (x + width > screen_width) + { + width = screen_width - x; + } + + if (y + height > screen_height) + { + height = screen_height - y; + } + + if (width <= 0 || height <= 0) + return NULL; + + dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, + original_width, original_height); + src = gdk_pixbuf_get_from_drawable (NULL, root, NULL, x, y, 0, 0, + width, height); + gdk_pixbuf_copy_area (src, 0, 0, width, height, dest, 0, 0); + + g_object_unref (G_OBJECT (src)); + + return dest; +} + +static void +shadow_paint(GtkWidget *widget, GdkRectangle *area, enum side shadow) +{ + GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *private = gtk_menu_get_private (menu); + gint width, height; + GdkGC *gc = widget->style->black_gc; + + switch (shadow) + { + case EAST_SIDE: + if (private->east != NULL) + { + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + width = gdk_pixbuf_get_width (private->east); + height = gdk_pixbuf_get_height (private->east); + + gdk_draw_pixbuf (private->east_shadow, gc, private->east, 0, 0, 0, 0, + width, height, GDK_RGB_DITHER_NONE, 0, 0); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + } + break; + case SOUTH_SIDE: + if (private->south != NULL) + { + if (area) + gdk_gc_set_clip_rectangle (gc, area); + + width = gdk_pixbuf_get_width (private->south); + height = gdk_pixbuf_get_height (private->south); + + gdk_draw_pixbuf (private->south_shadow, gc, private->south, 0, 0, 0, 0, + width, height, GDK_RGB_DITHER_NONE, 0, 0); + + if (area) + gdk_gc_set_clip_rectangle (gc, NULL); + } + break; + default: + break; + } +} + +static void +pixbuf_add_shadow (GdkPixbuf *pb, + enum side shadow) +{ + gint width, rowstride, height; + gint i; + guchar *pixels, *p; + + width = gdk_pixbuf_get_width (pb); + height = gdk_pixbuf_get_height (pb); + rowstride = gdk_pixbuf_get_rowstride (pb); + pixels = gdk_pixbuf_get_pixels (pb); + + switch (shadow) + { + case EAST_SIDE: + if (height > 5) + { + for (i = 0; i < width; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * width; j += 3, k++) + { + p[j] = (guchar) (p[j] * top_right_corner [i * width + k]); + p[j + 1] = (guchar) (p[j + 1] * top_right_corner [i * width + k]); + p[j + 2] = (guchar) (p[j + 2] * top_right_corner [i * width + k]); + } + } + + i = 5; + } + else + { + i = 0; + } + + for (;i < height; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * width; j += 3, k++) + { + p[j] = (guchar) (p[j] * shadow_strip_l[width - 1 - k]); + p[j + 1] = (guchar) (p[j + 1] * shadow_strip_l[width - 1 - k]); + p[j + 2] = (guchar) (p[j + 2] * shadow_strip_l[width - 1 - k]); + } + } + break; + + case SOUTH_SIDE: + for (i = 0; i < height; i++) + { + gint j, k; + + p = pixels + (i * rowstride); + for (j = 0, k = 0; j < 3 * height; j += 3, k++) + { + + p[j] = (guchar) (p[j] * bottom_left_corner[i * height + k]); + p[j + 1] = (guchar) (p[j + 1] * bottom_left_corner[i * height + k]); + p[j + 2] = (guchar) (p[j + 2] * bottom_left_corner[i * height + k]); + } + + p = pixels + (i * rowstride) + 3 * height; + for (j = 0, k = 0; j < (width * 3) - (6 * height); j += 3, k++) + { + p[j] = (guchar) (p[j] * bottom_right_corner [i * height]); + p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner [i * height]); + p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner [i * height]); + } + + p = pixels + (i * rowstride) + ((width * 3) - (3 * height)); + for (j = 0, k = 0; j < 3 * height; j += 3, k++) + { + p[j] = (guchar) (p[j] * bottom_right_corner[i * height + k]); + p[j + 1] = (guchar) (p[j + 1] * bottom_right_corner[i * height + k]); + p[j + 2] = (guchar) (p[j + 2] * bottom_right_corner[i * height + k]); + } + } + break; + + default: + break; + } +} + +static gboolean +map_shadow_windows (gpointer data) +{ + GtkMenu *menu = GTK_MENU (data); + GtkMenuPrivate *private = gtk_menu_get_private (menu); + GtkWidget *widget = GTK_WIDGET (data); + GdkPixbuf *pixbuf; + + pixbuf = get_pixbuf (menu, + private->x + widget->allocation.width, private->y, + 5, widget->allocation.height); + if (pixbuf != NULL) + { + pixbuf_add_shadow (pixbuf, EAST_SIDE); + if (private->east != NULL) + { + g_object_unref (G_OBJECT (private->east)); + } + private->east = pixbuf; + } + + pixbuf = get_pixbuf (menu, + private->x, private->y + widget->allocation.height, + widget->allocation.width + 5, 5); + if (pixbuf != NULL) + { + pixbuf_add_shadow (pixbuf, SOUTH_SIDE); + if (private->south != NULL) + { + g_object_unref (G_OBJECT (private->south)); + } + private->south = pixbuf; + } + + gdk_window_move_resize (private->east_shadow, + private->x + widget->allocation.width, private->y, + 5, widget->allocation.height); + + gdk_window_move_resize (private->south_shadow, + private->x, private->y + widget->allocation.height, + widget->allocation.width + 5, 5); + + gdk_window_show (private->east_shadow); + gdk_window_show (private->south_shadow); + + shadow_paint(widget, NULL, EAST_SIDE); + shadow_paint(widget, NULL, SOUTH_SIDE); + + private->timeout_id = 0; + return FALSE; +} + +static void +shadow_add_timeout(GtkWidget *widget) +{ + GtkMenuPrivate *private = gtk_menu_get_private (GTK_MENU (widget)); + gboolean menu_shadow; + gint shadow_delay; + + if (private->have_position) + { + g_object_get (G_OBJECT (gtk_widget_get_settings (widget)), + "gtk-menu-drop-shadow", &menu_shadow, NULL); + + if (menu_shadow) + { + if (private->timeout_id > 0) + { + g_source_remove (private->timeout_id); + } + + + g_object_get (G_OBJECT (gtk_widget_get_settings (widget)), + "gtk-menu-shadow-delay", &shadow_delay, + NULL); + + private->timeout_id = g_timeout_add (shadow_delay, map_shadow_windows, widget); + } + } +} + +static void +shadow_remove_timeout (GtkWidget *widget) +{ + GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *private = gtk_menu_get_private (menu); + + if (private->timeout_id > 0) + { + g_source_remove (private->timeout_id); + private->timeout_id = 0; + } + else + { + if (private->east_shadow) + gdk_window_hide (private->east_shadow); + + if (private->south_shadow) + gdk_window_hide (private->south_shadow); + + if (private->east) + { + g_object_unref (G_OBJECT (private->east)); + private->east = NULL; + } + + if (private->south) + { + g_object_unref (G_OBJECT (private->south)); + private->south = NULL; + } + } +} + static void gtk_menu_class_init (GtkMenuClass *class) { @@ -713,6 +1077,20 @@ DEFAULT_POPDOWN_DELAY, GTK_PARAM_READWRITE)); + gtk_settings_install_property (g_param_spec_boolean ("gtk-menu-drop-shadow", + _("Display menu drop-shadow"), + _("Whether menu drop-shadow should be displayed"), + TRUE, + G_PARAM_READWRITE)); + + gtk_settings_install_property (g_param_spec_int ("gtk-menu-shadow-delay", + _("Delay before drop-shadow appear"), + _("Minimum time before drop-shadow appear under the menu"), + 0, + G_MAXINT, + DEFAULT_SHADOW_DELAY, + G_PARAM_READWRITE)); + } @@ -925,6 +1303,15 @@ menu->lower_arrow_prelight = FALSE; priv->have_layout = FALSE; + + /* Shadow patch */ + priv->east_shadow = NULL; + priv->south_shadow = NULL; + + priv->east = NULL; + priv->south = NULL; + + priv->timeout_id = 0; } static void @@ -997,6 +1384,7 @@ return; } + shadow_remove_timeout(GTK_WIDGET(menu)); if (menu->torn_off) { gtk_window_set_screen (GTK_WINDOW (menu->tearoff_window), new_screen); @@ -1448,6 +1836,7 @@ if (xgrab_shell == widget) popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */ + shadow_add_timeout(GTK_WIDGET (menu)); gtk_grab_add (GTK_WIDGET (menu)); } @@ -1458,7 +1847,7 @@ GtkMenuShell *menu_shell; g_return_if_fail (GTK_IS_MENU (menu)); - + menu_shell = GTK_MENU_SHELL (menu); private = gtk_menu_get_private (menu); @@ -1523,6 +1912,7 @@ menu_shell->have_xgrab = FALSE; gtk_grab_remove (GTK_WIDGET (menu)); + shadow_remove_timeout(GTK_WIDGET (menu)); menu_grab_transfer_window_destroy (menu); } @@ -1990,10 +2380,16 @@ if (GTK_WIDGET_REALIZED (widget)) { GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *private; + private = gtk_menu_get_private (menu); + gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL); gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + gdk_window_set_back_pixmap (private->east_shadow, NULL, FALSE); + gdk_window_set_back_pixmap (private->south_shadow, NULL, FALSE); } } @@ -2004,6 +2400,7 @@ gint attributes_mask; gint border_width; GtkMenu *menu; + GtkMenuPrivate *private; GtkWidget *child; GList *children; guint vertical_padding; @@ -2011,6 +2408,7 @@ g_return_if_fail (GTK_IS_MENU (widget)); menu = GTK_MENU (widget); + private = gtk_menu_get_private (menu); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); @@ -2081,6 +2479,25 @@ gdk_window_show (menu->bin_window); gdk_window_show (menu->view_window); + + /* Drop shadow */ + + attributes.window_type = GDK_WINDOW_TEMP; + attributes.override_redirect = TRUE; + + attributes_mask = GDK_WA_NOREDIR | GDK_WA_VISUAL | GDK_WA_COLORMAP; + + /* East drop shadow */ + private->east_shadow = gdk_window_new (gtk_widget_get_root_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (private->east_shadow, menu); + gdk_window_set_back_pixmap (private->east_shadow, NULL, FALSE); + + /* South drop shadow */ + private->south_shadow = gdk_window_new (gtk_widget_get_root_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (private->south_shadow, menu); + gdk_window_set_back_pixmap (private->south_shadow, NULL, FALSE); } static gboolean @@ -2143,10 +2560,12 @@ gtk_menu_unrealize (GtkWidget *widget) { GtkMenu *menu; + GtkMenuPrivate *private; g_return_if_fail (GTK_IS_MENU (widget)); menu = GTK_MENU (widget); + private = gtk_menu_get_private (menu); menu_grab_transfer_window_destroy (menu); @@ -2158,6 +2577,15 @@ gdk_window_destroy (menu->bin_window); menu->bin_window = NULL; + /* Shadows */ + gdk_window_set_user_data (private->east_shadow, NULL); + gdk_window_destroy (private->east_shadow); + private->east_shadow = NULL; + + gdk_window_set_user_data (private->south_shadow, NULL); + gdk_window_destroy (private->south_shadow); + private->south_shadow = NULL; + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); } @@ -2312,8 +2740,15 @@ y, width, height); - } + if (GTK_WIDGET_MAPPED (widget)) + { + /* Remap the shadows as the menu size has changed */ + shadow_remove_timeout(widget); + shadow_add_timeout(widget); + } + } + if (menu_shell->children) { gint base_width = width / gtk_menu_get_n_columns (menu); @@ -2410,7 +2845,7 @@ } } } - } + } } static void @@ -3048,7 +3483,7 @@ gtk_menu_stop_scrolling (menu); } - } + } } static gboolean