1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2012 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 #include "gtkcolorswatchprivate.h"
24 #include "gtkcolorchooserprivate.h"
25 #include "gtkroundedboxprivate.h"
26 #include "gtkthemingbackgroundprivate.h"
28 #include "gtkicontheme.h"
31 #include "gtkmenuitem.h"
32 #include "gtkmenushell.h"
33 #include "gtkprivate.h"
35 #include "a11y/gtkcolorswatchaccessible.h"
38 struct _GtkColorSwatchPrivate
44 guint contains_pointer : 1;
63 static guint signals[LAST_SIGNAL];
65 G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
68 gtk_color_swatch_init (GtkColorSwatch *swatch)
70 swatch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swatch,
71 GTK_TYPE_COLOR_SWATCH,
72 GtkColorSwatchPrivate);
74 gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
75 gtk_widget_set_events (GTK_WIDGET (swatch), GDK_BUTTON_PRESS_MASK
76 | GDK_BUTTON_RELEASE_MASK
78 | GDK_ENTER_NOTIFY_MASK
79 | GDK_LEAVE_NOTIFY_MASK);
80 swatch->priv->use_alpha = TRUE;
81 swatch->priv->selectable = TRUE;
84 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
87 swatch_draw (GtkWidget *widget,
90 GtkColorSwatch *swatch = (GtkColorSwatch*)widget;
91 GtkThemingBackground background;
92 gdouble width, height;
93 GtkStyleContext *context;
96 GtkIconInfo *icon_info = NULL;
98 theme = gtk_icon_theme_get_default ();
99 context = gtk_widget_get_style_context (widget);
100 state = gtk_widget_get_state_flags (widget);
101 width = gtk_widget_get_allocated_width (widget);
102 height = gtk_widget_get_allocated_height (widget);
106 gtk_style_context_save (context);
107 gtk_style_context_set_state (context, state);
109 _gtk_theming_background_init_from_context (&background, context,
113 if (swatch->priv->has_color)
115 cairo_pattern_t *pattern;
116 cairo_matrix_t matrix;
118 if (swatch->priv->use_alpha)
122 _gtk_rounded_box_path (&background.clip_box, cr);
123 cairo_clip_preserve (cr);
125 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
126 cairo_fill_preserve (cr);
128 pattern = _gtk_color_chooser_get_checkered_pattern ();
129 cairo_matrix_init_scale (&matrix, 0.125, 0.125);
130 cairo_pattern_set_matrix (pattern, &matrix);
132 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
133 cairo_mask (cr, pattern);
134 cairo_pattern_destroy (pattern);
138 background.bg_color = swatch->priv->color;
142 background.bg_color = swatch->priv->color;
143 background.bg_color.alpha = 1.0;
146 _gtk_theming_background_render (&background, cr);
149 _gtk_theming_background_render (&background, cr);
151 gtk_render_frame (context, cr,
152 0, 0, width, height);
154 if (gtk_widget_has_visible_focus (widget))
156 cairo_set_line_width (cr, 2);
157 if (swatch->priv->has_color && INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) < 0.5)
158 cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
160 cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
161 _gtk_rounded_box_shrink (&background.clip_box, 3, 3, 3, 3);
162 _gtk_rounded_box_path (&background.clip_box, cr);
166 if (swatch->priv->icon)
168 icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16,
169 GTK_ICON_LOOKUP_GENERIC_FALLBACK
170 | GTK_ICON_LOOKUP_USE_BUILTIN);
172 else if ((state & GTK_STATE_FLAG_SELECTED) != 0)
175 GtkBorder border_width;
178 gtk_style_context_add_class (context, "color-active-badge");
179 gtk_style_context_get_background_color (context, state, &bg);
180 gtk_style_context_get_border_color (context, state, &border);
181 gtk_style_context_get_border (context, state, &border_width);
183 cairo_new_sub_path (cr);
184 cairo_arc (cr, width / 2, height / 2, 10, 0, 2 * G_PI);
185 cairo_close_path (cr);
186 gdk_cairo_set_source_rgba (cr, &bg);
187 cairo_fill_preserve (cr);
189 gdk_cairo_set_source_rgba (cr, &border);
190 cairo_set_line_width (cr, border_width.left);
193 gicon = g_themed_icon_new ("object-select-symbolic");
194 /* fallback for themes that don't have object-select-symbolic */
195 g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply");
197 icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16,
198 GTK_ICON_LOOKUP_GENERIC_FALLBACK
199 | GTK_ICON_LOOKUP_USE_BUILTIN);
200 g_object_unref (gicon);
203 if (icon_info != NULL)
207 pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context,
212 gtk_render_icon (context, cr, pixbuf,
213 (width - gdk_pixbuf_get_width (pixbuf)) / 2,
214 (height - gdk_pixbuf_get_height (pixbuf)) / 2);
215 g_object_unref (pixbuf);
218 gtk_icon_info_free (icon_info);
222 gtk_style_context_restore (context);
228 drag_set_color_icon (GdkDragContext *context,
229 const GdkRGBA *color)
231 cairo_surface_t *surface;
234 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 48, 32);
235 cr = cairo_create (surface);
236 gdk_cairo_set_source_rgba (cr, color);
239 cairo_surface_set_device_offset (surface, -4, -4);
240 gtk_drag_set_icon_surface (context, surface);
243 cairo_surface_destroy (surface);
247 swatch_drag_begin (GtkWidget *widget,
248 GdkDragContext *context)
250 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
253 gtk_color_swatch_get_rgba (swatch, &color);
254 drag_set_color_icon (context, &color);
258 swatch_drag_data_get (GtkWidget *widget,
259 GdkDragContext *context,
260 GtkSelectionData *selection_data,
264 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
268 gtk_color_swatch_get_rgba (swatch, &color);
270 vals[0] = color.red * 0xffff;
271 vals[1] = color.green * 0xffff;
272 vals[2] = color.blue * 0xffff;
273 vals[3] = color.alpha * 0xffff;
275 gtk_selection_data_set (selection_data,
276 gdk_atom_intern_static_string ("application/x-color"),
277 16, (guchar *)vals, 8);
281 swatch_drag_data_received (GtkWidget *widget,
282 GdkDragContext *context,
285 GtkSelectionData *selection_data,
293 length = gtk_selection_data_get_length (selection_data);
298 /* We accept drops with the wrong format, since the KDE color
299 * chooser incorrectly drops application/x-color with format 8.
303 g_warning ("Received invalid color data\n");
307 vals = (guint16 *) gtk_selection_data_get_data (selection_data);
309 color.red = (gdouble)vals[0] / 0xffff;
310 color.green = (gdouble)vals[1] / 0xffff;
311 color.blue = (gdouble)vals[2] / 0xffff;
312 color.alpha = (gdouble)vals[3] / 0xffff;
314 gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
318 swatch_get_preferred_width (GtkWidget *widget,
326 swatch_get_preferred_height (GtkWidget *widget,
334 swatch_key_press (GtkWidget *widget,
337 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
339 if (event->keyval == GDK_KEY_space ||
340 event->keyval == GDK_KEY_Return ||
341 event->keyval == GDK_KEY_ISO_Enter||
342 event->keyval == GDK_KEY_KP_Enter ||
343 event->keyval == GDK_KEY_KP_Space)
345 if (swatch->priv->has_color &&
346 swatch->priv->selectable &&
347 (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
348 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
350 g_signal_emit (swatch, signals[ACTIVATE], 0);
354 if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event))
361 swatch_enter_notify (GtkWidget *widget,
362 GdkEventCrossing *event)
364 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
365 swatch->priv->contains_pointer = TRUE;
370 swatch_leave_notify (GtkWidget *widget,
371 GdkEventCrossing *event)
373 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
374 swatch->priv->contains_pointer = FALSE;
379 emit_customize (GtkColorSwatch *swatch)
381 g_signal_emit (swatch, signals[CUSTOMIZE], 0);
385 popup_position_func (GtkMenu *menu,
396 GdkRectangle monitor;
399 widget = GTK_WIDGET (user_data);
400 g_return_if_fail (gtk_widget_get_realized (widget));
401 window = gtk_widget_get_window (widget);
403 screen = gtk_widget_get_screen (widget);
404 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
407 gtk_menu_set_monitor (menu, monitor_num);
409 gdk_window_get_origin (window, &root_x, &root_y);
410 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
412 /* Put corner of menu centered on swatch */
413 *x = root_x + gtk_widget_get_allocated_width (widget) / 2;
414 *y = root_y + gtk_widget_get_allocated_height (widget) / 2;
417 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
418 *x = CLAMP (*x, monitor.x, MAX (monitor.x, monitor.width - req.width));
419 *y = CLAMP (*y, monitor.y, MAX (monitor.y, monitor.height - req.height));
423 do_popup (GtkWidget *swatch,
424 GdkEventButton *event)
429 menu = gtk_menu_new ();
430 item = gtk_menu_item_new_with_mnemonic (_("_Customize"));
431 gtk_menu_attach_to_widget (GTK_MENU (menu), swatch, NULL);
433 g_signal_connect_swapped (item, "activate",
434 G_CALLBACK (emit_customize), swatch);
436 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
438 gtk_widget_show_all (item);
441 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
442 NULL, NULL, event->button, event->time);
444 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
445 popup_position_func, swatch,
446 0, gtk_get_current_event_time ());
450 swatch_button_press (GtkWidget *widget,
451 GdkEventButton *event)
453 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
455 gtk_widget_grab_focus (widget);
457 if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
458 swatch->priv->has_color)
460 do_popup (widget, event);
463 else if (event->type == GDK_2BUTTON_PRESS &&
464 event->button == GDK_BUTTON_PRIMARY)
466 g_signal_emit (swatch, signals[ACTIVATE], 0);
474 swatch_button_release (GtkWidget *widget,
475 GdkEventButton *event)
477 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
480 if (event->button == GDK_BUTTON_PRIMARY &&
481 swatch->priv->contains_pointer)
483 flags = gtk_widget_get_state_flags (widget);
484 if (!swatch->priv->has_color)
486 g_signal_emit (swatch, signals[ACTIVATE], 0);
489 else if (swatch->priv->selectable &&
490 (flags & GTK_STATE_FLAG_SELECTED) == 0)
492 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
501 swatch_realize (GtkWidget *widget)
503 GtkAllocation allocation;
505 GdkWindowAttr attributes;
506 gint attributes_mask;
508 gtk_widget_set_realized (widget, TRUE);
509 gtk_widget_get_allocation (widget, &allocation);
511 attributes.window_type = GDK_WINDOW_CHILD;
512 attributes.x = allocation.x;
513 attributes.y = allocation.y;
514 attributes.width = allocation.width;
515 attributes.height = allocation.height;
516 attributes.wclass = GDK_INPUT_OUTPUT;
517 attributes.visual = gtk_widget_get_visual (widget);
518 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
523 &attributes, attributes_mask);
524 gdk_window_set_user_data (window, widget);
525 gtk_widget_set_window (widget, window);
529 swatch_popup_menu (GtkWidget *swatch)
531 do_popup (swatch, NULL);
535 /* GObject implementation {{{1 */
538 swatch_get_property (GObject *object,
543 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
549 gtk_color_swatch_get_rgba (swatch, &color);
550 g_value_set_boxed (value, &color);
552 case PROP_SELECTABLE:
553 g_value_set_boolean (value, gtk_color_swatch_get_selectable (swatch));
556 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
562 swatch_set_property (GObject *object,
567 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
572 gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
574 case PROP_SELECTABLE:
575 gtk_color_swatch_set_selectable (swatch, g_value_get_boolean (value));
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
584 swatch_finalize (GObject *object)
586 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
588 g_free (swatch->priv->icon);
590 G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
594 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
596 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
597 GObjectClass *object_class = (GObjectClass *)class;
599 object_class->get_property = swatch_get_property;
600 object_class->set_property = swatch_set_property;
601 object_class->finalize = swatch_finalize;
603 widget_class->get_preferred_width = swatch_get_preferred_width;
604 widget_class->get_preferred_height = swatch_get_preferred_height;
605 widget_class->draw = swatch_draw;
606 widget_class->drag_begin = swatch_drag_begin;
607 widget_class->drag_data_get = swatch_drag_data_get;
608 widget_class->drag_data_received = swatch_drag_data_received;
609 widget_class->key_press_event = swatch_key_press;
610 widget_class->popup_menu = swatch_popup_menu;
611 widget_class->button_press_event = swatch_button_press;
612 widget_class->button_release_event = swatch_button_release;
613 widget_class->enter_notify_event = swatch_enter_notify;
614 widget_class->leave_notify_event = swatch_leave_notify;
615 widget_class->realize = swatch_realize;
618 g_signal_new ("activate",
619 GTK_TYPE_COLOR_SWATCH,
621 G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
622 NULL, NULL, NULL, G_TYPE_NONE, 0);
625 g_signal_new ("customize",
626 GTK_TYPE_COLOR_SWATCH,
628 G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
629 NULL, NULL, NULL, G_TYPE_NONE, 0);
631 g_object_class_install_property (object_class, PROP_RGBA,
632 g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
633 GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
634 g_object_class_install_property (object_class, PROP_SELECTABLE,
635 g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the swatch is selectable"),
636 TRUE, GTK_PARAM_READWRITE));
638 g_type_class_add_private (object_class, sizeof (GtkColorSwatchPrivate));
640 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
643 /* Public API {{{1 */
646 gtk_color_swatch_new (void)
648 return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
651 static const GtkTargetEntry dnd_targets[] = {
652 { "application/x-color", 0 }
656 gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
657 const GdkRGBA *color)
659 GtkStyleContext *context;
661 context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
663 if (!swatch->priv->has_color)
665 gtk_drag_source_set (GTK_WIDGET (swatch),
666 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
667 dnd_targets, G_N_ELEMENTS (dnd_targets),
668 GDK_ACTION_COPY | GDK_ACTION_MOVE);
672 gtk_style_context_remove_class (context, "color-light");
673 gtk_style_context_remove_class (context, "color-dark");
676 swatch->priv->has_color = TRUE;
677 swatch->priv->color = *color;
679 if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
680 gtk_style_context_add_class (context, "color-light");
682 gtk_style_context_add_class (context, "color-dark");
684 gtk_widget_queue_draw (GTK_WIDGET (swatch));
685 g_object_notify (G_OBJECT (swatch), "rgba");
689 gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
692 if (swatch->priv->has_color)
694 color->red = swatch->priv->color.red;
695 color->green = swatch->priv->color.green;
696 color->blue = swatch->priv->color.blue;
697 color->alpha = swatch->priv->color.alpha;
711 gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
714 swatch->priv->icon = g_strdup (icon);
715 gtk_widget_queue_draw (GTK_WIDGET (swatch));
719 gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
724 gtk_drag_dest_set (GTK_WIDGET (swatch),
725 GTK_DEST_DEFAULT_HIGHLIGHT |
726 GTK_DEST_DEFAULT_MOTION |
727 GTK_DEST_DEFAULT_DROP,
728 dnd_targets, G_N_ELEMENTS (dnd_targets),
733 gtk_drag_dest_unset (GTK_WIDGET (swatch));
738 gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
741 swatch->priv->use_alpha = use_alpha;
742 gtk_widget_queue_draw (GTK_WIDGET (swatch));
746 gtk_color_swatch_set_selectable (GtkColorSwatch *swatch,
749 if (selectable == swatch->priv->selectable)
752 swatch->priv->selectable = selectable;
753 g_object_notify (G_OBJECT (swatch), "selectable");
757 gtk_color_swatch_get_selectable (GtkColorSwatch *swatch)
759 return swatch->priv->selectable;
762 /* vim:set foldmethod=marker: */