aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ld-canvas.c255
-rw-r--r--src/ld-canvas.h5
-rw-r--r--src/ld-window-main.c20
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 <math.h>
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include "config.h"
@@ -40,6 +41,22 @@
#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.
* @library: A library 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)