diff options
Diffstat (limited to '')
-rw-r--r-- | src/nautilus-selection-canvas-item.c | 554 |
1 files changed, 554 insertions, 0 deletions
diff --git a/src/nautilus-selection-canvas-item.c b/src/nautilus-selection-canvas-item.c new file mode 100644 index 0000000..b54cb68 --- /dev/null +++ b/src/nautilus-selection-canvas-item.c @@ -0,0 +1,554 @@ +/* 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 <http://www.gnu.org/licenses/>. + * + * Authors: Federico Mena <federico@nuclecu.unam.mx> + * Cosimo Cecchi <cosimoc@redhat.com> + */ + +#include <config.h> + +#include "nautilus-selection-canvas-item.h" + +#include <math.h> + +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); +} |