/* Nautilus - Canvas item for floating selection. * * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation * Copyright (C) 2011 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, see . * * Authors: Federico Mena * Cosimo Cecchi */ #include #include "nautilus-selection-canvas-item.h" #include enum { PROP_X1 = 1, PROP_Y1, PROP_X2, PROP_Y2, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES] = { NULL }; typedef struct { /*< public >*/ 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; /* Corners of item */ }; 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; GtkStyleContext *context; 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; } context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas)); gtk_style_context_save (context); gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND); cairo_save (cr); gtk_render_background (context, cr, cx1, cy1, cx2 - cx1, cy2 - cy1); gtk_render_frame (context, cr, cx1, cy1, cx2 - cx1, cy2 - cy1); cairo_restore (cr); gtk_style_context_restore (context); } 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; 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; hwidth = (1.0 / item->canvas->pixels_per_unit) / 2.0; x1 -= hwidth; y1 -= hwidth; x2 += hwidth; y2 += hwidth; /* Is point inside rectangle (which can be hollow if it has no fill set)? */ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) { return 0.0; } /* 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); } static void request_redraw_borders (EelCanvas *canvas, Rect *update_rect, int width) { /* Top */ eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y0, update_rect->x1, update_rect->y0 + width); /* Bottom */ eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y1 - width, update_rect->x1, update_rect->y1); /* Left */ eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y0, update_rect->x0 + width, update_rect->y1); /* Right */ 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; GtkStyleContext *context; GtkBorder border; 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; context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas)); gtk_style_context_save (context); gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND); gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border); gtk_style_context_restore (context); cx1 -= border.left; cy1 -= border.top; cx2 += border.right; cy2 += border.bottom; update_rect = make_rect (cx1, cy1, cx2, cy2); request_redraw_borders (item->canvas, &update_rect, border.left + border.top + border.right + border.bottom); 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 = border.left + border.top + border.right + border.bottom; item->x1 = cx1; item->y1 = cy1; item->x2 = cx2; item->y2 = cy2; } 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; GtkStyleContext *context; GtkBorder border; self = NAUTILUS_SELECTION_CANVAS_ITEM (item); context = gtk_widget_get_style_context (GTK_WIDGET (item->canvas)); gtk_style_context_save (context); gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND); gtk_style_context_get_border (context, GTK_STATE_FLAG_NORMAL, &border); gtk_style_context_restore (context); *x1 = self->priv->x1 - (border.left / item->canvas->pixels_per_unit) / 2.0; *y1 = self->priv->y1 - (border.top / item->canvas->pixels_per_unit) / 2.0; *x2 = self->priv->x2 + (border.right / item->canvas->pixels_per_unit) / 2.0; *y2 = self->priv->y2 + (border.bottom / item->canvas->pixels_per_unit) / 2.0; } 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; 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; 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; 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); 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); }