]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcolorswatch.c
entry: Use GtkSelectionWindow for touch text selection
[~andy/gtk] / gtk / gtkcolorswatch.c
index bf5edf654dbd446effcf033fbe287ab3350edf7a..c6081d7907f1adadc9339f088297f497801fbf5b 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 
-#include "gtkcolorswatch.h"
+#include "gtkcolorswatchprivate.h"
+
+#include "gtkcolorchooserprivate.h"
 #include "gtkroundedboxprivate.h"
+#include "gtkthemingbackgroundprivate.h"
 #include "gtkdnd.h"
 #include "gtkicontheme.h"
 #include "gtkmain.h"
 #include "gtkmenu.h"
 #include "gtkmenuitem.h"
 #include "gtkmenushell.h"
-#include "gtkbindings.h"
+#include "gtkpressandholdprivate.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
+#include "a11y/gtkcolorswatchaccessibleprivate.h"
 
 
 struct _GtkColorSwatchPrivate
@@ -37,17 +39,21 @@ struct _GtkColorSwatchPrivate
   GdkRGBA color;
   gdouble radius[4];
   gchar *icon;
-  guint    selected         : 1;
   guint    has_color        : 1;
-  guint    can_drop         : 1;
   guint    contains_pointer : 1;
+  guint    use_alpha        : 1;
+  guint    selectable       : 1;
+
+  GdkWindow *event_window;
+
+  GtkPressAndHold *press_and_hold;
 };
 
 enum
 {
   PROP_ZERO,
-  PROP_COLOR,
-  PROP_SELECTED
+  PROP_RGBA,
+  PROP_SELECTABLE
 };
 
 enum
@@ -59,7 +65,7 @@ enum
 
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_DRAWING_AREA)
+G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
 
 static void
 gtk_color_swatch_init (GtkColorSwatch *swatch)
@@ -69,62 +75,82 @@ gtk_color_swatch_init (GtkColorSwatch *swatch)
                                               GtkColorSwatchPrivate);
 
   gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
-  gtk_widget_set_events (GTK_WIDGET (swatch), GDK_BUTTON_PRESS_MASK
-                                              | GDK_BUTTON_RELEASE_MASK
-                                              | GDK_EXPOSURE_MASK
-                                              | GDK_ENTER_NOTIFY_MASK
-                                              | GDK_LEAVE_NOTIFY_MASK);
-}
-
-static void
-swatch_finalize (GObject *object)
-{
-  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
-
-  g_free (swatch->priv->icon);
+  gtk_widget_set_has_window (GTK_WIDGET (swatch), FALSE);
 
-  G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
+  swatch->priv->use_alpha = TRUE;
+  swatch->priv->selectable = TRUE;
 }
 
 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
