static void
nautilus_selection_canvas_item_dispose (GObject *obj)
{
	NautilusSelectionCanvasItem *self = NAUTILUS_SELECTION_CANVAS_ITEM (obj);

	if (self->priv->fade_out_handler_id != 0) {
		g_source_remove (self->priv->fade_out_handler_id);
		self->priv->fade_out_handler_id = 0;
	}

	G_OBJECT_CLASS (nautilus_selection_canvas_item_parent_class)->dispose (obj);
}
static void
nautilus_selection_canvas_item_dispose (GObject *obj)
{
    NautilusSelectionCanvasItem *self = NAUTILUS_SELECTION_CANVAS_ITEM (obj);

    if (self->priv->fade_out_tick_id != 0)
    {
        gtk_widget_remove_tick_callback (GTK_WIDGET (EEL_CANVAS_ITEM (self)->canvas), self->priv->fade_out_tick_id);
        self->priv->fade_out_tick_id = 0;
    }

    G_OBJECT_CLASS (nautilus_selection_canvas_item_parent_class)->dispose (obj);
}
static void
nautilus_selection_canvas_item_translate (EelCanvasItem *item,
					  double dx,
					  double dy)
{
	NautilusSelectionCanvasItem *self;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (item);

	self->priv->x1 += dx;
	self->priv->y1 += dy;
	self->priv->x2 += dx;
	self->priv->y2 += dy;
}
static void
nautilus_selection_canvas_item_get_property (GObject *object,
					     guint param_id,
					     GValue *value,
					     GParamSpec *pspec)
{
	NautilusSelectionCanvasItem *self;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (object);

	switch (param_id) {
	case PROP_X1:
		g_value_set_double (value,  self->priv->x1);
		break;

	case PROP_Y1:
		g_value_set_double (value,  self->priv->y1);
		break;

	case PROP_X2:
		g_value_set_double (value,  self->priv->x2);
		break;

	case PROP_Y2:
		g_value_set_double (value,  self->priv->y2);
		break;

	case PROP_FILL_COLOR_RGBA:
		g_value_set_boxed (value,  &self->priv->fill_color);
		break;

	case PROP_OUTLINE_COLOR_RGBA:
		g_value_set_boxed (value,  &self->priv->outline_color);
		break;

	case PROP_OUTLINE_STIPPLING:
		g_value_set_boolean (value, self->priv->outline_stippling);
		break;
	case PROP_WIDTH_PIXELS:
		g_value_set_uint (value, self->priv->width);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	}
}
static void
nautilus_selection_canvas_item_bounds (EelCanvasItem *item,
				       double *x1,
				       double *y1,
				       double *x2,
				       double *y2)
{
	NautilusSelectionCanvasItem *self;
	double hwidth;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (item);

	hwidth = (self->priv->width / item->canvas->pixels_per_unit) / 2.0;

	*x1 = self->priv->x1 - hwidth;
	*y1 = self->priv->y1 - hwidth;
	*x2 = self->priv->x2 + hwidth;
	*y2 = self->priv->y2 + hwidth;
}
static void
nautilus_selection_canvas_item_draw (EelCanvasItem *item,
				     cairo_t *cr,
				     cairo_region_t *region)
{
	NautilusSelectionCanvasItem *self;
	double x1, y1, x2, y2;
	int cx1, cy1, cx2, cy2;
	double i2w_dx, i2w_dy;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (item);

	/* Get canvas pixel coordinates */
	i2w_dx = 0.0;
	i2w_dy = 0.0;
	eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
	
	x1 = self->priv->x1 + i2w_dx;
	y1 = self->priv->y1 + i2w_dy;
	x2 = self->priv->x2 + i2w_dx;
	y2 = self->priv->y2 + i2w_dy;

	eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
	eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
	
	if (cx2 <= cx1 || cy2 <= cy1 ) {
		return;
	}

	cairo_save (cr);

	if (self->priv->fill_set) {
		GdkRGBA actual_fill;

		actual_fill = self->priv->fill_color;

		if (self->priv->fade_out_handler_id != 0) {
			actual_fill.alpha = self->priv->fade_out_fill_alpha;
		}

		gdk_cairo_set_source_rgba (cr, &actual_fill);
		cairo_rectangle (cr,
				 cx1, cy1,
				 cx2 - cx1 + 1,
				 cy2 - cy1 + 1);
		cairo_fill (cr);
	}

	if (self->priv->outline_set) {
		GdkRGBA actual_outline;

		actual_outline = self->priv->outline_color;

		if (self->priv->fade_out_handler_id != 0) {
			actual_outline.alpha = self->priv->fade_out_outline_alpha;
		}

		gdk_cairo_set_source_rgba (cr, &actual_outline);
		cairo_set_line_width (cr, (int) self->priv->width);

		if (self->priv->outline_stippling) {
			double dash[2] = { DASH_ON, DASH_OFF };

			cairo_set_dash (cr, dash, G_N_ELEMENTS (dash), 0);
		}

		cairo_rectangle (cr,
				 cx1 + 0.5, cy1 + 0.5,
				 cx2 - cx1,
				 cy2 - cy1);
		cairo_stroke (cr);
	}

	cairo_restore (cr);
}
static void
nautilus_selection_canvas_item_set_property (GObject *object,
					     guint param_id,
					     const GValue *value,
					     GParamSpec *pspec)
{
	EelCanvasItem *item;
	NautilusSelectionCanvasItem *self;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (object);
	item = EEL_CANVAS_ITEM (object);

	switch (param_id) {
	case PROP_X1:
		self->priv->x1 = g_value_get_double (value);

		eel_canvas_item_request_update (item);
		break;

	case PROP_Y1:
		self->priv->y1 = g_value_get_double (value);

		eel_canvas_item_request_update (item);
		break;

	case PROP_X2:
		self->priv->x2 = g_value_get_double (value);

		eel_canvas_item_request_update (item);
		break;

	case PROP_Y2:
		self->priv->y2 = g_value_get_double (value);

		eel_canvas_item_request_update (item);
		break;

	case PROP_FILL_COLOR_RGBA: {
		GdkRGBA *color;

		color = g_value_get_boxed (value);

		do_set_fill (self, color != NULL);

		if (color != NULL) {
			self->priv->fill_color = *color;
		}

		eel_canvas_item_request_redraw (item);		
		break;
	}

	case PROP_OUTLINE_COLOR_RGBA: {
		GdkRGBA *color;

		color = g_value_get_boxed (value);

		do_set_outline (self, color != NULL);

		if (color != NULL) {
			self->priv->outline_color = *color;
		}

		eel_canvas_item_request_redraw (item);		
		break;
	}

	case PROP_OUTLINE_STIPPLING:
		self->priv->outline_stippling = g_value_get_boolean (value);

		eel_canvas_item_request_redraw (item);
		break;

	case PROP_WIDTH_PIXELS:
		self->priv->width = g_value_get_uint (value);

		eel_canvas_item_request_update (item);
		break;

	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
		break;
	}
}
static void
nautilus_selection_canvas_item_update (EelCanvasItem *item,
				       double i2w_dx,
				       double i2w_dy,
				       gint flags)
{
	NautilusSelectionCanvasItem *self;
	NautilusSelectionCanvasItemDetails *priv;
	double x1, y1, x2, y2;
	int cx1, cy1, cx2, cy2;
	int repaint_rects_count, i;
	int width_pixels;
	int width_lt, width_rb;
	Rect update_rect, repaint_rects[4];

	if (EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update)
		(* EEL_CANVAS_ITEM_CLASS (nautilus_selection_canvas_item_parent_class)->update) (item, i2w_dx, i2w_dy, flags);

	self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
	priv = self->priv;

	x1 = priv->x1 + i2w_dx;
	y1 = priv->y1 + i2w_dy;
	x2 = priv->x2 + i2w_dx;
	y2 = priv->y2 + i2w_dy;

	eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
	eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);

	update_rect = make_rect (cx1, cy1, cx2+1, cy2+1);
	diff_rects (update_rect, priv->last_update_rect,
		    &repaint_rects_count, repaint_rects);
	for (i = 0; i < repaint_rects_count; i++) {
		eel_canvas_request_redraw (item->canvas,
					   repaint_rects[i].x0, repaint_rects[i].y0,
					   repaint_rects[i].x1, repaint_rects[i].y1);
	}

	priv->last_update_rect = update_rect;

	if (priv->outline_set) {
		/* Outline and bounding box */
		width_pixels = (int) priv->width;
		width_lt = width_pixels / 2;
		width_rb = (width_pixels + 1) / 2;
		
		cx1 -= width_lt;
		cy1 -= width_lt;
		cx2 += width_rb;
		cy2 += width_rb;

		update_rect = make_rect (cx1, cy1, cx2, cy2);
		request_redraw_borders (item->canvas, &update_rect,
					(width_lt + width_rb));
		request_redraw_borders (item->canvas, &priv->last_outline_update_rect,
					priv->last_outline_update_width);
		priv->last_outline_update_rect = update_rect;
		priv->last_outline_update_width = width_lt + width_rb;
		
		item->x1 = cx1;
		item->y1 = cy1;
		item->x2 = cx2+1;
		item->y2 = cy2+1;
	} else {
		item->x1 = cx1;
		item->y1 = cy1;
		item->x2 = cx2+1;
		item->y2 = cy2+1;
	}
}
static double
nautilus_selection_canvas_item_point (EelCanvasItem *item,
				      double x,
				      double y,
				      int cx,
				      int cy,
				      EelCanvasItem **actual_item)
{
	NautilusSelectionCanvasItem *self;
	double x1, y1, x2, y2;
	double hwidth;
	double dx, dy;
	double tmp;

	self = NAUTILUS_SELECTION_CANVAS_ITEM (item);
	*actual_item = item;

	/* Find the bounds for the rectangle plus its outline width */

	x1 = self->priv->x1;
	y1 = self->priv->y1;
	x2 = self->priv->x2;
	y2 = self->priv->y2;

	if (self->priv->outline_set) {
		hwidth = (self->priv->width / item->canvas->pixels_per_unit) / 2.0;

		x1 -= hwidth;
		y1 -= hwidth;
		x2 += hwidth;
		y2 += hwidth;
	} else
		hwidth = 0.0;

	/* Is point inside rectangle (which can be hollow if it has no fill set)? */

	if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
		if (self->priv->fill_set || !self->priv->outline_set)
			return 0.0;

		dx = x - x1;
		tmp = x2 - x;
		if (tmp < dx)
			dx = tmp;

		dy = y - y1;
		tmp = y2 - y;
		if (tmp < dy)
			dy = tmp;

		if (dy < dx)
			dx = dy;

		dx -= 2.0 * hwidth;

		if (dx < 0.0)
			return 0.0;
		else
			return dx;
	}

	/* Point is outside rectangle */

	if (x < x1)
		dx = x1 - x;
	else if (x > x2)
		dx = x - x2;
	else
		dx = 0.0;

	if (y < y1)
		dy = y1 - y;
	else if (y > y2)
		dy = y - y2;
	else
		dy = 0.0;

	return sqrt (dx * dx + dy * dy);
}