diff options
-rw-r--r-- | gtk/gtk.h | 3 | ||||
-rw-r--r-- | gtk/gtkcolorchooserwidget.c | 20 | ||||
-rw-r--r-- | gtk/gtkcoloreditor.c | 562 | ||||
-rw-r--r-- | gtk/gtkcolorswatch.c | 89 |
4 files changed, 649 insertions, 25 deletions
@@ -78,6 +78,9 @@ #include <gtk/gtkcheckmenuitem.h> #include <gtk/gtkclipboard.h> #include <gtk/gtkcolorbutton.h> +#include <gtk/gtkcolorchooser.h> +#include <gtk/gtkcolorchooserdialog.h> +#include <gtk/gtkcolorchooserwidget.h> #include <gtk/gtkcolorsel.h> #include <gtk/gtkcolorseldialog.h> #include <gtk/gtkcombobox.h> diff --git a/gtk/gtkcolorchooserwidget.c b/gtk/gtkcolorchooserwidget.c index 06fd27e20b..c14510133d 100644 --- a/gtk/gtkcolorchooserwidget.c +++ b/gtk/gtkcolorchooserwidget.c @@ -101,7 +101,6 @@ swatch_customize (GtkColorSwatch *swatch, gtk_widget_hide (cc->priv->palette); gtk_widget_show (cc->priv->editor); - g_print ("TODO: customize color\n"); } static void @@ -206,6 +205,7 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc) { GtkWidget *grid; GtkWidget *p; + GtkWidget *button; GtkWidget *label; gint i; GdkRGBA color, color1, color2; @@ -301,11 +301,11 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc) gtk_grid_set_column_spacing (GTK_GRID (grid), 4); gtk_container_add (GTK_CONTAINER (cc->priv->palette), grid); - p = gtk_color_swatch_new (); - gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 10, 10, 10, 10); - connect_button_signals (p, cc); - gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (p), "list-add-symbolic"); - gtk_grid_attach (GTK_GRID (grid), p, 0, 0, 1, 1); + button = gtk_color_swatch_new (); + gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (button), 10, 10, 10, 10); + connect_button_signals (button, cc); + gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), "list-add-symbolic"); + gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); cc->priv->settings = g_settings_new_with_path ("org.gtk.Settings.ColorChooser", "/org/gtk/settings/color-chooser/"); @@ -325,10 +325,14 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc) if (i == 8) break; } - if (i > 0) - gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 10, 10, 1); g_variant_unref (variant); + if (i > 0) + { + gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 10, 10, 1); + gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (button), 10, 1, 1, 10); + } + cc->priv->editor = gtk_color_editor_new (); gtk_container_add (GTK_CONTAINER (cc), cc->priv->editor); diff --git a/gtk/gtkcoloreditor.c b/gtk/gtkcoloreditor.c index f1346986cc..d10dc31f44 100644 --- a/gtk/gtkcoloreditor.c +++ b/gtk/gtkcoloreditor.c @@ -17,18 +17,45 @@ * Boston, MA 02111-1307, USA. */ +/* TODO + * - split out sv-plane + * - focus indication + * - custom sliders + * - pop-up entries + */ + #include "config.h" -#include "gtkintl.h" #include "gtkcolorchooserprivate.h" #include "gtkcoloreditor.h" +#include "gtkgrid.h" +#include "gtkscale.h" +#include "gtkaspectframe.h" +#include "gtkdrawingarea.h" +#include "gtkentry.h" #include "gtkhsv.h" +#include "gtkadjustment.h" +#include "gtkintl.h" +#include <math.h> struct _GtkColorEditorPrivate { - GtkWidget *hsv; + GtkWidget *grid; + GtkWidget *swatch; + GtkWidget *entry; + GtkWidget *h_slider; + GtkWidget *sv_plane; + GtkWidget *a_slider; + + GtkAdjustment *h_adj; + GtkAdjustment *a_adj; + cairo_surface_t *surface; GdkRGBA color; + gdouble h, s, v; + gint x, y; + gboolean in_drag; + gboolean text_changed; }; enum @@ -43,35 +70,529 @@ G_DEFINE_TYPE_WITH_CODE (GtkColorEditor, gtk_color_editor, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER, gtk_color_editor_iface_init)) +static gboolean +sv_draw (GtkWidget *widget, + cairo_t *cr, + GtkColorEditor *editor) +{ + gint x, y; + gint width, height; + + cairo_set_source_surface (cr, editor->priv->surface, 0, 0); + cairo_paint (cr); + + x = editor->priv->x; + y = editor->priv->y; + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + + cairo_move_to (cr, 0, y + 0.5); + cairo_line_to (cr, width, y + 0.5); + + cairo_move_to (cr, x + 0.5, 0); + cairo_line_to (cr, x + 0.5, height); + + cairo_set_line_width (cr, 3.0); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6); + cairo_stroke_preserve (cr); + + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8); + cairo_stroke (cr); + + return FALSE; +} + static void -hsv_changed (GtkHSV *hsv, - GtkColorEditor *editor) +create_sv_surface (GtkColorEditor *editor) { + GtkWidget *sv_plane; + cairo_t *cr; + cairo_surface_t *surface; + gint width, height, stride; + cairo_surface_t *tmp; + guint red, green, blue; + guint32 *data, *p; gdouble h, s, v; + gdouble r, g, b; + gdouble sf, vf; + gint x, y; + + sv_plane = editor->priv->sv_plane; + + if (!gtk_widget_get_realized (sv_plane)) + return; + + width = gtk_widget_get_allocated_width (sv_plane); + height = gtk_widget_get_allocated_height (sv_plane); + + surface = gdk_window_create_similar_surface (gtk_widget_get_window (sv_plane), + CAIRO_CONTENT_COLOR, + width, height); + + if (editor->priv->surface) + cairo_surface_destroy (editor->priv->surface); + editor->priv->surface = surface; + + if (width == 1 || height == 1) + return; + + stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width); + + data = g_malloc (4 * height * stride); + + h = editor->priv->h; + sf = 1.0 / (height - 1); + vf = 1.0 / (width - 1); + for (y = 0; y < height; y++) + { + s = CLAMP (1.0 - y * sf, 0.0, 1.0); + p = data + y * (stride / 4); + for (x = 0; x < width; x++) + { + v = x * vf; + gtk_hsv_to_rgb (h, s, v, &r, &g, &b); + red = CLAMP (r * 255, 0, 255); + green = CLAMP (g * 255, 0, 255); + blue = CLAMP (b * 255, 0, 255); + p[x] = (red << 16) | (green << 8) | blue; + } + } + + tmp = cairo_image_surface_create_for_data ((guchar *)data, CAIRO_FORMAT_RGB24, + width, height, stride); + cr = cairo_create (surface); + + cairo_set_source_surface (cr, tmp, 0, 0); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (tmp); + g_free (data); +} + +static void +hsv_to_xy (GtkColorEditor *editor) +{ + GtkWidget *sv_plane; + gint width, height; + + sv_plane = editor->priv->sv_plane; + + width = gtk_widget_get_allocated_width (GTK_WIDGET (sv_plane)); + height = gtk_widget_get_allocated_height (GTK_WIDGET (sv_plane)); + + editor->priv->x = CLAMP (width * editor->priv->v, 0, width - 1); + editor->priv->y = CLAMP (height * (1 - editor->priv->s), 0, height - 1); +} + +static gboolean +sv_configure (GtkWidget *widget, + GdkEventConfigure *event, + GtkColorEditor *editor) +{ + create_sv_surface (editor); + hsv_to_xy (editor); + return TRUE; +} + +static void +set_cross_grab (GtkWidget *widget, + GdkDevice *device, + guint32 time) +{ + GdkCursor *cursor; + + cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (widget)), + GDK_CROSSHAIR); + gdk_device_grab (device, + gtk_widget_get_window (widget), + GDK_OWNERSHIP_NONE, + FALSE, + GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON_RELEASE_MASK, + cursor, + time); + g_object_unref (cursor); +} + +static gboolean +sv_grab_broken (GtkWidget *widget, + GdkEventGrabBroken *event, + GtkColorEditor *editor) +{ + editor->priv->in_drag = FALSE; + + return TRUE; +} + +static guint +scale_round (gdouble value, gdouble scale) +{ + value = floor (value * scale + 0.5); + value = MAX (value, 0); + value = MIN (value, scale); + return (guint)value; +} + +static void +update_entry (GtkColorEditor *editor) +{ + gchar *text; + + text = g_strdup_printf ("#%02X%02X%02X", + scale_round (editor->priv->color.red, 255), + scale_round (editor->priv->color.green, 255), + scale_round (editor->priv->color.blue, 255)); + gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text); + editor->priv->text_changed = FALSE; + g_free (text); +} + +static void +sv_update_color (GtkColorEditor *editor, + gint x, + gint y) +{ + GtkWidget *sv_plane; + + sv_plane = editor->priv->sv_plane; + + editor->priv->x = x; + editor->priv->y = y; + + editor->priv->s = CLAMP (1 - y * (1.0 / gtk_widget_get_allocated_height (sv_plane)), 0, 1); + editor->priv->v = CLAMP (x * (1.0 / gtk_widget_get_allocated_width (sv_plane)), 0, 1); + gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, + &editor->priv->color.red, + &editor->priv->color.green, + &editor->priv->color.blue); + update_entry (editor); + gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); + gtk_widget_queue_draw (editor->priv->swatch); + gtk_widget_queue_draw (sv_plane); +} + +static gboolean +sv_button_press (GtkWidget *widget, + GdkEventButton *event, + GtkColorEditor *editor) +{ + if (editor->priv->in_drag || event->button != GDK_BUTTON_PRIMARY) + return FALSE; + + editor->priv->in_drag = TRUE; + set_cross_grab (widget, gdk_event_get_device ((GdkEvent*)event), event->time); + sv_update_color (editor, event->x, event->y); + gtk_widget_grab_focus (widget); + + return TRUE; +} + +static gboolean +sv_button_release (GtkWidget *widget, + GdkEventButton *event, + GtkColorEditor *editor) +{ + if (!editor->priv->in_drag || event->button != GDK_BUTTON_PRIMARY) + return FALSE; + + editor->priv->in_drag = FALSE; + + sv_update_color (editor, event->x, event->y); + gdk_device_ungrab (gdk_event_get_device ((GdkEvent *) event), event->time); + + return TRUE; +} + +static gboolean +sv_motion (GtkWidget *widget, + GdkEventMotion *event, + GtkColorEditor *editor) +{ + if (!editor->priv->in_drag) + return FALSE; + + gdk_event_request_motions (event); + sv_update_color (editor, event->x, event->y); + + return TRUE; +} - gtk_hsv_get_color (hsv, &h, &s, &v); - gtk_hsv_to_rgb (h, s, v, +static void +sv_move (GtkColorEditor *editor, + gdouble ds, + gdouble dv) +{ + if (editor->priv->s + ds > 1) + { + if (editor->priv->s < 1) + editor->priv->s = 1; + else + goto error; + } + else if (editor->priv->s + ds < 0) + { + if (editor->priv->s > 0) + editor->priv->s = 0; + else + goto error; + } + else + { + editor->priv->s += ds; + } + + if (editor->priv->v + dv > 1) + { + if (editor->priv->v < 1) + editor->priv->v = 1; + else + goto error; + } + else if (editor->priv->v + dv < 0) + { + if (editor->priv->v > 0) + editor->priv->v = 0; + else + goto error; + } + else + { + editor->priv->v += dv; + } + + gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, &editor->priv->color.red, &editor->priv->color.green, &editor->priv->color.blue); - editor->priv->color.alpha = 1; + + hsv_to_xy (editor); + + update_entry (editor); + gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); + gtk_widget_queue_draw (editor->priv->swatch); + gtk_widget_queue_draw (editor->priv->sv_plane); + return; + +error: + gtk_widget_error_bell (editor->priv->sv_plane); +} + +static gboolean +sv_key_press (GtkWidget *widget, + GdkEventKey *event, + GtkColorEditor *editor) +{ + gdouble step; + + /* FIXME: turn into bindings */ + if ((event->state & GDK_MOD1_MASK) != 0) + step = 0.1; + else + step = 0.01; + + if (event->keyval == GDK_KEY_Up || + event->keyval == GDK_KEY_KP_Up) + sv_move (editor, step, 0); + else if (event->keyval == GDK_KEY_Down || + event->keyval == GDK_KEY_KP_Down) + sv_move (editor, -step, 0); + else if (event->keyval == GDK_KEY_Left || + event->keyval == GDK_KEY_KP_Left) + sv_move (editor, 0, -step); + else if (event->keyval == GDK_KEY_Right || + event->keyval == GDK_KEY_KP_Right) + sv_move (editor, 0, step); + else + return FALSE; + + return TRUE; +} + +static void +entry_apply (GtkWidget *entry, + GtkColorEditor *editor) +{ + GdkRGBA color; + gchar *text; + + if (!editor->priv->text_changed) + return; + + text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1); + if (gdk_rgba_parse (&color, text)) + { + editor->priv->color.red = color.red; + editor->priv->color.green = color.green; + editor->priv->color.blue = color.blue; + gtk_rgb_to_hsv (editor->priv->color.red, + editor->priv->color.green, + editor->priv->color.blue, + &editor->priv->h, &editor->priv->s, &editor->priv->v); + hsv_to_xy (editor); + gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); + gtk_widget_queue_draw (GTK_WIDGET (editor)); + g_object_notify (G_OBJECT (editor), "color"); + } + + editor->priv->text_changed = FALSE; + + g_free (text); +} + +static gboolean +entry_focus_out (GtkWidget *entry, + GdkEventFocus *event, + GtkColorEditor *editor) +{ + entry_apply (entry, editor); + return FALSE; +} + +static void +entry_text_changed (GtkWidget *entry, + GParamSpec *pspec, + GtkColorEditor *editor) +{ + editor->priv->text_changed = TRUE; +} + +static void +h_changed (GtkAdjustment *adj, + GtkColorEditor *editor) +{ + editor->priv->h = gtk_adjustment_get_value (adj); + + gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v, + &editor->priv->color.red, + &editor->priv->color.green, + &editor->priv->color.blue); + create_sv_surface (editor); + gtk_widget_queue_draw (editor->priv->sv_plane); + gtk_widget_queue_draw (editor->priv->swatch); + update_entry (editor); + g_object_notify (G_OBJECT (editor), "color"); +} + +static void +a_changed (GtkAdjustment *adj, + GtkColorEditor *editor) +{ + editor->priv->color.alpha = gtk_adjustment_get_value (adj); + gtk_widget_queue_draw (editor->priv->swatch); + g_object_notify (G_OBJECT (editor), "color"); +} + +static cairo_pattern_t * +get_checkered_pattern (void) +{ + /* need to respect pixman's stride being a multiple of 4 */ + static unsigned char data[8] = { 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00 }; + static cairo_surface_t *checkered = NULL; + cairo_pattern_t *pattern; + + if (checkered == NULL) + checkered = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_A8, + 2, 2, 4); + + pattern = cairo_pattern_create_for_surface (checkered); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST); + + return pattern; +} + +static gboolean +swatch_draw (GtkWidget *swatch, + cairo_t *cr, + GtkColorEditor *editor) +{ + cairo_pattern_t *checkered; + + cairo_set_source_rgb (cr, 0.33, 0.33, 0.33); + cairo_paint (cr); + + cairo_set_source_rgb (cr, 0.66, 0.66, 0.66); + cairo_scale (cr, 8, 8); + + checkered = get_checkered_pattern (); + cairo_mask (cr, checkered); + cairo_pattern_destroy (checkered); + + gdk_cairo_set_source_rgba (cr, &editor->priv->color); + cairo_paint (cr); + + return TRUE; } static void gtk_color_editor_init (GtkColorEditor *editor) { - GtkWidget *hsv; + GtkWidget *grid; + GtkAdjustment *adj; editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor, GTK_TYPE_COLOR_EDITOR, GtkColorEditorPrivate); gtk_widget_push_composite_child (); - editor->priv->hsv = hsv = gtk_hsv_new (); - gtk_hsv_set_metrics (GTK_HSV (hsv), 300, 20); - g_signal_connect (hsv, "changed", G_CALLBACK (hsv_changed), editor); - gtk_widget_show (hsv); - gtk_container_add (GTK_CONTAINER (editor), hsv); + editor->priv->grid = grid = gtk_grid_new (); + gtk_grid_set_row_spacing (GTK_GRID (grid), 12); + gtk_grid_set_column_spacing (GTK_GRID (grid), 12); + + editor->priv->swatch = gtk_drawing_area_new (); + g_signal_connect (editor->priv->swatch, "draw", G_CALLBACK (swatch_draw), editor); + editor->priv->entry = gtk_entry_new (); + g_signal_connect (editor->priv->entry, "activate", + G_CALLBACK (entry_apply), editor); + g_signal_connect (editor->priv->entry, "notify::text", + G_CALLBACK (entry_text_changed), editor); + g_signal_connect (editor->priv->entry, "focus-out-event", + G_CALLBACK (entry_focus_out), editor); + + adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0); + g_signal_connect (adj, "value-changed", G_CALLBACK (h_changed), editor); + editor->priv->h_slider = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj); + editor->priv->h_adj = adj; + + gtk_scale_set_draw_value (GTK_SCALE (editor->priv->h_slider), FALSE); + editor->priv->sv_plane = gtk_drawing_area_new (); + gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300); + gtk_widget_set_hexpand (editor->priv->sv_plane, TRUE); + gtk_widget_set_vexpand (editor->priv->sv_plane, TRUE); + + adj = gtk_adjustment_new (1, 0, 1, 0.01, 0.1, 0); + editor->priv->a_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj); + g_signal_connect (adj, "value-changed", G_CALLBACK (a_changed), editor); + gtk_scale_set_draw_value (GTK_SCALE (editor->priv->a_slider), FALSE); + editor->priv->a_adj = adj; + + gtk_widget_set_can_focus (editor->priv->sv_plane, TRUE); + gtk_widget_set_events (editor->priv->sv_plane, GDK_KEY_PRESS_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK); + + g_signal_connect (editor->priv->sv_plane, "draw", G_CALLBACK (sv_draw), editor); + g_signal_connect (editor->priv->sv_plane, "configure-event", G_CALLBACK (sv_configure), editor); + g_signal_connect (editor->priv->sv_plane, "button-press-event", G_CALLBACK (sv_button_press), editor); + g_signal_connect (editor->priv->sv_plane, "button-release-event", G_CALLBACK (sv_button_release), editor); + g_signal_connect (editor->priv->sv_plane, "motion-notify-event", G_CALLBACK (sv_motion), editor); + g_signal_connect (editor->priv->sv_plane, "grab-broken-event", G_CALLBACK (sv_grab_broken), editor); + g_signal_connect (editor->priv->sv_plane, "key-press-event", G_CALLBACK (sv_key_press), editor); + + gtk_grid_attach (GTK_GRID (grid), editor->priv->swatch, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->entry, 2, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->h_slider, 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->sv_plane, 1, 1, 2, 1); + gtk_grid_attach (GTK_GRID (grid), editor->priv->a_slider, 1, 2, 2, 1); + gtk_widget_show_all (grid); + + gtk_container_add (GTK_CONTAINER (editor), grid); gtk_widget_pop_composite_child (); } @@ -147,14 +668,23 @@ gtk_color_editor_set_color (GtkColorChooser *chooser, const GdkRGBA *color) { GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser); - gdouble h, s, v; editor->priv->color.red = color->red; editor->priv->color.green = color->green; editor->priv->color.blue = color->blue; + gtk_rgb_to_hsv (editor->priv->color.red, + editor->priv->color.green, + editor->priv->color.blue, + &editor->priv->h, &editor->priv->s, &editor->priv->v); + hsv_to_xy (editor); + gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h); + update_entry (editor); + editor->priv->color.alpha = color->alpha; - gtk_rgb_to_hsv (color->red, color->green, color->blue, &h, &s, &v); - gtk_hsv_set_color (GTK_HSV (editor->priv->hsv), h, s, v); + gtk_adjustment_set_value (editor->priv->a_adj, editor->priv->color.alpha); + + gtk_widget_queue_draw (GTK_WIDGET (editor)); + g_object_notify (G_OBJECT (editor), "color"); } diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c index 25dbcb0c18..bf5edf654d 100644 --- a/gtk/gtkcolorswatch.c +++ b/gtk/gtkcolorswatch.c @@ -23,6 +23,11 @@ #include "gtkroundedboxprivate.h" #include "gtkdnd.h" #include "gtkicontheme.h" +#include "gtkmain.h" +#include "gtkmenu.h" +#include "gtkmenuitem.h" +#include "gtkmenushell.h" +#include "gtkbindings.h" #include "gtkprivate.h" #include "gtkintl.h" @@ -343,6 +348,9 @@ swatch_key_press (GtkWidget *widget, return TRUE; } + if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event)) + return TRUE; + return FALSE; } @@ -364,6 +372,77 @@ swatch_leave (GtkWidget *widget, return FALSE; } +static void +emit_customize (GtkColorSwatch *swatch) +{ + g_signal_emit (swatch, signals[CUSTOMIZE], 0); +} + +static void +popup_position_func (GtkMenu *menu, + gint *x, + gint *y, + gboolean *push_in, + gpointer user_data) +{ + GtkWidget *widget; + GtkRequisition req; + gint root_x, root_y; + GdkScreen *screen; + GdkWindow *window; + GdkRectangle monitor; + gint monitor_num; + + widget = GTK_WIDGET (user_data); + g_return_if_fail (gtk_widget_get_realized (widget)); + window = gtk_widget_get_window (widget); + + screen = gtk_widget_get_screen (widget); + monitor_num = gdk_screen_get_monitor_at_window (screen, window); + if (monitor_num < 0) + monitor_num = 0; + gtk_menu_set_monitor (menu, monitor_num); + + gdk_window_get_origin (window, &root_x, &root_y); + gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL); + + /* Put corner of menu centered on swatch */ + *x = root_x + gtk_widget_get_allocated_width (widget) / 2; + *y = root_y + gtk_widget_get_allocated_height (widget) / 2; + + /* Ensure sanity */ + gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); + *x = CLAMP (*x, monitor.x, MAX (monitor.x, monitor.width - req.width)); + *y = CLAMP (*y, monitor.y, MAX (monitor.y, monitor.height - req.height)); +} + +static void +do_popup (GtkWidget *swatch, + GdkEventButton *event) +{ + GtkWidget *menu; + GtkWidget *item; + + menu = gtk_menu_new (); + item = gtk_menu_item_new_with_mnemonic (_("_Customize")); + gtk_menu_attach_to_widget (GTK_MENU (menu), swatch, NULL); + + g_signal_connect_swapped (item, "activate", + G_CALLBACK (emit_customize), swatch); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + gtk_widget_show_all (item); + + if (event) + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + NULL, NULL, event->button, event->time); + else + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + popup_position_func, swatch, + 0, gtk_get_current_event_time ()); +} + static gboolean swatch_button_press (GtkWidget *widget, GdkEventButton *event) @@ -374,7 +453,7 @@ swatch_button_press (GtkWidget *widget, if (gdk_event_triggers_context_menu ((GdkEvent *) event)) { - g_signal_emit (swatch, signals[CUSTOMIZE], 0); + do_popup (widget, event); return TRUE; } else if (event->button == GDK_BUTTON_PRIMARY && @@ -413,6 +492,13 @@ swatch_button_release (GtkWidget *widget, return FALSE; } +static gboolean +swatch_menu (GtkWidget *swatch) +{ + do_popup (swatch, NULL); + return TRUE; +} + static void gtk_color_swatch_class_init (GtkColorSwatchClass *class) { @@ -430,6 +516,7 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class) widget_class->drag_data_get = swatch_drag_data_get; widget_class->drag_data_received = swatch_drag_data_received; widget_class->key_press_event = swatch_key_press; + widget_class->popup_menu = swatch_menu; widget_class->button_press_event = swatch_button_press; widget_class->button_release_event = swatch_button_release; widget_class->enter_notify_event = swatch_enter; |