1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2012 Red Hat, Inc.
3 *
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.
8 *
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.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include "gtkcolorswatchprivate.h"
21
22 #include "gtkcolorchooserprivate.h"
23 #include "gtkdnd.h"
24 #include "gtkicontheme.h"
25 #include "gtkmain.h"
26 #include "gtkmenu.h"
27 #include "gtkmenuitem.h"
28 #include "gtkmenushell.h"
29 #include "gtkprivate.h"
30 #include "gtkintl.h"
31 #include "gtkrenderprivate.h"
32 #include "gtkiconhelperprivate.h"
33 #include "gtkcssnodeprivate.h"
34 #include "gtkcsscustomgadgetprivate.h"
35 #include "gtkwidgetprivate.h"
36 #include "gtkstylecontextprivate.h"
37 #include "a11y/gtkcolorswatchaccessibleprivate.h"
38
39
40 /*
41 * GtkColorSwatch has two CSS nodes, the main one named colorswatch
42 * and a subnode named overlay. The main node gets the .light or .dark
43 * style classes added depending on the brightness of the color that
44 * the swatch is showing.
45 *
46 * The color swatch has the .activatable style class by default. It can
47 * be removed for non-activatable swatches.
48 */
49
50 struct _GtkColorSwatchPrivate
51 {
52 GdkRGBA color;
53 gdouble radius[4];
54 gchar *icon;
55 guint has_color : 1;
56 guint use_alpha : 1;
57 guint selectable : 1;
58 guint has_menu : 1;
59
60 GdkWindow *event_window;
61
62 GtkGesture *long_press_gesture;
63 GtkGesture *multipress_gesture;
64 GtkCssGadget *gadget;
65 GtkCssGadget *overlay_gadget;
66
67 GtkWidget *popover;
68 };
69
70 enum
71 {
72 PROP_ZERO,
73 PROP_RGBA,
74 PROP_SELECTABLE,
75 PROP_HAS_MENU
76 };
77
78 enum
79 {
80 ACTIVATE,
81 CUSTOMIZE,
82 LAST_SIGNAL
83 };
84
85 static guint signals[LAST_SIGNAL];
86
87
G_DEFINE_TYPE_WITH_PRIVATE(GtkColorSwatch,gtk_color_swatch,GTK_TYPE_WIDGET)88 G_DEFINE_TYPE_WITH_PRIVATE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
89
90 static gboolean
91 swatch_draw (GtkWidget *widget,
92 cairo_t *cr)
93 {
94 gtk_css_gadget_draw (GTK_COLOR_SWATCH (widget)->priv->gadget, cr);
95
96 return FALSE;
97 }
98
99 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
100
101 static gboolean
gtk_color_swatch_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)102 gtk_color_swatch_render (GtkCssGadget *gadget,
103 cairo_t *cr,
104 int x,
105 int y,
106 int width,
107 int height,
108 gpointer data)
109 {
110 GtkWidget *widget;
111 GtkColorSwatch *swatch;
112 GtkStyleContext *context;
113
114 widget = gtk_css_gadget_get_owner (gadget);
115 swatch = GTK_COLOR_SWATCH (widget);
116 context = gtk_widget_get_style_context (widget);
117
118 if (swatch->priv->has_color)
119 {
120 cairo_pattern_t *pattern;
121 cairo_matrix_t matrix;
122 GtkAllocation allocation, border_allocation;
123
124 gtk_widget_get_allocation (widget, &allocation);
125 gtk_css_gadget_get_border_allocation (gadget, &border_allocation, NULL);
126
127 border_allocation.x -= allocation.x;
128 border_allocation.y -= allocation.y;
129
130 gtk_render_content_path (context, cr,
131 border_allocation.x,
132 border_allocation.y,
133 border_allocation.width,
134 border_allocation.height);
135
136 if (swatch->priv->use_alpha)
137 {
138 cairo_save (cr);
139
140 cairo_clip_preserve (cr);
141
142 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
143 cairo_fill_preserve (cr);
144
145 pattern = _gtk_color_chooser_get_checkered_pattern ();
146 cairo_matrix_init_scale (&matrix, 0.125, 0.125);
147 cairo_pattern_set_matrix (pattern, &matrix);
148
149 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
150 cairo_mask (cr, pattern);
151 cairo_pattern_destroy (pattern);
152
153 cairo_restore (cr);
154
155 gdk_cairo_set_source_rgba (cr, &swatch->priv->color);
156 }
157 else
158 {
159 cairo_set_source_rgb (cr,
160 swatch->priv->color.red,
161 swatch->priv->color.green,
162 swatch->priv->color.blue);
163 }
164
165 cairo_fill (cr);
166 }
167
168 gtk_css_gadget_draw (swatch->priv->overlay_gadget, cr);
169
170 return gtk_widget_has_visible_focus (widget);
171 }
172
173 static void
drag_set_color_icon(GdkDragContext * context,const GdkRGBA * color)174 drag_set_color_icon (GdkDragContext *context,
175 const GdkRGBA *color)
176 {
177 cairo_surface_t *surface;
178 cairo_t *cr;
179
180 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 48, 32);
181 cr = cairo_create (surface);
182 gdk_cairo_set_source_rgba (cr, color);
183 cairo_paint (cr);
184
185 cairo_surface_set_device_offset (surface, -4, -4);
186 gtk_drag_set_icon_surface (context, surface);
187
188 cairo_destroy (cr);
189 cairo_surface_destroy (surface);
190 }
191
192 static void
swatch_drag_begin(GtkWidget * widget,GdkDragContext * context)193 swatch_drag_begin (GtkWidget *widget,
194 GdkDragContext *context)
195 {
196 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
197 GdkRGBA color;
198
199 gtk_color_swatch_get_rgba (swatch, &color);
200 drag_set_color_icon (context, &color);
201 }
202
203 static void
swatch_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)204 swatch_drag_data_get (GtkWidget *widget,
205 GdkDragContext *context,
206 GtkSelectionData *selection_data,
207 guint info,
208 guint time)
209 {
210 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
211 guint16 vals[4];
212 GdkRGBA color;
213
214 gtk_color_swatch_get_rgba (swatch, &color);
215
216 vals[0] = color.red * 0xffff;
217 vals[1] = color.green * 0xffff;
218 vals[2] = color.blue * 0xffff;
219 vals[3] = color.alpha * 0xffff;
220
221 gtk_selection_data_set (selection_data,
222 gdk_atom_intern_static_string ("application/x-color"),
223 16, (guchar *)vals, 8);
224 }
225
226 static void
swatch_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)227 swatch_drag_data_received (GtkWidget *widget,
228 GdkDragContext *context,
229 gint x,
230 gint y,
231 GtkSelectionData *selection_data,
232 guint info,
233 guint time)
234 {
235 gint length;
236 guint16 *vals;
237 GdkRGBA color;
238
239 length = gtk_selection_data_get_length (selection_data);
240
241 if (length < 0)
242 return;
243
244 /* We accept drops with the wrong format, since the KDE color
245 * chooser incorrectly drops application/x-color with format 8.
246 */
247 if (length != 8)
248 {
249 g_warning ("Received invalid color data");
250 return;
251 }
252
253 vals = (guint16 *) gtk_selection_data_get_data (selection_data);
254
255 color.red = (gdouble)vals[0] / 0xffff;
256 color.green = (gdouble)vals[1] / 0xffff;
257 color.blue = (gdouble)vals[2] / 0xffff;
258 color.alpha = (gdouble)vals[3] / 0xffff;
259
260 gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
261 }
262
263 static void
gtk_color_swatch_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer unused)264 gtk_color_swatch_measure (GtkCssGadget *gadget,
265 GtkOrientation orientation,
266 int for_size,
267 int *minimum,
268 int *natural,
269 int *minimum_baseline,
270 int *natural_baseline,
271 gpointer unused)
272 {
273 GtkWidget *widget;
274 GtkColorSwatch *swatch;
275 gint w, h, min;
276
277 widget = gtk_css_gadget_get_owner (gadget);
278 swatch = GTK_COLOR_SWATCH (widget);
279
280 gtk_css_gadget_get_preferred_size (swatch->priv->overlay_gadget,
281 orientation,
282 -1,
283 minimum, natural,
284 NULL, NULL);
285
286 gtk_widget_get_size_request (widget, &w, &h);
287 if (orientation == GTK_ORIENTATION_HORIZONTAL)
288 min = w < 0 ? 48 : w;
289 else
290 min = h < 0 ? 32 : h;
291
292 *minimum = MAX (*minimum, min);
293 *natural = MAX (*natural, min);
294 }
295
296 static gboolean
swatch_key_press(GtkWidget * widget,GdkEventKey * event)297 swatch_key_press (GtkWidget *widget,
298 GdkEventKey *event)
299 {
300 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
301
302 if (event->keyval == GDK_KEY_space ||
303 event->keyval == GDK_KEY_Return ||
304 event->keyval == GDK_KEY_ISO_Enter||
305 event->keyval == GDK_KEY_KP_Enter ||
306 event->keyval == GDK_KEY_KP_Space)
307 {
308 if (swatch->priv->has_color &&
309 swatch->priv->selectable &&
310 (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
311 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
312 else
313 g_signal_emit (swatch, signals[ACTIVATE], 0);
314 return TRUE;
315 }
316
317 if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event))
318 return TRUE;
319
320 return FALSE;
321 }
322
323 static gboolean
swatch_enter_notify(GtkWidget * widget,GdkEventCrossing * event)324 swatch_enter_notify (GtkWidget *widget,
325 GdkEventCrossing *event)
326 {
327 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
328
329 return FALSE;
330 }
331
332 static gboolean
swatch_leave_notify(GtkWidget * widget,GdkEventCrossing * event)333 swatch_leave_notify (GtkWidget *widget,
334 GdkEventCrossing *event)
335 {
336 gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
337
338 return FALSE;
339 }
340
341 static void
emit_customize(GtkColorSwatch * swatch)342 emit_customize (GtkColorSwatch *swatch)
343 {
344 g_signal_emit (swatch, signals[CUSTOMIZE], 0);
345 }
346
347 static void
do_popup(GtkColorSwatch * swatch)348 do_popup (GtkColorSwatch *swatch)
349 {
350 if (swatch->priv->popover == NULL)
351 {
352 GtkWidget *box;
353 GtkWidget *item;
354
355 swatch->priv->popover = gtk_popover_new (GTK_WIDGET (swatch));
356 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
357 gtk_container_add (GTK_CONTAINER (swatch->priv->popover), box);
358 g_object_set (box, "margin", 10, NULL);
359 item = g_object_new (GTK_TYPE_MODEL_BUTTON,
360 "text", _("C_ustomize"),
361 NULL);
362 g_signal_connect_swapped (item, "clicked",
363 G_CALLBACK (emit_customize), swatch);
364 gtk_container_add (GTK_CONTAINER (box), item);
365 gtk_widget_show_all (box);
366 }
367
368 gtk_popover_popup (GTK_POPOVER (swatch->priv->popover));
369 }
370
371 static gboolean
swatch_primary_action(GtkColorSwatch * swatch)372 swatch_primary_action (GtkColorSwatch *swatch)
373 {
374 GtkWidget *widget = (GtkWidget *)swatch;
375 GtkStateFlags flags;
376
377 flags = gtk_widget_get_state_flags (widget);
378 if (!swatch->priv->has_color)
379 {
380 g_signal_emit (swatch, signals[ACTIVATE], 0);
381 return TRUE;
382 }
383 else if (swatch->priv->selectable &&
384 (flags & GTK_STATE_FLAG_SELECTED) == 0)
385 {
386 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
387 return TRUE;
388 }
389
390 return FALSE;
391 }
392
393 static void
hold_action(GtkGestureLongPress * gesture,gdouble x,gdouble y,GtkColorSwatch * swatch)394 hold_action (GtkGestureLongPress *gesture,
395 gdouble x,
396 gdouble y,
397 GtkColorSwatch *swatch)
398 {
399 do_popup (swatch);
400 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
401 }
402
403 static void
tap_action(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,GtkColorSwatch * swatch)404 tap_action (GtkGestureMultiPress *gesture,
405 gint n_press,
406 gdouble x,
407 gdouble y,
408 GtkColorSwatch *swatch)
409 {
410 guint button;
411
412 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
413
414 if (button == GDK_BUTTON_PRIMARY)
415 {
416 if (n_press == 1)
417 swatch_primary_action (swatch);
418 else if (n_press > 1)
419 g_signal_emit (swatch, signals[ACTIVATE], 0);
420 }
421 else if (button == GDK_BUTTON_SECONDARY)
422 {
423 if (swatch->priv->has_color && swatch->priv->has_menu)
424 do_popup (swatch);
425 }
426 }
427
428 static void
swatch_map(GtkWidget * widget)429 swatch_map (GtkWidget *widget)
430 {
431 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
432
433 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->map (widget);
434
435 if (swatch->priv->event_window)
436 gdk_window_show (swatch->priv->event_window);
437 }
438
439 static void
swatch_unmap(GtkWidget * widget)440 swatch_unmap (GtkWidget *widget)
441 {
442 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
443
444 if (swatch->priv->event_window)
445 gdk_window_hide (swatch->priv->event_window);
446
447 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unmap (widget);
448 }
449
450 static void
swatch_realize(GtkWidget * widget)451 swatch_realize (GtkWidget *widget)
452 {
453 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
454 GtkAllocation allocation;
455 GdkWindow *window;
456 GdkWindowAttr attributes;
457 gint attributes_mask;
458
459 gtk_widget_get_allocation (widget, &allocation);
460 gtk_widget_set_realized (widget, TRUE);
461
462 attributes.window_type = GDK_WINDOW_CHILD;
463 attributes.x = allocation.x;
464 attributes.y = allocation.y;
465 attributes.width = allocation.width;
466 attributes.height = allocation.height;
467 attributes.wclass = GDK_INPUT_ONLY;
468 attributes.event_mask = gtk_widget_get_events (widget);
469 attributes.event_mask |= GDK_BUTTON_PRESS_MASK
470 | GDK_BUTTON_RELEASE_MASK
471 | GDK_ENTER_NOTIFY_MASK
472 | GDK_LEAVE_NOTIFY_MASK
473 | GDK_TOUCH_MASK;
474
475 attributes_mask = GDK_WA_X | GDK_WA_Y;
476
477 window = gtk_widget_get_parent_window (widget);
478 gtk_widget_set_window (widget, window);
479 g_object_ref (window);
480
481 swatch->priv->event_window = gdk_window_new (window, &attributes, attributes_mask);
482 gtk_widget_register_window (widget, swatch->priv->event_window);
483 }
484
485 static void
swatch_unrealize(GtkWidget * widget)486 swatch_unrealize (GtkWidget *widget)
487 {
488 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
489
490 if (swatch->priv->event_window)
491 {
492 gtk_widget_unregister_window (widget, swatch->priv->event_window);
493 gdk_window_destroy (swatch->priv->event_window);
494 swatch->priv->event_window = NULL;
495 }
496
497 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unrealize (widget);
498 }
499
500 static void
swatch_size_allocate(GtkWidget * widget,GtkAllocation * allocation)501 swatch_size_allocate (GtkWidget *widget,
502 GtkAllocation *allocation)
503 {
504 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
505 GtkAllocation clip, clip2;
506
507 gtk_widget_set_allocation (widget, allocation);
508
509 gtk_css_gadget_allocate (swatch->priv->gadget,
510 allocation,
511 gtk_widget_get_allocated_baseline (widget),
512 &clip);
513 gtk_css_gadget_allocate (swatch->priv->overlay_gadget,
514 allocation,
515 gtk_widget_get_allocated_baseline (widget),
516 &clip2);
517
518 gdk_rectangle_union (&clip, &clip2, &clip);
519
520 gtk_widget_set_clip (widget, &clip);
521
522 if (gtk_widget_get_realized (widget))
523 {
524 GtkAllocation border_allocation;
525 gtk_css_gadget_get_border_allocation(swatch->priv->gadget, &border_allocation, NULL);
526 gdk_window_move_resize (swatch->priv->event_window,
527 border_allocation.x,
528 border_allocation.y,
529 border_allocation.width,
530 border_allocation.height);
531 }
532
533 }
534
535 static void
swatch_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)536 swatch_get_preferred_width (GtkWidget *widget,
537 gint *minimum,
538 gint *natural)
539 {
540 gtk_css_gadget_get_preferred_size (GTK_COLOR_SWATCH (widget)->priv->gadget,
541 GTK_ORIENTATION_HORIZONTAL,
542 -1,
543 minimum, natural,
544 NULL, NULL);
545 }
546
547 static void
swatch_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)548 swatch_get_preferred_height (GtkWidget *widget,
549 gint *minimum,
550 gint *natural)
551 {
552 gtk_css_gadget_get_preferred_size (GTK_COLOR_SWATCH (widget)->priv->gadget,
553 GTK_ORIENTATION_VERTICAL,
554 -1,
555 minimum, natural,
556 NULL, NULL);
557 }
558
559 static gboolean
swatch_popup_menu(GtkWidget * widget)560 swatch_popup_menu (GtkWidget *widget)
561 {
562 do_popup (GTK_COLOR_SWATCH (widget));
563 return TRUE;
564 }
565
566 static void
update_icon(GtkColorSwatch * swatch)567 update_icon (GtkColorSwatch *swatch)
568 {
569 GtkIconHelper *icon_helper = GTK_ICON_HELPER (swatch->priv->overlay_gadget);
570
571 if (swatch->priv->icon)
572 _gtk_icon_helper_set_icon_name (icon_helper, swatch->priv->icon, GTK_ICON_SIZE_BUTTON);
573 else if (gtk_widget_get_state_flags (GTK_WIDGET (swatch)) & GTK_STATE_FLAG_SELECTED)
574 _gtk_icon_helper_set_icon_name (icon_helper, "object-select-symbolic", GTK_ICON_SIZE_BUTTON);
575 else
576 _gtk_icon_helper_clear (icon_helper);
577 }
578
579 static void
swatch_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_state)580 swatch_state_flags_changed (GtkWidget *widget,
581 GtkStateFlags previous_state)
582 {
583 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
584
585 gtk_css_gadget_set_state (swatch->priv->gadget, gtk_widget_get_state_flags (widget));
586 gtk_css_gadget_set_state (swatch->priv->overlay_gadget, gtk_widget_get_state_flags (widget));
587
588 update_icon (swatch);
589
590 GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->state_flags_changed (widget, previous_state);
591 }
592
593 /* GObject implementation {{{1 */
594
595 static void
swatch_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)596 swatch_get_property (GObject *object,
597 guint prop_id,
598 GValue *value,
599 GParamSpec *pspec)
600 {
601 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
602 GdkRGBA color;
603
604 switch (prop_id)
605 {
606 case PROP_RGBA:
607 gtk_color_swatch_get_rgba (swatch, &color);
608 g_value_set_boxed (value, &color);
609 break;
610 case PROP_SELECTABLE:
611 g_value_set_boolean (value, gtk_color_swatch_get_selectable (swatch));
612 break;
613 case PROP_HAS_MENU:
614 g_value_set_boolean (value, swatch->priv->has_menu);
615 break;
616 default:
617 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
618 break;
619 }
620 }
621
622 static void
swatch_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)623 swatch_set_property (GObject *object,
624 guint prop_id,
625 const GValue *value,
626 GParamSpec *pspec)
627 {
628 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
629
630 switch (prop_id)
631 {
632 case PROP_RGBA:
633 gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
634 break;
635 case PROP_SELECTABLE:
636 gtk_color_swatch_set_selectable (swatch, g_value_get_boolean (value));
637 break;
638 case PROP_HAS_MENU:
639 swatch->priv->has_menu = g_value_get_boolean (value);
640 break;
641 default:
642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643 break;
644 }
645 }
646
647 static void
swatch_finalize(GObject * object)648 swatch_finalize (GObject *object)
649 {
650 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
651
652 g_free (swatch->priv->icon);
653 g_clear_object (&swatch->priv->gadget);
654 g_clear_object (&swatch->priv->overlay_gadget);
655
656 G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
657 }
658
659 static void
swatch_dispose(GObject * object)660 swatch_dispose (GObject *object)
661 {
662 GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
663
664 if (swatch->priv->popover)
665 {
666 gtk_widget_destroy (swatch->priv->popover);
667 swatch->priv->popover = NULL;
668 }
669
670 g_clear_object (&swatch->priv->long_press_gesture);
671 g_clear_object (&swatch->priv->multipress_gesture);
672
673 G_OBJECT_CLASS (gtk_color_swatch_parent_class)->dispose (object);
674 }
675
676 static void
gtk_color_swatch_class_init(GtkColorSwatchClass * class)677 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
678 {
679 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
680 GObjectClass *object_class = (GObjectClass *)class;
681
682 object_class->get_property = swatch_get_property;
683 object_class->set_property = swatch_set_property;
684 object_class->finalize = swatch_finalize;
685 object_class->dispose = swatch_dispose;
686
687 widget_class->get_preferred_width = swatch_get_preferred_width;
688 widget_class->get_preferred_height = swatch_get_preferred_height;
689 widget_class->draw = swatch_draw;
690 widget_class->drag_begin = swatch_drag_begin;
691 widget_class->drag_data_get = swatch_drag_data_get;
692 widget_class->drag_data_received = swatch_drag_data_received;
693 widget_class->key_press_event = swatch_key_press;
694 widget_class->popup_menu = swatch_popup_menu;
695 widget_class->enter_notify_event = swatch_enter_notify;
696 widget_class->leave_notify_event = swatch_leave_notify;
697 widget_class->realize = swatch_realize;
698 widget_class->unrealize = swatch_unrealize;
699 widget_class->map = swatch_map;
700 widget_class->unmap = swatch_unmap;
701 widget_class->size_allocate = swatch_size_allocate;
702 widget_class->state_flags_changed = swatch_state_flags_changed;
703
704 signals[ACTIVATE] =
705 g_signal_new (I_("activate"),
706 GTK_TYPE_COLOR_SWATCH,
707 G_SIGNAL_RUN_FIRST,
708 G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
709 NULL, NULL, NULL, G_TYPE_NONE, 0);
710
711 signals[CUSTOMIZE] =
712 g_signal_new (I_("customize"),
713 GTK_TYPE_COLOR_SWATCH,
714 G_SIGNAL_RUN_FIRST,
715 G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
716 NULL, NULL, NULL, G_TYPE_NONE, 0);
717
718 g_object_class_install_property (object_class, PROP_RGBA,
719 g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
720 GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
721 g_object_class_install_property (object_class, PROP_SELECTABLE,
722 g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the swatch is selectable"),
723 TRUE, GTK_PARAM_READWRITE));
724 g_object_class_install_property (object_class, PROP_HAS_MENU,
725 g_param_spec_boolean ("has-menu", P_("Has Menu"), P_("Whether the swatch should offer customization"),
726 TRUE, GTK_PARAM_READWRITE));
727
728 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
729 gtk_widget_class_set_css_name (widget_class, "colorswatch");
730 }
731
732 static void
gtk_color_swatch_init(GtkColorSwatch * swatch)733 gtk_color_swatch_init (GtkColorSwatch *swatch)
734 {
735 GtkCssNode *widget_node;
736
737 swatch->priv = gtk_color_swatch_get_instance_private (swatch);
738 swatch->priv->use_alpha = TRUE;
739 swatch->priv->selectable = TRUE;
740 swatch->priv->has_menu = TRUE;
741
742 gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
743 gtk_widget_set_has_window (GTK_WIDGET (swatch), FALSE);
744
745 swatch->priv->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (swatch));
746 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (swatch->priv->long_press_gesture),
747 TRUE);
748 g_signal_connect (swatch->priv->long_press_gesture, "pressed",
749 G_CALLBACK (hold_action), swatch);
750
751 swatch->priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (swatch));
752 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (swatch->priv->multipress_gesture), 0);
753 g_signal_connect (swatch->priv->multipress_gesture, "pressed",
754 G_CALLBACK (tap_action), swatch);
755
756 widget_node = gtk_widget_get_css_node (GTK_WIDGET (swatch));
757 swatch->priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
758 GTK_WIDGET (swatch),
759 gtk_color_swatch_measure,
760 NULL,
761 gtk_color_swatch_render,
762 NULL,
763 NULL);
764 gtk_css_gadget_add_class (swatch->priv->gadget, "activatable");
765
766 swatch->priv->overlay_gadget = gtk_icon_helper_new_named ("overlay", GTK_WIDGET (swatch));
767 _gtk_icon_helper_set_force_scale_pixbuf (GTK_ICON_HELPER (swatch->priv->overlay_gadget), TRUE);
768 gtk_css_node_set_parent (gtk_css_gadget_get_node (swatch->priv->overlay_gadget), widget_node);
769
770 }
771
772 /* Public API {{{1 */
773
774 GtkWidget *
gtk_color_swatch_new(void)775 gtk_color_swatch_new (void)
776 {
777 return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
778 }
779
780 static const GtkTargetEntry dnd_targets[] = {
781 { "application/x-color", 0 }
782 };
783
784 void
gtk_color_swatch_set_rgba(GtkColorSwatch * swatch,const GdkRGBA * color)785 gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
786 const GdkRGBA *color)
787 {
788 GtkStyleContext *context;
789
790 context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
791
792 if (!swatch->priv->has_color)
793 {
794 gtk_drag_source_set (GTK_WIDGET (swatch),
795 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
796 dnd_targets, G_N_ELEMENTS (dnd_targets),
797 GDK_ACTION_COPY | GDK_ACTION_MOVE);
798 }
799
800 swatch->priv->has_color = TRUE;
801 swatch->priv->color = *color;
802
803 if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
804 {
805 gtk_style_context_add_class (context, "light");
806 gtk_style_context_remove_class (context, "dark");
807 }
808 else
809 {
810 gtk_style_context_add_class (context, "dark");
811 gtk_style_context_remove_class (context, "light");
812 }
813
814 gtk_widget_queue_draw (GTK_WIDGET (swatch));
815 g_object_notify (G_OBJECT (swatch), "rgba");
816 }
817
818 gboolean
gtk_color_swatch_get_rgba(GtkColorSwatch * swatch,GdkRGBA * color)819 gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
820 GdkRGBA *color)
821 {
822 if (swatch->priv->has_color)
823 {
824 color->red = swatch->priv->color.red;
825 color->green = swatch->priv->color.green;
826 color->blue = swatch->priv->color.blue;
827 color->alpha = swatch->priv->color.alpha;
828 return TRUE;
829 }
830 else
831 {
832 color->red = 1.0;
833 color->green = 1.0;
834 color->blue = 1.0;
835 color->alpha = 1.0;
836 return FALSE;
837 }
838 }
839
840 void
gtk_color_swatch_set_icon(GtkColorSwatch * swatch,const gchar * icon)841 gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
842 const gchar *icon)
843 {
844 swatch->priv->icon = g_strdup (icon);
845 update_icon (swatch);
846 gtk_widget_queue_draw (GTK_WIDGET (swatch));
847 }
848
849 void
gtk_color_swatch_set_can_drop(GtkColorSwatch * swatch,gboolean can_drop)850 gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
851 gboolean can_drop)
852 {
853 if (can_drop)
854 {
855 gtk_drag_dest_set (GTK_WIDGET (swatch),
856 GTK_DEST_DEFAULT_HIGHLIGHT |
857 GTK_DEST_DEFAULT_MOTION |
858 GTK_DEST_DEFAULT_DROP,
859 dnd_targets, G_N_ELEMENTS (dnd_targets),
860 GDK_ACTION_COPY);
861 }
862 else
863 {
864 gtk_drag_dest_unset (GTK_WIDGET (swatch));
865 }
866 }
867
868 void
gtk_color_swatch_set_use_alpha(GtkColorSwatch * swatch,gboolean use_alpha)869 gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
870 gboolean use_alpha)
871 {
872 swatch->priv->use_alpha = use_alpha;
873 gtk_widget_queue_draw (GTK_WIDGET (swatch));
874 }
875
876 void
gtk_color_swatch_set_selectable(GtkColorSwatch * swatch,gboolean selectable)877 gtk_color_swatch_set_selectable (GtkColorSwatch *swatch,
878 gboolean selectable)
879 {
880 if (selectable == swatch->priv->selectable)
881 return;
882
883 swatch->priv->selectable = selectable;
884 g_object_notify (G_OBJECT (swatch), "selectable");
885 }
886
887 gboolean
gtk_color_swatch_get_selectable(GtkColorSwatch * swatch)888 gtk_color_swatch_get_selectable (GtkColorSwatch *swatch)
889 {
890 return swatch->priv->selectable;
891 }
892
893 /* vim:set foldmethod=marker: */
894