]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkhsv.c
Translation updated by Ivar Smolin.
[~andy/gtk] / gtk / gtkhsv.c
index 45a286df7065930452e5d1eb8d7932bc9d9bb219..58c4494809d6ac2a8a7c97249d2b04f0f44ff652 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
 #include <math.h>
-#include "gtksignal.h"
+#include <string.h>
 #include "gtkhsv.h"
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
+#include "gtkcontainer.h"
+#include "gtkmarshalers.h"
+#include "gtkintl.h"
+#include "gtkalias.h"
 
 /*
  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
@@ -66,6 +73,9 @@ typedef struct {
   
   /* Dragging mode */
   DragMode mode;
+
+  guint focus_on_ring : 1;
+  
 } HSVPrivate;
 
 \f
@@ -74,90 +84,57 @@ typedef struct {
 
 enum {
   CHANGED,
+  MOVE,
   LAST_SIGNAL
 };
 
-static void gtk_hsv_class_init     (GtkHSVClass    *class);
-static void gtk_hsv_init           (GtkHSV         *hsv);
-static void gtk_hsv_destroy        (GtkObject      *object);
-static void gtk_hsv_map            (GtkWidget      *widget);
-static void gtk_hsv_unmap          (GtkWidget      *widget);
-static void gtk_hsv_realize        (GtkWidget      *widget);
-static void gtk_hsv_unrealize      (GtkWidget      *widget);
-static void gtk_hsv_size_request   (GtkWidget      *widget,
-                                   GtkRequisition *requisition);
-static void gtk_hsv_size_allocate  (GtkWidget      *widget,
-                                   GtkAllocation  *allocation);
-static gint gtk_hsv_button_press   (GtkWidget      *widget,
-                                   GdkEventButton *event);
-static gint gtk_hsv_button_release (GtkWidget      *widget,
-                                   GdkEventButton *event);
-static gint gtk_hsv_motion         (GtkWidget      *widget,
-                                   GdkEventMotion *event);
-static gint gtk_hsv_expose         (GtkWidget      *widget,
-                                   GdkEventExpose *event);
+static void     gtk_hsv_destroy        (GtkObject        *object);
+static void     gtk_hsv_map            (GtkWidget        *widget);
+static void     gtk_hsv_unmap          (GtkWidget        *widget);
+static void     gtk_hsv_realize        (GtkWidget        *widget);
+static void     gtk_hsv_unrealize      (GtkWidget        *widget);
+static void     gtk_hsv_size_request   (GtkWidget        *widget,
+                                       GtkRequisition   *requisition);
+static void     gtk_hsv_size_allocate  (GtkWidget        *widget,
+                                       GtkAllocation    *allocation);
+static gint     gtk_hsv_button_press   (GtkWidget        *widget,
+                                       GdkEventButton   *event);
+static gint     gtk_hsv_button_release (GtkWidget        *widget,
+                                       GdkEventButton   *event);
+static gint     gtk_hsv_motion         (GtkWidget        *widget,
+                                       GdkEventMotion   *event);
+static gint     gtk_hsv_expose         (GtkWidget        *widget,
+                                       GdkEventExpose   *event);
+static gboolean gtk_hsv_grab_broken    (GtkWidget          *widget,
+                                       GdkEventGrabBroken *event);
+static gboolean gtk_hsv_focus          (GtkWidget        *widget,
+                                       GtkDirectionType  direction);
+static void     gtk_hsv_move           (GtkHSV           *hsv,
+                                       GtkDirectionType  dir);
 
 static guint hsv_signals[LAST_SIGNAL];
-static GtkWidgetClass *parent_class;
 
-
-/**
- * gtk_hsv_get_type:
- * @void:
- *
- * Registers the &GtkHSV class if necessary, and returns the type ID associated
- * to it.
- *
- * Return value: The type ID of the &GtkHSV class.
- **/
-GtkType
-gtk_hsv_get_type (void)
-{
-  static GtkType hsv_type = 0;
-  
-  if (!hsv_type) {
-    static const GtkTypeInfo hsv_info = {
-      "GtkHSV",
-      sizeof (GtkHSV),
-      sizeof (GtkHSVClass),
-      (GtkClassInitFunc) gtk_hsv_class_init,
-      (GtkObjectInitFunc) gtk_hsv_init,
-      NULL, /* reserved_1 */
-      NULL, /* reserved_2 */
-      (GtkClassInitFunc) NULL
-    };
-    
-    hsv_type = gtk_type_unique (GTK_TYPE_WIDGET, &hsv_info);
-  }
-  
-  return hsv_type;
-}
+G_DEFINE_TYPE (GtkHSV, gtk_hsv, GTK_TYPE_WIDGET)
 
 /* Class initialization function for the HSV color selector */
 static void
 gtk_hsv_class_init (GtkHSVClass *class)
 {
+  GObjectClass   *gobject_class;
   GtkObjectClass *object_class;
   GtkWidgetClass *widget_class;
+  GtkHSVClass    *hsv_class;
+  GtkBindingSet  *binding_set;
   
+  gobject_class = (GObjectClass *) class;
   object_class = (GtkObjectClass *) class;
   widget_class = (GtkWidgetClass *) class;
-  
-  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
-  
-  hsv_signals[CHANGED] =
-    gtk_signal_new ("changed",
-                   GTK_RUN_FIRST,
-                   GTK_CLASS_TYPE (object_class),
-                   GTK_SIGNAL_OFFSET (GtkHSVClass, changed),
-                   gtk_marshal_NONE__NONE,
-                   GTK_TYPE_NONE, 0);
-  gtk_object_class_add_signals (object_class, hsv_signals, LAST_SIGNAL);
+  hsv_class = GTK_HSV_CLASS (class);
   
   object_class->destroy = gtk_hsv_destroy;
   
   widget_class->map = gtk_hsv_map;
-  widget_class->unmap = gtk_hsv_unmap;
+  widget_class->unmap = gtk_hsv_unmap;                                      
   widget_class->realize = gtk_hsv_realize;
   widget_class->unrealize = gtk_hsv_unrealize;
   widget_class->size_request = gtk_hsv_size_request;
@@ -166,6 +143,62 @@ gtk_hsv_class_init (GtkHSVClass *class)
   widget_class->button_release_event = gtk_hsv_button_release;
   widget_class->motion_notify_event = gtk_hsv_motion;
   widget_class->expose_event = gtk_hsv_expose;
+  widget_class->focus = gtk_hsv_focus;
+  widget_class->grab_broken_event = gtk_hsv_grab_broken;
+  
+  hsv_class->move = gtk_hsv_move;
+  
+  hsv_signals[CHANGED] =
+    g_signal_new (I_("changed"),
+                 G_OBJECT_CLASS_TYPE (object_class),
+                 G_SIGNAL_RUN_FIRST,
+                 G_STRUCT_OFFSET (GtkHSVClass, changed),
+                 NULL, NULL,
+                 _gtk_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+  hsv_signals[MOVE] =
+    g_signal_new (I_("move"),
+                 G_OBJECT_CLASS_TYPE (object_class),
+                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                 G_STRUCT_OFFSET (GtkHSVClass, move),
+                 NULL, NULL,
+                 _gtk_marshal_VOID__ENUM,
+                 G_TYPE_NONE, 1,
+                 GTK_TYPE_DIRECTION_TYPE);
+
+  binding_set = gtk_binding_set_by_class (class);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_UP);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_UP);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_DOWN);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_DOWN);
+
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_RIGHT);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_RIGHT);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_LEFT);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
+                                "move", 1,
+                                G_TYPE_ENUM, GTK_DIR_LEFT);
+
+  g_type_class_add_private (gobject_class, sizeof (HSVPrivate));   
 }
 
 /* Object initialization function for the HSV color selector */
