aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--liblogdiag/ld-canvas.c758
-rw-r--r--liblogdiag/ld-types.c52
-rw-r--r--liblogdiag/ld-types.h10
3 files changed, 608 insertions, 212 deletions
diff --git a/liblogdiag/ld-canvas.c b/liblogdiag/ld-canvas.c
index 564a838..f89cbfd 100644
--- a/liblogdiag/ld-canvas.c
+++ b/liblogdiag/ld-canvas.c
@@ -61,11 +61,13 @@ enum
{
OPER_0,
OPER_ADD_OBJECT,
+ OPER_CONNECT,
OPER_SELECT,
OPER_MOVE_SELECTION
};
typedef struct _AddObjectData AddObjectData;
+typedef struct _ConnectData ConnectData;
typedef struct _SelectData SelectData;
typedef struct _MoveSelectionData MoveSelectionData;
@@ -75,6 +77,12 @@ struct _AddObjectData
gboolean visible;
};
+struct _ConnectData
+{
+ LdDiagramConnection *connection;
+ LdPoint origin;
+};
+
struct _SelectData
{
LdPoint drag_last_pos;
@@ -115,7 +123,7 @@ struct _LdCanvasColor
* @y: the Y coordinate of the center of view.
* @zoom: the current zoom of the canvas.
* @terminal: position of the highlighted terminal.
- * @terminal_highlighted: whether a terminal is highlighted.
+ * @terminal_hovered: whether a terminal is hovered.
* @drag_start_pos: position of the mouse pointer when dragging started.
* @drag_operation: the operation to start when dragging starts.
* @operation: the current operation.
@@ -136,7 +144,7 @@ struct _LdCanvasPrivate
gdouble zoom;
LdPoint terminal;
- gboolean terminal_highlighted;
+ gboolean terminal_hovered;
LdPoint drag_start_pos;
gint drag_operation;
@@ -145,6 +153,7 @@ struct _LdCanvasPrivate
union
{
AddObjectData add_object;
+ ConnectData connect;
SelectData select;
MoveSelectionData move_selection;
}
@@ -203,46 +212,76 @@ static void diagram_disconnect_signals (LdCanvas *self);
static gdouble ld_canvas_get_base_unit_in_px (GtkWidget *self);
static gdouble ld_canvas_get_scale_in_px (LdCanvas *self);
+/* Helper functions. */
static void ld_canvas_color_set (LdCanvasColor *color,
gdouble r, gdouble g, gdouble b, gdouble a);
static void ld_canvas_color_apply (LdCanvasColor *color, cairo_t *cr);
+static gdouble point_to_line_segment_distance
+ (const LdPoint *point, const LdPoint *p1, const LdPoint *p2);
+
+/* Generic functions. */
+static gboolean object_hit_test (LdCanvas *self,
+ LdDiagramObject *object, const LdPoint *point);
+static gboolean get_object_clip_area (LdCanvas *self,
+ LdDiagramObject *object, LdRectangle *rect);
+
+static void move_object_to_point (LdCanvas *self, LdDiagramObject *object,
+ const LdPoint *point);
+static LdDiagramObject *get_object_at_point (LdCanvas *self,
+ const LdPoint *point);
+
static void move_selection (LdCanvas *self, gdouble dx, gdouble dy);
-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_area (LdCanvas *self, LdDiagramSymbol *symbol,
- LdRectangle *rect);
-static gboolean get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol,
- LdRectangle *rect);
-static gboolean get_object_area (LdCanvas *self, LdDiagramObject *object,
- LdRectangle *rect);
-static gboolean object_hit_test (LdCanvas *self, LdDiagramObject *object,
- gdouble x, gdouble y);
-static void check_terminals (LdCanvas *self, gdouble x, gdouble y);
-static void hide_terminals (LdCanvas *self);
+
static void queue_draw (LdCanvas *self, LdRectangle *rect);
static void queue_object_draw (LdCanvas *self, LdDiagramObject *object);
+
+/* Symbol terminals. */
+static void check_terminals (LdCanvas *self, const LdPoint *point);
+static void hide_terminals (LdCanvas *self);
static void queue_terminal_draw (LdCanvas *self, LdPoint *terminal);
+/* Diagram symbol. */
+static gboolean symbol_hit_test (LdCanvas *self,
+ LdDiagramSymbol *symbol, const LdPoint *point);
+static gboolean get_symbol_clip_area (LdCanvas *self,
+ LdDiagramSymbol *symbol, LdRectangle *rect);
+
+static gboolean get_symbol_area (LdCanvas *self,
+ LdDiagramSymbol *symbol, LdRectangle *rect);
+static LdSymbol *resolve_symbol (LdCanvas *self,
+ LdDiagramSymbol *diagram_symbol);
+
+/* Diagram connection. */
+static gboolean connection_hit_test (LdCanvas *self,
+ LdDiagramConnection *connection, const LdPoint *point);
+static gboolean get_connection_clip_area (LdCanvas *self,
+ LdDiagramConnection *connection, LdRectangle *rect);
+
+static gboolean get_connection_area (LdCanvas *self,
+ LdDiagramConnection *connection, LdRectangle *rect);
+
+/* Operations. */
static void ld_canvas_real_cancel_operation (LdCanvas *self);
static void oper_add_object_end (LdCanvas *self);
-static void oper_select_begin (LdCanvas *self, gdouble x, gdouble y);
+static void oper_connect_begin (LdCanvas *self, const LdPoint *point);
+static void oper_connect_end (LdCanvas *self);
+static void oper_connect_motion (LdCanvas *self, const LdPoint *point);
+
+static void oper_select_begin (LdCanvas *self, const LdPoint *point);
static void oper_select_end (LdCanvas *self);
static void oper_select_get_rectangle (LdCanvas *self, LdRectangle *rect);
static void oper_select_queue_draw (LdCanvas *self);
static void oper_select_draw (GtkWidget *widget, DrawData *data);
-static void oper_select_motion (LdCanvas *self, gdouble x, gdouble y);
+static void oper_select_motion (LdCanvas *self, const LdPoint *point);
-static void oper_move_selection_begin (LdCanvas *self, gdouble x, gdouble y);
+static void oper_move_selection_begin (LdCanvas *self, const LdPoint *point);
static void oper_move_selection_end (LdCanvas *self);
-static void oper_move_selection_motion (LdCanvas *self, gdouble x, gdouble y);
+static void oper_move_selection_motion (LdCanvas *self, const LdPoint *point);
+/* Events, rendering. */
static void simulate_motion (LdCanvas *self);
static gboolean on_motion_notify (GtkWidget *widget, GdkEventMotion *event,
gpointer user_data);
@@ -262,6 +301,7 @@ static void draw_diagram (GtkWidget *widget, DrawData *data);
static void draw_terminal (GtkWidget *widget, DrawData *data);
static void draw_object (LdDiagramObject *diagram_object, DrawData *data);
static void draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data);
+static void draw_connection (LdDiagramConnection *connection, DrawData *data);
G_DEFINE_TYPE (LdCanvas, ld_canvas, GTK_TYPE_DRAWING_AREA);
@@ -957,45 +997,76 @@ ld_canvas_color_apply (LdCanvasColor *color, cairo_t *cr)
cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
}
-static void
-move_selection (LdCanvas *self, gdouble dx, gdouble dy)
+static gdouble
+point_to_line_segment_distance
+ (const LdPoint *point, const LdPoint *p1, const LdPoint *p2)
{
- LdDiagram *diagram;
- GList *selection, *iter;
+ gdouble dx, dy, u;
- diagram = self->priv->diagram;
- if (!diagram)
- return;
+ dx = p2->x - p1->x;
+ dy = p2->y - p1->y;
- selection = ld_diagram_get_selection (diagram);
- if (!selection)
- return;
+ if (dx == 0. && dy == 0.)
+ return ld_point_distance (point, p1->x, p1->y);
- ld_diagram_begin_user_action (diagram);
- for (iter = selection; iter; iter = g_list_next (iter))
- {
- gdouble x, y;
+ /* Find projection of the point onto the line. */
+ u = ((point->x - p1->x) * dx + (point->y - p1->y) * dy)
+ / (dx * dx + dy * dy);
- g_object_get (iter->data, "x", &x, "y", &y, NULL);
- x += dx;
- y += dy;
- g_object_set (iter->data, "x", x, "y", y, NULL);
- }
- ld_diagram_end_user_action (diagram);
+ /* The projection is beyond the line segment. */
+ if (u < 0.)
+ return ld_point_distance (point, p1->x, p1->y);
+ else if (u > 1.)
+ return ld_point_distance (point, p2->x, p2->y);
+
+ /* The projection is on the line segment. */
+ return ld_point_distance (point, p1->x + u * dx, p1->y + u * dy);
+}
+
+
+/* ===== Generic functions ================================================= */
+
+static gboolean
+object_hit_test (LdCanvas *self, LdDiagramObject *object, const LdPoint *point)
+{
+ if (LD_IS_DIAGRAM_SYMBOL (object))
+ return symbol_hit_test (self,
+ LD_DIAGRAM_SYMBOL (object), point);
+ if (LD_IS_DIAGRAM_CONNECTION (object))
+ return connection_hit_test (self,
+ LD_DIAGRAM_CONNECTION (object), point);
+ return FALSE;
+}
+
+static gboolean
+get_object_clip_area (LdCanvas *self,
+ LdDiagramObject *object, LdRectangle *rect)
+{
+ if (LD_IS_DIAGRAM_SYMBOL (object))
+ return get_symbol_clip_area (self,
+ LD_DIAGRAM_SYMBOL (object), rect);
+ if (LD_IS_DIAGRAM_CONNECTION (object))
+ return get_connection_clip_area (self,
+ LD_DIAGRAM_CONNECTION (object), rect);
+ return FALSE;
}
static void
-move_object_to_coords (LdCanvas *self, LdDiagramObject *object,
- gdouble x, gdouble y)
+move_object_to_point (LdCanvas *self, LdDiagramObject *object,
+ const LdPoint *point)
{
- gdouble dx, dy;
+ gdouble diagram_x, diagram_y;
- ld_canvas_widget_to_diagram_coords (self, x, y, &dx, &dy);
- g_object_set (object, "x", floor (dx + 0.5), "y", floor (dy + 0.5), NULL);
+ ld_canvas_widget_to_diagram_coords (self,
+ point->x, point->y, &diagram_x, &diagram_y);
+ g_object_set (object,
+ "x", floor (diagram_x + 0.5),
+ "y", floor (diagram_y + 0.5),
+ NULL);
}
static LdDiagramObject *
-get_object_at_coords (LdCanvas *self, gdouble x, gdouble y)
+get_object_at_point (LdCanvas *self, const LdPoint *point)
{
GList *objects, *iter;
@@ -1006,108 +1077,72 @@ get_object_at_coords (LdCanvas *self, gdouble x, gdouble y)
LdDiagramObject *object;
object = LD_DIAGRAM_OBJECT (iter->data);
- if (object_hit_test (self, object, x, y))
+ if (object_hit_test (self, object, point))
return object;
}
return NULL;
}
-static gboolean
-is_object_selected (LdCanvas *self, LdDiagramObject *object)
-{
- return g_list_find (ld_diagram_get_selection (self->priv->diagram),
- object) != NULL;
-}
-
-static LdSymbol *
-resolve_diagram_symbol (LdCanvas *self, LdDiagramSymbol *diagram_symbol)
-{
- LdSymbol *symbol;
- gchar *klass;
-
- if (!self->priv->library)
- return NULL;
-
- klass = ld_diagram_symbol_get_class (diagram_symbol);
- symbol = ld_library_find_symbol (self->priv->library, klass);
- g_free (klass);
- return symbol;
-}
-
-static gboolean
-get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol, LdRectangle *rect)
+static void
+move_selection (LdCanvas *self, gdouble dx, gdouble dy)
{
- gdouble object_x, object_y;
- LdSymbol *library_symbol;
- LdRectangle area;
- gdouble x1, x2;
- gdouble y1, y2;
-
- g_object_get (symbol, "x", &object_x, "y", &object_y, NULL);
+ LdDiagram *diagram;
+ GList *selection, *iter;
- library_symbol = resolve_diagram_symbol (self, symbol);
- if (library_symbol)
- ld_symbol_get_area (library_symbol, &area);
- else
- return FALSE;
+ diagram = self->priv->diagram;
+ if (!diagram)
+ return;
- /* TODO: Rotate the rectangle for other orientations. */
- ld_canvas_diagram_to_widget_coords (self,
- object_x + area.x,
- object_y + area.y,
- &x1, &y1);
- ld_canvas_diagram_to_widget_coords (self,
- object_x + area.x + area.width,
- object_y + area.y + area.height,
- &x2, &y2);
+ selection = ld_diagram_get_selection (diagram);
+ if (!selection)
+ return;
- x1 = floor (x1);
- y1 = floor (y1);
- x2 = ceil (x2);
- y2 = ceil (y2);
+ ld_diagram_begin_user_action (diagram);
+ for (iter = selection; iter; iter = g_list_next (iter))
+ {
+ gdouble x, y;
- rect->x = x1;
- rect->y = y1;
- rect->width = x2 - x1;
- rect->height = y2 - y1;
- return TRUE;
+ g_object_get (iter->data, "x", &x, "y", &y, NULL);
+ x += dx;
+ y += dy;
+ g_object_set (iter->data, "x", x, "y", y, NULL);
+ }
+ ld_diagram_end_user_action (diagram);
}
static gboolean
-get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol,
- LdRectangle *rect)
+is_object_selected (LdCanvas *self, LdDiagramObject *object)
{
- LdRectangle object_rect;
-
- if (!get_object_area (self, LD_DIAGRAM_OBJECT (symbol), &object_rect))
- return FALSE;
-
- *rect = object_rect;
- ld_rectangle_extend (rect, SYMBOL_CLIP_TOLERANCE);
- return TRUE;
+ return g_list_find (ld_diagram_get_selection (self->priv->diagram),
+ object) != NULL;
}
-static gboolean
-get_object_area (LdCanvas *self, LdDiagramObject *object, LdRectangle *rect)
+static void
+queue_draw (LdCanvas *self, LdRectangle *rect)
{
- if (LD_IS_DIAGRAM_SYMBOL (object))
- return get_symbol_area (self, LD_DIAGRAM_SYMBOL (object), rect);
- return FALSE;
+ LdRectangle area;
+
+ area = *rect;
+ ld_rectangle_extend (&area, QUEUE_DRAW_EXTEND);
+ gtk_widget_queue_draw_area (GTK_WIDGET (self),
+ area.x, area.y, area.width, area.height);
}
-static gboolean
-object_hit_test (LdCanvas *self, LdDiagramObject *object, gdouble x, gdouble y)
+static void
+queue_object_draw (LdCanvas *self, LdDiagramObject *object)
{
LdRectangle rect;
- if (!get_object_area (self, object, &rect))
- return FALSE;
- ld_rectangle_extend (&rect, OBJECT_BORDER_TOLERANCE);
- return ld_rectangle_contains (&rect, x, y);
+ if (!get_object_clip_area (self, object, &rect))
+ return;
+ queue_draw (self, &rect);
}
+
+/* ===== Symbol terminals ================================================== */
+
static void
-check_terminals (LdCanvas *self, gdouble x, gdouble y)
+check_terminals (LdCanvas *self, const LdPoint *point)
{
GList *objects, *iter;
LdDiagramSymbol *closest_symbol = NULL;
@@ -1128,7 +1163,7 @@ check_terminals (LdCanvas *self, gdouble x, gdouble y)
continue;
diagram_symbol = LD_DIAGRAM_SYMBOL (iter->data);
- symbol = resolve_diagram_symbol (self, diagram_symbol);
+ symbol = resolve_symbol (self, diagram_symbol);
if (!symbol)
continue;
@@ -1139,16 +1174,16 @@ check_terminals (LdCanvas *self, gdouble x, gdouble y)
for (i = 0; i < terminals->length; i++)
{
- LdPoint cur_term;
+ LdPoint cur_term, widget_coords;
gdouble distance;
cur_term = terminals->points[i];
cur_term.x += object_x;
cur_term.y += object_y;
- ld_canvas_diagram_to_widget_coords (self,
- cur_term.x, cur_term.y, &cur_term.x, &cur_term.y);
- distance = ld_point_distance (&cur_term, x, y);
+ ld_canvas_diagram_to_widget_coords (self,
+ cur_term.x, cur_term.y, &widget_coords.x, &widget_coords.y);
+ distance = ld_point_distance (&widget_coords, point->x, point->y);
if (distance <= closest_distance)
{
closest_symbol = diagram_symbol;
@@ -1162,7 +1197,7 @@ check_terminals (LdCanvas *self, gdouble x, gdouble y)
if (closest_symbol)
{
- self->priv->terminal_highlighted = TRUE;
+ self->priv->terminal_hovered = TRUE;
self->priv->terminal = closest_terminal;
queue_terminal_draw (self, &closest_terminal);
}
@@ -1171,47 +1206,213 @@ check_terminals (LdCanvas *self, gdouble x, gdouble y)
static void
hide_terminals (LdCanvas *self)
{
- if (self->priv->terminal_highlighted)
+ if (self->priv->terminal_hovered)
{
- self->priv->terminal_highlighted = FALSE;
+ self->priv->terminal_hovered = FALSE;
queue_terminal_draw (self, &self->priv->terminal);
}
}
static void
-queue_draw (LdCanvas *self, LdRectangle *rect)
+queue_terminal_draw (LdCanvas *self, LdPoint *terminal)
+{
+ LdRectangle rect;
+ LdPoint widget_coords;
+
+ ld_canvas_diagram_to_widget_coords (self,
+ terminal->x, terminal->y, &widget_coords.x, &widget_coords.y);
+
+ rect.x = widget_coords.x - TERMINAL_RADIUS;
+ rect.y = widget_coords.y - TERMINAL_RADIUS;
+ rect.width = 2 * TERMINAL_RADIUS;
+ rect.height = 2 * TERMINAL_RADIUS;
+
+ queue_draw (self, &rect);
+}
+
+
+/* ===== Diagram symbol ==================================================== */
+
+static gboolean
+symbol_hit_test (LdCanvas *self, LdDiagramSymbol *symbol, const LdPoint *point)
+{
+ LdRectangle rect;
+
+ if (!get_symbol_area (self, symbol, &rect))
+ return FALSE;
+ ld_rectangle_extend (&rect, OBJECT_BORDER_TOLERANCE);
+ return ld_rectangle_contains_point (&rect, point);
+}
+
+static gboolean
+get_symbol_clip_area (LdCanvas *self,
+ LdDiagramSymbol *symbol, LdRectangle *rect)
{
+ LdRectangle object_rect;
+
+ if (!get_symbol_area (self, symbol, &object_rect))
+ return FALSE;
+
+ *rect = object_rect;
+ ld_rectangle_extend (rect, SYMBOL_CLIP_TOLERANCE);
+ return TRUE;
+}
+
+static gboolean
+get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol, LdRectangle *rect)
+{
+ gdouble object_x, object_y;
+ LdSymbol *library_symbol;
LdRectangle area;
+ gdouble x1, x2;
+ gdouble y1, y2;
- area = *rect;
- ld_rectangle_extend (&area, QUEUE_DRAW_EXTEND);
- gtk_widget_queue_draw_area (GTK_WIDGET (self),
- area.x, area.y, area.width, area.height);
+ g_object_get (symbol, "x", &object_x, "y", &object_y, NULL);
+
+ library_symbol = resolve_symbol (self, symbol);
+ if (library_symbol)
+ ld_symbol_get_area (library_symbol, &area);
+ else
+ return FALSE;
+
+ /* TODO: Rotate the rectangle for other orientations. */
+ ld_canvas_diagram_to_widget_coords (self,
+ object_x + area.x,
+ object_y + area.y,
+ &x1, &y1);
+ ld_canvas_diagram_to_widget_coords (self,
+ object_x + area.x + area.width,
+ object_y + area.y + area.height,
+ &x2, &y2);
+
+ x1 = floor (x1);
+ y1 = floor (y1);
+ x2 = ceil (x2);
+ y2 = ceil (y2);
+
+ rect->x = x1;
+ rect->y = y1;
+ rect->width = x2 - x1;
+ rect->height = y2 - y1;
+ return TRUE;
}
-static void
-queue_object_draw (LdCanvas *self, LdDiagramObject *object)
+static LdSymbol *
+resolve_symbol (LdCanvas *self, LdDiagramSymbol *diagram_symbol)
{
- if (LD_IS_DIAGRAM_SYMBOL (object))
+ LdSymbol *symbol;
+ gchar *klass;
+
+ if (!self->priv->library)
+ return NULL;
+
+ klass = ld_diagram_symbol_get_class (diagram_symbol);
+ symbol = ld_library_find_symbol (self->priv->library, klass);
+ g_free (klass);
+ return symbol;
+}
+
+
+/* ===== Diagram connection ================================================ */
+
+static gboolean
+connection_hit_test (LdCanvas *self, LdDiagramConnection *connection,
+ const LdPoint *point)
+{
+ gdouble object_x, object_y, length;
+ LdPointArray *points;
+ guint i;
+
+ g_object_get (connection, "x", &object_x, "y", &object_y, NULL);
+
+ points = ld_diagram_connection_get_points (connection);
+ if (points->length < 2)
{
- LdRectangle rect;
+ ld_point_array_free (points);
+ return FALSE;
+ }
- if (!get_symbol_clip_area (self, LD_DIAGRAM_SYMBOL (object), &rect))
- return;
- queue_draw (self, &rect);
+ for (i = 0; i < points->length; i++)
+ {
+ ld_canvas_diagram_to_widget_coords (self,
+ points->points[i].x + object_x,
+ points->points[i].y + object_y,
+ &points->points[i].x,
+ &points->points[i].y);
+
+ if (!i)
+ continue;
+
+ length = point_to_line_segment_distance
+ (point, &points->points[i - 1], &points->points[i]);
+ if (length <= OBJECT_BORDER_TOLERANCE)
+ {
+ ld_point_array_free (points);
+ return TRUE;
+ }
}
+ ld_point_array_free (points);
+ return FALSE;
}
-static void
-queue_terminal_draw (LdCanvas *self, LdPoint *terminal)
+static gboolean
+get_connection_clip_area (LdCanvas *self,
+ LdDiagramConnection *connection, LdRectangle *rect)
{
- LdRectangle rect;
+ return get_connection_area (self, connection, rect);
+}
- rect.x = terminal->x - TERMINAL_RADIUS;
- rect.y = terminal->y - TERMINAL_RADIUS;
- rect.width = 2 * TERMINAL_RADIUS;
- rect.height = 2 * TERMINAL_RADIUS;
- queue_draw (self, &rect);
+static gboolean
+get_connection_area (LdCanvas *self,
+ LdDiagramConnection *connection, LdRectangle *rect)
+{
+ gdouble x_origin, y_origin;
+ gdouble x, y, x_min, x_max, y_min, y_max;
+ LdPointArray *points;
+ guint i;
+
+ points = ld_diagram_connection_get_points (connection);
+ if (!points->length)
+ {
+ ld_point_array_free (points);
+ return FALSE;
+ }
+
+ g_object_get (connection, "x", &x_origin, "y", &y_origin, NULL);
+
+ ld_canvas_diagram_to_widget_coords (self,
+ x_origin + points->points[0].x,
+ y_origin + points->points[0].y,
+ &x, &y);
+
+ x_max = x_min = x;
+ y_max = y_min = y;
+
+ for (i = 1; i < points->length; i++)
+ {
+ ld_canvas_diagram_to_widget_coords (self,
+ x_origin + points->points[i].x,
+ y_origin + points->points[i].y,
+ &x, &y);
+
+ if (x < x_min)
+ x_min = x;
+ else if (x > x_max)
+ x_max = x;
+
+ if (y < y_min)
+ y_min = y;
+ else if (y > y_max)
+ y_max = y;
+ }
+
+ rect->x = x_min;
+ rect->y = y_min;
+ rect->width = x_max - x_min;
+ rect->height = y_max - y_min;
+
+ ld_point_array_free (points);
+ return TRUE;
}
@@ -1270,7 +1471,94 @@ oper_add_object_end (LdCanvas *self)
}
static void
-oper_select_begin (LdCanvas *self, gdouble x, gdouble y)
+oper_connect_begin (LdCanvas *self, const LdPoint *point)
+{
+ ConnectData *data;
+
+ ld_canvas_real_cancel_operation (self);
+
+ self->priv->operation = OPER_CONNECT;
+ self->priv->operation_end = oper_connect_end;
+
+ data = &OPER_DATA (self, connect);
+ data->connection = ld_diagram_connection_new (NULL);
+
+ data->origin = self->priv->terminal;
+ g_object_set (data->connection,
+ "x", data->origin.x,
+ "y", data->origin.y,
+ NULL);
+
+ self->priv->terminal_hovered = FALSE;
+
+ oper_connect_motion (self, point);
+}
+
+static void
+oper_connect_end (LdCanvas *self)
+{
+ ConnectData *data;
+
+ data = &OPER_DATA (self, connect);
+ queue_object_draw (self, LD_DIAGRAM_OBJECT (data->connection));
+
+ ld_diagram_insert_object (self->priv->diagram,
+ LD_DIAGRAM_OBJECT (data->connection), -1);
+
+ g_object_unref (data->connection);
+}
+
+static void
+oper_connect_motion (LdCanvas *self, const LdPoint *point)
+{
+ ConnectData *data;
+ LdPointArray *points;
+ gdouble diagram_x, diagram_y;
+
+ data = &OPER_DATA (self, connect);
+
+ /* Find an orthogonal path between the points. */
+ /* TODO: This alghorithm is pretty lame, needs to be improved. */
+ points = ld_point_array_sized_new (4);
+ points->length = 4;
+
+ points->points[0].x = 0;
+ points->points[0].y = 0;
+
+ ld_canvas_widget_to_diagram_coords (self,
+ point->x, point->y, &diagram_x, &diagram_y);
+ points->points[3].x = floor (diagram_x - data->origin.x + 0.5);
+ points->points[3].y = floor (diagram_y - data->origin.y + 0.5);
+
+ if (ABS (points->points[3].x) > ABS (points->points[3].y))
+ {
+ points->points[1].x = points->points[3].x / 2;
+ points->points[1].y = 0;
+ points->points[2].x = points->points[3].x / 2;
+ points->points[2].y = points->points[3].y;
+ }
+ else
+ {
+ points->points[1].x = 0;
+ points->points[1].y = points->points[3].y / 2;
+ points->points[2].x = points->points[3].x;
+ points->points[2].y = points->points[3].y / 2;
+ }
+
+ queue_object_draw (self, LD_DIAGRAM_OBJECT (data->connection));
+ ld_diagram_connection_set_points (data->connection, points);
+ queue_object_draw (self, LD_DIAGRAM_OBJECT (data->connection));
+ ld_point_array_free (points);
+
+ check_terminals (self, point);
+
+ if (self->priv->terminal.x == data->origin.x
+ && self->priv->terminal.y == data->origin.y)
+ self->priv->terminal_hovered = FALSE;
+}
+
+static void
+oper_select_begin (LdCanvas *self, const LdPoint *point)
{
SelectData *data;
@@ -1283,7 +1571,7 @@ oper_select_begin (LdCanvas *self, gdouble x, gdouble y)
data->drag_last_pos.x = self->priv->drag_start_pos.x;
data->drag_last_pos.y = self->priv->drag_start_pos.y;
- oper_select_motion (self, x, y);
+ oper_select_motion (self, point);
}
static void
@@ -1339,17 +1627,16 @@ oper_select_draw (GtkWidget *widget, DrawData *data)
}
static void
-oper_select_motion (LdCanvas *self, gdouble x, gdouble y)
+oper_select_motion (LdCanvas *self, const LdPoint *point)
{
SelectData *data;
GList *objects, *iter;
- LdRectangle selection_rect, object_rect;
+ LdRectangle selection_rect, rect;
data = &OPER_DATA (self, select);
oper_select_queue_draw (self);
- data->drag_last_pos.x = x;
- data->drag_last_pos.y = y;
+ data->drag_last_pos = *point;
oper_select_queue_draw (self);
oper_select_get_rectangle (self, &selection_rect);
@@ -1360,11 +1647,21 @@ oper_select_motion (LdCanvas *self, gdouble x, gdouble y)
LdDiagramObject *object;
object = LD_DIAGRAM_OBJECT (iter->data);
- if (!get_object_area (self, object, &object_rect))
- continue;
+ if (LD_IS_DIAGRAM_SYMBOL (object))
+ {
+ if (!get_symbol_area (self,
+ LD_DIAGRAM_SYMBOL (object), &rect))
+ continue;
+ }
+ else if (LD_IS_DIAGRAM_CONNECTION (object))
+ {
+ if (!get_connection_area (self,
+ LD_DIAGRAM_CONNECTION (object), &rect))
+ continue;
+ }
- ld_rectangle_extend (&object_rect, OBJECT_BORDER_TOLERANCE);
- if (ld_rectangle_intersects (&object_rect, &selection_rect))
+ ld_rectangle_extend (&rect, OBJECT_BORDER_TOLERANCE);
+ if (ld_rectangle_contains (&selection_rect, &rect))
ld_diagram_select (self->priv->diagram, object);
else
ld_diagram_unselect (self->priv->diagram, object);
@@ -1372,7 +1669,7 @@ oper_select_motion (LdCanvas *self, gdouble x, gdouble y)
}
static void
-oper_move_selection_begin (LdCanvas *self, gdouble x, gdouble y)
+oper_move_selection_begin (LdCanvas *self, const LdPoint *point)
{
MoveSelectionData *data;
@@ -1384,10 +1681,9 @@ oper_move_selection_begin (LdCanvas *self, gdouble x, gdouble y)
ld_diagram_begin_user_action (self->priv->diagram);
data = &OPER_DATA (self, move_selection);
- data->move_origin.x = self->priv->drag_start_pos.x;
- data->move_origin.y = self->priv->drag_start_pos.y;
+ data->move_origin = self->priv->drag_start_pos;
- oper_move_selection_motion (self, x, y);
+ oper_move_selection_motion (self, point);
}
static void
@@ -1397,7 +1693,7 @@ oper_move_selection_end (LdCanvas *self)
}
static void
-oper_move_selection_motion (LdCanvas *self, gdouble x, gdouble y)
+oper_move_selection_motion (LdCanvas *self, const LdPoint *point)
{
MoveSelectionData *data;
gdouble scale, dx, dy, move_x, move_y;
@@ -1406,8 +1702,8 @@ oper_move_selection_motion (LdCanvas *self, gdouble x, gdouble y)
scale = ld_canvas_get_scale_in_px (self);
data = &OPER_DATA (self, move_selection);
- dx = x - data->move_origin.x;
- dy = y - data->move_origin.y;
+ dx = point->x - data->move_origin.x;
+ dy = point->y - data->move_origin.y;
move_x = dx < 0 ? ceil (dx / scale) : floor (dx / scale);
move_y = dy < 0 ? ceil (dy / scale) : floor (dy / scale);
@@ -1457,9 +1753,13 @@ simulate_motion (LdCanvas *self)
static gboolean
on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
+ LdPoint point;
LdCanvas *self;
AddObjectData *add_data;
+ point.x = event->x;
+ point.y = event->y;
+
self = LD_CANVAS (widget);
switch (self->priv->operation)
{
@@ -1468,14 +1768,17 @@ on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
add_data->visible = TRUE;
queue_object_draw (self, add_data->object);
- move_object_to_coords (self, add_data->object, event->x, event->y);
+ move_object_to_point (self, add_data->object, &point);
queue_object_draw (self, add_data->object);
break;
+ case OPER_CONNECT:
+ oper_connect_motion (self, &point);
+ break;
case OPER_SELECT:
- oper_select_motion (self, event->x, event->y);
+ oper_select_motion (self, &point);
break;
case OPER_MOVE_SELECTION:
- oper_move_selection_motion (self, event->x, event->y);
+ oper_move_selection_motion (self, &point);
break;
case OPER_0:
if (event->state & GDK_BUTTON1_MASK
@@ -1484,15 +1787,18 @@ on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
switch (self->priv->drag_operation)
{
+ case OPER_CONNECT:
+ oper_connect_begin (self, &point);
+ break;
case OPER_SELECT:
- oper_select_begin (self, event->x, event->y);
+ oper_select_begin (self, &point);
break;
case OPER_MOVE_SELECTION:
- oper_move_selection_begin (self, event->x, event->y);
+ oper_move_selection_begin (self, &point);
break;
}
}
- check_terminals (self, event->x, event->y);
+ check_terminals (self, &point);
break;
}
return FALSE;
@@ -1521,6 +1827,7 @@ on_leave_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
static gboolean
on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
+ LdPoint point;
LdCanvas *self;
AddObjectData *data;
LdDiagramObject *object;
@@ -1530,6 +1837,9 @@ on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
+ point.x = event->x;
+ point.y = event->y;
+
self = LD_CANVAS (widget);
if (!self->priv->diagram)
return FALSE;
@@ -1541,17 +1851,22 @@ on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
data = &OPER_DATA (self, add_object);
queue_object_draw (self, data->object);
- move_object_to_coords (self, data->object, event->x, event->y);
+ move_object_to_point (self, data->object, &point);
ld_diagram_insert_object (self->priv->diagram, data->object, -1);
/* XXX: "cancel" causes confusion. */
ld_canvas_real_cancel_operation (self);
break;
case OPER_0:
- self->priv->drag_start_pos.x = event->x;
- self->priv->drag_start_pos.y = event->y;
+ self->priv->drag_start_pos = point;
- object = get_object_at_coords (self, event->x, event->y);
+ if (self->priv->terminal_hovered)
+ {
+ self->priv->drag_operation = OPER_CONNECT;
+ break;
+ }
+
+ object = get_object_at_point (self, &point);
if (!object)
{
ld_diagram_unselect_all (self->priv->diagram);
@@ -1574,12 +1889,16 @@ on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static gboolean
on_button_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
+ LdPoint point;
LdCanvas *self;
LdDiagramObject *object;
if (event->button != 1)
return FALSE;
+ point.x = event->x;
+ point.y = event->y;
+
self = LD_CANVAS (widget);
if (!self->priv->diagram)
return FALSE;
@@ -1588,10 +1907,11 @@ on_button_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
case OPER_SELECT:
case OPER_MOVE_SELECTION:
+ case OPER_CONNECT:
ld_canvas_real_cancel_operation (self);
break;
case OPER_0:
- object = get_object_at_coords (self, event->x, event->y);
+ object = get_object_at_point (self, &point);
if (object && is_object_selected (self, object))
{
if (!(event->state & GDK_SHIFT_MASK))
@@ -1608,8 +1928,11 @@ on_scroll (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
gdouble prev_x, prev_y;
gdouble new_x, new_y;
+ LdPoint point;
LdCanvas *self;
+ point.x = event->x;
+ point.y = event->y;
self = LD_CANVAS (widget);
ld_canvas_widget_to_diagram_coords (self,
@@ -1634,7 +1957,7 @@ on_scroll (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
self->priv->x += prev_x - new_x;
self->priv->y += prev_y - new_y;
- check_terminals (self, event->x, event->y);
+ check_terminals (self, &point);
return TRUE;
}
@@ -1721,16 +2044,20 @@ static void
draw_terminal (GtkWidget *widget, DrawData *data)
{
LdCanvasPrivate *priv;
+ LdPoint widget_coords;
priv = data->self->priv;
- if (!priv->terminal_highlighted)
+ if (!priv->terminal_hovered)
return;
ld_canvas_color_apply (COLOR_GET (data->self, COLOR_TERMINAL), data->cr);
cairo_set_line_width (data->cr, 1);
cairo_new_path (data->cr);
- cairo_arc (data->cr, priv->terminal.x, priv->terminal.y,
+ ld_canvas_diagram_to_widget_coords (data->self,
+ priv->terminal.x, priv->terminal.y,
+ &widget_coords.x, &widget_coords.y);
+ cairo_arc (data->cr, widget_coords.x, widget_coords.y,
TERMINAL_RADIUS, 0, 2 * G_PI);
cairo_stroke (data->cr);
}
@@ -1753,12 +2080,17 @@ draw_diagram (GtkWidget *widget, DrawData *data)
switch (data->self->priv->operation)
{
- AddObjectData *op_data;
+ AddObjectData *add_data;
+ ConnectData *connect_data;
case OPER_ADD_OBJECT:
- op_data = &OPER_DATA (data->self, add_object);
- if (op_data->visible)
- draw_object (op_data->object, data);
+ add_data = &OPER_DATA (data->self, add_object);
+ if (add_data->visible)
+ draw_object (add_data->object, data);
+ break;
+ case OPER_CONNECT:
+ connect_data = &OPER_DATA (data->self, connect);
+ draw_object (LD_DIAGRAM_OBJECT (connect_data->connection), data);
break;
}
@@ -1780,6 +2112,8 @@ draw_object (LdDiagramObject *diagram_object, DrawData *data)
if (LD_IS_DIAGRAM_SYMBOL (diagram_object))
draw_symbol (LD_DIAGRAM_SYMBOL (diagram_object), data);
+ else if (LD_IS_DIAGRAM_CONNECTION (diagram_object))
+ draw_connection (LD_DIAGRAM_CONNECTION (diagram_object), data);
}
static void
@@ -1789,7 +2123,7 @@ draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
LdRectangle clip_rect;
gdouble x, y;
- symbol = resolve_diagram_symbol (data->self, diagram_symbol);
+ symbol = resolve_symbol (data->self, diagram_symbol);
/* TODO: Resolve this better; draw a cross or whatever. */
if (!symbol)
@@ -1813,13 +2147,51 @@ draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
cairo_clip (data->cr);
/* TODO: Rotate the space for other orientations. */
- ld_canvas_diagram_to_widget_coords (data->self,
- ld_diagram_object_get_x (LD_DIAGRAM_OBJECT (diagram_symbol)),
- ld_diagram_object_get_y (LD_DIAGRAM_OBJECT (diagram_symbol)),
- &x, &y);
+ g_object_get (diagram_symbol, "x", &x, "y", &y, NULL);
+ ld_canvas_diagram_to_widget_coords (data->self, x, y, &x, &y);
cairo_translate (data->cr, x, y);
cairo_scale (data->cr, data->scale, data->scale);
+
ld_symbol_draw (symbol, data->cr);
+ cairo_restore (data->cr);
+}
+
+static void
+draw_connection (LdDiagramConnection *connection, DrawData *data)
+{
+ LdRectangle clip_rect;
+ LdPointArray *points;
+ gdouble x, y;
+ guint i;
+
+ if (!get_connection_clip_area (data->self, connection, &clip_rect)
+ || !ld_rectangle_intersects (&clip_rect, &data->exposed_rect))
+ return;
+
+ points = ld_diagram_connection_get_points (connection);
+ if (points->length < 2)
+ goto draw_connection_end;
+ cairo_save (data->cr);
+
+ g_object_get (connection, "x", &x, "y", &y, NULL);
+ ld_canvas_diagram_to_widget_coords (data->self, x, y, &x, &y);
+ cairo_translate (data->cr, x, y);
+ cairo_scale (data->cr, data->scale, data->scale);
+
+ for (i = 1; i < points->length; i++)
+ {
+ cairo_move_to (data->cr,
+ points->points[i - 1].x,
+ points->points[i - 1].y);
+ cairo_line_to (data->cr,
+ points->points[i].x,
+ points->points[i].y);
+ cairo_stroke (data->cr);
+ }
cairo_restore (data->cr);
+
+draw_connection_end:
+ ld_point_array_free (points);
+ return;
}
diff --git a/liblogdiag/ld-types.c b/liblogdiag/ld-types.c
index 7721e36..d7e3dfb 100644
--- a/liblogdiag/ld-types.c
+++ b/liblogdiag/ld-types.c
@@ -90,7 +90,7 @@ DEFINE_BOXED_TRIVIAL_FREE (LdPoint, ld_point)
* Compute the distance between two points.
*/
gdouble
-ld_point_distance (LdPoint *self, gdouble x, gdouble y)
+ld_point_distance (const LdPoint *self, gdouble x, gdouble y)
{
gdouble dx, dy;
@@ -292,38 +292,58 @@ DEFINE_BOXED_TRIVIAL_COPY (LdRectangle, ld_rectangle)
DEFINE_BOXED_TRIVIAL_FREE (LdRectangle, ld_rectangle)
/**
- * ld_rectangle_contains:
+ * ld_rectangle_intersects:
* @self: an #LdRectangle structure.
- * @x: the X coordinate of the point to be checked.
- * @y: the Y coordinate of the point to be checked.
+ * @rect: an #LdRectangle to be checked for intersection.
*
- * Return value: %TRUE if the rectangle contains the specified point.
+ * Return value: %TRUE if the two rectangles intersect.
*/
gboolean
-ld_rectangle_contains (LdRectangle *self, gdouble x, gdouble y)
+ld_rectangle_intersects (const LdRectangle *self, const LdRectangle *rect)
{
g_return_val_if_fail (self != NULL, FALSE);
- return (x >= self->x && x <= self->x + self->width
- && y >= self->y && y <= self->y + self->height);
+ g_return_val_if_fail (rect != NULL, FALSE);
+
+ return !(self->x > rect->x + rect->width
+ || self->y > rect->y + rect->height
+ || self->x + self->width < rect->x
+ || self->y + self->height < rect->y);
}
/**
- * ld_rectangle_intersects:
+ * ld_rectangle_contains:
* @self: an #LdRectangle structure.
- * @rect: an #LdRectangle to be checked for intersection.
+ * @rect: an #LdRectangle to be checked for containment.
*
- * Return value: %TRUE if the two rectangles intersect.
+ * Return value: %TRUE if @self fully contains @rect.
*/
gboolean
-ld_rectangle_intersects (LdRectangle *self, LdRectangle *rect)
+ld_rectangle_contains (const LdRectangle *self, const LdRectangle *rect)
{
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (rect != NULL, FALSE);
- return !(self->x > rect->x + rect->width
- || self->y > rect->y + rect->height
- || self->x + self->width < rect->x
- || self->y + self->height < rect->y);
+ return (self->x <= rect->x
+ && self->y <= rect->y
+ && self->x + self->width >= rect->x + rect->width
+ && self->y + self->height >= rect->y + rect->height);
+}
+
+/**
+ * ld_rectangle_contains_point:
+ * @self: an #LdRectangle structure.
+ * @point: the point to be checked.
+ *
+ * Return value: %TRUE if the rectangle contains the specified point.
+ */
+gboolean
+ld_rectangle_contains_point (const LdRectangle *self, const LdPoint *point)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (point != NULL, FALSE);
+
+ return (point->x >= self->x && point->x <= self->x + self->width
+ && point->y >= self->y && point->y <= self->y + self->height);
}
/**
diff --git a/liblogdiag/ld-types.h b/liblogdiag/ld-types.h
index 29c0acc..30590e7 100644
--- a/liblogdiag/ld-types.h
+++ b/liblogdiag/ld-types.h
@@ -39,7 +39,7 @@ GType ld_point_get_type (void) G_GNUC_CONST;
LdPoint *ld_point_copy (const LdPoint *self);
void ld_point_free (LdPoint *self);
-gdouble ld_point_distance (LdPoint *self, gdouble x, gdouble y);
+gdouble ld_point_distance (const LdPoint *self, gdouble x, gdouble y);
/**
@@ -88,8 +88,12 @@ GType ld_rectangle_get_type (void) G_GNUC_CONST;
LdRectangle *ld_rectangle_copy (const LdRectangle *self);
void ld_rectangle_free (LdRectangle *self);
-gboolean ld_rectangle_contains (LdRectangle *self, gdouble x, gdouble y);
-gboolean ld_rectangle_intersects (LdRectangle *self, LdRectangle *rect);
+gboolean ld_rectangle_intersects (const LdRectangle *self,
+ const LdRectangle *rect);
+gboolean ld_rectangle_contains (const LdRectangle *self,
+ const LdRectangle *rect);
+gboolean ld_rectangle_contains_point (const LdRectangle *self,
+ const LdPoint *point);
void ld_rectangle_extend (LdRectangle *self, gdouble border);