| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| enum |
| { |
| PROP_X1 = 1, |
| PROP_Y1, |
| PROP_X2, |
| PROP_Y2, |
| PROP_FILL_COLOR_RGBA, |
| PROP_OUTLINE_COLOR_RGBA, |
| PROP_OUTLINE_STIPPLING, |
| PROP_WIDTH_PIXELS, |
| NUM_PROPERTIES |
| }; |
| |
| static GParamSpec *properties[NUM_PROPERTIES] = { NULL }; |
| |
| typedef struct |
| { |
| |
| int x0, y0, x1, y1; |
| } Rect; |
| |
| struct _NautilusSelectionCanvasItemDetails |
| { |
| Rect last_update_rect; |
| Rect last_outline_update_rect; |
| int last_outline_update_width; |
| |
| double x1, y1, x2, y2; |
| double width; |
| |
| GdkRGBA fill_color; |
| GdkRGBA outline_color; |
| |
| gboolean outline_stippling; |
| |
| |
| |
| unsigned int fill_set : 1; |
| unsigned int outline_set : 1; |
| |
| double fade_out_fill_alpha; |
| double fade_out_outline_alpha; |
| |
| gint64 fade_out_start_time; |
| gint64 fade_out_end_time; |
| |
| guint fade_out_tick_id; |
| }; |
| |
| G_DEFINE_TYPE (NautilusSelectionCanvasItem, nautilus_selection_canvas_item, EEL_TYPE_CANVAS_ITEM); |
| |
| |
| |
| 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); |
| |
| |
| 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_tick_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_tick_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 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; |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| 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; |
| } |
| } |
| |
| |
| |
| 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); |
| } |
| |
| static void |
| request_redraw_borders (EelCanvas *canvas, |
| Rect *update_rect, |
| int width) |
| { |
| eel_canvas_request_redraw (canvas, |
| update_rect->x0, update_rect->y0, |
| update_rect->x1, update_rect->y0 + width); |
| eel_canvas_request_redraw (canvas, |
| update_rect->x0, update_rect->y1 - width, |
| update_rect->x1, update_rect->y1); |
| eel_canvas_request_redraw (canvas, |
| update_rect->x0, update_rect->y0, |
| update_rect->x0 + width, update_rect->y1); |
| eel_canvas_request_redraw (canvas, |
| update_rect->x1 - width, update_rect->y0, |
| update_rect->x1, update_rect->y1); |
| } |
| |
| static Rect make_rect (int x0, |
| int y0, |
| int x1, |
| int y1); |
| |
| static int |
| rect_empty (const Rect *src) |
| { |
| return (src->x1 <= src->x0 || src->y1 <= src->y0); |
| } |
| |
| static gboolean |
| rects_intersect (Rect r1, |
| Rect r2) |
| { |
| if (r1.x0 >= r2.x1) |
| { |
| return FALSE; |
| } |
| if (r2.x0 >= r1.x1) |
| { |
| return FALSE; |
| } |
| if (r1.y0 >= r2.y1) |
| { |
| return FALSE; |
| } |
| if (r2.y0 >= r1.y1) |
| { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static void |
| diff_rects_guts (Rect ra, |
| Rect rb, |
| int *count, |
| Rect result[4]) |
| { |
| if (ra.x0 < rb.x0) |
| { |
| result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1); |
| } |
| if (ra.y0 < rb.y0) |
| { |
| result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0); |
| } |
| if (ra.x1 < rb.x1) |
| { |
| result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1); |
| } |
| if (ra.y1 < rb.y1) |
| { |
| result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1); |
| } |
| } |
| |
| static void |
| diff_rects (Rect r1, |
| Rect r2, |
| int *count, |
| Rect result[4]) |
| { |
| g_assert (count != NULL); |
| g_assert (result != NULL); |
| |
| *count = 0; |
| |
| if (rects_intersect (r1, r2)) |
| { |
| diff_rects_guts (r1, r2, count, result); |
| diff_rects_guts (r2, r1, count, result); |
| } |
| else |
| { |
| if (!rect_empty (&r1)) |
| { |
| result[(*count)++] = r1; |
| } |
| if (!rect_empty (&r2)) |
| { |
| result[(*count)++] = r2; |
| } |
| } |
| } |
| |
| static Rect |
| make_rect (int x0, |
| int y0, |
| int x1, |
| int y1) |
| { |
| Rect r; |
| |
| r.x0 = x0; |
| r.y0 = y0; |
| r.x1 = x1; |
| r.y1 = y1; |
| return r; |
| } |
| |
| 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) |
| { |
| |
| 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 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_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 gboolean |
| fade_and_request_redraw (GtkWidget *canvas, |
| GdkFrameClock *frame_clock, |
| gpointer user_data) |
| { |
| NautilusSelectionCanvasItem *self = user_data; |
| gint64 frame_time; |
| gdouble percentage; |
| |
| frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
| if (frame_time >= self->priv->fade_out_end_time) |
| { |
| self->priv->fade_out_tick_id = 0; |
| eel_canvas_item_destroy (EEL_CANVAS_ITEM (self)); |
| |
| return G_SOURCE_REMOVE; |
| } |
| |
| percentage = 1.0 - (gdouble) (frame_time - self->priv->fade_out_start_time) / |
| (gdouble) (self->priv->fade_out_end_time - self->priv->fade_out_start_time); |
| |
| self->priv->fade_out_fill_alpha = self->priv->fill_color.alpha * percentage; |
| self->priv->fade_out_outline_alpha = self->priv->outline_color.alpha * percentage; |
| |
| eel_canvas_item_request_redraw (EEL_CANVAS_ITEM (self)); |
| |
| return G_SOURCE_CONTINUE; |
| } |
| |
| void |
| nautilus_selection_canvas_item_fade_out (NautilusSelectionCanvasItem *self, |
| guint transition_time) |
| { |
| EelCanvasItem *item = EEL_CANVAS_ITEM (self); |
| GtkWidget *widget; |
| GdkFrameClock *clock; |
| |
| self->priv->fade_out_fill_alpha = self->priv->fill_color.alpha; |
| self->priv->fade_out_outline_alpha = self->priv->outline_color.alpha; |
| |
| widget = GTK_WIDGET (item->canvas); |
| clock = gtk_widget_get_frame_clock (widget); |
| self->priv->fade_out_start_time = gdk_frame_clock_get_frame_time (clock); |
| self->priv->fade_out_end_time = self->priv->fade_out_start_time + 1000 * transition_time; |
| |
| self->priv->fade_out_tick_id = |
| gtk_widget_add_tick_callback (GTK_WIDGET (item->canvas), fade_and_request_redraw, self, NULL); |
| } |
| |
| 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 |
| do_set_fill (NautilusSelectionCanvasItem *self, |
| gboolean fill_set) |
| { |
| if (self->priv->fill_set != fill_set) |
| { |
| self->priv->fill_set = fill_set; |
| eel_canvas_item_request_update (EEL_CANVAS_ITEM (self)); |
| } |
| } |
| |
| static void |
| do_set_outline (NautilusSelectionCanvasItem *self, |
| gboolean outline_set) |
| { |
| if (self->priv->outline_set != outline_set) |
| { |
| self->priv->outline_set = outline_set; |
| eel_canvas_item_request_update (EEL_CANVAS_ITEM (self)); |
| } |
| } |
| |
| 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_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_class_init (NautilusSelectionCanvasItemClass *klass) |
| { |
| EelCanvasItemClass *item_class; |
| GObjectClass *gobject_class; |
| |
| gobject_class = G_OBJECT_CLASS (klass); |
| item_class = EEL_CANVAS_ITEM_CLASS (klass); |
| |
| gobject_class->set_property = nautilus_selection_canvas_item_set_property; |
| gobject_class->get_property = nautilus_selection_canvas_item_get_property; |
| gobject_class->dispose = nautilus_selection_canvas_item_dispose; |
| |
| item_class->draw = nautilus_selection_canvas_item_draw; |
| item_class->point = nautilus_selection_canvas_item_point; |
| item_class->update = nautilus_selection_canvas_item_update; |
| item_class->bounds = nautilus_selection_canvas_item_bounds; |
| item_class->translate = nautilus_selection_canvas_item_translate; |
| |
| properties[PROP_X1] = |
| g_param_spec_double ("x1", NULL, NULL, |
| -G_MAXDOUBLE, G_MAXDOUBLE, 0, |
| G_PARAM_READWRITE); |
| properties[PROP_Y1] = |
| g_param_spec_double ("y1", NULL, NULL, |
| -G_MAXDOUBLE, G_MAXDOUBLE, 0, |
| G_PARAM_READWRITE); |
| properties[PROP_X2] = |
| g_param_spec_double ("x2", NULL, NULL, |
| -G_MAXDOUBLE, G_MAXDOUBLE, 0, |
| G_PARAM_READWRITE); |
| properties[PROP_Y2] = |
| g_param_spec_double ("y2", NULL, NULL, |
| -G_MAXDOUBLE, G_MAXDOUBLE, 0, |
| G_PARAM_READWRITE); |
| properties[PROP_FILL_COLOR_RGBA] = |
| g_param_spec_boxed ("fill-color-rgba", NULL, NULL, |
| GDK_TYPE_RGBA, |
| G_PARAM_READWRITE); |
| properties[PROP_OUTLINE_COLOR_RGBA] = |
| g_param_spec_boxed ("outline-color-rgba", NULL, NULL, |
| GDK_TYPE_RGBA, |
| G_PARAM_READWRITE); |
| properties[PROP_OUTLINE_STIPPLING] = |
| g_param_spec_boolean ("outline-stippling", NULL, NULL, |
| FALSE, G_PARAM_READWRITE); |
| properties[PROP_WIDTH_PIXELS] = |
| g_param_spec_uint ("width-pixels", NULL, NULL, |
| 0, G_MAXUINT, 0, |
| G_PARAM_READWRITE); |
| |
| g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties); |
| g_type_class_add_private (klass, sizeof (NautilusSelectionCanvasItemDetails)); |
| } |
| |
| static void |
| nautilus_selection_canvas_item_init (NautilusSelectionCanvasItem *self) |
| { |
| self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_SELECTION_CANVAS_ITEM, |
| NautilusSelectionCanvasItemDetails); |
| } |