@@ -173,11 +206,13 @@ static void
 gtk_hsv_init (GtkHSV *hsv)
 {
   HSVPrivate *priv;
+
+  priv = G_TYPE_INSTANCE_GET_PRIVATE (hsv, GTK_TYPE_HSV, HSVPrivate);
   
-  priv = g_new0 (HSVPrivate, 1);
   hsv->priv = priv;
-  
+
   GTK_WIDGET_SET_FLAGS (hsv, GTK_NO_WINDOW);
+  GTK_WIDGET_SET_FLAGS (hsv, GTK_CAN_FOCUS);
   
   priv->h = 0.0;
   priv->s = 0.0;
@@ -191,59 +226,44 @@ gtk_hsv_init (GtkHSV *hsv)
 static void
 gtk_hsv_destroy (GtkObject *object)
 {
-  GtkHSV *hsv;
-  
-  g_return_if_fail (GTK_IS_HSV (object));
-  
-  hsv = GTK_HSV (object);
-
-  if (hsv->priv)
-    {
-      g_free (hsv->priv);
-      hsv->priv = NULL;
-    }
-
-  GTK_OBJECT_CLASS (parent_class)->destroy (object);
+  GTK_OBJECT_CLASS (gtk_hsv_parent_class)->destroy (object);
 }
 
 /* Default signal handlers */
 
+    
 /* Map handler for the HSV color selector */
+
 static void
 gtk_hsv_map (GtkWidget *widget)
 {
   GtkHSV *hsv;
   HSVPrivate *priv;
-  
+
   hsv = GTK_HSV (widget);
   priv = hsv->priv;
-  
-  if (GTK_WIDGET_MAPPED (widget))
-    return;
-  
-  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
-  
+
+  GTK_WIDGET_CLASS (gtk_hsv_parent_class)->map (widget);
+
   gdk_window_show (priv->window);
 }
 
 /* Unmap handler for the HSV color selector */
+
 static void
 gtk_hsv_unmap (GtkWidget *widget)
 {
   GtkHSV *hsv;
   HSVPrivate *priv;
-  
+
   hsv = GTK_HSV (widget);
   priv = hsv->priv;
-  
-  if (!GTK_WIDGET_MAPPED (widget))
-    return;
-  
-  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-  
+
   gdk_window_hide (priv->window);
-}
 
+  GTK_WIDGET_CLASS (gtk_hsv_parent_class)->unmap (widget);
+}                                                                           
+                                      
 /* Realize handler for the HSV color selector */
 static void
 gtk_hsv_realize (GtkWidget *widget)
@@ -268,16 +288,19 @@ gtk_hsv_realize (GtkWidget *widget)
   attr.height = widget->allocation.height;
   attr.wclass = GDK_INPUT_ONLY;
   attr.event_mask = gtk_widget_get_events (widget);