+#define ACTIVE_BADGE_RADIUS 10
 
 static gboolean
 swatch_draw (GtkWidget *widget,
              cairo_t   *cr)
 {
   GtkColorSwatch *swatch = (GtkColorSwatch*)widget;
-  GtkRoundedBox box;
-  gint i;
+  GtkThemingBackground background;
   gdouble width, height;
   GtkStyleContext *context;
   GtkStateFlags state;
-  GdkRGBA bg;
+  GtkIconTheme *theme;
+  GtkIconInfo *icon_info = NULL;
 
+  theme = gtk_icon_theme_get_default ();
   context = gtk_widget_get_style_context (widget);
   state = gtk_widget_get_state_flags (widget);
   width = gtk_widget_get_allocated_width (widget);
   height = gtk_widget_get_allocated_height (widget);
 
-  gtk_style_context_save (context);
   cairo_save (cr);
 
+  gtk_style_context_save (context);
   gtk_style_context_set_state (context, state);
 
-  _gtk_rounded_box_init_rect (&box, 0, 0, width, height);
-  for (i = 0; i < 4; i++)
-    box.corner[i].horizontal = box.corner[i].vertical = swatch->priv->radius[i];
-
-  _gtk_rounded_box_path (&box, cr);
+  _gtk_theming_background_init_from_context (&background, context,
+                                             0, 0, width, height,
+                                             GTK_JUNCTION_NONE);
 
   if (swatch->priv->has_color)
     {
-      gdk_cairo_set_source_rgba (cr, &swatch->priv->color);
-      cairo_fill_preserve (cr);
+      cairo_pattern_t *pattern;
+      cairo_matrix_t matrix;
+
+      if (swatch->priv->use_alpha)
+        {
+          cairo_save (cr);
+
+          _gtk_rounded_box_path (&background.padding_box, cr);
+          cairo_clip_preserve (cr);
+
+          cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
+          cairo_fill_preserve (cr);
+
+          pattern = _gtk_color_chooser_get_checkered_pattern ();
+          cairo_matrix_init_scale (&matrix, 0.125, 0.125);
+          cairo_pattern_set_matrix (pattern, &matrix);
+
+          cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
+          cairo_mask (cr, pattern);
+          cairo_pattern_destroy (pattern);
+
+          cairo_restore (cr);
+
+          background.bg_color = swatch->priv->color;
+        }
+      else
+        {
+          background.bg_color = swatch->priv->color;
+          background.bg_color.alpha = 1.0;
+        }
+
+      _gtk_theming_background_render (&background, cr);
     }
+  else
+    _gtk_theming_background_render (&background, cr);
 
-  cairo_set_source_rgb (cr, 0.4, 0.4, 0.4);
-  cairo_set_line_width (cr, 1);
-  cairo_stroke (cr);
+  gtk_render_frame (context, cr,
+                    0, 0, width, height);
 
   if (gtk_widget_has_visible_focus (widget))
     {
@@ -133,42 +159,76 @@ swatch_draw (GtkWidget *widget,
         cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
       else
         cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
-      _gtk_rounded_box_shrink (&box, 3, 3, 3, 3);
-      _gtk_rounded_box_path (&box, cr);
-      cairo_stroke (cr);
+        _gtk_rounded_box_shrink (&background.padding_box, 3, 3, 3, 3);
+        _gtk_rounded_box_path (&background.padding_box, cr);
+        cairo_stroke (cr);
     }
 
   if (swatch->priv->icon)
     {
-      GdkPixbuf *pixbuf;
-      GtkIconTheme *theme;
-      theme = gtk_icon_theme_get_default ();
-      pixbuf = gtk_icon_theme_load_icon (theme, "list-add-symbolic", 16,
-                                         GTK_ICON_LOOKUP_GENERIC_FALLBACK
-                                         | GTK_ICON_LOOKUP_USE_BUILTIN,
-                                         NULL);
-
-      gtk_render_icon (context, cr, pixbuf,
-                       (width - gdk_pixbuf_get_width (pixbuf)) / 2,
-                       (height - gdk_pixbuf_get_height (pixbuf)) / 2);
-      g_object_unref (pixbuf);
+      icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16,
+                                              GTK_ICON_LOOKUP_GENERIC_FALLBACK
+                                              | GTK_ICON_LOOKUP_USE_BUILTIN);
     }
-  else  if (swatch->priv->selected)
+  else if ((state & GTK_STATE_FLAG_SELECTED) != 0)
     {
-      gtk_style_context_get_background_color (context, state, &bg);
-      cairo_new_sub_path (cr);
-      cairo_arc (cr, width / 2, height / 2, 10, 0, 2 * G_PI);
-      cairo_close_path (cr);
-      gdk_cairo_set_source_rgba (cr, &bg);
-      cairo_fill_preserve (cr);
-      if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
-        cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
+      GdkRGBA bg, border;
+      GtkBorder border_width;
+      GIcon *gicon;
+
+      gtk_style_context_add_class (context, "color-active-badge");
+      _gtk_theming_background_init_from_context (&background, context,
+                                                 (width - 2 * ACTIVE_BADGE_RADIUS) / 2, (height - 2 * ACTIVE_BADGE_RADIUS) / 2,
+                                                 2 * ACTIVE_BADGE_RADIUS, 2* ACTIVE_BADGE_RADIUS,
+                                                 GTK_JUNCTION_NONE);
+
+      if (_gtk_theming_background_has_background_image (&background))
+        {
+          _gtk_theming_background_render (&background, cr);
+        }
       else
-        cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
-      cairo_set_line_width (cr, 2);
-      cairo_stroke (cr);
-      gtk_style_context_set_state (context, state | GTK_STATE_FLAG_ACTIVE);
-      gtk_render_check (context, cr, width / 2 - 5, height / 2 - 5, 10, 10);
+        {
+          gtk_style_context_get_background_color (context, state, &bg);
+          gtk_style_context_get_border_color (context, state, &border);
+          gtk_style_context_get_border (context, state, &border_width);
+
+          cairo_new_sub_path (cr);
+          cairo_arc (cr, width / 2, height / 2, ACTIVE_BADGE_RADIUS, 0, 2 * G_PI);
+          cairo_close_path (cr);
+          gdk_cairo_set_source_rgba (cr, &bg);
+          cairo_fill_preserve (cr);
+
+          gdk_cairo_set_source_rgba (cr, &border);
+          cairo_set_line_width (cr, border_width.left);
+          cairo_stroke (cr);
+
+          gicon = g_themed_icon_new ("object-select-symbolic");
+          /* fallback for themes that don't have object-select-symbolic */
+          g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply");
+
+          icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16,
+                                                      GTK_ICON_LOOKUP_GENERIC_FALLBACK
+                                                      | GTK_ICON_LOOKUP_USE_BUILTIN);
+          g_object_unref (gicon);
+        }
+    }
+
+  if (icon_info != NULL)
+    {
+      GdkPixbuf *pixbuf;
+
+      pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context,
+                                                        NULL, NULL);
+
+      if (pixbuf != NULL)
+        {
+          gtk_render_icon (context, cr, pixbuf,
+                           (width - gdk_pixbuf_get_width (pixbuf)) / 2,
+                           (height - gdk_pixbuf_get_height (pixbuf)) / 2);
+          g_object_unref (pixbuf);
+        }
+
+      g_object_unref (icon_info);
     }
 
   cairo_restore (cr);
