From d3e5483c848d0a75b767082c7ca09b96f0dd5a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Tue, 4 Jan 2011 09:54:05 +0100 Subject: [PATCH] Make adding symbols to the diagram possible. This required creating a simple framework for operations to LdCanvas. It is probable that it's going to require some changes when other operations are added. The basic idea behind it seems to be good, though. --- src/ld-canvas.c | 255 ++++++++++++++++++++++++++++++++++++++++++- src/ld-canvas.h | 5 + src/ld-window-main.c | 20 +++- 3 files changed, 277 insertions(+), 3 deletions(-) diff --git a/src/ld-canvas.c b/src/ld-canvas.c index 08311ff..e8c6021 100644 --- a/src/ld-canvas.c +++ b/src/ld-canvas.c @@ -10,6 +10,7 @@ #include #include +#include #include "config.h" @@ -39,6 +40,22 @@ /* The default screen resolution in DPI units. */ #define DEFAULT_SCREEN_RESOLUTION 96 +/* + * OperationEnd: + * + * Called upon ending an operation. + */ +typedef void (*OperationEnd) (LdCanvas *self); + +struct _AddObjectData +{ + LdDiagramObject *object; + gboolean visible; +}; + +typedef struct _AddObjectData AddObjectData; + + /* * LdCanvasPrivate: * @diagram: A diagram object assigned to this canvas as a model. @@ -48,6 +65,9 @@ * @x: The X coordinate of the center of view. * @y: The Y coordinate of the center of view. * @zoom: The current zoom of the canvas. + * @operation: The current operation. + * @operation_data: Data related to the current operation. + * @operation_end: A callback to end the operation. */ struct _LdCanvasPrivate { @@ -60,8 +80,18 @@ struct _LdCanvasPrivate gdouble x; gdouble y; gdouble zoom; + + gint operation; + OperationEnd operation_end; + union + { + AddObjectData add_object; + } + operation_data; }; +#define OPER_DATA(self, member) ((self)->priv->operation_data.member) + G_DEFINE_TYPE (LdCanvas, ld_canvas, GTK_TYPE_DRAWING_AREA); enum @@ -71,7 +101,11 @@ enum PROP_LIBRARY }; -typedef struct _DrawData DrawData; +enum +{ + OPER_0, + OPER_ADD_OBJECT +}; /* * DrawData: @@ -88,6 +122,9 @@ struct _DrawData gdouble scale; }; +typedef struct _DrawData DrawData; + + static void ld_canvas_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ld_canvas_set_property (GObject *object, guint property_id, @@ -104,11 +141,26 @@ static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation, static gdouble ld_canvas_get_base_unit_in_px (GtkWidget *self); static gdouble ld_canvas_get_scale_in_px (LdCanvas *self); +static gboolean on_motion_notify (GtkWidget *widget, GdkEventMotion *event, + gpointer user_data); +static gboolean on_leave_notify (GtkWidget *widget, GdkEventCrossing *event, + gpointer user_data); +static gboolean on_button_press (GtkWidget *widget, GdkEventButton *event, + gpointer user_data); +static gboolean on_button_release (GtkWidget *widget, GdkEventButton *event, + gpointer user_data); + +static void move_object_to_widget_coords (LdCanvas *self, + LdDiagramObject *object, gdouble x, gdouble y); 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 void queue_object_redraw (LdCanvas *self, LdDiagramObject *object); + +static void ld_canvas_real_cancel_operation (LdCanvas *self); +static void ld_canvas_add_object_end (LdCanvas *self); static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data); @@ -123,6 +175,7 @@ ld_canvas_class_init (LdCanvasClass *klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; + GtkBindingSet *binding_set; GParamSpec *pspec; widget_class = GTK_WIDGET_CLASS (klass); @@ -133,6 +186,11 @@ ld_canvas_class_init (LdCanvasClass *klass) object_class->finalize = ld_canvas_finalize; klass->set_scroll_adjustments = ld_canvas_real_set_scroll_adjustments; + klass->cancel_operation = ld_canvas_real_cancel_operation; + + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, + "cancel-operation", 0); /** * LdCanvas:diagram: @@ -169,6 +227,18 @@ ld_canvas_class_init (LdCanvasClass *klass) g_cclosure_user_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); +/** + * LdCanvas::cancel-operation: + * + * Cancel any current operation. + */ + klass->cancel_operation_signal = g_signal_new + ("cancel-operation", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (LdCanvasClass, cancel_operation), NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_type_class_add_private (klass, sizeof (LdCanvasPrivate)); } @@ -187,9 +257,21 @@ ld_canvas_init (LdCanvas *self) g_signal_connect (self, "size-allocate", G_CALLBACK (on_size_allocate), NULL); + g_signal_connect (self, "motion-notify-event", + G_CALLBACK (on_motion_notify), NULL); + g_signal_connect (self, "leave-notify-event", + G_CALLBACK (on_leave_notify), NULL); + g_signal_connect (self, "button-press-event", + G_CALLBACK (on_button_press), NULL); + g_signal_connect (self, "button-release-event", + G_CALLBACK (on_button_release), NULL); + + g_object_set (self, "can-focus", TRUE, NULL); + gtk_widget_add_events (GTK_WIDGET (self), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_LEAVE_NOTIFY_MASK); } static void @@ -557,8 +639,74 @@ ld_canvas_diagram_to_widget_coords (LdCanvas *self, } +/* ===== Operations ======================================================== */ + +static void +ld_canvas_real_cancel_operation (LdCanvas *self) +{ + g_return_if_fail (LD_IS_CANVAS (self)); + + if (self->priv->operation) + { + if (self->priv->operation_end) + self->priv->operation_end (self); + self->priv->operation = OPER_0; + self->priv->operation_end = NULL; + } +} + +/** + * ld_canvas_add_object_begin: + * @self: An #LdCanvas object. + * @object: (transfer full): The object to be added to the diagram. + * + * Begin an operation for adding an object into the diagram. + */ +void +ld_canvas_add_object_begin (LdCanvas *self, LdDiagramObject *object) +{ + AddObjectData *data; + + g_return_if_fail (LD_IS_CANVAS (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + ld_canvas_real_cancel_operation (self); + + self->priv->operation = OPER_ADD_OBJECT; + self->priv->operation_end = ld_canvas_add_object_end; + + data = &OPER_DATA (self, add_object); + data->object = object; +} + +static void +ld_canvas_add_object_end (LdCanvas *self) +{ + AddObjectData *data; + + data = &OPER_DATA (self, add_object); + if (data->object) + { + queue_object_redraw (self, data->object); + g_object_unref (data->object); + data->object = NULL; + } +} + + /* ===== Events, rendering ================================================= */ +static void +move_object_to_widget_coords (LdCanvas *self, LdDiagramObject *object, + gdouble x, gdouble y) +{ + gdouble dx, dy; + + ld_canvas_widget_to_diagram_coords (self, x, y, &dx, &dy); + ld_diagram_object_set_x (object, floor (dx + 0.5)); + ld_diagram_object_set_y (object, floor (dy + 0.5)); +} + static LdSymbol * resolve_diagram_symbol (LdCanvas *self, LdDiagramSymbol *diagram_symbol) { @@ -606,6 +754,98 @@ get_symbol_clip_area_on_widget (LdCanvas *self, LdDiagramSymbol *diagram_symbol, return TRUE; } +static void +queue_object_redraw (LdCanvas *self, LdDiagramObject *object) +{ + if (LD_IS_DIAGRAM_SYMBOL (object)) + { + gdouble x, y, width, height; + + if (!get_symbol_clip_area_on_widget (self, LD_DIAGRAM_SYMBOL (object), + &x, &y, &width, &height)) + return; + gtk_widget_queue_draw_area (GTK_WIDGET (self), + floor (x), floor (y), ceil (width), ceil (height)); + } +} + +static gboolean +on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + LdCanvas *self; + + self = LD_CANVAS (widget); + switch (self->priv->operation) + { + AddObjectData *data; + + case OPER_ADD_OBJECT: + data = &OPER_DATA (self, add_object); + data->visible = TRUE; + + queue_object_redraw (self, data->object); + move_object_to_widget_coords (self, data->object, event->x, event->y); + queue_object_redraw (self, data->object); + break; + } + return FALSE; +} + +static gboolean +on_leave_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) +{ + LdCanvas *self; + + self = LD_CANVAS (widget); + switch (self->priv->operation) + { + AddObjectData *data; + + case OPER_ADD_OBJECT: + data = &OPER_DATA (self, add_object); + data->visible = FALSE; + + queue_object_redraw (self, data->object); + break; + } + return FALSE; +} + +static gboolean +on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + LdCanvas *self; + + if (!gtk_widget_has_focus (widget)) + gtk_widget_grab_focus (widget); + + self = LD_CANVAS (widget); + switch (self->priv->operation) + { + AddObjectData *data; + + case OPER_ADD_OBJECT: + data = &OPER_DATA (self, add_object); + + queue_object_redraw (self, data->object); + move_object_to_widget_coords (self, data->object, event->x, event->y); + + if (self->priv->diagram) + ld_diagram_insert_object (self->priv->diagram, data->object, -1); + + /* XXX: "cancel" causes confusion. */ + ld_canvas_real_cancel_operation (self); + break; + } + return FALSE; +} + +static gboolean +on_button_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + return FALSE; +} + static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { @@ -680,6 +920,17 @@ draw_diagram (GtkWidget *widget, DrawData *data) objects = ld_diagram_get_objects (data->self->priv->diagram); g_slist_foreach (objects, (GFunc) draw_object, data); + switch (data->self->priv->operation) + { + AddObjectData *op_data; + + case OPER_ADD_OBJECT: + op_data = &OPER_DATA (data->self, add_object); + if (op_data->visible) + draw_object (op_data->object, data); + break; + } + cairo_restore (data->cr); } diff --git a/src/ld-canvas.h b/src/ld-canvas.h index bb41b3d..74b3c33 100644 --- a/src/ld-canvas.h +++ b/src/ld-canvas.h @@ -48,8 +48,11 @@ struct _LdCanvasClass /*< private >*/ GtkDrawingAreaClass parent_class; + guint cancel_operation_signal; + void (*set_scroll_adjustments) (LdCanvas *self, GtkAdjustment *horizontal, GtkAdjustment *vertical); + void (*cancel_operation) (LdCanvas *self); }; @@ -75,6 +78,8 @@ void ld_canvas_widget_to_diagram_coords (LdCanvas *self, void ld_canvas_diagram_to_widget_coords (LdCanvas *self, gdouble dx, gdouble dy, gdouble *wx, gdouble *wy); +void ld_canvas_add_object_begin (LdCanvas *self, LdDiagramObject *object); + /* TODO: The rest of the interface. */ diff --git a/src/ld-window-main.c b/src/ld-window-main.c index 16c223c..32212a1 100644 --- a/src/ld-window-main.c +++ b/src/ld-window-main.c @@ -761,7 +761,25 @@ on_canvas_button_release (GtkWidget *widget, GdkEventButton *event, if (event->button != 1) return FALSE; - /* TODO: Add the selected symbol into the document on the position. */ + if (data->active_item != -1) + { + LdDiagramSymbol *symbol; + const gchar *category_name, *symbol_name; + gchar *klass; + + category_name = ld_symbol_category_get_name + (g_object_get_data (G_OBJECT (data->active_button), "category")); + symbol_name = ld_symbol_get_name + (data->items[data->active_item].symbol); + + klass = g_build_path (LD_LIBRARY_IDENTIFIER_SEPARATOR, + category_name, symbol_name, NULL); + symbol = ld_diagram_symbol_new (klass); + g_free (klass); + + ld_canvas_add_object_begin (self->priv->canvas, + LD_DIAGRAM_OBJECT (symbol)); + } /* We've either chosen a symbol or canceled the menu, so hide it. */ if (data->active_button)