-  attr.event_mask |= (GDK_BUTTON_PRESS_MASK
+  attr.event_mask |= (GDK_KEY_PRESS_MASK
+                      | GDK_BUTTON_PRESS_MASK
                      | GDK_BUTTON_RELEASE_MASK
-                     | GDK_POINTER_MOTION_MASK);
+                     | GDK_POINTER_MOTION_MASK
+                      | GDK_ENTER_NOTIFY_MASK
+                      | GDK_LEAVE_NOTIFY_MASK);
   
   attr_mask = GDK_WA_X | GDK_WA_Y;
   
   parent_window = gtk_widget_get_parent_window (widget);
   
   widget->window = parent_window;
-  gdk_window_ref (widget->window);
+  g_object_ref (widget->window);
   
   priv->window = gdk_window_new (parent_window, &attr, attr_mask);
   gdk_window_set_user_data (priv->window, hsv);
@@ -303,11 +326,11 @@ gtk_hsv_unrealize (GtkWidget *widget)
   gdk_window_destroy (priv->window);
   priv->window = NULL;
   
-  gdk_gc_unref (priv->gc);
+  g_object_unref (priv->gc);
   priv->gc = NULL;
   
-  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
-    GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+  if (GTK_WIDGET_CLASS (gtk_hsv_parent_class)->unrealize)
+    GTK_WIDGET_CLASS (gtk_hsv_parent_class)->unrealize (widget);
 }
 
 /* Size_request handler for the HSV color selector */
@@ -315,14 +338,18 @@ static void
 gtk_hsv_size_request (GtkWidget      *widget,
                      GtkRequisition *requisition)
 {
-  GtkHSV *hsv;
-  HSVPrivate *priv;
-  
-  hsv = GTK_HSV (widget);
-  priv = hsv->priv;
+  GtkHSV *hsv = GTK_HSV (widget);
+  HSVPrivate *priv = hsv->priv;
+  gint focus_width;
+  gint focus_pad;
+
+  gtk_widget_style_get (widget,
+                       "focus-line-width", &focus_width,
+                       "focus-padding", &focus_pad,
+                       NULL);
   
-  requisition->width = priv->size;
-  requisition->height = priv->size;
+  requisition->width = priv->size + 2 * (focus_width + focus_pad);
+  requisition->height = priv->size + 2 * (focus_width + focus_pad);
 }
 
 /* Size_allocate handler for the HSV color selector */