@@ -189,7 +249,7 @@ drag_set_color_icon (GdkDragContext *context,
   gdk_cairo_set_source_rgba (cr, color);
   cairo_paint (cr);
 
-  cairo_surface_set_device_offset (surface, -2, -2);
+  cairo_surface_set_device_offset (surface, -4, -4);
   gtk_drag_set_icon_surface (context, surface);
 
   cairo_destroy (cr);
@@ -203,7 +263,7 @@ swatch_drag_begin (GtkWidget      *widget,
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
   GdkRGBA color;
 
-  gtk_color_swatch_get_color (swatch, &color);
+  gtk_color_swatch_get_rgba (swatch, &color);
   drag_set_color_icon (context, &color);
 }
 
@@ -218,7 +278,7 @@ swatch_drag_data_get (GtkWidget        *widget,
   guint16 vals[4];
   GdkRGBA color;
 
-  gtk_color_swatch_get_color (swatch, &color);
+  gtk_color_swatch_get_rgba (swatch, &color);
 
   vals[0] = color.red * 0xffff;
   vals[1] = color.green * 0xffff;
@@ -264,53 +324,7 @@ swatch_drag_data_received (GtkWidget        *widget,
   color.blue  = (gdouble)vals[2] / 0xffff;
   color.alpha = (gdouble)vals[3] / 0xffff;
 
-  gtk_color_swatch_set_color (GTK_COLOR_SWATCH (widget), &color);
-}
-
-static void
-swatch_get_property (GObject    *object,
-                     guint       prop_id,
-                     GValue     *value,
-                     GParamSpec *pspec)
-{
-  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
-  GdkRGBA color;
-
-  switch (prop_id)
-    {
-    case PROP_COLOR:
-      gtk_color_swatch_get_color (swatch, &color);
-      g_value_set_boxed (value, &color);
-      break;
-    case PROP_SELECTED:
-      g_value_set_boolean (value, swatch->priv->selected);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-swatch_set_property (GObject      *object,
-                     guint         prop_id,
-                     const GValue *value,
-                     GParamSpec   *pspec)
-{
-  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
-
-  switch (prop_id)
-    {
-    case PROP_COLOR:
-      gtk_color_swatch_set_color (swatch, g_value_get_boxed (value));
-      break;
-    case PROP_SELECTED:
-      gtk_color_swatch_set_selected (swatch, g_value_get_boolean (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
 }
 
 static void
@@ -341,8 +355,10 @@ swatch_key_press (GtkWidget   *widget,
       event->keyval == GDK_KEY_KP_Enter ||
       event->keyval == GDK_KEY_KP_Space)
     {
-      if (swatch->priv->has_color && !swatch->priv->selected)
-        gtk_color_swatch_set_selected (swatch, TRUE);
+      if (swatch->priv->has_color && 
+          swatch->priv->selectable &&
+          (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
+        gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
       else
         g_signal_emit (swatch, signals[ACTIVATE], 0);
       return TRUE;
@@ -355,20 +371,24 @@ swatch_key_press (GtkWidget   *widget,
 }
 
 static gboolean
-swatch_enter (GtkWidget        *widget,
-              GdkEventCrossing *event)
+swatch_enter_notify (GtkWidget        *widget,
+                     GdkEventCrossing *event)
 {
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
   swatch->priv->contains_pointer = TRUE;
+  gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
+
   return FALSE;
 }
 
 static gboolean
-swatch_leave (GtkWidget        *widget,
-              GdkEventCrossing *event)
+swatch_leave_notify (GtkWidget        *widget,
+                     GdkEventCrossing *event)
 {
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
   swatch->priv->contains_pointer = FALSE;
+  gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
+
   return FALSE;
 }
 
@@ -449,19 +469,42 @@ swatch_button_press (GtkWidget      *widget,
 {
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
 
-  gtk_widget_grab_focus (GTK_WIDGET (swatch));
+  gtk_widget_grab_focus (widget);
 
-  if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+  if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
+      swatch->priv->has_color)
     {
       do_popup (widget, event);
       return TRUE;
     }
-  else if (event->button == GDK_BUTTON_PRIMARY &&
-           swatch->priv->selected)
+  else if (event->type == GDK_2BUTTON_PRESS &&
+           event->button == GDK_BUTTON_PRIMARY)
+    {
+      g_signal_emit (swatch, signals[ACTIVATE], 0);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+swatch_primary_action (GtkColorSwatch *swatch)
+{
+  GtkWidget *widget = (GtkWidget *)swatch;
+  GtkStateFlags flags;
+
+  flags = gtk_widget_get_state_flags (widget);
+  if (!swatch->priv->has_color)
     {
       g_signal_emit (swatch, signals[ACTIVATE], 0);
       return TRUE;
     }
+  else if (swatch->priv->selectable &&
+           (flags & GTK_STATE_FLAG_SELECTED) == 0)
+    {
+      gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
+      return TRUE;
+    }
 
   return FALSE;
 }
@@ -472,33 +515,219 @@ swatch_button_release (GtkWidget      *widget,
 {
   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
 
-  gtk_widget_grab_focus (GTK_WIDGET (swatch));
-
   if (event->button == GDK_BUTTON_PRIMARY &&
       swatch->priv->contains_pointer)
+    return swatch_primary_action (swatch);
+
+  return FALSE;
+}
+
+static void
+hold_action (GtkPressAndHold *pah,
+             gint             x,
+             gint             y,
+             GtkColorSwatch  *swatch)
+{
+  emit_customize (swatch);
+}
+
+static void
+tap_action (GtkPressAndHold *pah,
+            gint             x,
+            gint             y,
+            GtkColorSwatch  *swatch)
+{
+  swatch_primary_action (swatch);
+}
+
+static gboolean
+swatch_touch (GtkWidget     *widget,
+              GdkEventTouch *event)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+
+  if (!swatch->priv->press_and_hold)
     {
-      if (!swatch->priv->has_color)
-        {
-          g_signal_emit (swatch, signals[ACTIVATE], 0);
-          return TRUE;
-        }
-      else if (!swatch->priv->selected)
-        {
-          gtk_color_swatch_set_selected (swatch, TRUE);
-          return TRUE;
-        }
+      gint drag_threshold;
+
+      g_object_get (gtk_widget_get_settings (widget),
+                    "gtk-dnd-drag-threshold", &drag_threshold,
+                    NULL);
+
+      swatch->priv->press_and_hold = gtk_press_and_hold_new ();
+
+      g_object_set (swatch->priv->press_and_hold,
+                    "drag-threshold", drag_threshold,
+                    "hold-time", 1000,
+                    NULL);
+
+      g_signal_connect (swatch->priv->press_and_hold, "hold",
+                        G_CALLBACK (hold_action), swatch);
+      g_signal_connect (swatch->priv->press_and_hold, "tap",
+                        G_CALLBACK (tap_action), swatch);
     }
 
-  return FALSE;
+  gtk_press_and_hold_process_event (swatch->priv->press_and_hold, (GdkEvent *)event);
+
+  return TRUE;
+}
+
+static void
+swatch_map (GtkWidget *widget)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+
+  GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->map (widget);
+
+  if (swatch->priv->event_window)
+    gdk_window_show (swatch->priv->event_window);
+}
+
+static void
+swatch_unmap (GtkWidget *widget)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+
+  if (swatch->priv->event_window)
+    gdk_window_hide (swatch->priv->event_window);
+
+  GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unmap (widget);
+}
+
+static void
+swatch_realize (GtkWidget *widget)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+  GtkAllocation allocation;
+  GdkWindow *window;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_set_realized (widget, TRUE);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.event_mask = gtk_widget_get_events (widget);
+  attributes.event_mask |= GDK_BUTTON_PRESS_MASK
+                           | GDK_BUTTON_RELEASE_MASK
+                           | GDK_ENTER_NOTIFY_MASK
+                           | GDK_LEAVE_NOTIFY_MASK
+                           | GDK_TOUCH_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+  window = gtk_widget_get_parent_window (widget);
+  gtk_widget_set_window (widget, window);
+  g_object_ref (window);
+
+  swatch->priv->event_window = 
+    gdk_window_new (window,
+                    &attributes, attributes_mask);
+  gtk_widget_register_window (widget, swatch->priv->event_window);
+}
+
+static void
+swatch_unrealize (GtkWidget *widget)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+
+  if (swatch->priv->event_window)
+    {
+      gtk_widget_unregister_window (widget, swatch->priv->event_window);
+      gdk_window_destroy (swatch->priv->event_window);
+      swatch->priv->event_window = NULL;
+    }
+
+  GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unrealize (widget);
+}
+
+static void
+swatch_size_allocate (GtkWidget *widget,
+                      GtkAllocation *allocation)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (gtk_widget_get_realized (widget))
+    gdk_window_move_resize (swatch->priv->event_window,
+                            allocation->x,
+                            allocation->y,
+                            allocation->width,
+                            allocation->height);
 }
 
 static gboolean
-swatch_menu (GtkWidget *swatch)
+swatch_popup_menu (GtkWidget *swatch)
 {
   do_popup (swatch, NULL);
   return TRUE;
 }
 
+/* GObject implementation {{{1 */
+
+static void
+swatch_get_property (GObject    *object,
+                     guint       prop_id,
+                     GValue     *value,
+                     GParamSpec *pspec)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
+  GdkRGBA color;
+
+  switch (prop_id)
+    {
+    case PROP_RGBA:
+      gtk_color_swatch_get_rgba (swatch, &color);
+      g_value_set_boxed (value, &color);
+      break;
+    case PROP_SELECTABLE:
+      g_value_set_boolean (value, gtk_color_swatch_get_selectable (swatch));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+swatch_set_property (GObject      *object,
+                     guint         prop_id,
+                     const GValue *value,
+                     GParamSpec   *pspec)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
+
+  switch (prop_id)
+    {
+    case PROP_RGBA:
+      gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
+      break;
+    case PROP_SELECTABLE:
+      gtk_color_swatch_set_selectable (swatch, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+swatch_finalize (GObject *object)
+{
+  GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
+
+  g_free (swatch->priv->icon);
+  g_clear_object (&swatch->priv->press_and_hold);
+
+  G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
+}
+
 static void
 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
 {
@@ -516,11 +745,17 @@ 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->popup_menu = swatch_popup_menu;
   widget_class->button_press_event = swatch_button_press;
   widget_class->button_release_event = swatch_button_release;
-  widget_class->enter_notify_event = swatch_enter;
-  widget_class->leave_notify_event = swatch_leave;
+  widget_class->enter_notify_event = swatch_enter_notify;
+  widget_class->leave_notify_event = swatch_leave_notify;
+  widget_class->realize = swatch_realize;
+  widget_class->unrealize = swatch_unrealize;
+  widget_class->map = swatch_map;
+  widget_class->unmap = swatch_unmap;
+  widget_class->size_allocate = swatch_size_allocate;
+  widget_class->touch_event = swatch_touch;
 
   signals[ACTIVATE] =
     g_signal_new ("activate",
@@ -536,17 +771,17 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class)
                   G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
                   NULL, NULL, NULL, G_TYPE_NONE, 0);
 
-  g_object_class_install_property (object_class, PROP_COLOR,
-      g_param_spec_boxed ("color", P_("Color"), P_("Color"),
+  g_object_class_install_property (object_class, PROP_RGBA,
+      g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
                           GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class, PROP_SELECTED,
-      g_param_spec_boolean ("selected", P_("Selected"), P_("Selected"),
-                            FALSE, GTK_PARAM_READWRITE));
+  g_object_class_install_property (object_class, PROP_SELECTABLE,
+      g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the swatch is selectable"),
+                            TRUE, GTK_PARAM_READWRITE));
 
   g_type_class_add_private (object_class, sizeof (GtkColorSwatchPrivate));
-}
 
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
+}
 
 /* Public API {{{1 */
 
@@ -556,33 +791,46 @@ gtk_color_swatch_new (void)
   return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
 }
 
+static const GtkTargetEntry dnd_targets[] = {
+  { "application/x-color", 0 }
+};
+
 void
-gtk_color_swatch_set_color (GtkColorSwatch *swatch,
-                            const GdkRGBA  *color)
+gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
+                           const GdkRGBA  *color)
 {
-  static const GtkTargetEntry targets[] = {
-    { "application/x-color", 0 }
-  };
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
 
   if (!swatch->priv->has_color)
-    gtk_drag_source_set (GTK_WIDGET (swatch),
-                         GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
-                         targets, 1,
-                         GDK_ACTION_COPY | GDK_ACTION_MOVE);
+    {
+      gtk_drag_source_set (GTK_WIDGET (swatch),
+                           GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+                           dnd_targets, G_N_ELEMENTS (dnd_targets),
+                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
+    }
+  else
+    {
+      gtk_style_context_remove_class (context, "color-light");
+      gtk_style_context_remove_class (context, "color-dark");
+    }
 
   swatch->priv->has_color = TRUE;
-  swatch->priv->color.red = color->red;
-  swatch->priv->color.green = color->green;
-  swatch->priv->color.blue = color->blue;
-  swatch->priv->color.alpha = color->alpha;
+  swatch->priv->color = *color;
+
+  if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
+    gtk_style_context_add_class (context, "color-light");
+  else
+    gtk_style_context_add_class (context, "color-dark");
 
   gtk_widget_queue_draw (GTK_WIDGET (swatch));
-  g_object_notify (G_OBJECT (swatch), "color");
+  g_object_notify (G_OBJECT (swatch), "rgba");
 }
 
 gboolean
-gtk_color_swatch_get_color (GtkColorSwatch *swatch,
-                            GdkRGBA        *color)
+gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
+                           GdkRGBA        *color)
 {
   if (swatch->priv->has_color)
     {
@@ -603,57 +851,55 @@ gtk_color_swatch_get_color (GtkColorSwatch *swatch,
 }
 
 void
-gtk_color_swatch_set_corner_radii (GtkColorSwatch *swatch,
-                                   gdouble         top_left,
-                                   gdouble         top_right,
-                                   gdouble         bottom_right,
-                                   gdouble         bottom_left)
+gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
+                           const gchar    *icon)
 {
-  swatch->priv->radius[0] = top_left;
-  swatch->priv->radius[1] = top_right;
-  swatch->priv->radius[2] = bottom_right;
-  swatch->priv->radius[3] = bottom_left;
-
+  swatch->priv->icon = g_strdup (icon);
   gtk_widget_queue_draw (GTK_WIDGET (swatch));
 }
 
 void
-gtk_color_swatch_set_selected (GtkColorSwatch *swatch,
-                               gboolean        selected)
+gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
+                               gboolean        can_drop)
 {
-  if (swatch->priv->selected != selected)
+  if (can_drop)
+    {
+      gtk_drag_dest_set (GTK_WIDGET (swatch),
+                         GTK_DEST_DEFAULT_HIGHLIGHT |
+                         GTK_DEST_DEFAULT_MOTION |
+                         GTK_DEST_DEFAULT_DROP,
+                         dnd_targets, G_N_ELEMENTS (dnd_targets),
+                         GDK_ACTION_COPY);
+    }
+  else
     {
-      swatch->priv->selected = selected;
-      gtk_widget_queue_draw (GTK_WIDGET (swatch));
-      g_object_notify (G_OBJECT (swatch), "selected");
+      gtk_drag_dest_unset (GTK_WIDGET (swatch));
     }
 }
 
 void
-gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
-                           const gchar    *icon)
+gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
+                                gboolean        use_alpha)
 {
-  swatch->priv->icon = g_strdup (icon);
+  swatch->priv->use_alpha = use_alpha;
   gtk_widget_queue_draw (GTK_WIDGET (swatch));
 }
 
 void
-gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
-                               gboolean        can_drop)
+gtk_color_swatch_set_selectable (GtkColorSwatch *swatch,
+                                 gboolean selectable)
 {
-  static const GtkTargetEntry targets[] = {
-    { "application/x-color", 0 }
-  };
+  if (selectable == swatch->priv->selectable)
+    return;
 
-  if (!swatch->priv->can_drop)
-    gtk_drag_dest_set (GTK_WIDGET (swatch),
-                       GTK_DEST_DEFAULT_HIGHLIGHT |
-                       GTK_DEST_DEFAULT_MOTION |
-                       GTK_DEST_DEFAULT_DROP,
-                       targets, 1,
-                       GDK_ACTION_COPY);
+  swatch->priv->selectable = selectable;
+  g_object_notify (G_OBJECT (swatch), "selectable");
+}
 
-  swatch->priv->can_drop = can_drop;
+gboolean
+gtk_color_swatch_get_selectable (GtkColorSwatch *swatch)
+{
+  return swatch->priv->selectable;
 }
 
 /* vim:set foldmethod=marker: */