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"
37 struct _GtkColorSwatchPrivate
43 guint contains_pointer : 1;
60 static guint signals[LAST_SIGNAL];
62 G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_DRAWING_AREA)
65 gtk_color_swatch_init (GtkColorSwatch *swatch)
67 swatch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swatch,
68 GTK_TYPE_COLOR_SWATCH,
69 GtkColorSwatchPrivate);
71 gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
72 gtk_widget_set_events (GTK_WIDGET (swatch), GDK_BUTTON_PRESS_MASK
73 | GDK_BUTTON_RELEASE_MASK
75 | GDK_ENTER_NOTIFY_MASK
76 | GDK_LEAVE_NOTIFY_MASK);
77 swatch->priv->use_alpha = TRUE;
80 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
83 swatch_draw (GtkWidget *widget,
86 GtkColorSwatch *swatch = (GtkColorSwatch*)widget;
87 GtkThemingBackground background;
88 gdouble width, height;
89 GtkStyleContext *context;
92 GtkIconInfo *icon_info = NULL;
94 theme = gtk_icon_theme_get_default ();
95 context = gtk_widget_get_style_context (widget);
96 state = gtk_widget_get_state_flags (widget);
97 width = gtk_widget_get_allocated_width (widget);
98 height = gtk_widget_get_allocated_height (widget);
102 gtk_style_context_save (context);
103 gtk_style_context_set_state (context, state);
105 _gtk_theming_background_init_from_context (&background, context,
109 if (swatch->priv->has_color)
111 cairo_pattern_t *pattern;
112 cairo_matrix_t matrix;
114 if (swatch->priv->use_alpha)
118 _gtk_rounded_box_path (&background.clip_box, cr);
119 cairo_clip_preserve (cr);
121 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
122 cairo_fill_preserve (cr);
124 pattern = _gtk_color_chooser_get_checkered_pattern ();
125 cairo_matrix_init_scale (&matrix, 0.125, 0.125);
126 cairo_pattern_set_matrix (pattern, &matrix);
128 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
129 cairo_mask (cr, pattern);
130 cairo_pattern_destroy (pattern);
134 background.bg_color = swatch->priv->color;
138 background.bg_color = swatch->priv->color;
139 background.bg_color.alpha = 1.0;
142 _gtk_theming_background_render (&background, cr);
145 gtk_render_frame (context, cr,
146 0, 0, width, height);
148 if (gtk_widget_has_visible_focus (widget))
150 cairo_set_line_width (cr, 2);
151 if (swatch->priv->has_color && INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) < 0.5)
152 cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
154 cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
155 _gtk_rounded_box_shrink (&background.clip_box, 3, 3, 3, 3);
156 _gtk_rounded_box_path (&background.clip_box, cr);
160 if (swatch->priv->icon)
162 icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16,
163 GTK_ICON_LOOKUP_GENERIC_FALLBACK
164 | GTK_ICON_LOOKUP_USE_BUILTIN);
166 else if ((state & GTK_STATE_FLAG_SELECTED) != 0)
169 GtkBorder border_width;
172 gtk_style_context_add_class (context, "color-active-badge");
173 gtk_style_context_get_background_color (context, state, &bg);
174 gtk_style_context_get_border_color (context, state, &border);
175 gtk_style_context_get_border (context, state, &border_width);
177 cairo_new_sub_path (cr);
178 cairo_arc (cr, width / 2, height / 2, 10, 0, 2 * G_PI);
179 cairo_close_path (cr);
180 gdk_cairo_set_source_rgba (cr, &bg);
181 cairo_fill_preserve (cr);
183 gdk_cairo_set_source_rgba (cr, &border);
184 cairo_set_line_width (cr, border_width.left);
187 gicon = g_themed_icon_new ("object-select-symbolic");
188 /* fallback for themes that don't have object-select-symbolic */
189 g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply");
191 icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16,
192 GTK_ICON_LOOKUP_GENERIC_FALLBACK
193 | GTK_ICON_LOOKUP_USE_BUILTIN);
194 g_object_unref (gicon);
197 if (icon_info != NULL)
201 pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context,
206 gtk_render_icon (context, cr, pixbuf,
207 (width - gdk_pixbuf_get_width (pixbuf)) / 2,
208 (height - gdk_pixbuf_get_height (pixbuf)) / 2);
209 g_object_unref (pixbuf);
212 gtk_icon_info_free (icon_info);
216 gtk_style_context_restore (context);
222 drag_set_color_icon (GdkDragContext *context,
223 const GdkRGBA *color)
225 cairo_surface_t *surface;
228 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 48, 32);
229 cr = cairo_create (surface);
230 gdk_cairo_set_source_rgba (cr, color);
233 cairo_surface_set_device_offset (surface, -4, -4);
234 gtk_drag_set_icon_surface (context, surface);
237 cairo_surface_destroy (surface);
241 swatch_drag_begin (GtkWidget *widget,
242 GdkDragContext *context)
244 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
247 gtk_color_swatch_get_rgba (swatch, &color);
248 drag_set_color_icon (context, &color);
252 swatch_drag_data_get (GtkWidget *widget,
253 GdkDragContext *context,
254 GtkSelectionData *selection_data,
258 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
262 gtk_color_swatch_get_rgba (swatch, &color);
264 vals[0] = color.red * 0xffff;
265 vals[1] = color.green * 0xffff;
266 vals[2] = color.blue * 0xffff;
267 vals[3] = color.alpha * 0xffff;
269 gtk_selection_data_set (selection_data,
270 gdk_atom_intern_static_string ("application/x-color"),
271 16, (guchar *)vals, 8);
275 swatch_drag_data_received (GtkWidget *widget,
276 GdkDragContext *context,
279 GtkSelectionData *selection_data,
287 length = gtk_selection_data_get_length (selection_data);
292 /* We accept drops with the wrong format, since the KDE color
293 * chooser incorrectly drops application/x-color with format 8.
297 g_warning ("Received invalid color data\n");
301 vals = (guint16 *) gtk_selection_data_get_data (selection_data);
303 color.red = (gdouble)vals[0] / 0xffff;
304 color.green = (gdouble)vals[1] / 0xffff;
305 color.blue = (gdouble)vals[2] / 0xffff;
306 color.alpha = (gdouble)vals[3] / 0xffff;
308 gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
312 swatch_get_preferred_width (GtkWidget *widget,
320 swatch_get_preferred_height (GtkWidget *widget,
328 swatch_key_press (GtkWidget *widget,
331 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
333 if (event->keyval == GDK_KEY_space ||
334 event->keyval == GDK_KEY_Return ||
335 event->keyval == GDK_KEY_ISO_Enter||
336 event->keyval == GDK_KEY_KP_Enter ||
337 event->keyval == GDK_KEY_KP_Space)
339 if (swatch->priv->has_color && (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
340 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
342 g_signal_emit (swatch, signals[ACTIVATE], 0);
346 if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event))
353 swatch_enter_notify (GtkWidget *widget,
354 GdkEventCrossing *event)
356 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
357 swatch->priv->contains_pointer = TRUE;
362 swatch_leave_notify (GtkWidget *widget,
363 GdkEventCrossing *event)
365 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
366 swatch->priv->contains_pointer = FALSE;
371 emit_customize (GtkColorSwatch *swatch)
373 g_signal_emit (swatch, signals[CUSTOMIZE], 0);
377 popup_position_func (GtkMenu *menu,
388 GdkRectangle monitor;
391 widget = GTK_WIDGET (user_data);
392 g_return_if_fail (gtk_widget_get_realized (widget));
393 window = gtk_widget_get_window (widget);
395 screen = gtk_widget_get_screen (widget);
396 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
399 gtk_menu_set_monitor (menu, monitor_num);
401 gdk_window_get_origin (window, &root_x, &root_y);
402 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
404 /* Put corner of menu centered on swatch */
405 *x = root_x + gtk_widget_get_allocated_width (widget) / 2;
406 *y = root_y + gtk_widget_get_allocated_height (widget) / 2;
409 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
410 *x = CLAMP (*x, monitor.x, MAX (monitor.x, monitor.width - req.width));
411 *y = CLAMP (*y, monitor.y, MAX (monitor.y, monitor.height - req.height));
415 do_popup (GtkWidget *swatch,
416 GdkEventButton *event)
421 menu = gtk_menu_new ();
422 item = gtk_menu_item_new_with_mnemonic (_("_Customize"));
423 gtk_menu_attach_to_widget (GTK_MENU (menu), swatch, NULL);
425 g_signal_connect_swapped (item, "activate",
426 G_CALLBACK (emit_customize), swatch);
428 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
430 gtk_widget_show_all (item);
433 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
434 NULL, NULL, event->button, event->time);
436 gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
437 popup_position_func, swatch,
438 0, gtk_get_current_event_time ());
442 swatch_button_press (GtkWidget *widget,
443 GdkEventButton *event)
445 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
447 gtk_widget_grab_focus (widget);
449 if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
450 swatch->priv->has_color)
452 do_popup (widget, event);
455 else if (event->type == GDK_2BUTTON_PRESS &&
456 event->button == GDK_BUTTON_PRIMARY)
458 g_signal_emit (swatch, signals[ACTIVATE], 0);
466 swatch_button_release (GtkWidget *widget,
467 GdkEventButton *event)
469 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
472 if (event->button == GDK_BUTTON_PRIMARY &&
473 swatch->priv->contains_pointer)
475 flags = gtk_widget_get_state_flags (widget);
476 if (!swatch->priv->has_color)
478 g_signal_emit (swatch, signals[ACTIVATE], 0);
481 else if ((flags & GTK_STATE_FLAG_SELECTED) == 0)
483 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
492 swatch_popup_menu (GtkWidget *swatch)
494 do_popup (swatch, NULL);
498 /* GObject implementation {{{1 */
501 swatch_get_property (GObject *object,
506 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
512 gtk_color_swatch_get_rgba (swatch, &color);
513 g_value_set_boxed (value, &color);
516 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
522 swatch_set_property (GObject *object,
527 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
532 gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
535 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
541 swatch_finalize (GObject *object)
543 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
545 g_free (swatch->priv->icon);
547 G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
551 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
553 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
554 GObjectClass *object_class = (GObjectClass *)class;
556 object_class->get_property = swatch_get_property;
557 object_class->set_property = swatch_set_property;
558 object_class->finalize = swatch_finalize;
560 widget_class->get_preferred_width = swatch_get_preferred_width;
561 widget_class->get_preferred_height = swatch_get_preferred_height;
562 widget_class->draw = swatch_draw;
563 widget_class->drag_begin = swatch_drag_begin;
564 widget_class->drag_data_get = swatch_drag_data_get;
565 widget_class->drag_data_received = swatch_drag_data_received;
566 widget_class->key_press_event = swatch_key_press;
567 widget_class->popup_menu = swatch_popup_menu;
568 widget_class->button_press_event = swatch_button_press;
569 widget_class->button_release_event = swatch_button_release;
570 widget_class->enter_notify_event = swatch_enter_notify;
571 widget_class->leave_notify_event = swatch_leave_notify;
574 g_signal_new ("activate",
575 GTK_TYPE_COLOR_SWATCH,
577 G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
578 NULL, NULL, NULL, G_TYPE_NONE, 0);
581 g_signal_new ("customize",
582 GTK_TYPE_COLOR_SWATCH,
584 G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
585 NULL, NULL, NULL, G_TYPE_NONE, 0);
587 g_object_class_install_property (object_class, PROP_RGBA,
588 g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
589 GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
591 g_type_class_add_private (object_class, sizeof (GtkColorSwatchPrivate));
594 /* Public API {{{1 */
597 gtk_color_swatch_new (void)
599 return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
602 static const GtkTargetEntry dnd_targets[] = {
603 { "application/x-color", 0 }
607 gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
608 const GdkRGBA *color)
610 GtkStyleContext *context;
612 context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
614 if (!swatch->priv->has_color)
616 gtk_drag_source_set (GTK_WIDGET (swatch),
617 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
618 dnd_targets, G_N_ELEMENTS (dnd_targets),
619 GDK_ACTION_COPY | GDK_ACTION_MOVE);
623 gtk_style_context_remove_class (context, "color-light");
624 gtk_style_context_remove_class (context, "color-dark");
627 swatch->priv->has_color = TRUE;
628 swatch->priv->color = *color;
630 if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
631 gtk_style_context_add_class (context, "color-light");
633 gtk_style_context_add_class (context, "color-dark");
635 gtk_widget_queue_draw (GTK_WIDGET (swatch));
636 g_object_notify (G_OBJECT (swatch), "rgba");
640 gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
643 if (swatch->priv->has_color)
645 color->red = swatch->priv->color.red;
646 color->green = swatch->priv->color.green;
647 color->blue = swatch->priv->color.blue;
648 color->alpha = swatch->priv->color.alpha;
662 gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
665 swatch->priv->icon = g_strdup (icon);
666 gtk_widget_queue_draw (GTK_WIDGET (swatch));
670 gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
675 gtk_drag_dest_set (GTK_WIDGET (swatch),
676 GTK_DEST_DEFAULT_HIGHLIGHT |
677 GTK_DEST_DEFAULT_MOTION |
678 GTK_DEST_DEFAULT_DROP,
679 dnd_targets, G_N_ELEMENTS (dnd_targets),
684 gtk_drag_dest_unset (GTK_WIDGET (swatch));
689 gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
692 swatch->priv->use_alpha = use_alpha;
693 gtk_widget_queue_draw (GTK_WIDGET (swatch));
696 /* vim:set foldmethod=marker: */