@@ -516,7 +543,7 @@ compute_triangle (GtkHSV *hsv,
   
   priv = hsv->priv;
   
-  center = priv->size / 2.0;
+  center = GTK_WIDGET (hsv)->requisition.width / 2.0;
   outer = priv->size / 2.0;
   inner = outer - priv->ring_width;
   angle = priv->h * 2.0 * G_PI;
@@ -560,15 +587,12 @@ compute_sv (GtkHSV  *hsv,
            gdouble *s,
            gdouble *v)
 {
-  HSVPrivate *priv;
   int ihx, ihy, isx, isy, ivx, ivy;
   double hx, hy, sx, sy, vx, vy;
   double center;
   
-  priv = hsv->priv;
-  
   compute_triangle (hsv, &ihx, &ihy, &isx, &isy, &ivx, &ivy);
-  center = priv->size / 2.0;
+  center = GTK_WIDGET (hsv)->requisition.width / 2.0;
   hx = ihx - center;
   hy = center - ihy;
   sx = isx - center;
@@ -625,8 +649,12 @@ compute_sv (GtkHSV  *hsv,
        {
          if (*v > 1.0)
            *v = 1.0;
-         
-         *s = (y - sy - *v * (vy - sy)) / (*v * (hy - vy));
+
+         if (fabs (hy - vy) < fabs (hx - vx))
+           *s = (x - sx - *v * (vx - sx)) / (*v * (hx - vx));
+         else
+           *s = (y - sy - *v * (vy - sy)) / (*v * (hy - vy));
+           
          if (*s < 0.0)
            *s = 0.0;
          else if (*s > 1.0)
@@ -660,14 +688,11 @@ compute_v (GtkHSV *hsv,
           gdouble x,
           gdouble y)
 {
-  HSVPrivate *priv;
   double center;
   double dx, dy;
   double angle;
   
-  priv = hsv->priv;
-  
-  center = priv->size / 2.0;
+  center = GTK_WIDGET (hsv)->requisition.width / 2.0;
   dx = x - center;
   dy = center - y;
   
@@ -689,7 +714,8 @@ set_cross_grab (GtkHSV *hsv,
   
   priv = hsv->priv;
   
-  cursor = gdk_cursor_new (GDK_CROSSHAIR);
+  cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (hsv)),
+                                      GDK_CROSSHAIR);
   gdk_pointer_grab (priv->window, FALSE,
                    (GDK_POINTER_MOTION_MASK
                     | GDK_POINTER_MOTION_HINT_MASK
@@ -697,7 +723,21 @@ set_cross_grab (GtkHSV *hsv,
                    NULL,
                    cursor,
                    time);
-  gdk_cursor_destroy (cursor);
+  gdk_cursor_unref (cursor);
+}
+
+static gboolean 
+gtk_hsv_grab_broken (GtkWidget          *widget,
+                    GdkEventGrabBroken *event)
+{
+  GtkHSV *hsv = GTK_HSV (widget);
+  HSVPrivate *priv;
+  
+  priv = hsv->priv;
+  
+  priv->mode = DRAG_NONE;
+  
+  return TRUE;
 }
 
 /* Button_press_event handler for the HSV color selector */
@@ -727,6 +767,9 @@ gtk_hsv_button_press (GtkWidget      *widget,
                         compute_v (hsv, x, y),
                         priv->s,
                         priv->v);
+
+      gtk_widget_grab_focus (widget);
+      priv->focus_on_ring = TRUE;
       
       return TRUE;
     }
@@ -740,6 +783,10 @@ gtk_hsv_button_press (GtkWidget      *widget,
       
       compute_sv (hsv, x, y, &s, &v);
       gtk_hsv_set_color (hsv, priv->h, s, v);
+
+      gtk_widget_grab_focus (widget);
+      priv->focus_on_ring = FALSE;
+      
       return TRUE;
     }
   
@@ -782,8 +829,8 @@ gtk_hsv_button_release (GtkWidget      *widget,
   } else
     g_assert_not_reached ();
   
-  gdk_pointer_ungrab (event->time);
-  
+  gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
+                             event->time);
   return TRUE;
 }
 
@@ -795,7 +842,6 @@ gtk_hsv_motion (GtkWidget      *widget,
   GtkHSV *hsv;
   HSVPrivate *priv;
   double x, y;
-  gint ix, iy;
   GdkModifierType mods;
   
   hsv = GTK_HSV (widget);
@@ -804,18 +850,11 @@ gtk_hsv_motion (GtkWidget      *widget,
   if (priv->mode == DRAG_NONE)
     return FALSE;
   
-  if (event->is_hint)
-    {
-      gdk_window_get_pointer (priv->window, &ix, &iy, &mods);
-      x = ix;
-      y = iy;
-    }
-  else
-    {
-      x = event->x;
-      y = event->y;
-    }
-  
+  gdk_event_request_motions (event);
+  x = event->x;
+  y = event->y;
+  mods = event->state;
+
   if (priv->mode == DRAG_H)
     {
       gtk_hsv_set_color (hsv, compute_v (hsv, x, y), priv->s, priv->v);
@@ -840,39 +879,46 @@ gtk_hsv_motion (GtkWidget      *widget,
 /* Paints the hue ring */
 static void
 paint_ring (GtkHSV      *hsv,
-           GdkDrawable *drawable,
+           cairo_t     *cr,
            gint         x,
            gint         y,
            gint         width,
            gint         height)
 {
+  GtkWidget *widget = GTK_WIDGET (hsv);
   HSVPrivate *priv;
   int xx, yy;
   gdouble dx, dy, dist;
   gdouble center;
   gdouble inner, outer;
-  guchar *buf, *p;
+  guint32 *buf, *p;
   gdouble angle;
   gdouble hue;
   gdouble r, g, b;
-  GdkBitmap *mask;
-  GdkGC *gc;
-  GdkColor color;
+  cairo_surface_t *source;
+  cairo_t *source_cr;
+  gint focus_width;
+  gint focus_pad;
+
+  gtk_widget_style_get (widget,
+                       "focus-line-width", &focus_width,
+                       "focus-padding", &focus_pad,
+                       NULL);
   
   priv = hsv->priv;
   
-  center = priv->size / 2.0;
+  center = widget->requisition.width / 2.0;
   
   outer = priv->size / 2.0;
   inner = outer - priv->ring_width;
   
-  /* Paint the ring */
+  /* Create an image initialized with the ring colors */
   
-  buf = g_new (guchar, width * height * 3);
+  buf = g_new (guint32, width * height);
   
   for (yy = 0; yy < height; yy++)
     {
-      p = buf + yy * width * 3;
+      p = buf + yy * width;
       
       dy = -(yy + y - center);
       
@@ -881,10 +927,8 @@ paint_ring (GtkHSV      *hsv,
          dx = xx + x - center;
          
          dist = dx * dx + dy * dy;
-         if (dist < (inner * inner) || dist > (outer * outer))
+         if (dist < ((inner-1) * (inner-1)) || dist > ((outer+1) * (outer+1)))
            {
-             *p++ = 0;
-             *p++ = 0;
              *p++ = 0;
              continue;
            }
@@ -900,51 +944,20 @@ paint_ring (GtkHSV      *hsv,
          b = 1.0;
          hsv_to_rgb (&r, &g, &b);
          
-         *p++ = floor (r * 255 + 0.5);
-         *p++ = floor (g * 255 + 0.5);
-         *p++ = floor (b * 255 + 0.5);
+         *p++ = (((int)floor (r * 255 + 0.5) << 16) |
+                 ((int)floor (g * 255 + 0.5) << 8) |
+                 (int)floor (b * 255 + 0.5));
        }
     }
-  
-  /* Create clipping mask */
-  
-  mask = gdk_pixmap_new (NULL, width, height, 1);
-  gc = gdk_gc_new (mask);
-  
-  color.pixel = 0;
-  gdk_gc_set_foreground (gc, &color);
-  gdk_draw_rectangle (mask, gc, TRUE,
-                     0, 0, width, height);
-  
-  
-  color.pixel = 1;
-  gdk_gc_set_foreground (gc, &color);
-  gdk_draw_arc (mask, gc, TRUE,
-               -x, -y,
-               priv->size - 1, priv->size - 1,
-               0, 360 * 64);
-  
-  color.pixel = 0;
-  gdk_gc_set_foreground (gc, &color);
-  gdk_draw_arc (mask, gc, TRUE,
-               -x + priv->ring_width - 1, -y + priv->ring_width - 1,
-               priv->size - 2 * priv->ring_width + 1, priv->size - 2 * priv->ring_width + 1,
-               0, 360 * 64);
-  
-  gdk_gc_unref (gc);
-  
-  gdk_gc_set_clip_mask (priv->gc, mask);
-  gdk_gc_set_clip_origin (priv->gc, 0, 0);
-  
-  /* Draw ring */
-  
-  gdk_draw_rgb_image_dithalign (drawable, priv->gc, 0, 0, width, height,
-                               GDK_RGB_DITHER_MAX,
-                               buf,
-                               width * 3,
-                               x, y);
-  
-  /* Draw value marker */
+
+  source = cairo_image_surface_create_for_data ((char *)buf,
+                                               CAIRO_FORMAT_RGB24,
+                                               width, height, 4 * width);
+
+  /* Now draw the value marker onto the source image, so that it
+   * will get properly clipped at the edges of the ring
+   */
+  source_cr = cairo_create (source);
   
   r = priv->h;
   g = 1.0;
@@ -952,32 +965,35 @@ paint_ring (GtkHSV      *hsv,
   hsv_to_rgb (&r, &g, &b);
   
   if (INTENSITY (r, g, b) > 0.5)
-    gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
+    cairo_set_source_rgb (source_cr, 0., 0., 0.);
   else
-    gdk_rgb_gc_set_foreground (priv->gc, 0xffffff);
-  
-  gdk_draw_line (drawable, priv->gc,
-                -x + center, -y + center,
+    cairo_set_source_rgb (source_cr, 1., 1., 1.);
+
+  cairo_move_to (source_cr, -x + center, - y + center);
+  cairo_line_to (source_cr,
                 -x + center + cos (priv->h * 2.0 * G_PI) * center,
                 -y + center - sin (priv->h * 2.0 * G_PI) * center);
+  cairo_stroke (source_cr);
+  cairo_destroy (source_cr);
+
+  /* Draw the ring using the source image */
+
+  cairo_save (cr);
+    
+  cairo_set_source_surface (cr, source, x, y);
+  cairo_surface_destroy (source);
+
+  cairo_set_line_width (cr, priv->ring_width);
+  cairo_new_path (cr);
+  cairo_arc (cr,
+            center, center,
+            priv->size / 2. - priv->ring_width / 2.,
+            0, 2 * G_PI);
+  cairo_stroke (cr);
   
-  gdk_gc_set_clip_mask (priv->gc, NULL);
-  gdk_bitmap_unref (mask);
+  cairo_restore (cr);
   
   g_free (buf);
-  
-  /* Draw ring outline */
-  
-  gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
-  
-  gdk_draw_arc (drawable, priv->gc, FALSE,
-               -x, -y,
-               priv->size - 1, priv->size - 1,
-               0, 360 * 64);
-  gdk_draw_arc (drawable, priv->gc, FALSE,
-               -x + priv->ring_width - 1, -y + priv->ring_width - 1,
-               priv->size - 2 * priv->ring_width + 1, priv->size - 2 * priv->ring_width + 1,
-               0, 360 * 64);
 }
 
 /* Converts an HSV triplet to an integer RGB triplet */
@@ -1002,29 +1018,35 @@ get_color (gdouble h,
                               ? ((a) + ((b) - (a)) * ((i) - (v1)) / ((v2) - (v1)))     \
                               : (a))
 
+/* Number of pixels we extend out from the edges when creating
+ * color source to avoid artifacts
+ */
+#define PAD 3
+
 /* Paints the HSV triangle */
 static void
 paint_triangle (GtkHSV      *hsv,
-               GdkDrawable *drawable,
+               cairo_t     *cr,
                gint         x,
                gint         y,
                gint         width,
                gint         height)
 {
+  GtkWidget *widget = GTK_WIDGET (hsv);
   HSVPrivate *priv;
   gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
   gint x1, y1, r1, g1, b1; /* First vertex in scanline order */
   gint x2, y2, r2, g2, b2; /* Second vertex */
   gint x3, y3, r3, g3, b3; /* Third vertex */
   gint t;
-  guchar *buf, *p;
+  guint32 *buf, *p, c;
   gint xl, xr, rl, rr, gl, gr, bl, br; /* Scanline data */
   gint xx, yy;
-  GdkBitmap *mask;
-  GdkGC *gc;
-  GdkColor color;
-  GdkPoint points[3];
+  gint x_interp, y_interp;
+  gint x_start, x_end;
+  cairo_surface_t *source;
   gdouble r, g, b;
+  gchar *detail;
   
   priv = hsv->priv;
   
@@ -1073,42 +1095,37 @@ paint_triangle (GtkHSV      *hsv,
   
   /* Shade the triangle */
   
-  buf = g_new (guchar, width * height * 3);
+  buf = g_new (guint32, width * height);
   
   for (yy = 0; yy < height; yy++)
     {
-      p = buf + yy * width * 3;
+      p = buf + yy * width;
       
-      if (yy + y < y1 || yy + y > y3)
-       for (xx = 0; xx < width; xx++)
-         {
-           *p++ = 0;
-           *p++ = 0;
-           *p++ = 0;
-         }
-      else {
-       if (yy + y < y2)
+      if (yy + y >= y1 - PAD && yy + y < y3 + PAD) {
+       y_interp = CLAMP (yy + y, y1, y3);
+       
+       if (y_interp < y2)
          {
-           xl = LERP (x1, x2, y1, y2, yy + y);
+           xl = LERP (x1, x2, y1, y2, y_interp);
            
-           rl = LERP (r1, r2, y1, y2, yy + y);
-           gl = LERP (g1, g2, y1, y2, yy + y);
-           bl = LERP (b1, b2, y1, y2, yy + y);
+           rl = LERP (r1, r2, y1, y2, y_interp);
+           gl = LERP (g1, g2, y1, y2, y_interp);
+           bl = LERP (b1, b2, y1, y2, y_interp);
          }
        else
          {
-           xl = LERP (x2, x3, y2, y3, yy + y);
+           xl = LERP (x2, x3, y2, y3, y_interp);
            
-           rl = LERP (r2, r3, y2, y3, yy + y);
-           gl = LERP (g2, g3, y2, y3, yy + y);
-           bl = LERP (b2, b3, y2, y3, yy + y);
+           rl = LERP (r2, r3, y2, y3, y_interp);
+           gl = LERP (g2, g3, y2, y3, y_interp);
+           bl = LERP (b2, b3, y2, y3, y_interp);
          }
        
-       xr = LERP (x1, x3, y1, y3, yy + y);
+       xr = LERP (x1, x3, y1, y3, y_interp);
        
-       rr = LERP (r1, r3, y1, y3, yy + y);
-       gr = LERP (g1, g3, y1, y3, yy + y);
-       br = LERP (b1, b3, y1, y3, yy + y);
+       rr = LERP (r1, r3, y1, y3, y_interp);
+       gr = LERP (g1, g3, y1, y3, y_interp);
+       br = LERP (b1, b3, y1, y3, y_interp);
        
        if (xl > xr)
          {
@@ -1117,70 +1134,49 @@ paint_triangle (GtkHSV      *hsv,
            SWAP (gl, gr, t);
            SWAP (bl, br, t);
          }
-       
-       for (xx = 0; xx < width; xx++)
+
+       x_start = MAX (xl - PAD, x);
+       x_end = MIN (xr + PAD, x + width);
+       x_start = MIN (x_start, x_end);
+
+       c = (rl << 16) | (gl << 8) | bl;
+
+       for (xx = x; xx < x_start; xx++)
+         *p++ = c;
+         
+       for (; xx < x_end; xx++)
          {
-           if (xx + x < xl || xx + x > xr)
-             {
-               *p++ = 0;
-               *p++ = 0;
-               *p++ = 0;
-             }
-           else
-             {
-               *p++ = LERP (rl, rr, xl, xr, xx + x);
-               *p++ = LERP (gl, gr, xl, xr, xx + x);
-               *p++ = LERP (bl, br, xl, xr, xx + x);
-             }
+           x_interp = CLAMP (xx, xl, xr);
+               
+           *p++ = ((LERP (rl, rr, xl, xr, x_interp) << 16) |
+                   (LERP (gl, gr, xl, xr, x_interp) << 8) |
+                   LERP (bl, br, xl, xr, x_interp));
          }
+
+       c = (rr << 16) | (gr << 8) | br;
+
+       for (; xx < x + width; xx++)
+         *p++ = c;
       }
     }
+
+  source = cairo_image_surface_create_for_data ((char *)buf,
+                                               CAIRO_FORMAT_RGB24,
+                                               width, height, 4 * width);
   
-  /* Create clipping mask */
-  
-  mask = gdk_pixmap_new (NULL, width, height, 1);
-  gc = gdk_gc_new (mask);
-  
-  color.pixel = 0;
-  gdk_gc_set_foreground (gc, &color);
-  gdk_draw_rectangle (mask, gc, TRUE,
-                     0, 0, width, height);
-  
-  color.pixel = 1;
-  gdk_gc_set_foreground (gc, &color);
-  
-  points[0].x = x1 - x;
-  points[0].y = y1 - y;
-  points[1].x = x2 - x;
-  points[1].y = y2 - y;
-  points[2].x = x3 - x;
-  points[2].y = y3 - y;
-  gdk_draw_polygon (mask, gc, TRUE, points, 3);
-  
-  gdk_gc_unref (gc);
-  
-  gdk_gc_set_clip_mask (priv->gc, mask);
-  gdk_gc_set_clip_origin (priv->gc, 0, 0);
-  
-  /* Draw triangle */
-  
-  gdk_draw_rgb_image_dithalign (drawable, priv->gc, 0, 0, width, height,
-                               GDK_RGB_DITHER_MAX,
-                               buf,
-                               width * 3,
-                               x, y);
+  /* Draw a triangle with the image as a source */
+
+  cairo_set_source_surface (cr, source, x, y);
+  cairo_surface_destroy (source);
   
-  gdk_gc_set_clip_mask (priv->gc, NULL);
-  gdk_bitmap_unref (mask);
+  cairo_move_to (cr, x1, y1);
+  cairo_line_to (cr, x2, y2);
+  cairo_line_to (cr, x3, y3);
+  cairo_close_path (cr);
+  cairo_fill (cr);
   
   g_free (buf);
   
-  /* Draw triangle outline */
-  
-  gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
-  
-  gdk_draw_polygon (drawable, priv->gc, FALSE, points, 3);
-  
   /* Draw value marker */
   
   xx = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5);
@@ -1190,33 +1186,60 @@ paint_triangle (GtkHSV      *hsv,
   g = priv->s;
   b = priv->v;
   hsv_to_rgb (&r, &g, &b);
-  
+
   if (INTENSITY (r, g, b) > 0.5)
-    gdk_rgb_gc_set_foreground (priv->gc, 0x000000);
+    {
+      detail = "colorwheel_light";
+      cairo_set_source_rgb (cr, 0., 0., 0.);
+    }
   else
-    gdk_rgb_gc_set_foreground (priv->gc, 0xffffff);
-  
-  gdk_draw_arc (drawable, priv->gc, FALSE,
-               xx - 3, yy - 3,
-               6, 6,
-               0, 360 * 64);
-  gdk_draw_arc (drawable, priv->gc, FALSE,
-               xx - 2, yy - 2,
-               4, 4,
-               0, 360 * 64);
+    {
+      detail = "colorwheel_dark";
+      cairo_set_source_rgb (cr, 1., 1., 1.);
+    }
+
+#define RADIUS 4
+#define FOCUS_RADIUS 6
+
+  cairo_new_path (cr);
+  cairo_arc (cr, xx, yy, RADIUS, 0, 2 * G_PI);
+  cairo_stroke (cr);
+  
+  /* Draw focus outline */
+
+  if (GTK_WIDGET_HAS_FOCUS (hsv) &&
+      !priv->focus_on_ring)
+    {
+      gint focus_width;
+      gint focus_pad;
+
+      gtk_widget_style_get (widget,
+                           "focus-line-width", &focus_width,
+                           "focus-padding", &focus_pad,
+                           NULL);
+  
+      gtk_paint_focus (widget->style, widget->window,
+                      GTK_WIDGET_STATE (widget),
+                      NULL, widget, detail,
+                      widget->allocation.x + xx - FOCUS_RADIUS - focus_width - focus_pad, 
+                      widget->allocation.y + yy - FOCUS_RADIUS - focus_width - focus_pad, 
+                      2 * (FOCUS_RADIUS + focus_width + focus_pad), 
+                      2 * (FOCUS_RADIUS + focus_width + focus_pad));
+    }
+  
 }
 
 /* Paints the contents of the HSV color selector */
 static void
 paint (GtkHSV      *hsv,
-       GdkDrawable *drawable,
+       cairo_t     *cr,
        gint         x,
        gint         y,
        gint         width,
        gint         height)
 {
-  paint_ring (hsv, drawable, x, y, width, height);
-  paint_triangle (hsv, drawable, x, y, width, height);
+  paint_ring (hsv, cr, x, y, width, height);
+  paint_triangle (hsv, cr, x, y, width, height);
 }
 
 /* Expose_event handler for the HSV color selector */
@@ -1227,14 +1250,14 @@ gtk_hsv_expose (GtkWidget      *widget,
   GtkHSV *hsv;
   HSVPrivate *priv;
   GdkRectangle rect, dest;
-  GdkPixmap *pixmap;
+  cairo_t *cr;
   
   hsv = GTK_HSV (widget);
   priv = hsv->priv;
   
   if (!(GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window))
     return FALSE;
-  
+
   rect.x = widget->allocation.x;
   rect.y = widget->allocation.y;
   rect.width = widget->allocation.width;
@@ -1243,34 +1266,85 @@ gtk_hsv_expose (GtkWidget      *widget,
   if (!gdk_rectangle_intersect (&event->area, &rect, &dest))
     return FALSE;
   
-  pixmap = gdk_pixmap_new (widget->window, dest.width, dest.height,
-                          gtk_widget_get_visual (widget)->depth);
-  
-  rect = dest;
-  rect.x = 0;
-  rect.y = 0;
-  
-  gdk_draw_rectangle (pixmap,
-                     widget->style->bg_gc[GTK_WIDGET_STATE (widget)],
-                     TRUE,
-                     0, 0, dest.width, dest.height);
-  paint (hsv, pixmap,
-        dest.x - widget->allocation.x, dest.y - widget->allocation.y,
+  cr = gdk_cairo_create (widget->window);
+
+  cairo_translate (cr, widget->allocation.x, widget->allocation.y);
+  paint (hsv, cr,
+        dest.x - widget->allocation.x,
+        dest.y - widget->allocation.y,
         dest.width, dest.height);
-  
-  gdk_draw_pixmap (widget->window,
-                  priv->gc,
-                  pixmap,
-                  0, 0,
-                  dest.x,
-                  dest.y,
-                  event->area.width, event->area.height);
-  
-  gdk_pixmap_unref (pixmap);
-  
+  cairo_destroy (cr);
+
+  if (GTK_WIDGET_HAS_FOCUS (hsv) && priv->focus_on_ring)
+    gtk_paint_focus (widget->style, widget->window,
+                    GTK_WIDGET_STATE (widget),
+                    &event->area, widget, NULL,
+                    widget->allocation.x,
+                    widget->allocation.y, 
+                    widget->allocation.width, 
+                    widget->allocation.height);
+
   return FALSE;
 }
 
+static gboolean
+gtk_hsv_focus (GtkWidget       *widget,
+               GtkDirectionType dir)
+{
+  GtkHSV *hsv;
+  HSVPrivate *priv;
+
+  hsv = GTK_HSV (widget);
+  priv = hsv->priv;
+
+  if (!GTK_WIDGET_HAS_FOCUS (hsv))
+    {
+      if (dir == GTK_DIR_TAB_BACKWARD)
+        priv->focus_on_ring = FALSE;
+      else
+        priv->focus_on_ring = TRUE;
+
+      gtk_widget_grab_focus (GTK_WIDGET (hsv));
+      return TRUE;
+    }
+  
+  switch (dir)
+    {
+    case GTK_DIR_UP:
+      if (priv->focus_on_ring)
+        return FALSE;
+      else
+        priv->focus_on_ring = TRUE;
+      break;
+
+    case GTK_DIR_DOWN:
+      if (priv->focus_on_ring)
+        priv->focus_on_ring = FALSE;
+      else
+        return FALSE;
+      break;
+
+    case GTK_DIR_LEFT:
+    case GTK_DIR_TAB_BACKWARD:
+      if (priv->focus_on_ring)
+        return FALSE;
+      else
+        priv->focus_on_ring = TRUE;
+      break;
+
+    case GTK_DIR_RIGHT:
+    case GTK_DIR_TAB_FORWARD:
+      if (priv->focus_on_ring)
+        priv->focus_on_ring = FALSE;
+      else
+        return FALSE;
+      break;
+    }
+
+  gtk_widget_queue_draw (GTK_WIDGET (hsv));
+
+  return TRUE;
+}
 
 /**
  * gtk_hsv_new:
@@ -1283,7 +1357,7 @@ gtk_hsv_expose (GtkWidget      *widget,
 GtkWidget*
 gtk_hsv_new (void)
 {
-  return GTK_WIDGET (gtk_type_new (GTK_TYPE_HSV));
+  return g_object_new (GTK_TYPE_HSV, NULL);
 }
 
 /**
@@ -1304,7 +1378,6 @@ gtk_hsv_set_color (GtkHSV *hsv,
 {
   HSVPrivate *priv;
   
-  g_return_if_fail (hsv != NULL);
   g_return_if_fail (GTK_IS_HSV (hsv));
   g_return_if_fail (h >= 0.0 && h <= 1.0);
   g_return_if_fail (s >= 0.0 && s <= 1.0);
@@ -1316,7 +1389,7 @@ gtk_hsv_set_color (GtkHSV *hsv,
   priv->s = s;
   priv->v = v;
   
-  gtk_signal_emit (GTK_OBJECT (hsv), hsv_signals[CHANGED]);
+  g_signal_emit (hsv, hsv_signals[CHANGED], 0);
   
   gtk_widget_queue_draw (GTK_WIDGET (hsv));
 }
@@ -1336,7 +1409,6 @@ gtk_hsv_get_color (GtkHSV *hsv, double *h, double *s, double *v)
 {
   HSVPrivate *priv;
   
-  g_return_if_fail (hsv != NULL);
   g_return_if_fail (GTK_IS_HSV (hsv));
   
   priv = hsv->priv;
@@ -1367,7 +1439,6 @@ gtk_hsv_set_metrics (GtkHSV *hsv,
   HSVPrivate *priv;
   int same_size;
   
-  g_return_if_fail (hsv != NULL);
   g_return_if_fail (GTK_IS_HSV (hsv));
   g_return_if_fail (size > 0);
   g_return_if_fail (ring_width > 0);
@@ -1401,7 +1472,6 @@ gtk_hsv_get_metrics (GtkHSV *hsv,
 {
   HSVPrivate *priv;
   
-  g_return_if_fail (hsv != NULL);
   g_return_if_fail (GTK_IS_HSV (hsv));
   
   priv = hsv->priv;
@@ -1431,7 +1501,6 @@ gtk_hsv_is_adjusting (GtkHSV *hsv)
 {
   HSVPrivate *priv;
   
-  g_return_val_if_fail (hsv != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_HSV (hsv), FALSE);
   
   priv = hsv->priv;
@@ -1510,3 +1579,84 @@ gtk_rgb_to_hsv (gdouble  r,
   if (v)
     *v = b;
 }
+
+static void
+gtk_hsv_move (GtkHSV          *hsv,
+              GtkDirectionType dir)
+{
+  HSVPrivate *priv;
+  gdouble hue, sat, val;
+  gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
+  gint x, y; /* position in triangle */
+  
+  priv = hsv->priv;
+
+  hue = priv->h;
+  sat = priv->s;
+  val = priv->v;
+
+  compute_triangle (hsv, &hx, &hy, &sx, &sy, &vx, &vy);
+
+  x = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5);
+  y = floor (sy + (vy - sy) * priv->v + (hy - vy) * priv->s * priv->v + 0.5);
+
+#define HUE_DELTA 0.002
+  switch (dir)
+    {
+    case GTK_DIR_UP:
+      if (priv->focus_on_ring)
+        hue += HUE_DELTA;
+      else
+        {
+          y -= 1;
+          compute_sv (hsv, x, y, &sat, &val);
+        }
+      break;
+
+    case GTK_DIR_DOWN:
+      if (priv->focus_on_ring)
+        hue -= HUE_DELTA;
+      else
+        {
+          y += 1;
+          compute_sv (hsv, x, y, &sat, &val);
+        }
+      break;
+
+    case GTK_DIR_LEFT:
+      if (priv->focus_on_ring)
+        hue += HUE_DELTA;
+      else
+        {
+          x -= 1;
+          compute_sv (hsv, x, y, &sat, &val);
+        }
+      break;
+
+    case GTK_DIR_RIGHT:
+      if (priv->focus_on_ring)
+        hue -= HUE_DELTA
+          ;
+      else
+        {
+          x += 1;
+          compute_sv (hsv, x, y, &sat, &val);
+        }
+      break;
+
+    default:
+      /* we don't care about the tab directions */
+      break;
+    }
+
+  /* Wrap */
+  if (hue < 0.0)
+    hue = 1.0;
+  else if (hue > 1.0)
+    hue = 0.0;
+  
+  gtk_hsv_set_color (hsv, hue, sat, val);
+}
+
+#define __GTK_HSV_C__
+#include "gtkaliasdef.c"