Allow object selection in LdCanvas.
Refactor a part of LdCanvas in the process.
This commit is contained in:
parent
1704b94650
commit
c815d0c16a
152
src/ld-canvas.c
152
src/ld-canvas.c
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue