Allow object selection in LdCanvas.

Refactor a part of LdCanvas in the process.
This commit is contained in:
Přemysl Eric Janouch 2011-01-07 04:52:44 +01:00
parent 1704b94650
commit c815d0c16a

View File

@ -34,8 +34,11 @@
/* Milimetres per inch. */
#define MM_PER_INCH 25.4
/* Tolerance for object borders. */
#define OBJECT_BORDER_TOLERANCE 3
/* Tolerance on all sides of symbols for strokes. */
#define SYMBOL_AREA_CLIP_TOLERANCE 5
#define SYMBOL_CLIP_TOLERANCE 5
/* The default screen resolution in DPI units. */
#define DEFAULT_SCREEN_RESOLUTION 96
@ -184,15 +187,21 @@ static gboolean ld_canvas_rect_intersects (LdCanvasRect *first,
LdCanvasRect *second);
static void ld_canvas_rect_extend (LdCanvasRect *rect, gdouble border);
static void move_object_to_widget_coords (LdCanvas *self,
LdDiagramObject *object, gdouble x, gdouble y);
static gboolean is_object_in_selection (LdCanvas *self,
LdDiagramObject *object);
static void move_object_to_coords (LdCanvas *self, LdDiagramObject *object,
gdouble x, gdouble y);
static LdDiagramObject *get_object_at_coords (LdCanvas *self,
gdouble x, gdouble y);
static gboolean is_object_selected (LdCanvas *self, LdDiagramObject *object);
static LdSymbol *resolve_diagram_symbol (LdCanvas *self,
LdDiagramSymbol *diagram_symbol);
static gboolean get_symbol_clip_area_on_widget (LdCanvas *self,
LdDiagramSymbol *diagram_symbol, gdouble *x, gdouble *y,
gdouble *width, gdouble *height);
static gboolean get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol,
LdCanvasRect *rect);
static gboolean get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol,
LdCanvasRect *rect);
static gboolean get_object_area (LdCanvas *self, LdDiagramObject *object,
LdCanvasRect *rect);
static gboolean object_hit_test (LdCanvas *self, LdDiagramObject *object,
gdouble x, gdouble y);
static void queue_object_redraw (LdCanvas *self, LdDiagramObject *object);
static void ld_canvas_real_cancel_operation (LdCanvas *self);
@ -815,7 +824,7 @@ ld_canvas_rect_extend (LdCanvasRect *rect, gdouble border)
}
static void
move_object_to_widget_coords (LdCanvas *self, LdDiagramObject *object,
move_object_to_coords (LdCanvas *self, LdDiagramObject *object,
gdouble x, gdouble y)
{
gdouble dx, dy;
@ -825,8 +834,26 @@ move_object_to_widget_coords (LdCanvas *self, LdDiagramObject *object,
ld_diagram_object_set_y (object, floor (dy + 0.5));
}
static LdDiagramObject *
get_object_at_coords (LdCanvas *self, gdouble x, gdouble y)
{
GSList *objects, *iter;
/* Iterate from the top object downwards. */
objects = (GSList *) ld_diagram_get_objects (self->priv->diagram);
for (iter = objects; iter; iter = g_slist_next (iter))
{
LdDiagramObject *object;
object = LD_DIAGRAM_OBJECT (iter->data);
if (object_hit_test (self, object, x, y))
return object;
}
return NULL;
}
static gboolean
is_object_in_selection (LdCanvas *self, LdDiagramObject *object)
is_object_selected (LdCanvas *self, LdDiagramObject *object)
{
return g_slist_find (ld_diagram_get_selection (self->priv->diagram),
object) != NULL;
@ -843,26 +870,26 @@ resolve_diagram_symbol (LdCanvas *self, LdDiagramSymbol *diagram_symbol)
}
static gboolean
get_symbol_clip_area_on_widget (LdCanvas *self, LdDiagramSymbol *diagram_symbol,
gdouble *x, gdouble *y, gdouble *width, gdouble *height)
get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol, LdCanvasRect *rect)
{
LdSymbol *symbol;
LdDiagramObject *object;
gdouble object_x, object_y;
LdSymbol *library_symbol;
LdSymbolArea area;
gdouble x1, x2;
gdouble y1, y2;
gdouble object_x, object_y;
symbol = resolve_diagram_symbol (self, diagram_symbol);
object = LD_DIAGRAM_OBJECT (symbol);
object_x = ld_diagram_object_get_x (object);
object_y = ld_diagram_object_get_y (object);
object_x = ld_diagram_object_get_x (LD_DIAGRAM_OBJECT (diagram_symbol));
object_y = ld_diagram_object_get_y (LD_DIAGRAM_OBJECT (diagram_symbol));
if (symbol)
ld_symbol_get_area (symbol, &area);
library_symbol = resolve_diagram_symbol (self, symbol);
if (library_symbol)
ld_symbol_get_area (library_symbol, &area);
else
return FALSE;
/* TODO: Rotate the space for other orientations. */
/* TODO: Rotate the rectangle for other orientations. */
ld_canvas_diagram_to_widget_coords (self,
object_x + area.x,
object_y + area.y,
@ -872,25 +899,58 @@ get_symbol_clip_area_on_widget (LdCanvas *self, LdDiagramSymbol *diagram_symbol,
object_y + area.y + area.height,
&x2, &y2);
*x = x1 - SYMBOL_AREA_CLIP_TOLERANCE;
*y = y1 - SYMBOL_AREA_CLIP_TOLERANCE;
*width = x2 - x1 + 2 * SYMBOL_AREA_CLIP_TOLERANCE;
*height = y2 - y1 + 2 * SYMBOL_AREA_CLIP_TOLERANCE;
rect->x = x1;
rect->y = y1;
rect->width = x2 - x1;
rect->height = y2 - y1;
return TRUE;
}
static gboolean
get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol,
LdCanvasRect *rect)
{
LdCanvasRect object_rect;
if (!get_object_area (self, LD_DIAGRAM_OBJECT (symbol), &object_rect))
return FALSE;
*rect = object_rect;
ld_canvas_rect_extend (rect, SYMBOL_CLIP_TOLERANCE);
return TRUE;
}
static gboolean
get_object_area (LdCanvas *self, LdDiagramObject *object, LdCanvasRect *rect)
{
if (LD_IS_DIAGRAM_SYMBOL (object))
return get_symbol_area (self, LD_DIAGRAM_SYMBOL (object), rect);
return FALSE;
}
static gboolean
object_hit_test (LdCanvas *self, LdDiagramObject *object, gdouble x, gdouble y)
{
LdCanvasRect rect;
if (!get_object_area (self, object, &rect))
return FALSE;
ld_canvas_rect_extend (&rect, OBJECT_BORDER_TOLERANCE);
return ld_canvas_rect_contains (&rect, x, y);
}
static void
queue_object_redraw (LdCanvas *self, LdDiagramObject *object)
{
if (LD_IS_DIAGRAM_SYMBOL (object))
{
gdouble x, y, width, height;
LdCanvasRect rect;
if (!get_symbol_clip_area_on_widget (self, LD_DIAGRAM_SYMBOL (object),
&x, &y, &width, &height))
if (!get_symbol_clip_area (self, LD_DIAGRAM_SYMBOL (object), &rect))
return;
gtk_widget_queue_draw_area (GTK_WIDGET (self),
floor (x), floor (y), ceil (width), ceil (height));
floor (rect.x), floor (rect.y),
ceil (rect.width), ceil (rect.height));
}
}
@ -909,7 +969,7 @@ on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
data->visible = TRUE;
queue_object_redraw (self, data->object);
move_object_to_widget_coords (self, data->object, event->x, event->y);
move_object_to_coords (self, data->object, event->x, event->y);
queue_object_redraw (self, data->object);
break;
}
@ -953,7 +1013,7 @@ on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
data = &OPER_DATA (self, add_object);
queue_object_redraw (self, data->object);
move_object_to_widget_coords (self, data->object, event->x, event->y);
move_object_to_coords (self, data->object, event->x, event->y);
if (self->priv->diagram)
ld_diagram_insert_object (self->priv->diagram, data->object, 0);
@ -961,6 +1021,19 @@ on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
/* XXX: "cancel" causes confusion. */
ld_canvas_real_cancel_operation (self);
break;
case OPER_0:
if (self->priv->diagram)
{
LdDiagramObject *object;
if (event->state != GDK_SHIFT_MASK)
ld_diagram_unselect_all (self->priv->diagram);
object = get_object_at_coords (self, event->x, event->y);
if (object)
ld_diagram_selection_add (self->priv->diagram, object, 0);
}
break;
}
return FALSE;
}
@ -1063,7 +1136,7 @@ draw_object (LdDiagramObject *diagram_object, DrawData *data)
g_return_if_fail (LD_IS_DIAGRAM_OBJECT (diagram_object));
g_return_if_fail (data != NULL);
if (is_object_in_selection (data->self, diagram_object))
if (is_object_selected (data->self, diagram_object))
ld_canvas_color_apply (COLOR_GET (data->self,
COLOR_SELECTION), data->cr);
else
@ -1078,7 +1151,8 @@ static void
draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
{
LdSymbol *symbol;
gdouble x, y, width, height;
LdCanvasRect clip_rect;
gdouble x, y;
symbol = resolve_diagram_symbol (data->self, diagram_symbol);
@ -1090,18 +1164,14 @@ draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
return;
}
if (!get_symbol_clip_area_on_widget (data->self, diagram_symbol,
&x, &y, &width, &height))
return;
if (x > data->exposed_rect.x + data->exposed_rect.width
|| y > data->exposed_rect.y + data->exposed_rect.height
|| x + width < data->exposed_rect.x
|| y + height < data->exposed_rect.y)
if (!get_symbol_clip_area (data->self, diagram_symbol, &clip_rect)
|| !ld_canvas_rect_intersects (&clip_rect, &data->exposed_rect))
return;
cairo_save (data->cr);
cairo_rectangle (data->cr, x, y, width, height);
cairo_rectangle (data->cr, clip_rect.x, clip_rect.y,
clip_rect.width, clip_rect.height);
cairo_clip (data->cr);
/* TODO: Rotate the space for other orientations. */