From 7fa6efbaec6ee743a9ddfbab81b3e8079448e107 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Wed, 16 Feb 2011 07:50:07 +0100
Subject: Rename LdCanvas to LdDiagramView.
---
liblogdiag/ld-canvas.c | 2340 --------------------------------------
liblogdiag/ld-canvas.h | 93 --
liblogdiag/ld-diagram-object.c | 2 +-
liblogdiag/ld-diagram-view.c | 2346 +++++++++++++++++++++++++++++++++++++++
liblogdiag/ld-diagram-view.h | 94 ++
liblogdiag/ld-diagram.c | 2 +-
liblogdiag/ld-library-toolbar.c | 178 +--
liblogdiag/ld-library-toolbar.h | 6 +-
liblogdiag/ld-symbol.c | 4 +-
liblogdiag/liblogdiag.h | 2 +-
10 files changed, 2537 insertions(+), 2530 deletions(-)
delete mode 100644 liblogdiag/ld-canvas.c
delete mode 100644 liblogdiag/ld-canvas.h
create mode 100644 liblogdiag/ld-diagram-view.c
create mode 100644 liblogdiag/ld-diagram-view.h
(limited to 'liblogdiag')
diff --git a/liblogdiag/ld-canvas.c b/liblogdiag/ld-canvas.c
deleted file mode 100644
index 93c70a1..0000000
--- a/liblogdiag/ld-canvas.c
+++ /dev/null
@@ -1,2340 +0,0 @@
-/*
- * ld-canvas.c
- *
- * This file is a part of logdiag.
- * Copyright Přemysl Janouch 2010 - 2011. All rights reserved.
- *
- * See the file LICENSE for licensing information.
- *
- */
-
-#include
-#include
-#include
-
-#include "liblogdiag.h"
-#include "config.h"
-
-
-/**
- * SECTION:ld-canvas
- * @short_description: A canvas
- * @see_also: #LdDiagram
- *
- * #LdCanvas displays and enables the user to manipulate with an #LdDiagram.
- */
-
-/* Milimetres per inch. */
-#define MM_PER_INCH 25.4
-/* The default screen resolution in DPI units. */
-#define DEFAULT_SCREEN_RESOLUTION 96
-
-/* The maximal, minimal and default values of zoom. */
-#define ZOOM_MIN 0.01
-#define ZOOM_MAX 100
-#define ZOOM_DEFAULT 1
-/* Multiplication factor for zooming. */
-#define ZOOM_STEP 1.4
-
-/* When drawing is requested, extend all sides of
- * the rectangle to be drawn by this number of pixels.
- */
-#define QUEUE_DRAW_EXTEND 3
-/* Cursor tolerance for object borders. */
-#define OBJECT_BORDER_TOLERANCE 3
-/* Tolerance on all sides of symbols for strokes. */
-#define SYMBOL_CLIP_TOLERANCE 5
-
-/* Size of a highlighted terminal. */
-#define TERMINAL_RADIUS 5
-/* Tolerance around terminal points. */
-#define TERMINAL_HOVER_TOLERANCE 8
-
-/*
- * OperationEnd:
- *
- * Called upon ending an operation.
- */
-typedef void (*OperationEnd) (LdCanvas *self);
-
-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;
-
-struct _AddObjectData
-{
- LdDiagramObject *object;
- gboolean visible;
-};
-
-struct _ConnectData
-{
- LdDiagramConnection *connection;
- LdPoint origin;
-};
-
-struct _SelectData
-{
- LdPoint drag_last_pos;
-};
-
-struct _MoveSelectionData
-{
- LdPoint move_origin;
-};
-
-enum
-{
- COLOR_BASE,
- COLOR_GRID,
- COLOR_OBJECT,
- COLOR_SELECTION,
- COLOR_TERMINAL,
- COLOR_COUNT
-};
-
-typedef struct _LdCanvasColor LdCanvasColor;
-
-struct _LdCanvasColor
-{
- gdouble r;
- gdouble g;
- gdouble b;
- gdouble a;
-};
-
-/*
- * LdCanvasPrivate:
- * @diagram: a diagram object assigned to this canvas as a model.
- * @library: a library object assigned to this canvas as a model.
- * @adjustment_h: an adjustment object for the horizontal axis, if any.
- * @adjustment_v: an adjustment object for the vertical axis, if any.
- * @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.
- * @terminal: position of the highlighted terminal.
- * @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.
- * @operation_data: data related to the current operation.
- * @operation_end: a callback to end the operation.
- * @palette: colors used by the widget.
- */
-struct _LdCanvasPrivate
-{
- LdDiagram *diagram;
- LdLibrary *library;
-
- GtkAdjustment *adjustment_h;
- GtkAdjustment *adjustment_v;
-
- gdouble x;
- gdouble y;
- gdouble zoom;
-
- LdPoint terminal;
- gboolean terminal_hovered;
-
- LdPoint drag_start_pos;
- gint drag_operation;
-
- gint operation;
- union
- {
- AddObjectData add_object;
- ConnectData connect;
- SelectData select;
- MoveSelectionData move_selection;
- }
- operation_data;
- OperationEnd operation_end;
-
- LdCanvasColor palette[COLOR_COUNT];
-};
-
-#define OPER_DATA(self, member) ((self)->priv->operation_data.member)
-#define COLOR_GET(self, name) (&(self)->priv->palette[name])
-
-/*
- * DrawData:
- * @self: our #LdCanvas.
- * @cr: a cairo context to draw on.
- * @exposed_rect: the area that is to be redrawn.
- * @scale: computed size of one diagram unit in pixels.
- */
-typedef struct _DrawData DrawData;
-
-struct _DrawData
-{
- LdCanvas *self;
- cairo_t *cr;
- LdRectangle exposed_rect;
- gdouble scale;
-};
-
-enum
-{
- PROP_0,
- PROP_DIAGRAM,
- PROP_LIBRARY,
- PROP_ZOOM
-};
-
-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,
- const GValue *value, GParamSpec *pspec);
-static void ld_canvas_finalize (GObject *gobject);
-
-static void ld_canvas_real_set_scroll_adjustments
- (LdCanvas *self, GtkAdjustment *horizontal, GtkAdjustment *vertical);
-static void on_adjustment_value_changed
- (GtkAdjustment *adjustment, LdCanvas *self);
-static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
- gpointer user_data);
-static void update_adjustments (LdCanvas *self);
-static void ld_canvas_real_move (LdCanvas *self, gdouble dx, gdouble dy);
-
-static void diagram_connect_signals (LdCanvas *self);
-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 guint32 ld_canvas_color_to_cairo_argb (LdCanvasColor *color);
-
-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 gboolean is_object_selected (LdCanvas *self, LdDiagramObject *object);
-
-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 rotate_terminal (LdPoint *terminal, gint symbol_rotation);
-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 void rotate_symbol_area (LdRectangle *area, gint rotation);
-static void rotate_symbol (LdCanvas *self, LdDiagramSymbol *symbol);
-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_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, const LdPoint *point);
-
-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, const LdPoint *point);
-
-/* Events, rendering. */
-static void simulate_motion (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 gboolean on_scroll (GtkWidget *widget, GdkEventScroll *event,
- gpointer user_data);
-
-static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event,
- gpointer user_data);
-static void draw_grid (GtkWidget *widget, DrawData *data);
-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);
-
-static void
-ld_canvas_class_init (LdCanvasClass *klass)
-{
- GObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkBindingSet *binding_set;
- GParamSpec *pspec;
-
- widget_class = GTK_WIDGET_CLASS (klass);
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->get_property = ld_canvas_get_property;
- object_class->set_property = ld_canvas_set_property;
- object_class->finalize = ld_canvas_finalize;
-
- klass->set_scroll_adjustments = ld_canvas_real_set_scroll_adjustments;
- klass->cancel_operation = ld_canvas_real_cancel_operation;
- klass->move = ld_canvas_real_move;
-
- binding_set = gtk_binding_set_by_class (klass);
- gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
- "cancel-operation", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
- "move", 2, G_TYPE_DOUBLE, (gdouble) -1, G_TYPE_DOUBLE, (gdouble) 0);
- gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
- "move", 2, G_TYPE_DOUBLE, (gdouble) 1, G_TYPE_DOUBLE, (gdouble) 0);
- gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
- "move", 2, G_TYPE_DOUBLE, (gdouble) 0, G_TYPE_DOUBLE, (gdouble) -1);
- gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
- "move", 2, G_TYPE_DOUBLE, (gdouble) 0, G_TYPE_DOUBLE, (gdouble) 1);
-
-/**
- * LdCanvas:diagram:
- *
- * The underlying #LdDiagram object of this canvas.
- */
- pspec = g_param_spec_object ("diagram", "Diagram",
- "The underlying diagram object of this canvas.",
- LD_TYPE_DIAGRAM, G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_DIAGRAM, pspec);
-
-/**
- * LdCanvas:library:
- *
- * The #LdLibrary that this canvas retrieves symbols from.
- */
- pspec = g_param_spec_object ("library", "Library",
- "The library that this canvas retrieves symbols from.",
- LD_TYPE_LIBRARY, G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_LIBRARY, pspec);
-
-/**
- * LdCanvas:zoom:
- *
- * The zoom of this canvas.
- */
- pspec = g_param_spec_double ("zoom", "Zoom",
- "The zoom of this canvas.",
- ZOOM_MIN, ZOOM_MAX, ZOOM_DEFAULT, G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_ZOOM, pspec);
-
-/**
- * LdCanvas::set-scroll-adjustments:
- * @self: an #LdCanvas object.
- * @horizontal: the horizontal #GtkAdjustment.
- * @vertical: the vertical #GtkAdjustment.
- *
- * Set scroll adjustments for the canvas.
- */
- widget_class->set_scroll_adjustments_signal = g_signal_new
- ("set-scroll-adjustments", G_TYPE_FROM_CLASS (widget_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (LdCanvasClass, set_scroll_adjustments),
- NULL, NULL,
- ld_marshal_VOID__OBJECT_OBJECT,
- G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
-
-/**
- * LdCanvas::cancel-operation:
- * @self: an #LdCanvas object.
- *
- * 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);
-
-/**
- * LdCanvas::move:
- * @self: an #LdCanvas object.
- * @dx: The difference by which to move on the horizontal axis.
- * @dy: The difference by which to move on the vertical axis.
- *
- * Move the selection, if any, or the document.
- */
- klass->move_signal = g_signal_new
- ("move", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (LdCanvasClass, move), NULL, NULL,
- ld_marshal_VOID__DOUBLE_DOUBLE,
- G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
-
- g_type_class_add_private (klass, sizeof (LdCanvasPrivate));
-}
-
-static void
-ld_canvas_init (LdCanvas *self)
-{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE
- (self, LD_TYPE_CANVAS, LdCanvasPrivate);
-
- self->priv->x = 0;
- self->priv->y = 0;
- self->priv->zoom = ZOOM_DEFAULT;
-
- ld_canvas_color_set (COLOR_GET (self, COLOR_BASE), 1, 1, 1, 1);
- ld_canvas_color_set (COLOR_GET (self, COLOR_GRID), 0.5, 0.5, 0.5, 1);
- ld_canvas_color_set (COLOR_GET (self, COLOR_OBJECT), 0, 0, 0, 1);
- ld_canvas_color_set (COLOR_GET (self, COLOR_SELECTION), 1, 0, 0, 1);
- ld_canvas_color_set (COLOR_GET (self, COLOR_TERMINAL), 1, 0.5, 0.5, 1);
-
- g_signal_connect (self, "size-allocate",
- G_CALLBACK (on_size_allocate), NULL);
- g_signal_connect (self, "expose-event",
- G_CALLBACK (on_expose_event), 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_signal_connect (self, "scroll-event",
- G_CALLBACK (on_scroll), 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_LEAVE_NOTIFY_MASK);
-}
-
-static void
-ld_canvas_finalize (GObject *gobject)
-{
- LdCanvas *self;
-
- self = LD_CANVAS (gobject);
-
- ld_canvas_real_set_scroll_adjustments (self, NULL, NULL);
-
- if (self->priv->diagram)
- {
- diagram_disconnect_signals (self);
- g_object_unref (self->priv->diagram);
- }
- if (self->priv->library)
- g_object_unref (self->priv->library);
-
- /* Chain up to the parent class. */
- G_OBJECT_CLASS (ld_canvas_parent_class)->finalize (gobject);
-}
-
-static void
-ld_canvas_get_property (GObject *object, guint property_id,
- GValue *value, GParamSpec *pspec)
-{
- LdCanvas *self;
-
- self = LD_CANVAS (object);
- switch (property_id)
- {
- case PROP_DIAGRAM:
- g_value_set_object (value, ld_canvas_get_diagram (self));
- break;
- case PROP_LIBRARY:
- g_value_set_object (value, ld_canvas_get_library (self));
- break;
- case PROP_ZOOM:
- g_value_set_double (value, ld_canvas_get_zoom (self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-ld_canvas_set_property (GObject *object, guint property_id,
- const GValue *value, GParamSpec *pspec)
-{
- LdCanvas *self;
-
- self = LD_CANVAS (object);
- switch (property_id)
- {
- case PROP_DIAGRAM:
- ld_canvas_set_diagram (self, LD_DIAGRAM (g_value_get_object (value)));
- break;
- case PROP_LIBRARY:
- ld_canvas_set_library (self, LD_LIBRARY (g_value_get_object (value)));
- break;
- case PROP_ZOOM:
- ld_canvas_set_zoom (self, g_value_get_double (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-ld_canvas_real_set_scroll_adjustments (LdCanvas *self,
- GtkAdjustment *horizontal, GtkAdjustment *vertical)
-{
- /* TODO: Infinite canvas. */
- GtkWidget *widget;
- gdouble scale;
-
- widget = GTK_WIDGET (self);
- scale = ld_canvas_get_scale_in_px (self);
-
- if (horizontal != self->priv->adjustment_h)
- {
- if (self->priv->adjustment_h)
- {
- g_signal_handlers_disconnect_by_func (self->priv->adjustment_h,
- on_adjustment_value_changed, self);
- g_object_unref (self->priv->adjustment_h);
-
- self->priv->adjustment_h = NULL;
- }
- if (horizontal)
- {
- g_object_ref (horizontal);
- g_signal_connect (horizontal, "value-changed",
- G_CALLBACK (on_adjustment_value_changed), self);
-
- horizontal->upper = 100;
- horizontal->lower = -100;
- horizontal->step_increment = 0.5;
- horizontal->page_increment = 5;
- horizontal->page_size = widget->allocation.width / scale;
- horizontal->value = -horizontal->page_size / 2;
-
- self->priv->adjustment_h = horizontal;
- }
- }
-
- if (vertical != self->priv->adjustment_v)
- {
- if (self->priv->adjustment_v)
- {
- g_signal_handlers_disconnect_by_func (self->priv->adjustment_v,
- on_adjustment_value_changed, self);
- g_object_unref (self->priv->adjustment_v);
-
- self->priv->adjustment_v = NULL;
- }
- if (vertical)
- {
- g_object_ref (vertical);
- g_signal_connect (vertical, "value-changed",
- G_CALLBACK (on_adjustment_value_changed), self);
-
- vertical->upper = 100;
- vertical->lower = -100;
- vertical->step_increment = 0.5;
- vertical->page_increment = 5;
- vertical->page_size = widget->allocation.height / scale;
- vertical->value = -vertical->page_size / 2;
-
- self->priv->adjustment_v = vertical;
- }
- }
-}
-
-static void
-on_adjustment_value_changed (GtkAdjustment *adjustment, LdCanvas *self)
-{
- GtkWidget *widget;
- gdouble scale;
-
- widget = GTK_WIDGET (self);
- scale = ld_canvas_get_scale_in_px (self);
-
- if (adjustment == self->priv->adjustment_h)
- {
- self->priv->x = adjustment->value
- + widget->allocation.width / scale / 2;
- gtk_widget_queue_draw (widget);
- }
- else if (adjustment == self->priv->adjustment_v)
- {
- self->priv->y = adjustment->value
- + widget->allocation.height / scale / 2;
- gtk_widget_queue_draw (widget);
- }
-}
-
-static void
-on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
- gpointer user_data)
-{
- LdCanvas *self;
-
- self = LD_CANVAS (widget);
-
- /* FIXME: If the new allocation is bigger, we may see more than
- * what we're supposed to be able to see -> adjust X and Y.
- *
- * If the visible area is so large that we simply must see more,
- * let's disable the scrollbars in question.
- */
- update_adjustments (self);
-}
-
-static void
-update_adjustments (LdCanvas *self)
-{
- gdouble scale;
-
- scale = ld_canvas_get_scale_in_px (self);
-
- if (self->priv->adjustment_h)
- {
- self->priv->adjustment_h->page_size
- = GTK_WIDGET (self)->allocation.width / scale;
- self->priv->adjustment_h->value
- = self->priv->x - self->priv->adjustment_h->page_size / 2;
- gtk_adjustment_changed (self->priv->adjustment_h);
- }
- if (self->priv->adjustment_v)
- {
- self->priv->adjustment_v->page_size
- = GTK_WIDGET (self)->allocation.height / scale;
- self->priv->adjustment_v->value
- = self->priv->y - self->priv->adjustment_v->page_size / 2;
- gtk_adjustment_changed (self->priv->adjustment_v);
- }
-}
-
-static void
-ld_canvas_real_move (LdCanvas *self, gdouble dx, gdouble dy)
-{
- LdDiagram *diagram;
-
- diagram = self->priv->diagram;
- if (!diagram)
- return;
-
- /* TODO: Check/move boundaries, also implement normal
- * getters and setters for priv->x and priv->y.
- */
- if (ld_diagram_get_selection (diagram))
- move_selection (self, dx, dy);
- else
- {
- self->priv->x += dx;
- self->priv->y += dy;
-
- simulate_motion (self);
- update_adjustments (self);
- }
- gtk_widget_queue_draw (GTK_WIDGET (self));
-}
-
-
-/* ===== Generic interface etc. ============================================ */
-
-/**
- * ld_canvas_new:
- *
- * Create an instance.
- */
-GtkWidget *
-ld_canvas_new (void)
-{
- return g_object_new (LD_TYPE_CANVAS, NULL);
-}
-
-/**
- * ld_canvas_set_diagram:
- * @self: an #LdCanvas object.
- * @diagram: the #LdDiagram to be assigned to the canvas.
- *
- * Assign an #LdDiagram object to the canvas.
- */
-void
-ld_canvas_set_diagram (LdCanvas *self, LdDiagram *diagram)
-{
- g_return_if_fail (LD_IS_CANVAS (self));
- g_return_if_fail (LD_IS_DIAGRAM (diagram));
-
- if (self->priv->diagram)
- {
- diagram_disconnect_signals (self);
- g_object_unref (self->priv->diagram);
- }
-
- self->priv->diagram = diagram;
- diagram_connect_signals (self);
- g_object_ref (diagram);
-
- g_object_notify (G_OBJECT (self), "diagram");
-}
-
-/**
- * ld_canvas_get_diagram:
- * @self: an #LdCanvas object.
- *
- * Get the #LdDiagram object assigned to this canvas.
- * The reference count on the diagram is not incremented.
- */
-LdDiagram *
-ld_canvas_get_diagram (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), NULL);
- return self->priv->diagram;
-}
-
-static void
-diagram_connect_signals (LdCanvas *self)
-{
- g_return_if_fail (LD_IS_DIAGRAM (self->priv->diagram));
-
- g_signal_connect_swapped (self->priv->diagram, "changed",
- G_CALLBACK (gtk_widget_queue_draw), self);
- g_signal_connect_swapped (self->priv->diagram, "selection-changed",
- G_CALLBACK (gtk_widget_queue_draw), self);
-}
-
-static void
-diagram_disconnect_signals (LdCanvas *self)
-{
- g_return_if_fail (LD_IS_DIAGRAM (self->priv->diagram));
-
- g_signal_handlers_disconnect_matched (self->priv->diagram,
- G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
- gtk_widget_queue_draw, self);
-}
-
-/**
- * ld_canvas_set_library:
- * @self: an #LdCanvas object.
- * @library: the #LdLibrary to be assigned to the canvas.
- *
- * Assign an #LdLibrary object to the canvas.
- */
-void
-ld_canvas_set_library (LdCanvas *self, LdLibrary *library)
-{
- g_return_if_fail (LD_IS_CANVAS (self));
- g_return_if_fail (LD_IS_LIBRARY (library));
-
- if (self->priv->library)
- g_object_unref (self->priv->library);
-
- self->priv->library = library;
- g_object_ref (library);
-
- g_object_notify (G_OBJECT (self), "library");
-}
-
-/**
- * ld_canvas_get_library:
- * @self: an #LdCanvas object.
- *
- * Get the #LdLibrary object assigned to this canvas.
- * The reference count on the library is not incremented.
- */
-LdLibrary *
-ld_canvas_get_library (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), NULL);
- return self->priv->library;
-}
-
-/*
- * ld_canvas_get_base_unit_in_px:
- * @self: a #GtkWidget object to retrieve DPI from (indirectly).
- *
- * Return value: length of the base unit in pixels.
- */
-static gdouble
-ld_canvas_get_base_unit_in_px (GtkWidget *self)
-{
- gdouble resolution;
-
- g_return_val_if_fail (GTK_IS_WIDGET (self), 1);
-
- resolution = gdk_screen_get_resolution (gtk_widget_get_screen (self));
- if (resolution == -1)
- resolution = DEFAULT_SCREEN_RESOLUTION;
-
- /* XXX: It might look better if the unit was rounded to a whole number. */
- return resolution / MM_PER_INCH * LD_CANVAS_BASE_UNIT_LENGTH;
-}
-
-/*
- * ld_canvas_get_scale_in_px:
- * @self: an #LdCanvas object.
- *
- * Return value: displayed length of the base unit in pixels.
- */
-static gdouble
-ld_canvas_get_scale_in_px (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), 1);
-
- return ld_canvas_get_base_unit_in_px (GTK_WIDGET (self))
- * self->priv->zoom;
-}
-
-/**
- * ld_canvas_widget_to_diagram_coords:
- * @self: an #LdCanvas object.
- * @wx: the X coordinate to be translated.
- * @wy: the Y coordinate to be translated.
- * @dx: (out): the translated X coordinate.
- * @dy: (out): the translated Y coordinate.
- *
- * Translate coordinates located inside the canvas window
- * into diagram coordinates.
- */
-void
-ld_canvas_widget_to_diagram_coords (LdCanvas *self,
- gdouble wx, gdouble wy, gdouble *dx, gdouble *dy)
-{
- GtkWidget *widget;
- gdouble scale;
-
- g_return_if_fail (LD_IS_CANVAS (self));
- g_return_if_fail (dx != NULL);
- g_return_if_fail (dy != NULL);
-
- widget = GTK_WIDGET (self);
- scale = ld_canvas_get_scale_in_px (self);
-
- /* We know diagram coordinates of the center of the canvas, so we may
- * translate the given X and Y coordinates to this center and then scale
- * them by dividing them by the current scale.
- */
- *dx = self->priv->x + (wx - (widget->allocation.width * 0.5)) / scale;
- *dy = self->priv->y + (wy - (widget->allocation.height * 0.5)) / scale;
-}
-
-/**
- * ld_canvas_diagram_to_widget_coords:
- * @self: an #LdCanvas object.
- * @dx: the X coordinate to be translated.
- * @dy: the Y coordinate to be translated.
- * @wx: (out): the translated X coordinate.
- * @wy: (out): the translated Y coordinate.
- *
- * Translate diagram coordinates into canvas coordinates.
- */
-void
-ld_canvas_diagram_to_widget_coords (LdCanvas *self,
- gdouble dx, gdouble dy, gdouble *wx, gdouble *wy)
-{
- GtkWidget *widget;
- gdouble scale;
-
- g_return_if_fail (LD_IS_CANVAS (self));
- g_return_if_fail (wx != NULL);
- g_return_if_fail (wy != NULL);
-
- widget = GTK_WIDGET (self);
- scale = ld_canvas_get_scale_in_px (self);
-
- /* Just the reversal of ld_canvas_widget_to_diagram_coords(). */
- *wx = scale * (dx - self->priv->x) + 0.5 * widget->allocation.width;
- *wy = scale * (dy - self->priv->y) + 0.5 * widget->allocation.height;
-}
-
-/**
- * ld_canvas_get_zoom:
- * @self: an #LdCanvas object.
- *
- * Return value: zoom of the canvas.
- */
-gdouble
-ld_canvas_get_zoom (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), -1);
- return self->priv->zoom;
-}
-
-/**
- * ld_canvas_set_zoom:
- * @self: an #LdCanvas object.
- * @zoom: the zoom.
- *
- * Set zoom of the canvas.
- */
-void
-ld_canvas_set_zoom (LdCanvas *self, gdouble zoom)
-{
- gdouble clamped_zoom;
-
- g_return_if_fail (LD_IS_CANVAS (self));
-
- clamped_zoom = CLAMP (zoom, ZOOM_MIN, ZOOM_MAX);
- if (self->priv->zoom == clamped_zoom)
- return;
-
- self->priv->zoom = clamped_zoom;
-
- simulate_motion (self);
- update_adjustments (self);
- gtk_widget_queue_draw (GTK_WIDGET (self));
-
- g_object_notify (G_OBJECT (self), "zoom");
-}
-
-/**
- * ld_canvas_can_zoom_in:
- * @self: an #LdCanvas object.
- *
- * Return value: %TRUE if the view can be zoomed in.
- */
-gboolean
-ld_canvas_can_zoom_in (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), FALSE);
- return self->priv->zoom < ZOOM_MAX;
-}
-
-/**
- * ld_canvas_can_zoom_out:
- * @self: an #LdCanvas object.
- *
- * Return value: %TRUE if the view can be zoomed out.
- */
-gboolean
-ld_canvas_can_zoom_out (LdCanvas *self)
-{
- g_return_val_if_fail (LD_IS_CANVAS (self), FALSE);
- return self->priv->zoom > ZOOM_MIN;
-}
-
-/**
- * ld_canvas_zoom_in:
- * @self: an #LdCanvas object.
- *
- * Zoom the view in.
- */
-void
-ld_canvas_zoom_in (LdCanvas *self)
-{
- g_return_if_fail (LD_IS_CANVAS (self));
- ld_canvas_set_zoom (self, self->priv->zoom * ZOOM_STEP);
-}
-
-/**
- * ld_canvas_zoom_out:
- * @self: an #LdCanvas object.
- *
- * Zoom the view out.
- */
-void
-ld_canvas_zoom_out (LdCanvas *self)
-{
- g_return_if_fail (LD_IS_CANVAS (self));
- ld_canvas_set_zoom (self, self->priv->zoom / ZOOM_STEP);
-}
-
-
-/* ===== Helper functions ================================================== */
-
-static void
-ld_canvas_color_set (LdCanvasColor *color,
- gdouble r, gdouble g, gdouble b, gdouble a)
-{
- color->r = r;
- color->g = g;
- color->b = b;
- color->a = a;
-}
-
-static void
-ld_canvas_color_apply (LdCanvasColor *color, cairo_t *cr)
-{
- cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
-}
-
-static guint32
-ld_canvas_color_to_cairo_argb (LdCanvasColor *color)
-{
- return (guint) (color->a * 255) << 24
- | (guint) (color->r * color->a * 255) << 16
- | (guint) (color->g * color->a * 255) << 8
- | (guint) (color->b * color->a * 255);
-}
-
-static gdouble
-point_to_line_segment_distance
- (const LdPoint *point, const LdPoint *p1, const LdPoint *p2)
-{
- gdouble dx, dy, u;
-
- dx = p2->x - p1->x;
- dy = p2->y - p1->y;
-
- if (dx == 0. && dy == 0.)
- return ld_point_distance (point, p1->x, p1->y);
-
- /* Find projection of the point onto the line. */
- u = ((point->x - p1->x) * dx + (point->y - p1->y) * dy)
- / (dx * dx + dy * dy);
-
- /* 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_point (LdCanvas *self, LdDiagramObject *object,
- const LdPoint *point)
-{
- gdouble diagram_x, diagram_y;
-
- 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_point (LdCanvas *self, const LdPoint *point)
-{
- GList *objects, *iter;
-
- /* Iterate from the top object downwards. */
- objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
- for (iter = g_list_last (objects); iter; iter = g_list_previous (iter))
- {
- LdDiagramObject *object;
-
- object = LD_DIAGRAM_OBJECT (iter->data);
- if (object_hit_test (self, object, point))
- return object;
- }
- return NULL;
-}
-
-static void
-move_selection (LdCanvas *self, gdouble dx, gdouble dy)
-{
- LdDiagram *diagram;
- GList *selection, *iter;
-
- diagram = self->priv->diagram;
- if (!diagram)
- return;
-
- selection = ld_diagram_get_selection (diagram);
- if (!selection)
- return;
-
- ld_diagram_begin_user_action (diagram);
- for (iter = selection; iter; iter = g_list_next (iter))
- {
- gdouble x, y;
-
- 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
-is_object_selected (LdCanvas *self, LdDiagramObject *object)
-{
- return g_list_find (ld_diagram_get_selection (self->priv->diagram),
- object) != NULL;
-}
-
-static void
-queue_draw (LdCanvas *self, LdRectangle *rect)
-{
- 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 void
-queue_object_draw (LdCanvas *self, LdDiagramObject *object)
-{
- LdRectangle rect;
-
- if (!get_object_clip_area (self, object, &rect))
- return;
- queue_draw (self, &rect);
-}
-
-
-/* ===== Symbol terminals ================================================== */
-
-static void
-check_terminals (LdCanvas *self, const LdPoint *point)
-{
- GList *objects, *iter;
- LdDiagramSymbol *closest_symbol = NULL;
- gdouble closest_distance = TERMINAL_HOVER_TOLERANCE;
- LdPoint closest_terminal;
-
- objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
- for (iter = objects; iter; iter = g_list_next (iter))
- {
- gdouble object_x, object_y;
- LdDiagramSymbol *diagram_symbol;
- LdSymbol *symbol;
- const LdPointArray *terminals;
- guint i;
- gint rotation;
-
- if (!LD_IS_DIAGRAM_SYMBOL (iter->data))
- continue;
-
- diagram_symbol = LD_DIAGRAM_SYMBOL (iter->data);
- symbol = resolve_symbol (self, diagram_symbol);
- if (!symbol)
- continue;
-
- g_object_get (diagram_symbol, "x", &object_x, "y", &object_y,
- "rotation", &rotation, NULL);
-
- terminals = ld_symbol_get_terminals (symbol);
- for (i = 0; i < terminals->length; i++)
- {
- LdPoint cur_term, widget_coords;
- gdouble distance;
-
- cur_term = terminals->points[i];
- rotate_terminal (&cur_term, rotation);
- cur_term.x += object_x;
- cur_term.y += object_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;
- closest_distance = distance;
- closest_terminal = cur_term;
- }
- }
- }
-
- hide_terminals (self);
-
- if (closest_symbol)
- {
- self->priv->terminal_hovered = TRUE;
- self->priv->terminal = closest_terminal;
- queue_terminal_draw (self, &closest_terminal);
- }
-}
-
-static void
-rotate_terminal (LdPoint *terminal, gint symbol_rotation)
-{
- gdouble temp;
-
- switch (symbol_rotation)
- {
- case LD_DIAGRAM_SYMBOL_ROTATION_90:
- temp = terminal->y;
- terminal->y = terminal->x;
- terminal->x = -temp;
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_180:
- terminal->y = -terminal->y;
- terminal->x = -terminal->x;
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_270:
- temp = terminal->x;
- terminal->x = terminal->y;
- terminal->y = -temp;
- break;
- }
-}
-
-static void
-hide_terminals (LdCanvas *self)
-{
- if (self->priv->terminal_hovered)
- {
- self->priv->terminal_hovered = FALSE;
- queue_terminal_draw (self, &self->priv->terminal);
- }
-}
-
-static void
-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;
- gint rotation;
-
- g_object_get (symbol, "x", &object_x, "y", &object_y,
- "rotation", &rotation, NULL);
-
- library_symbol = resolve_symbol (self, symbol);
- if (library_symbol)
- ld_symbol_get_area (library_symbol, &area);
- else
- return FALSE;
-
- rotate_symbol_area (&area, rotation);
-
- 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
-rotate_symbol_area (LdRectangle *area, gint rotation)
-{
- gdouble temp;
-
- switch (rotation)
- {
- case LD_DIAGRAM_SYMBOL_ROTATION_90:
- temp = area->y;
- area->y = area->x;
- area->x = -(temp + area->height);
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_180:
- area->y = -(area->y + area->height);
- area->x = -(area->x + area->width);
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_270:
- temp = area->x;
- area->x = area->y;
- area->y = -(temp + area->width);
- break;
- }
-
- switch (rotation)
- {
- case LD_DIAGRAM_SYMBOL_ROTATION_90:
- case LD_DIAGRAM_SYMBOL_ROTATION_270:
- temp = area->width;
- area->width = area->height;
- area->height = temp;
- break;
- }
-}
-
-static void
-rotate_symbol (LdCanvas *self, LdDiagramSymbol *symbol)
-{
- gint rotation;
-
- g_object_get (symbol, "rotation", &rotation, NULL);
- queue_object_draw (self, LD_DIAGRAM_OBJECT (symbol));
-
- switch (rotation)
- {
- case LD_DIAGRAM_SYMBOL_ROTATION_0:
- rotation = LD_DIAGRAM_SYMBOL_ROTATION_90;
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_90:
- rotation = LD_DIAGRAM_SYMBOL_ROTATION_180;
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_180:
- rotation = LD_DIAGRAM_SYMBOL_ROTATION_270;
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_270:
- rotation = LD_DIAGRAM_SYMBOL_ROTATION_0;
- break;
- }
-
- g_object_set (symbol, "rotation", rotation, NULL);
- queue_object_draw (self, LD_DIAGRAM_OBJECT (symbol));
-}
-
-static LdSymbol *
-resolve_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;
-}
-
-
-/* ===== 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)
- {
- ld_point_array_free (points);
- return FALSE;
- }
-
- 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 gboolean
-get_connection_clip_area (LdCanvas *self,
- LdDiagramConnection *connection, LdRectangle *rect)
-{
- return get_connection_area (self, connection, 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;
-}
-
-
-/* ===== 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));
-
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
-
- self->priv->operation = OPER_ADD_OBJECT;
- self->priv->operation_end = oper_add_object_end;
-
- data = &OPER_DATA (self, add_object);
- data->object = object;
-}
-
-static void
-oper_add_object_end (LdCanvas *self)
-{
- AddObjectData *data;
-
- data = &OPER_DATA (self, add_object);
- if (data->object)
- {
- queue_object_draw (self, data->object);
- g_object_unref (data->object);
- data->object = NULL;
- }
-}
-
-static void
-oper_connect_begin (LdCanvas *self, const LdPoint *point)
-{
- ConnectData *data;
-
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
-
- 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;
-
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
-
- self->priv->operation = OPER_SELECT;
- self->priv->operation_end = oper_select_end;
-
- data = &OPER_DATA (self, select);
- 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, point);
-}
-
-static void
-oper_select_end (LdCanvas *self)
-{
- oper_select_queue_draw (self);
-}
-
-static void
-oper_select_get_rectangle (LdCanvas *self, LdRectangle *rect)
-{
- SelectData *data;
-
- data = &OPER_DATA (self, select);
- rect->x = MIN (self->priv->drag_start_pos.x, data->drag_last_pos.x);
- rect->y = MIN (self->priv->drag_start_pos.y, data->drag_last_pos.y);
- rect->width = ABS (self->priv->drag_start_pos.x - data->drag_last_pos.x);
- rect->height = ABS (self->priv->drag_start_pos.y - data->drag_last_pos.y);
-}
-
-static void
-oper_select_queue_draw (LdCanvas *self)
-{
- LdRectangle rect;
- SelectData *data;
-
- data = &OPER_DATA (self, select);
- oper_select_get_rectangle (self, &rect);
- queue_draw (self, &rect);
-}
-
-static void
-oper_select_draw (GtkWidget *widget, DrawData *data)
-{
- static const double dashes[] = {3, 5};
- SelectData *select_data;
-
- g_return_if_fail (data->self->priv->operation == OPER_SELECT);
-
- ld_canvas_color_apply (COLOR_GET (data->self, COLOR_GRID), data->cr);
- cairo_set_line_width (data->cr, 1);
- cairo_set_line_cap (data->cr, CAIRO_LINE_CAP_SQUARE);
- cairo_set_dash (data->cr, dashes, G_N_ELEMENTS (dashes), 0);
-
- select_data = &OPER_DATA (data->self, select);
-
- cairo_rectangle (data->cr,
- data->self->priv->drag_start_pos.x - 0.5,
- data->self->priv->drag_start_pos.y - 0.5,
- select_data->drag_last_pos.x - data->self->priv->drag_start_pos.x + 1,
- select_data->drag_last_pos.y - data->self->priv->drag_start_pos.y + 1);
- cairo_stroke (data->cr);
-}
-
-static void
-oper_select_motion (LdCanvas *self, const LdPoint *point)
-{
- SelectData *data;
- GList *objects, *iter;
- LdRectangle selection_rect, rect;
-
- data = &OPER_DATA (self, select);
-
- oper_select_queue_draw (self);
- data->drag_last_pos = *point;
- oper_select_queue_draw (self);
-
- oper_select_get_rectangle (self, &selection_rect);
- objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
-
- for (iter = objects; iter; iter = g_list_next (iter))
- {
- LdDiagramObject *object;
-
- object = LD_DIAGRAM_OBJECT (iter->data);
- 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 (&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);
- }
-}
-
-static void
-oper_move_selection_begin (LdCanvas *self, const LdPoint *point)
-{
- MoveSelectionData *data;
-
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
-
- self->priv->operation = OPER_MOVE_SELECTION;
- self->priv->operation_end = oper_move_selection_end;
-
- ld_diagram_begin_user_action (self->priv->diagram);
-
- data = &OPER_DATA (self, move_selection);
- data->move_origin = self->priv->drag_start_pos;
-
- oper_move_selection_motion (self, point);
-}
-
-static void
-oper_move_selection_end (LdCanvas *self)
-{
- ld_diagram_end_user_action (self->priv->diagram);
-}
-
-static void
-oper_move_selection_motion (LdCanvas *self, const LdPoint *point)
-{
- MoveSelectionData *data;
- gdouble scale, dx, dy, move_x, move_y;
- gdouble move = FALSE;
-
- scale = ld_canvas_get_scale_in_px (self);
- data = &OPER_DATA (self, move_selection);
-
- 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);
-
- if (ABS (move_x) >= 1)
- {
- data->move_origin.x += move_x * scale;
- move = TRUE;
- }
- if (ABS (move_y) >= 1)
- {
- data->move_origin.y += move_y * scale;
- move = TRUE;
- }
-
- if (move)
- move_selection (self, move_x, move_y);
-}
-
-
-/* ===== Events, rendering ================================================= */
-
-static void
-simulate_motion (LdCanvas *self)
-{
- GdkEventMotion event;
- GtkWidget *widget;
- gint x, y;
- GdkModifierType state;
-
- widget = GTK_WIDGET (self);
-
- if (gdk_window_get_pointer (widget->window, &x, &y, &state)
- != widget->window)
- return;
-
- memset (&event, 0, sizeof (event));
- event.type = GDK_MOTION_NOTIFY;
- event.window = widget->window;
- event.x = x;
- event.y = y;
- event.state = state;
-
- on_motion_notify (widget, &event, NULL);
-}
-
-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)
- {
- case OPER_ADD_OBJECT:
- add_data = &OPER_DATA (self, add_object);
- add_data->visible = TRUE;
-
- queue_object_draw (self, add_data->object);
- 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, &point);
- break;
- case OPER_MOVE_SELECTION:
- oper_move_selection_motion (self, &point);
- break;
- case OPER_0:
- if (event->state & GDK_BUTTON1_MASK
- && (event->x != self->priv->drag_start_pos.x
- || event->y != self->priv->drag_start_pos.y))
- {
- switch (self->priv->drag_operation)
- {
- case OPER_CONNECT:
- oper_connect_begin (self, &point);
- break;
- case OPER_SELECT:
- oper_select_begin (self, &point);
- break;
- case OPER_MOVE_SELECTION:
- oper_move_selection_begin (self, &point);
- break;
- }
- }
- check_terminals (self, &point);
- 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_draw (self, data->object);
- break;
- }
- return FALSE;
-}
-
-static gboolean
-on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
-{
- LdPoint point;
- LdCanvas *self;
- AddObjectData *data;
- LdDiagramObject *object;
-
- point.x = event->x;
- point.y = event->y;
-
- self = LD_CANVAS (widget);
- if (!self->priv->diagram)
- return FALSE;
-
- if (event->button == 3 && self->priv->operation == OPER_0)
- {
- object = get_object_at_point (self, &point);
- if (object && LD_IS_DIAGRAM_SYMBOL (object))
- rotate_symbol (self, LD_DIAGRAM_SYMBOL (object));
- return FALSE;
- }
-
- if (event->button != 1)
- return FALSE;
- if (!gtk_widget_has_focus (widget))
- gtk_widget_grab_focus (widget);
-
- self->priv->drag_operation = OPER_0;
- switch (self->priv->operation)
- {
- case OPER_ADD_OBJECT:
- data = &OPER_DATA (self, add_object);
-
- queue_object_draw (self, data->object);
- move_object_to_point (self, data->object, &point);
- ld_diagram_insert_object (self->priv->diagram, data->object, -1);
-
- /* XXX: "cancel" causes confusion. */
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
- break;
- case OPER_0:
- self->priv->drag_start_pos = point;
-
- 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);
- self->priv->drag_operation = OPER_SELECT;
- }
- else if (!is_object_selected (self, object))
- {
- if (event->state != GDK_SHIFT_MASK)
- ld_diagram_unselect_all (self->priv->diagram);
- ld_diagram_select (self->priv->diagram, object);
- self->priv->drag_operation = OPER_MOVE_SELECTION;
- }
- else
- self->priv->drag_operation = OPER_MOVE_SELECTION;
- break;
- }
- return FALSE;
-}
-
-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;
-
- switch (self->priv->operation)
- {
- case OPER_SELECT:
- case OPER_MOVE_SELECTION:
- case OPER_CONNECT:
- g_signal_emit (self,
- LD_CANVAS_GET_CLASS (self)->cancel_operation_signal, 0);
- break;
- case OPER_0:
- object = get_object_at_point (self, &point);
- if (object && is_object_selected (self, object))
- {
- if (!(event->state & GDK_SHIFT_MASK))
- ld_diagram_unselect_all (self->priv->diagram);
- ld_diagram_select (self->priv->diagram, object);
- }
- break;
- }
- return FALSE;
-}
-
-static gboolean
-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,
- event->x, event->y, &prev_x, &prev_y);
-
- switch (event->direction)
- {
- case GDK_SCROLL_UP:
- ld_canvas_zoom_in (self);
- break;
- case GDK_SCROLL_DOWN:
- ld_canvas_zoom_out (self);
- break;
- default:
- return FALSE;
- }
-
- ld_canvas_widget_to_diagram_coords (self,
- event->x, event->y, &new_x, &new_y);
-
- /* Focus on the point under the cursor. */
- self->priv->x += prev_x - new_x;
- self->priv->y += prev_y - new_y;
-
- check_terminals (self, &point);
- return TRUE;
-}
-
-static gboolean
-on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
-{
- DrawData data;
-
- data.cr = gdk_cairo_create (widget->window);
- data.self = LD_CANVAS (widget);
- data.scale = ld_canvas_get_scale_in_px (data.self);
- data.exposed_rect.x = event->area.x;
- data.exposed_rect.y = event->area.y;
- data.exposed_rect.width = event->area.width;
- data.exposed_rect.height = event->area.height;
-
- gdk_cairo_rectangle (data.cr, &event->area);
- cairo_clip (data.cr);
-
- ld_canvas_color_apply (COLOR_GET (data.self, COLOR_BASE), data.cr);
- cairo_paint (data.cr);
-
- draw_grid (widget, &data);
- draw_diagram (widget, &data);
- draw_terminal (widget, &data);
-
- if (data.self->priv->operation == OPER_SELECT)
- oper_select_draw (widget, &data);
-
- cairo_destroy (data.cr);
- return FALSE;
-}
-
-static void
-draw_grid (GtkWidget *widget, DrawData *data)
-{
- gdouble grid_step;
- gint grid_factor;
- gdouble x_init, y_init;
- gdouble x, y;
- cairo_surface_t *grid_surface;
- gint stride;
- unsigned char *pixels;
- guint32 color;
-
- grid_step = data->scale;
- grid_factor = 1;
- while (grid_step < 5)
- {
- grid_step *= 5;
- grid_factor *= 5;
- }
-
- /* Paint manually on our own raster surface for speed. */
- stride = cairo_format_stride_for_width
- (CAIRO_FORMAT_ARGB32, data->exposed_rect.width);
- pixels = g_malloc0 (stride * data->exposed_rect.height);
- grid_surface = cairo_image_surface_create_for_data
- (pixels, CAIRO_FORMAT_ARGB32,
- data->exposed_rect.width, data->exposed_rect.height, stride);
-
- /* Get coordinates of the top-left point. */
- ld_canvas_widget_to_diagram_coords (data->self,
- data->exposed_rect.x, data->exposed_rect.y, &x_init, &y_init);
-
- x_init = ceil (x_init);
- x_init = x_init - (gint) x_init % grid_factor;
- y_init = ceil (y_init);
- y_init = y_init - (gint) y_init % grid_factor;
-
- ld_canvas_diagram_to_widget_coords (data->self,
- x_init, y_init, &x_init, &y_init);
-
- x_init -= data->exposed_rect.x;
- y_init -= data->exposed_rect.y;
-
- while (x_init < 0)
- x_init += grid_step;
- while (y_init < 0)
- y_init += grid_step;
-
- color = ld_canvas_color_to_cairo_argb (COLOR_GET (data->self, COLOR_GRID));
-
- for (x = x_init; x < data->exposed_rect.width; x += grid_step)
- for (y = y_init; y < data->exposed_rect.height; y += grid_step)
- *((guint32 *) (pixels + stride * (gint) y) + (gint) x) = color;
-
- cairo_set_source_surface (data->cr, grid_surface,
- data->exposed_rect.x, data->exposed_rect.y);
- cairo_paint (data->cr);
-
- cairo_surface_destroy (grid_surface);
- g_free (pixels);
-}
-
-static void
-draw_terminal (GtkWidget *widget, DrawData *data)
-{
- LdCanvasPrivate *priv;
- LdPoint widget_coords;
-
- priv = data->self->priv;
- 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);
- 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);
-}
-
-static void
-draw_diagram (GtkWidget *widget, DrawData *data)
-{
- GList *objects, *iter;
-
- if (!data->self->priv->diagram)
- return;
-
- cairo_save (data->cr);
- cairo_set_line_width (data->cr, 1 / data->scale);
-
- /* Draw objects from the diagram, from bottom to top. */
- objects = (GList *) ld_diagram_get_objects (data->self->priv->diagram);
- for (iter = objects; iter; iter = g_list_next (iter))
- draw_object (LD_DIAGRAM_OBJECT (iter->data), data);
-
- switch (data->self->priv->operation)
- {
- AddObjectData *add_data;
- ConnectData *connect_data;
-
- case OPER_ADD_OBJECT:
- 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;
- }
-
- cairo_restore (data->cr);
-}
-
-static void
-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_selected (data->self, diagram_object))
- ld_canvas_color_apply (COLOR_GET (data->self,
- COLOR_SELECTION), data->cr);
- else
- ld_canvas_color_apply (COLOR_GET (data->self,
- COLOR_OBJECT), data->cr);
-
- 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
-draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
-{
- LdSymbol *symbol;
- LdRectangle clip_rect;
- gdouble x, y;
- gint rotation;
-
- symbol = resolve_symbol (data->self, diagram_symbol);
-
- /* TODO: Resolve this better; draw a cross or whatever. */
- if (!symbol)
- {
- gchar *klass;
-
- klass = ld_diagram_symbol_get_class (diagram_symbol);
- g_warning ("cannot find symbol `%s' in the library", klass);
- g_free (klass);
- return;
- }
-
- if (!get_symbol_clip_area (data->self, diagram_symbol, &clip_rect)
- || !ld_rectangle_intersects (&clip_rect, &data->exposed_rect))
- return;
-
- cairo_save (data->cr);
-
- cairo_rectangle (data->cr, clip_rect.x, clip_rect.y,
- clip_rect.width, clip_rect.height);
- cairo_clip (data->cr);
-
- g_object_get (diagram_symbol, "x", &x, "y", &y,
- "rotation", &rotation, 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);
-
- switch (rotation)
- {
- case LD_DIAGRAM_SYMBOL_ROTATION_90:
- cairo_rotate (data->cr, G_PI * 0.5);
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_180:
- cairo_rotate (data->cr, G_PI);
- break;
- case LD_DIAGRAM_SYMBOL_ROTATION_270:
- cairo_rotate (data->cr, G_PI * 1.5);
- break;
- }
-
- 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-canvas.h b/liblogdiag/ld-canvas.h
deleted file mode 100644
index 3091a34..0000000
--- a/liblogdiag/ld-canvas.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * ld-canvas.h
- *
- * This file is a part of logdiag.
- * Copyright Přemysl Janouch 2010 - 2011. All rights reserved.
- *
- * See the file LICENSE for licensing information.
- *
- */
-
-#ifndef __LD_CANVAS_H__
-#define __LD_CANVAS_H__
-
-G_BEGIN_DECLS
-
-
-#define LD_TYPE_CANVAS (ld_canvas_get_type ())
-#define LD_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST \
- ((obj), LD_TYPE_CANVAS, LdCanvas))
-#define LD_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \
- ((klass), LD_TYPE_CANVAS, LdCanvasClass))
-#define LD_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE \
- ((obj), LD_TYPE_CANVAS))
-#define LD_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \
- ((klass), LD_TYPE_CANVAS))
-#define LD_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \
- ((obj), LD_CANVAS, LdCanvasClass))
-
-typedef struct _LdCanvas LdCanvas;
-typedef struct _LdCanvasPrivate LdCanvasPrivate;
-typedef struct _LdCanvasClass LdCanvasClass;
-
-
-/**
- * LdCanvas:
- */
-struct _LdCanvas
-{
-/*< private >*/
- GtkDrawingArea parent_instance;
- LdCanvasPrivate *priv;
-};
-
-struct _LdCanvasClass
-{
-/*< private >*/
- GtkDrawingAreaClass parent_class;
-
- guint cancel_operation_signal;
- guint move_signal;
-
- void (*set_scroll_adjustments) (LdCanvas *self,
- GtkAdjustment *horizontal, GtkAdjustment *vertical);
- void (*cancel_operation) (LdCanvas *self);
- void (*move) (LdCanvas *self, gdouble dx, gdouble dy);
-};
-
-
-/**
- * LD_CANVAS_BASE_UNIT_LENGTH:
- *
- * Length of the base unit in milimetres.
- */
-#define LD_CANVAS_BASE_UNIT_LENGTH 2.5
-
-
-GType ld_canvas_get_type (void) G_GNUC_CONST;
-
-GtkWidget *ld_canvas_new (void);
-
-void ld_canvas_set_diagram (LdCanvas *self, LdDiagram *diagram);
-LdDiagram *ld_canvas_get_diagram (LdCanvas *self);
-void ld_canvas_set_library (LdCanvas *self, LdLibrary *library);
-LdLibrary *ld_canvas_get_library (LdCanvas *self);
-
-void ld_canvas_widget_to_diagram_coords (LdCanvas *self,
- gdouble wx, gdouble wy, gdouble *dx, gdouble *dy);
-void ld_canvas_diagram_to_widget_coords (LdCanvas *self,
- gdouble dx, gdouble dy, gdouble *wx, gdouble *wy);
-
-gdouble ld_canvas_get_zoom (LdCanvas *self);
-void ld_canvas_set_zoom (LdCanvas *self, gdouble zoom);
-gboolean ld_canvas_can_zoom_in (LdCanvas *self);
-void ld_canvas_zoom_in (LdCanvas *self);
-gboolean ld_canvas_can_zoom_out (LdCanvas *self);
-void ld_canvas_zoom_out (LdCanvas *self);
-
-void ld_canvas_add_object_begin (LdCanvas *self, LdDiagramObject *object);
-
-
-G_END_DECLS
-
-#endif /* ! __LD_CANVAS_H__ */
diff --git a/liblogdiag/ld-diagram-object.c b/liblogdiag/ld-diagram-object.c
index e39f89c..b0ae76b 100644
--- a/liblogdiag/ld-diagram-object.c
+++ b/liblogdiag/ld-diagram-object.c
@@ -17,7 +17,7 @@
/**
* SECTION:ld-diagram-object
* @short_description: A diagram object
- * @see_also: #LdDiagram, #LdCanvas
+ * @see_also: #LdDiagram, #LdDiagramView
*
* #LdDiagramObject represents an object in an #LdDiagram.
*/
diff --git a/liblogdiag/ld-diagram-view.c b/liblogdiag/ld-diagram-view.c
new file mode 100644
index 0000000..5991824
--- /dev/null
+++ b/liblogdiag/ld-diagram-view.c
@@ -0,0 +1,2346 @@
+/*
+ * ld-diagram-view.c
+ *
+ * This file is a part of logdiag.
+ * Copyright Přemysl Janouch 2010 - 2011. All rights reserved.
+ *
+ * See the file LICENSE for licensing information.
+ *
+ */
+
+#include
+#include
+#include
+
+#include "liblogdiag.h"
+#include "config.h"
+
+
+/**
+ * SECTION:ld-diagram-view
+ * @short_description: A widget that displays an LdDiagram
+ * @see_also: #LdDiagram
+ *
+ * #LdDiagramView displays and enables the user to manipulate with
+ * an #LdDiagram.
+ */
+
+/* Milimetres per inch. */
+#define MM_PER_INCH 25.4
+/* The default screen resolution in DPI units. */
+#define DEFAULT_SCREEN_RESOLUTION 96
+
+/* The maximal, minimal and default values of zoom. */
+#define ZOOM_MIN 0.01
+#define ZOOM_MAX 100
+#define ZOOM_DEFAULT 1
+/* Multiplication factor for zooming. */
+#define ZOOM_STEP 1.4
+
+/* When drawing is requested, extend all sides of
+ * the rectangle to be drawn by this number of pixels.
+ */
+#define QUEUE_DRAW_EXTEND 3
+/* Cursor tolerance for object borders. */
+#define OBJECT_BORDER_TOLERANCE 3
+/* Tolerance on all sides of symbols for strokes. */
+#define SYMBOL_CLIP_TOLERANCE 5
+
+/* Size of a highlighted terminal. */
+#define TERMINAL_RADIUS 5
+/* Tolerance around terminal points. */
+#define TERMINAL_HOVER_TOLERANCE 8
+
+/*
+ * OperationEnd:
+ *
+ * Called upon ending an operation.
+ */
+typedef void (*OperationEnd) (LdDiagramView *self);
+
+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;
+
+struct _AddObjectData
+{
+ LdDiagramObject *object;
+ gboolean visible;
+};
+
+struct _ConnectData
+{
+ LdDiagramConnection *connection;
+ LdPoint origin;
+};
+
+struct _SelectData
+{
+ LdPoint drag_last_pos;
+};
+
+struct _MoveSelectionData
+{
+ LdPoint move_origin;
+};
+
+enum
+{
+ COLOR_BASE,
+ COLOR_GRID,
+ COLOR_OBJECT,
+ COLOR_SELECTION,
+ COLOR_TERMINAL,
+ COLOR_COUNT
+};
+
+typedef struct _Color Color;
+
+struct _Color
+{
+ gdouble r;
+ gdouble g;
+ gdouble b;
+ gdouble a;
+};
+
+/*
+ * LdDiagramViewPrivate:
+ * @diagram: a diagram object assigned as a model.
+ * @library: a library object assigned as a model.
+ * @adjustment_h: an adjustment object for the horizontal axis, if any.
+ * @adjustment_v: an adjustment object for the vertical axis, if any.
+ * @x: the X coordinate of the center of view.
+ * @y: the Y coordinate of the center of view.
+ * @zoom: the current zoom.
+ * @terminal: position of the highlighted terminal.
+ * @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.
+ * @operation_data: data related to the current operation.
+ * @operation_end: a callback to end the operation.
+ * @palette: colors used by the widget.
+ */
+struct _LdDiagramViewPrivate
+{
+ LdDiagram *diagram;
+ LdLibrary *library;
+
+ GtkAdjustment *adjustment_h;
+ GtkAdjustment *adjustment_v;
+
+ gdouble x;
+ gdouble y;
+ gdouble zoom;
+
+ LdPoint terminal;
+ gboolean terminal_hovered;
+
+ LdPoint drag_start_pos;
+ gint drag_operation;
+
+ gint operation;
+ union
+ {
+ AddObjectData add_object;
+ ConnectData connect;
+ SelectData select;
+ MoveSelectionData move_selection;
+ }
+ operation_data;
+ OperationEnd operation_end;
+
+ Color palette[COLOR_COUNT];
+};
+
+#define OPER_DATA(self, member) ((self)->priv->operation_data.member)
+#define COLOR_GET(self, name) (&(self)->priv->palette[name])
+
+/*
+ * DrawData:
+ * @self: our #LdDiagramView.
+ * @cr: a cairo context to draw on.
+ * @exposed_rect: the area that is to be redrawn.
+ * @scale: computed size of one diagram unit in pixels.
+ */
+typedef struct _DrawData DrawData;
+
+struct _DrawData
+{
+ LdDiagramView *self;
+ cairo_t *cr;
+ LdRectangle exposed_rect;
+ gdouble scale;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DIAGRAM,
+ PROP_LIBRARY,
+ PROP_ZOOM
+};
+
+static void ld_diagram_view_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec);
+static void ld_diagram_view_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec);
+static void ld_diagram_view_finalize (GObject *gobject);
+
+static void ld_diagram_view_real_set_scroll_adjustments
+ (LdDiagramView *self, GtkAdjustment *horizontal, GtkAdjustment *vertical);
+static void on_adjustment_value_changed
+ (GtkAdjustment *adjustment, LdDiagramView *self);
+static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
+ gpointer user_data);
+static void update_adjustments (LdDiagramView *self);
+static void ld_diagram_view_real_move (LdDiagramView *self,
+ gdouble dx, gdouble dy);
+
+static void diagram_connect_signals (LdDiagramView *self);
+static void diagram_disconnect_signals (LdDiagramView *self);
+
+static gdouble ld_diagram_view_get_base_unit_in_px (GtkWidget *self);
+static gdouble ld_diagram_view_get_scale_in_px (LdDiagramView *self);
+
+/* Helper functions. */
+static void color_set (Color *color,
+ gdouble r, gdouble g, gdouble b, gdouble a);
+static void color_apply (Color *color, cairo_t *cr);
+static guint32 color_to_cairo_argb (Color *color);
+
+static gdouble point_to_line_segment_distance
+ (const LdPoint *point, const LdPoint *p1, const LdPoint *p2);
+
+/* Generic functions. */
+static gboolean object_hit_test (LdDiagramView *self,
+ LdDiagramObject *object, const LdPoint *point);
+static gboolean get_object_clip_area (LdDiagramView *self,
+ LdDiagramObject *object, LdRectangle *rect);
+
+static void move_object_to_point (LdDiagramView *self, LdDiagramObject *object,
+ const LdPoint *point);
+static LdDiagramObject *get_object_at_point (LdDiagramView *self,
+ const LdPoint *point);
+
+static void move_selection (LdDiagramView *self, gdouble dx, gdouble dy);
+static gboolean is_object_selected (LdDiagramView *self,
+ LdDiagramObject *object);
+
+static void queue_draw (LdDiagramView *self, LdRectangle *rect);
+static void queue_object_draw (LdDiagramView *self, LdDiagramObject *object);
+
+/* Symbol terminals. */
+static void check_terminals (LdDiagramView *self, const LdPoint *point);
+static void rotate_terminal (LdPoint *terminal, gint symbol_rotation);
+static void hide_terminals (LdDiagramView *self);
+static void queue_terminal_draw (LdDiagramView *self, LdPoint *terminal);
+
+/* Diagram symbol. */
+static gboolean symbol_hit_test (LdDiagramView *self,
+ LdDiagramSymbol *symbol, const LdPoint *point);
+static gboolean get_symbol_clip_area (LdDiagramView *self,
+ LdDiagramSymbol *symbol, LdRectangle *rect);
+
+static gboolean get_symbol_area (LdDiagramView *self,
+ LdDiagramSymbol *symbol, LdRectangle *rect);
+static void rotate_symbol_area (LdRectangle *area, gint rotation);
+static void rotate_symbol (LdDiagramView *self, LdDiagramSymbol *symbol);
+static LdSymbol *resolve_symbol (LdDiagramView *self,
+ LdDiagramSymbol *diagram_symbol);
+
+/* Diagram connection. */
+static gboolean connection_hit_test (LdDiagramView *self,
+ LdDiagramConnection *connection, const LdPoint *point);
+static gboolean get_connection_clip_area (LdDiagramView *self,
+ LdDiagramConnection *connection, LdRectangle *rect);
+
+static gboolean get_connection_area (LdDiagramView *self,
+ LdDiagramConnection *connection, LdRectangle *rect);
+
+/* Operations. */
+static void ld_diagram_view_real_cancel_operation (LdDiagramView *self);
+static void oper_add_object_end (LdDiagramView *self);
+
+static void oper_connect_begin (LdDiagramView *self, const LdPoint *point);
+static void oper_connect_end (LdDiagramView *self);
+static void oper_connect_motion (LdDiagramView *self, const LdPoint *point);
+
+static void oper_select_begin (LdDiagramView *self, const LdPoint *point);
+static void oper_select_end (LdDiagramView *self);
+static void oper_select_get_rectangle (LdDiagramView *self, LdRectangle *rect);
+static void oper_select_queue_draw (LdDiagramView *self);
+static void oper_select_draw (GtkWidget *widget, DrawData *data);
+static void oper_select_motion (LdDiagramView *self, const LdPoint *point);
+
+static void oper_move_selection_begin (LdDiagramView *self,
+ const LdPoint *point);
+static void oper_move_selection_end (LdDiagramView *self);
+static void oper_move_selection_motion (LdDiagramView *self,
+ const LdPoint *point);
+
+/* Events, rendering. */
+static void simulate_motion (LdDiagramView *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 gboolean on_scroll (GtkWidget *widget, GdkEventScroll *event,
+ gpointer user_data);
+
+static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event,
+ gpointer user_data);
+static void draw_grid (GtkWidget *widget, DrawData *data);
+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 (LdDiagramView, ld_diagram_view, GTK_TYPE_DRAWING_AREA);
+
+static void
+ld_diagram_view_class_init (LdDiagramViewClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
+ GParamSpec *pspec;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->get_property = ld_diagram_view_get_property;
+ object_class->set_property = ld_diagram_view_set_property;
+ object_class->finalize = ld_diagram_view_finalize;
+
+ klass->set_scroll_adjustments = ld_diagram_view_real_set_scroll_adjustments;
+ klass->cancel_operation = ld_diagram_view_real_cancel_operation;
+ klass->move = ld_diagram_view_real_move;
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+ "cancel-operation", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
+ "move", 2, G_TYPE_DOUBLE, (gdouble) -1, G_TYPE_DOUBLE, (gdouble) 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
+ "move", 2, G_TYPE_DOUBLE, (gdouble) 1, G_TYPE_DOUBLE, (gdouble) 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+ "move", 2, G_TYPE_DOUBLE, (gdouble) 0, G_TYPE_DOUBLE, (gdouble) -1);
+ gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+ "move", 2, G_TYPE_DOUBLE, (gdouble) 0, G_TYPE_DOUBLE, (gdouble) 1);
+
+/**
+ * LdDiagramView:diagram:
+ *
+ * The underlying #LdDiagram object of this view.
+ */
+ pspec = g_param_spec_object ("diagram", "Diagram",
+ "The underlying diagram object of this view.",
+ LD_TYPE_DIAGRAM, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_DIAGRAM, pspec);
+
+/**
+ * LdDiagramView:library:
+ *
+ * The #LdLibrary from which symbols are retrieved.
+ */
+ pspec = g_param_spec_object ("library", "Library",
+ "The library from which symbols are retrieved.",
+ LD_TYPE_LIBRARY, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_LIBRARY, pspec);
+
+/**
+ * LdDiagramView:zoom:
+ *
+ * The zoom of this view.
+ */
+ pspec = g_param_spec_double ("zoom", "Zoom",
+ "The zoom of this view.",
+ ZOOM_MIN, ZOOM_MAX, ZOOM_DEFAULT, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_ZOOM, pspec);
+
+/**
+ * LdDiagramView::set-scroll-adjustments:
+ * @self: an #LdDiagramView object.
+ * @horizontal: the horizontal #GtkAdjustment.
+ * @vertical: the vertical #GtkAdjustment.
+ *
+ * Set scroll adjustments for the widget.
+ */
+ widget_class->set_scroll_adjustments_signal = g_signal_new
+ ("set-scroll-adjustments", G_TYPE_FROM_CLASS (widget_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (LdDiagramViewClass, set_scroll_adjustments),
+ NULL, NULL,
+ ld_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
+
+/**
+ * LdDiagramView::cancel-operation:
+ * @self: an #LdDiagramView object.
+ *
+ * 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 (LdDiagramViewClass, cancel_operation), NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+/**
+ * LdDiagramView::move:
+ * @self: an #LdDiagramView object.
+ * @dx: The difference by which to move on the horizontal axis.
+ * @dy: The difference by which to move on the vertical axis.
+ *
+ * Move the selection, if any, or the document.
+ */
+ klass->move_signal = g_signal_new
+ ("move", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (LdDiagramViewClass, move), NULL, NULL,
+ ld_marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ g_type_class_add_private (klass, sizeof (LdDiagramViewPrivate));
+}
+
+static void
+ld_diagram_view_init (LdDiagramView *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE
+ (self, LD_TYPE_DIAGRAM_VIEW, LdDiagramViewPrivate);
+
+ self->priv->x = 0;
+ self->priv->y = 0;
+ self->priv->zoom = ZOOM_DEFAULT;
+
+ color_set (COLOR_GET (self, COLOR_BASE), 1, 1, 1, 1);
+ color_set (COLOR_GET (self, COLOR_GRID), 0.5, 0.5, 0.5, 1);
+ color_set (COLOR_GET (self, COLOR_OBJECT), 0, 0, 0, 1);
+ color_set (COLOR_GET (self, COLOR_SELECTION), 1, 0, 0, 1);
+ color_set (COLOR_GET (self, COLOR_TERMINAL), 1, 0.5, 0.5, 1);
+
+ g_signal_connect (self, "size-allocate",
+ G_CALLBACK (on_size_allocate), NULL);
+ g_signal_connect (self, "expose-event",
+ G_CALLBACK (on_expose_event), 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_signal_connect (self, "scroll-event",
+ G_CALLBACK (on_scroll), 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_LEAVE_NOTIFY_MASK);
+}
+
+static void
+ld_diagram_view_finalize (GObject *gobject)
+{
+ LdDiagramView *self;
+
+ self = LD_DIAGRAM_VIEW (gobject);
+
+ ld_diagram_view_real_set_scroll_adjustments (self, NULL, NULL);
+
+ if (self->priv->diagram)
+ {
+ diagram_disconnect_signals (self);
+ g_object_unref (self->priv->diagram);
+ }
+ if (self->priv->library)
+ g_object_unref (self->priv->library);
+
+ /* Chain up to the parent class. */
+ G_OBJECT_CLASS (ld_diagram_view_parent_class)->finalize (gobject);
+}
+
+static void
+ld_diagram_view_get_property (GObject *object, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ LdDiagramView *self;
+
+ self = LD_DIAGRAM_VIEW (object);
+ switch (property_id)
+ {
+ case PROP_DIAGRAM:
+ g_value_set_object (value, ld_diagram_view_get_diagram (self));
+ break;
+ case PROP_LIBRARY:
+ g_value_set_object (value, ld_diagram_view_get_library (self));
+ break;
+ case PROP_ZOOM:
+ g_value_set_double (value, ld_diagram_view_get_zoom (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ld_diagram_view_set_property (GObject *object, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ LdDiagramView *self;
+
+ self = LD_DIAGRAM_VIEW (object);
+ switch (property_id)
+ {
+ case PROP_DIAGRAM:
+ ld_diagram_view_set_diagram (self,
+ LD_DIAGRAM (g_value_get_object (value)));
+ break;
+ case PROP_LIBRARY:
+ ld_diagram_view_set_library (self,
+ LD_LIBRARY (g_value_get_object (value)));
+ break;
+ case PROP_ZOOM:
+ ld_diagram_view_set_zoom (self, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ld_diagram_view_real_set_scroll_adjustments (LdDiagramView *self,
+ GtkAdjustment *horizontal, GtkAdjustment *vertical)
+{
+ /* TODO: Infinite area. */
+ GtkWidget *widget;
+ gdouble scale;
+
+ widget = GTK_WIDGET (self);
+ scale = ld_diagram_view_get_scale_in_px (self);
+
+ if (horizontal != self->priv->adjustment_h)
+ {
+ if (self->priv->adjustment_h)
+ {
+ g_signal_handlers_disconnect_by_func (self->priv->adjustment_h,
+ on_adjustment_value_changed, self);
+ g_object_unref (self->priv->adjustment_h);
+
+ self->priv->adjustment_h = NULL;
+ }
+ if (horizontal)
+ {
+ g_object_ref (horizontal);
+ g_signal_connect (horizontal, "value-changed",
+ G_CALLBACK (on_adjustment_value_changed), self);
+
+ horizontal->upper = 100;
+ horizontal->lower = -100;
+ horizontal->step_increment = 0.5;
+ horizontal->page_increment = 5;
+ horizontal->page_size = widget->allocation.width / scale;
+ horizontal->value = -horizontal->page_size / 2;
+
+ self->priv->adjustment_h = horizontal;
+ }
+ }
+
+ if (vertical != self->priv->adjustment_v)
+ {
+ if (self->priv->adjustment_v)
+ {
+ g_signal_handlers_disconnect_by_func (self->priv->adjustment_v,
+ on_adjustment_value_changed, self);
+ g_object_unref (self->priv->adjustment_v);
+
+ self->priv->adjustment_v = NULL;
+ }
+ if (vertical)
+ {
+ g_object_ref (vertical);
+ g_signal_connect (vertical, "value-changed",
+ G_CALLBACK (on_adjustment_value_changed), self);
+
+ vertical->upper = 100;
+ vertical->lower = -100;
+ vertical->step_increment = 0.5;
+ vertical->page_increment = 5;
+ vertical->page_size = widget->allocation.height / scale;
+ vertical->value = -vertical->page_size / 2;
+
+ self->priv->adjustment_v = vertical;
+ }
+ }
+}
+
+static void
+on_adjustment_value_changed (GtkAdjustment *adjustment, LdDiagramView *self)
+{
+ GtkWidget *widget;
+ gdouble scale;
+
+ widget = GTK_WIDGET (self);
+ scale = ld_diagram_view_get_scale_in_px (self);
+
+ if (adjustment == self->priv->adjustment_h)
+ {
+ self->priv->x = adjustment->value
+ + widget->allocation.width / scale / 2;
+ gtk_widget_queue_draw (widget);
+ }
+ else if (adjustment == self->priv->adjustment_v)
+ {
+ self->priv->y = adjustment->value
+ + widget->allocation.height / scale / 2;
+ gtk_widget_queue_draw (widget);
+ }
+}
+
+static void
+on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
+ gpointer user_data)
+{
+ LdDiagramView *self;
+
+ self = LD_DIAGRAM_VIEW (widget);
+
+ /* FIXME: If the new allocation is bigger, we may see more than
+ * what we're supposed to be able to see -> adjust X and Y.
+ *
+ * If the visible area is so large that we simply must see more,
+ * let's disable the scrollbars in question.
+ */
+ update_adjustments (self);
+}
+
+static void
+update_adjustments (LdDiagramView *self)
+{
+ gdouble scale;
+
+ scale = ld_diagram_view_get_scale_in_px (self);
+
+ if (self->priv->adjustment_h)
+ {
+ self->priv->adjustment_h->page_size
+ = GTK_WIDGET (self)->allocation.width / scale;
+ self->priv->adjustment_h->value
+ = self->priv->x - self->priv->adjustment_h->page_size / 2;
+ gtk_adjustment_changed (self->priv->adjustment_h);
+ }
+ if (self->priv->adjustment_v)
+ {
+ self->priv->adjustment_v->page_size
+ = GTK_WIDGET (self)->allocation.height / scale;
+ self->priv->adjustment_v->value
+ = self->priv->y - self->priv->adjustment_v->page_size / 2;
+ gtk_adjustment_changed (self->priv->adjustment_v);
+ }
+}
+
+static void
+ld_diagram_view_real_move (LdDiagramView *self, gdouble dx, gdouble dy)
+{
+ LdDiagram *diagram;
+
+ diagram = self->priv->diagram;
+ if (!diagram)
+ return;
+
+ /* TODO: Check/move boundaries, also implement normal
+ * getters and setters for priv->x and priv->y.
+ */
+ if (ld_diagram_get_selection (diagram))
+ move_selection (self, dx, dy);
+ else
+ {
+ self->priv->x += dx;
+ self->priv->y += dy;
+
+ simulate_motion (self);
+ update_adjustments (self);
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+/* ===== Generic interface etc. ============================================ */
+
+/**
+ * ld_diagram_view_new:
+ *
+ * Create an instance.
+ */
+GtkWidget *
+ld_diagram_view_new (void)
+{
+ return g_object_new (LD_TYPE_DIAGRAM_VIEW, NULL);
+}
+
+/**
+ * ld_diagram_view_set_diagram:
+ * @self: an #LdDiagramView object.
+ * @diagram: the #LdDiagram to be assigned to the view.
+ *
+ * Assign an #LdDiagram object to the view.
+ */
+void
+ld_diagram_view_set_diagram (LdDiagramView *self, LdDiagram *diagram)
+{
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (LD_IS_DIAGRAM (diagram));
+
+ if (self->priv->diagram)
+ {
+ diagram_disconnect_signals (self);
+ g_object_unref (self->priv->diagram);
+ }
+
+ self->priv->diagram = diagram;
+ diagram_connect_signals (self);
+ g_object_ref (diagram);
+
+ g_object_notify (G_OBJECT (self), "diagram");
+}
+
+/**
+ * ld_diagram_view_get_diagram:
+ * @self: an #LdDiagramView object.
+ *
+ * Get the #LdDiagram object assigned to this view.
+ * The reference count on the diagram is not incremented.
+ */
+LdDiagram *
+ld_diagram_view_get_diagram (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), NULL);
+ return self->priv->diagram;
+}
+
+static void
+diagram_connect_signals (LdDiagramView *self)
+{
+ g_return_if_fail (LD_IS_DIAGRAM (self->priv->diagram));
+
+ g_signal_connect_swapped (self->priv->diagram, "changed",
+ G_CALLBACK (gtk_widget_queue_draw), self);
+ g_signal_connect_swapped (self->priv->diagram, "selection-changed",
+ G_CALLBACK (gtk_widget_queue_draw), self);
+}
+
+static void
+diagram_disconnect_signals (LdDiagramView *self)
+{
+ g_return_if_fail (LD_IS_DIAGRAM (self->priv->diagram));
+
+ g_signal_handlers_disconnect_matched (self->priv->diagram,
+ G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ gtk_widget_queue_draw, self);
+}
+
+/**
+ * ld_diagram_view_set_library:
+ * @self: an #LdDiagramView object.
+ * @library: the #LdLibrary to be assigned to the view.
+ *
+ * Assign an #LdLibrary object to the view.
+ */
+void
+ld_diagram_view_set_library (LdDiagramView *self, LdLibrary *library)
+{
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (LD_IS_LIBRARY (library));
+
+ if (self->priv->library)
+ g_object_unref (self->priv->library);
+
+ self->priv->library = library;
+ g_object_ref (library);
+
+ g_object_notify (G_OBJECT (self), "library");
+}
+
+/**
+ * ld_diagram_view_get_library:
+ * @self: an #LdDiagramView object.
+ *
+ * Get the #LdLibrary object assigned to this view.
+ * The reference count on the library is not incremented.
+ */
+LdLibrary *
+ld_diagram_view_get_library (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), NULL);
+ return self->priv->library;
+}
+
+/*
+ * ld_diagram_view_get_base_unit_in_px:
+ * @self: a #GtkWidget object to retrieve DPI from (indirectly).
+ *
+ * Return value: length of the base unit in pixels.
+ */
+static gdouble
+ld_diagram_view_get_base_unit_in_px (GtkWidget *self)
+{
+ gdouble resolution;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (self), 1);
+
+ resolution = gdk_screen_get_resolution (gtk_widget_get_screen (self));
+ if (resolution == -1)
+ resolution = DEFAULT_SCREEN_RESOLUTION;
+
+ /* XXX: It might look better if the unit was rounded to a whole number. */
+ return resolution / MM_PER_INCH * LD_DIAGRAM_VIEW_BASE_UNIT_LENGTH;
+}
+
+/*
+ * ld_diagram_view_get_scale_in_px:
+ * @self: an #LdDiagramView object.
+ *
+ * Return value: displayed length of the base unit in pixels.
+ */
+static gdouble
+ld_diagram_view_get_scale_in_px (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), 1);
+
+ return ld_diagram_view_get_base_unit_in_px (GTK_WIDGET (self))
+ * self->priv->zoom;
+}
+
+/**
+ * ld_diagram_view_widget_to_diagram_coords:
+ * @self: an #LdDiagramView object.
+ * @wx: the X coordinate to be translated.
+ * @wy: the Y coordinate to be translated.
+ * @dx: (out): the translated X coordinate.
+ * @dy: (out): the translated Y coordinate.
+ *
+ * Translate widget coordinates into diagram coordinates.
+ */
+void
+ld_diagram_view_widget_to_diagram_coords (LdDiagramView *self,
+ gdouble wx, gdouble wy, gdouble *dx, gdouble *dy)
+{
+ GtkWidget *widget;
+ gdouble scale;
+
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (dx != NULL);
+ g_return_if_fail (dy != NULL);
+
+ widget = GTK_WIDGET (self);
+ scale = ld_diagram_view_get_scale_in_px (self);
+
+ /* We know diagram coordinates of the center of view, so we may
+ * translate the given X and Y coordinates to this center and then scale
+ * them by dividing them by the current scale.
+ */
+ *dx = self->priv->x + (wx - (widget->allocation.width * 0.5)) / scale;
+ *dy = self->priv->y + (wy - (widget->allocation.height * 0.5)) / scale;
+}
+
+/**
+ * ld_diagram_view_diagram_to_widget_coords:
+ * @self: an #LdDiagramView object.
+ * @dx: the X coordinate to be translated.
+ * @dy: the Y coordinate to be translated.
+ * @wx: (out): the translated X coordinate.
+ * @wy: (out): the translated Y coordinate.
+ *
+ * Translate diagram coordinates into widget coordinates.
+ */
+void
+ld_diagram_view_diagram_to_widget_coords (LdDiagramView *self,
+ gdouble dx, gdouble dy, gdouble *wx, gdouble *wy)
+{
+ GtkWidget *widget;
+ gdouble scale;
+
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (wx != NULL);
+ g_return_if_fail (wy != NULL);
+
+ widget = GTK_WIDGET (self);
+ scale = ld_diagram_view_get_scale_in_px (self);
+
+ /* Just the reversal of ld_diagram_view_widget_to_diagram_coords(). */
+ *wx = scale * (dx - self->priv->x) + 0.5 * widget->allocation.width;
+ *wy = scale * (dy - self->priv->y) + 0.5 * widget->allocation.height;
+}
+
+/**
+ * ld_diagram_view_get_zoom:
+ * @self: an #LdDiagramView object.
+ *
+ * Return value: zoom of the view.
+ */
+gdouble
+ld_diagram_view_get_zoom (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), -1);
+ return self->priv->zoom;
+}
+
+/**
+ * ld_diagram_view_set_zoom:
+ * @self: an #LdDiagramView object.
+ * @zoom: the zoom.
+ *
+ * Set zoom of the view.
+ */
+void
+ld_diagram_view_set_zoom (LdDiagramView *self, gdouble zoom)
+{
+ gdouble clamped_zoom;
+
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+
+ clamped_zoom = CLAMP (zoom, ZOOM_MIN, ZOOM_MAX);
+ if (self->priv->zoom == clamped_zoom)
+ return;
+
+ self->priv->zoom = clamped_zoom;
+
+ simulate_motion (self);
+ update_adjustments (self);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ g_object_notify (G_OBJECT (self), "zoom");
+}
+
+/**
+ * ld_diagram_view_can_zoom_in:
+ * @self: an #LdDiagramView object.
+ *
+ * Return value: %TRUE if the view can be zoomed in.
+ */
+gboolean
+ld_diagram_view_can_zoom_in (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), FALSE);
+ return self->priv->zoom < ZOOM_MAX;
+}
+
+/**
+ * ld_diagram_view_can_zoom_out:
+ * @self: an #LdDiagramView object.
+ *
+ * Return value: %TRUE if the view can be zoomed out.
+ */
+gboolean
+ld_diagram_view_can_zoom_out (LdDiagramView *self)
+{
+ g_return_val_if_fail (LD_IS_DIAGRAM_VIEW (self), FALSE);
+ return self->priv->zoom > ZOOM_MIN;
+}
+
+/**
+ * ld_diagram_view_zoom_in:
+ * @self: an #LdDiagramView object.
+ *
+ * Zoom the view in.
+ */
+void
+ld_diagram_view_zoom_in (LdDiagramView *self)
+{
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ ld_diagram_view_set_zoom (self, self->priv->zoom * ZOOM_STEP);
+}
+
+/**
+ * ld_diagram_view_zoom_out:
+ * @self: an #LdDiagramView object.
+ *
+ * Zoom the view out.
+ */
+void
+ld_diagram_view_zoom_out (LdDiagramView *self)
+{
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ ld_diagram_view_set_zoom (self, self->priv->zoom / ZOOM_STEP);
+}
+
+
+/* ===== Helper functions ================================================== */
+
+static void
+color_set (Color *color, gdouble r, gdouble g, gdouble b, gdouble a)
+{
+ color->r = r;
+ color->g = g;
+ color->b = b;
+ color->a = a;
+}
+
+static void
+color_apply (Color *color, cairo_t *cr)
+{
+ cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
+}
+
+static guint32
+color_to_cairo_argb (Color *color)
+{
+ return (guint) (color->a * 255) << 24
+ | (guint) (color->r * color->a * 255) << 16
+ | (guint) (color->g * color->a * 255) << 8
+ | (guint) (color->b * color->a * 255);
+}
+
+static gdouble
+point_to_line_segment_distance
+ (const LdPoint *point, const LdPoint *p1, const LdPoint *p2)
+{
+ gdouble dx, dy, u;
+
+ dx = p2->x - p1->x;
+ dy = p2->y - p1->y;
+
+ if (dx == 0. && dy == 0.)
+ return ld_point_distance (point, p1->x, p1->y);
+
+ /* Find projection of the point onto the line. */
+ u = ((point->x - p1->x) * dx + (point->y - p1->y) * dy)
+ / (dx * dx + dy * dy);
+
+ /* 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 (LdDiagramView *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 (LdDiagramView *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_point (LdDiagramView *self, LdDiagramObject *object,
+ const LdPoint *point)
+{
+ gdouble diagram_x, diagram_y;
+
+ ld_diagram_view_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_point (LdDiagramView *self, const LdPoint *point)
+{
+ GList *objects, *iter;
+
+ /* Iterate from the top object downwards. */
+ objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
+ for (iter = g_list_last (objects); iter; iter = g_list_previous (iter))
+ {
+ LdDiagramObject *object;
+
+ object = LD_DIAGRAM_OBJECT (iter->data);
+ if (object_hit_test (self, object, point))
+ return object;
+ }
+ return NULL;
+}
+
+static void
+move_selection (LdDiagramView *self, gdouble dx, gdouble dy)
+{
+ LdDiagram *diagram;
+ GList *selection, *iter;
+
+ diagram = self->priv->diagram;
+ if (!diagram)
+ return;
+
+ selection = ld_diagram_get_selection (diagram);
+ if (!selection)
+ return;
+
+ ld_diagram_begin_user_action (diagram);
+ for (iter = selection; iter; iter = g_list_next (iter))
+ {
+ gdouble x, y;
+
+ 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
+is_object_selected (LdDiagramView *self, LdDiagramObject *object)
+{
+ return g_list_find (ld_diagram_get_selection (self->priv->diagram),
+ object) != NULL;
+}
+
+static void
+queue_draw (LdDiagramView *self, LdRectangle *rect)
+{
+ 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 void
+queue_object_draw (LdDiagramView *self, LdDiagramObject *object)
+{
+ LdRectangle rect;
+
+ if (!get_object_clip_area (self, object, &rect))
+ return;
+ queue_draw (self, &rect);
+}
+
+
+/* ===== Symbol terminals ================================================== */
+
+static void
+check_terminals (LdDiagramView *self, const LdPoint *point)
+{
+ GList *objects, *iter;
+ LdDiagramSymbol *closest_symbol = NULL;
+ gdouble closest_distance = TERMINAL_HOVER_TOLERANCE;
+ LdPoint closest_terminal;
+
+ objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
+ for (iter = objects; iter; iter = g_list_next (iter))
+ {
+ gdouble object_x, object_y;
+ LdDiagramSymbol *diagram_symbol;
+ LdSymbol *symbol;
+ const LdPointArray *terminals;
+ guint i;
+ gint rotation;
+
+ if (!LD_IS_DIAGRAM_SYMBOL (iter->data))
+ continue;
+
+ diagram_symbol = LD_DIAGRAM_SYMBOL (iter->data);
+ symbol = resolve_symbol (self, diagram_symbol);
+ if (!symbol)
+ continue;
+
+ g_object_get (diagram_symbol, "x", &object_x, "y", &object_y,
+ "rotation", &rotation, NULL);
+
+ terminals = ld_symbol_get_terminals (symbol);
+ for (i = 0; i < terminals->length; i++)
+ {
+ LdPoint cur_term, widget_coords;
+ gdouble distance;
+
+ cur_term = terminals->points[i];
+ rotate_terminal (&cur_term, rotation);
+ cur_term.x += object_x;
+ cur_term.y += object_y;
+
+ ld_diagram_view_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;
+ closest_distance = distance;
+ closest_terminal = cur_term;
+ }
+ }
+ }
+
+ hide_terminals (self);
+
+ if (closest_symbol)
+ {
+ self->priv->terminal_hovered = TRUE;
+ self->priv->terminal = closest_terminal;
+ queue_terminal_draw (self, &closest_terminal);
+ }
+}
+
+static void
+rotate_terminal (LdPoint *terminal, gint symbol_rotation)
+{
+ gdouble temp;
+
+ switch (symbol_rotation)
+ {
+ case LD_DIAGRAM_SYMBOL_ROTATION_90:
+ temp = terminal->y;
+ terminal->y = terminal->x;
+ terminal->x = -temp;
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_180:
+ terminal->y = -terminal->y;
+ terminal->x = -terminal->x;
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_270:
+ temp = terminal->x;
+ terminal->x = terminal->y;
+ terminal->y = -temp;
+ break;
+ }
+}
+
+static void
+hide_terminals (LdDiagramView *self)
+{
+ if (self->priv->terminal_hovered)
+ {
+ self->priv->terminal_hovered = FALSE;
+ queue_terminal_draw (self, &self->priv->terminal);
+ }
+}
+
+static void
+queue_terminal_draw (LdDiagramView *self, LdPoint *terminal)
+{
+ LdRectangle rect;
+ LdPoint widget_coords;
+
+ ld_diagram_view_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 (LdDiagramView *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 (LdDiagramView *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 (LdDiagramView *self, LdDiagramSymbol *symbol,
+ LdRectangle *rect)
+{
+ gdouble object_x, object_y;
+ LdSymbol *library_symbol;
+ LdRectangle area;
+ gdouble x1, x2;
+ gdouble y1, y2;
+ gint rotation;
+
+ g_object_get (symbol, "x", &object_x, "y", &object_y,
+ "rotation", &rotation, NULL);
+
+ library_symbol = resolve_symbol (self, symbol);
+ if (library_symbol)
+ ld_symbol_get_area (library_symbol, &area);
+ else
+ return FALSE;
+
+ rotate_symbol_area (&area, rotation);
+
+ ld_diagram_view_diagram_to_widget_coords (self,
+ object_x + area.x,
+ object_y + area.y,
+ &x1, &y1);
+ ld_diagram_view_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
+rotate_symbol_area (LdRectangle *area, gint rotation)
+{
+ gdouble temp;
+
+ switch (rotation)
+ {
+ case LD_DIAGRAM_SYMBOL_ROTATION_90:
+ temp = area->y;
+ area->y = area->x;
+ area->x = -(temp + area->height);
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_180:
+ area->y = -(area->y + area->height);
+ area->x = -(area->x + area->width);
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_270:
+ temp = area->x;
+ area->x = area->y;
+ area->y = -(temp + area->width);
+ break;
+ }
+
+ switch (rotation)
+ {
+ case LD_DIAGRAM_SYMBOL_ROTATION_90:
+ case LD_DIAGRAM_SYMBOL_ROTATION_270:
+ temp = area->width;
+ area->width = area->height;
+ area->height = temp;
+ break;
+ }
+}
+
+static void
+rotate_symbol (LdDiagramView *self, LdDiagramSymbol *symbol)
+{
+ gint rotation;
+
+ g_object_get (symbol, "rotation", &rotation, NULL);
+ queue_object_draw (self, LD_DIAGRAM_OBJECT (symbol));
+
+ switch (rotation)
+ {
+ case LD_DIAGRAM_SYMBOL_ROTATION_0:
+ rotation = LD_DIAGRAM_SYMBOL_ROTATION_90;
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_90:
+ rotation = LD_DIAGRAM_SYMBOL_ROTATION_180;
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_180:
+ rotation = LD_DIAGRAM_SYMBOL_ROTATION_270;
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_270:
+ rotation = LD_DIAGRAM_SYMBOL_ROTATION_0;
+ break;
+ }
+
+ g_object_set (symbol, "rotation", rotation, NULL);
+ queue_object_draw (self, LD_DIAGRAM_OBJECT (symbol));
+}
+
+static LdSymbol *
+resolve_symbol (LdDiagramView *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;
+}
+
+
+/* ===== Diagram connection ================================================ */
+
+static gboolean
+connection_hit_test (LdDiagramView *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)
+ {
+ ld_point_array_free (points);
+ return FALSE;
+ }
+
+ for (i = 0; i < points->length; i++)
+ {
+ ld_diagram_view_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 gboolean
+get_connection_clip_area (LdDiagramView *self,
+ LdDiagramConnection *connection, LdRectangle *rect)
+{
+ return get_connection_area (self, connection, rect);
+}
+
+static gboolean
+get_connection_area (LdDiagramView *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_diagram_view_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_diagram_view_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;
+}
+
+
+/* ===== Operations ======================================================== */
+
+static void
+ld_diagram_view_real_cancel_operation (LdDiagramView *self)
+{
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (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_diagram_view_add_object_begin:
+ * @self: an #LdDiagramView object.
+ * @object: (transfer full): the object to be added to the diagram.
+ *
+ * Begin an operation for adding an object into the diagram.
+ */
+void
+ld_diagram_view_add_object_begin (LdDiagramView *self, LdDiagramObject *object)
+{
+ AddObjectData *data;
+
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self));
+ g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object));
+
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+
+ self->priv->operation = OPER_ADD_OBJECT;
+ self->priv->operation_end = oper_add_object_end;
+
+ data = &OPER_DATA (self, add_object);
+ data->object = object;
+}
+
+static void
+oper_add_object_end (LdDiagramView *self)
+{
+ AddObjectData *data;
+
+ data = &OPER_DATA (self, add_object);
+ if (data->object)
+ {
+ queue_object_draw (self, data->object);
+ g_object_unref (data->object);
+ data->object = NULL;
+ }
+}
+
+static void
+oper_connect_begin (LdDiagramView *self, const LdPoint *point)
+{
+ ConnectData *data;
+
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+
+ 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 (LdDiagramView *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 (LdDiagramView *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_diagram_view_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 (LdDiagramView *self, const LdPoint *point)
+{
+ SelectData *data;
+
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+
+ self->priv->operation = OPER_SELECT;
+ self->priv->operation_end = oper_select_end;
+
+ data = &OPER_DATA (self, select);
+ 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, point);
+}
+
+static void
+oper_select_end (LdDiagramView *self)
+{
+ oper_select_queue_draw (self);
+}
+
+static void
+oper_select_get_rectangle (LdDiagramView *self, LdRectangle *rect)
+{
+ SelectData *data;
+
+ data = &OPER_DATA (self, select);
+ rect->x = MIN (self->priv->drag_start_pos.x, data->drag_last_pos.x);
+ rect->y = MIN (self->priv->drag_start_pos.y, data->drag_last_pos.y);
+ rect->width = ABS (self->priv->drag_start_pos.x - data->drag_last_pos.x);
+ rect->height = ABS (self->priv->drag_start_pos.y - data->drag_last_pos.y);
+}
+
+static void
+oper_select_queue_draw (LdDiagramView *self)
+{
+ LdRectangle rect;
+ SelectData *data;
+
+ data = &OPER_DATA (self, select);
+ oper_select_get_rectangle (self, &rect);
+ queue_draw (self, &rect);
+}
+
+static void
+oper_select_draw (GtkWidget *widget, DrawData *data)
+{
+ static const double dashes[] = {3, 5};
+ SelectData *select_data;
+
+ g_return_if_fail (data->self->priv->operation == OPER_SELECT);
+
+ color_apply (COLOR_GET (data->self, COLOR_GRID), data->cr);
+ cairo_set_line_width (data->cr, 1);
+ cairo_set_line_cap (data->cr, CAIRO_LINE_CAP_SQUARE);
+ cairo_set_dash (data->cr, dashes, G_N_ELEMENTS (dashes), 0);
+
+ select_data = &OPER_DATA (data->self, select);
+
+ cairo_rectangle (data->cr,
+ data->self->priv->drag_start_pos.x - 0.5,
+ data->self->priv->drag_start_pos.y - 0.5,
+ select_data->drag_last_pos.x - data->self->priv->drag_start_pos.x + 1,
+ select_data->drag_last_pos.y - data->self->priv->drag_start_pos.y + 1);
+ cairo_stroke (data->cr);
+}
+
+static void
+oper_select_motion (LdDiagramView *self, const LdPoint *point)
+{
+ SelectData *data;
+ GList *objects, *iter;
+ LdRectangle selection_rect, rect;
+
+ data = &OPER_DATA (self, select);
+
+ oper_select_queue_draw (self);
+ data->drag_last_pos = *point;
+ oper_select_queue_draw (self);
+
+ oper_select_get_rectangle (self, &selection_rect);
+ objects = (GList *) ld_diagram_get_objects (self->priv->diagram);
+
+ for (iter = objects; iter; iter = g_list_next (iter))
+ {
+ LdDiagramObject *object;
+
+ object = LD_DIAGRAM_OBJECT (iter->data);
+ 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 (&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);
+ }
+}
+
+static void
+oper_move_selection_begin (LdDiagramView *self, const LdPoint *point)
+{
+ MoveSelectionData *data;
+
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+
+ self->priv->operation = OPER_MOVE_SELECTION;
+ self->priv->operation_end = oper_move_selection_end;
+
+ ld_diagram_begin_user_action (self->priv->diagram);
+
+ data = &OPER_DATA (self, move_selection);
+ data->move_origin = self->priv->drag_start_pos;
+
+ oper_move_selection_motion (self, point);
+}
+
+static void
+oper_move_selection_end (LdDiagramView *self)
+{
+ ld_diagram_end_user_action (self->priv->diagram);
+}
+
+static void
+oper_move_selection_motion (LdDiagramView *self, const LdPoint *point)
+{
+ MoveSelectionData *data;
+ gdouble scale, dx, dy, move_x, move_y;
+ gdouble move = FALSE;
+
+ scale = ld_diagram_view_get_scale_in_px (self);
+ data = &OPER_DATA (self, move_selection);
+
+ 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);
+
+ if (ABS (move_x) >= 1)
+ {
+ data->move_origin.x += move_x * scale;
+ move = TRUE;
+ }
+ if (ABS (move_y) >= 1)
+ {
+ data->move_origin.y += move_y * scale;
+ move = TRUE;
+ }
+
+ if (move)
+ move_selection (self, move_x, move_y);
+}
+
+
+/* ===== Events, rendering ================================================= */
+
+static void
+simulate_motion (LdDiagramView *self)
+{
+ GdkEventMotion event;
+ GtkWidget *widget;
+ gint x, y;
+ GdkModifierType state;
+
+ widget = GTK_WIDGET (self);
+
+ if (gdk_window_get_pointer (widget->window, &x, &y, &state)
+ != widget->window)
+ return;
+
+ memset (&event, 0, sizeof (event));
+ event.type = GDK_MOTION_NOTIFY;
+ event.window = widget->window;
+ event.x = x;
+ event.y = y;
+ event.state = state;
+
+ on_motion_notify (widget, &event, NULL);
+}
+
+static gboolean
+on_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
+{
+ LdPoint point;
+ LdDiagramView *self;
+ AddObjectData *add_data;
+
+ point.x = event->x;
+ point.y = event->y;
+
+ self = LD_DIAGRAM_VIEW (widget);
+ switch (self->priv->operation)
+ {
+ case OPER_ADD_OBJECT:
+ add_data = &OPER_DATA (self, add_object);
+ add_data->visible = TRUE;
+
+ queue_object_draw (self, add_data->object);
+ 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, &point);
+ break;
+ case OPER_MOVE_SELECTION:
+ oper_move_selection_motion (self, &point);
+ break;
+ case OPER_0:
+ if (event->state & GDK_BUTTON1_MASK
+ && (event->x != self->priv->drag_start_pos.x
+ || event->y != self->priv->drag_start_pos.y))
+ {
+ switch (self->priv->drag_operation)
+ {
+ case OPER_CONNECT:
+ oper_connect_begin (self, &point);
+ break;
+ case OPER_SELECT:
+ oper_select_begin (self, &point);
+ break;
+ case OPER_MOVE_SELECTION:
+ oper_move_selection_begin (self, &point);
+ break;
+ }
+ }
+ check_terminals (self, &point);
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean
+on_leave_notify (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
+{
+ LdDiagramView *self;
+
+ self = LD_DIAGRAM_VIEW (widget);
+ switch (self->priv->operation)
+ {
+ AddObjectData *data;
+
+ case OPER_ADD_OBJECT:
+ data = &OPER_DATA (self, add_object);
+ data->visible = FALSE;
+
+ queue_object_draw (self, data->object);
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean
+on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ LdPoint point;
+ LdDiagramView *self;
+ AddObjectData *data;
+ LdDiagramObject *object;
+
+ point.x = event->x;
+ point.y = event->y;
+
+ self = LD_DIAGRAM_VIEW (widget);
+ if (!self->priv->diagram)
+ return FALSE;
+
+ if (event->button == 3 && self->priv->operation == OPER_0)
+ {
+ object = get_object_at_point (self, &point);
+ if (object && LD_IS_DIAGRAM_SYMBOL (object))
+ rotate_symbol (self, LD_DIAGRAM_SYMBOL (object));
+ return FALSE;
+ }
+
+ if (event->button != 1)
+ return FALSE;
+ if (!gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+ self->priv->drag_operation = OPER_0;
+ switch (self->priv->operation)
+ {
+ case OPER_ADD_OBJECT:
+ data = &OPER_DATA (self, add_object);
+
+ queue_object_draw (self, data->object);
+ move_object_to_point (self, data->object, &point);
+ ld_diagram_insert_object (self->priv->diagram, data->object, -1);
+
+ /* XXX: "cancel" causes confusion. */
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+ break;
+ case OPER_0:
+ self->priv->drag_start_pos = point;
+
+ 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);
+ self->priv->drag_operation = OPER_SELECT;
+ }
+ else if (!is_object_selected (self, object))
+ {
+ if (event->state != GDK_SHIFT_MASK)
+ ld_diagram_unselect_all (self->priv->diagram);
+ ld_diagram_select (self->priv->diagram, object);
+ self->priv->drag_operation = OPER_MOVE_SELECTION;
+ }
+ else
+ self->priv->drag_operation = OPER_MOVE_SELECTION;
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean
+on_button_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ LdPoint point;
+ LdDiagramView *self;
+ LdDiagramObject *object;
+
+ if (event->button != 1)
+ return FALSE;
+
+ point.x = event->x;
+ point.y = event->y;
+
+ self = LD_DIAGRAM_VIEW (widget);
+ if (!self->priv->diagram)
+ return FALSE;
+
+ switch (self->priv->operation)
+ {
+ case OPER_SELECT:
+ case OPER_MOVE_SELECTION:
+ case OPER_CONNECT:
+ g_signal_emit (self,
+ LD_DIAGRAM_VIEW_GET_CLASS (self)->cancel_operation_signal, 0);
+ break;
+ case OPER_0:
+ object = get_object_at_point (self, &point);
+ if (object && is_object_selected (self, object))
+ {
+ if (!(event->state & GDK_SHIFT_MASK))
+ ld_diagram_unselect_all (self->priv->diagram);
+ ld_diagram_select (self->priv->diagram, object);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean
+on_scroll (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
+{
+ gdouble prev_x, prev_y;
+ gdouble new_x, new_y;
+ LdPoint point;
+ LdDiagramView *self;
+
+ point.x = event->x;
+ point.y = event->y;
+ self = LD_DIAGRAM_VIEW (widget);
+
+ ld_diagram_view_widget_to_diagram_coords (self,
+ event->x, event->y, &prev_x, &prev_y);
+
+ switch (event->direction)
+ {
+ case GDK_SCROLL_UP:
+ ld_diagram_view_zoom_in (self);
+ break;
+ case GDK_SCROLL_DOWN:
+ ld_diagram_view_zoom_out (self);
+ break;
+ default:
+ return FALSE;
+ }
+
+ ld_diagram_view_widget_to_diagram_coords (self,
+ event->x, event->y, &new_x, &new_y);
+
+ /* Focus on the point under the cursor. */
+ self->priv->x += prev_x - new_x;
+ self->priv->y += prev_y - new_y;
+
+ check_terminals (self, &point);
+ return TRUE;
+}
+
+static gboolean
+on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ DrawData data;
+
+ data.cr = gdk_cairo_create (widget->window);
+ data.self = LD_DIAGRAM_VIEW (widget);
+ data.scale = ld_diagram_view_get_scale_in_px (data.self);
+ data.exposed_rect.x = event->area.x;
+ data.exposed_rect.y = event->area.y;
+ data.exposed_rect.width = event->area.width;
+ data.exposed_rect.height = event->area.height;
+
+ gdk_cairo_rectangle (data.cr, &event->area);
+ cairo_clip (data.cr);
+
+ color_apply (COLOR_GET (data.self, COLOR_BASE), data.cr);
+ cairo_paint (data.cr);
+
+ draw_grid (widget, &data);
+ draw_diagram (widget, &data);
+ draw_terminal (widget, &data);
+
+ if (data.self->priv->operation == OPER_SELECT)
+ oper_select_draw (widget, &data);
+
+ cairo_destroy (data.cr);
+ return FALSE;
+}
+
+static void
+draw_grid (GtkWidget *widget, DrawData *data)
+{
+ gdouble grid_step;
+ gint grid_factor;
+ gdouble x_init, y_init;
+ gdouble x, y;
+ cairo_surface_t *grid_surface;
+ gint stride;
+ unsigned char *pixels;
+ guint32 color;
+
+ grid_step = data->scale;
+ grid_factor = 1;
+ while (grid_step < 5)
+ {
+ grid_step *= 5;
+ grid_factor *= 5;
+ }
+
+ /* Paint manually on our own raster surface for speed. */
+ stride = cairo_format_stride_for_width
+ (CAIRO_FORMAT_ARGB32, data->exposed_rect.width);
+ pixels = g_malloc0 (stride * data->exposed_rect.height);
+ grid_surface = cairo_image_surface_create_for_data
+ (pixels, CAIRO_FORMAT_ARGB32,
+ data->exposed_rect.width, data->exposed_rect.height, stride);
+
+ /* Get coordinates of the top-left point. */
+ ld_diagram_view_widget_to_diagram_coords (data->self,
+ data->exposed_rect.x, data->exposed_rect.y, &x_init, &y_init);
+
+ x_init = ceil (x_init);
+ x_init = x_init - (gint) x_init % grid_factor;
+ y_init = ceil (y_init);
+ y_init = y_init - (gint) y_init % grid_factor;
+
+ ld_diagram_view_diagram_to_widget_coords (data->self,
+ x_init, y_init, &x_init, &y_init);
+
+ x_init -= data->exposed_rect.x;
+ y_init -= data->exposed_rect.y;
+
+ while (x_init < 0)
+ x_init += grid_step;
+ while (y_init < 0)
+ y_init += grid_step;
+
+ color = color_to_cairo_argb (COLOR_GET (data->self, COLOR_GRID));
+
+ for (x = x_init; x < data->exposed_rect.width; x += grid_step)
+ for (y = y_init; y < data->exposed_rect.height; y += grid_step)
+ *((guint32 *) (pixels + stride * (gint) y) + (gint) x) = color;
+
+ cairo_set_source_surface (data->cr, grid_surface,
+ data->exposed_rect.x, data->exposed_rect.y);
+ cairo_paint (data->cr);
+
+ cairo_surface_destroy (grid_surface);
+ g_free (pixels);
+}
+
+static void
+draw_terminal (GtkWidget *widget, DrawData *data)
+{
+ LdDiagramViewPrivate *priv;
+ LdPoint widget_coords;
+
+ priv = data->self->priv;
+ if (!priv->terminal_hovered)
+ return;
+
+ color_apply (COLOR_GET (data->self, COLOR_TERMINAL), data->cr);
+ cairo_set_line_width (data->cr, 1);
+
+ cairo_new_path (data->cr);
+ ld_diagram_view_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);
+}
+
+static void
+draw_diagram (GtkWidget *widget, DrawData *data)
+{
+ GList *objects, *iter;
+
+ if (!data->self->priv->diagram)
+ return;
+
+ cairo_save (data->cr);
+ cairo_set_line_width (data->cr, 1 / data->scale);
+
+ /* Draw objects from the diagram, from bottom to top. */
+ objects = (GList *) ld_diagram_get_objects (data->self->priv->diagram);
+ for (iter = objects; iter; iter = g_list_next (iter))
+ draw_object (LD_DIAGRAM_OBJECT (iter->data), data);
+
+ switch (data->self->priv->operation)
+ {
+ AddObjectData *add_data;
+ ConnectData *connect_data;
+
+ case OPER_ADD_OBJECT:
+ 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;
+ }
+
+ cairo_restore (data->cr);
+}
+
+static void
+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_selected (data->self, diagram_object))
+ color_apply (COLOR_GET (data->self, COLOR_SELECTION), data->cr);
+ else
+ color_apply (COLOR_GET (data->self, COLOR_OBJECT), data->cr);
+
+ 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
+draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data)
+{
+ LdSymbol *symbol;
+ LdRectangle clip_rect;
+ gdouble x, y;
+ gint rotation;
+
+ symbol = resolve_symbol (data->self, diagram_symbol);
+
+ /* TODO: Resolve this better; draw a cross or whatever. */
+ if (!symbol)
+ {
+ gchar *klass;
+
+ klass = ld_diagram_symbol_get_class (diagram_symbol);
+ g_warning ("cannot find symbol `%s' in the library", klass);
+ g_free (klass);
+ return;
+ }
+
+ if (!get_symbol_clip_area (data->self, diagram_symbol, &clip_rect)
+ || !ld_rectangle_intersects (&clip_rect, &data->exposed_rect))
+ return;
+
+ cairo_save (data->cr);
+
+ cairo_rectangle (data->cr, clip_rect.x, clip_rect.y,
+ clip_rect.width, clip_rect.height);
+ cairo_clip (data->cr);
+
+ g_object_get (diagram_symbol, "x", &x, "y", &y,
+ "rotation", &rotation, NULL);
+ ld_diagram_view_diagram_to_widget_coords (data->self, x, y, &x, &y);
+ cairo_translate (data->cr, x, y);
+ cairo_scale (data->cr, data->scale, data->scale);
+
+ switch (rotation)
+ {
+ case LD_DIAGRAM_SYMBOL_ROTATION_90:
+ cairo_rotate (data->cr, G_PI * 0.5);
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_180:
+ cairo_rotate (data->cr, G_PI);
+ break;
+ case LD_DIAGRAM_SYMBOL_ROTATION_270:
+ cairo_rotate (data->cr, G_PI * 1.5);
+ break;
+ }
+
+ 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_diagram_view_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-diagram-view.h b/liblogdiag/ld-diagram-view.h
new file mode 100644
index 0000000..dacb594
--- /dev/null
+++ b/liblogdiag/ld-diagram-view.h
@@ -0,0 +1,94 @@
+/*
+ * ld-diagram-view.h
+ *
+ * This file is a part of logdiag.
+ * Copyright Přemysl Janouch 2010 - 2011. All rights reserved.
+ *
+ * See the file LICENSE for licensing information.
+ *
+ */
+
+#ifndef __LD_DIAGRAM_VIEW_H__
+#define __LD_DIAGRAM_VIEW_H__
+
+G_BEGIN_DECLS
+
+
+#define LD_TYPE_DIAGRAM_VIEW (ld_diagram_view_get_type ())
+#define LD_DIAGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), LD_TYPE_DIAGRAM_VIEW, LdDiagramView))
+#define LD_DIAGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \
+ ((klass), LD_TYPE_DIAGRAM_VIEW, LdDiagramViewClass))
+#define LD_IS_DIAGRAM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), LD_TYPE_DIAGRAM_VIEW))
+#define LD_IS_DIAGRAM_VIEW_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((klass), LD_TYPE_DIAGRAM_VIEW))
+#define LD_DIAGRAM_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), LD_DIAGRAM_VIEW, LdDiagramViewClass))
+
+typedef struct _LdDiagramView LdDiagramView;
+typedef struct _LdDiagramViewPrivate LdDiagramViewPrivate;
+typedef struct _LdDiagramViewClass LdDiagramViewClass;
+
+
+/**
+ * LdDiagramView:
+ */
+struct _LdDiagramView
+{
+/*< private >*/
+ GtkDrawingArea parent_instance;
+ LdDiagramViewPrivate *priv;
+};
+
+struct _LdDiagramViewClass
+{
+/*< private >*/
+ GtkDrawingAreaClass parent_class;
+
+ guint cancel_operation_signal;
+ guint move_signal;
+
+ void (*set_scroll_adjustments) (LdDiagramView *self,
+ GtkAdjustment *horizontal, GtkAdjustment *vertical);
+ void (*cancel_operation) (LdDiagramView *self);
+ void (*move) (LdDiagramView *self, gdouble dx, gdouble dy);
+};
+
+
+/**
+ * LD_DIAGRAM_VIEW_BASE_UNIT_LENGTH:
+ *
+ * Length of the base unit in milimetres.
+ */
+#define LD_DIAGRAM_VIEW_BASE_UNIT_LENGTH 2.5
+
+
+GType ld_diagram_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ld_diagram_view_new (void);
+
+void ld_diagram_view_set_diagram (LdDiagramView *self, LdDiagram *diagram);
+LdDiagram *ld_diagram_view_get_diagram (LdDiagramView *self);
+void ld_diagram_view_set_library (LdDiagramView *self, LdLibrary *library);
+LdLibrary *ld_diagram_view_get_library (LdDiagramView *self);
+
+void ld_diagram_view_widget_to_diagram_coords (LdDiagramView *self,
+ gdouble wx, gdouble wy, gdouble *dx, gdouble *dy);
+void ld_diagram_view_diagram_to_widget_coords (LdDiagramView *self,
+ gdouble dx, gdouble dy, gdouble *wx, gdouble *wy);
+
+gdouble ld_diagram_view_get_zoom (LdDiagramView *self);
+void ld_diagram_view_set_zoom (LdDiagramView *self, gdouble zoom);
+gboolean ld_diagram_view_can_zoom_in (LdDiagramView *self);
+void ld_diagram_view_zoom_in (LdDiagramView *self);
+gboolean ld_diagram_view_can_zoom_out (LdDiagramView *self);
+void ld_diagram_view_zoom_out (LdDiagramView *self);
+
+void ld_diagram_view_add_object_begin (LdDiagramView *self,
+ LdDiagramObject *object);
+
+
+G_END_DECLS
+
+#endif /* ! __LD_DIAGRAM_VIEW_H__ */
diff --git a/liblogdiag/ld-diagram.c b/liblogdiag/ld-diagram.c
index bbe8d35..cd483ec 100644
--- a/liblogdiag/ld-diagram.c
+++ b/liblogdiag/ld-diagram.c
@@ -15,7 +15,7 @@
/**
* SECTION:ld-diagram
* @short_description: A model for diagrams
- * @see_also: #LdCanvas
+ * @see_also: #LdDiagramView
*
* #LdDiagram is a model used for storing diagrams.
*/
diff --git a/liblogdiag/ld-library-toolbar.c b/liblogdiag/ld-library-toolbar.c
index 4ce348d..fe891c8 100644
--- a/liblogdiag/ld-library-toolbar.c
+++ b/liblogdiag/ld-library-toolbar.c
@@ -61,25 +61,25 @@ struct _SymbolMenuData
enum
{
- CANVAS_HANDLER_EXPOSE,
- CANVAS_HANDLER_MOTION_NOTIFY,
- CANVAS_HANDLER_BUTTON_PRESS,
- CANVAS_HANDLER_BUTTON_RELEASE,
- CANVAS_HANDLER_COUNT
+ VIEW_HANDLER_EXPOSE,
+ VIEW_HANDLER_MOTION_NOTIFY,
+ VIEW_HANDLER_BUTTON_PRESS,
+ VIEW_HANDLER_BUTTON_RELEASE,
+ VIEW_HANDLER_COUNT
};
/*
* LdLibraryToolbarPrivate:
- * @library: a library object assigned to this canvas as a model.
- * @canvas: a canvas object for showing symbol menus.
- * @canvas_handlers: signal handlers that hook the canvas.
+ * @library: a library object assigned as a model.
+ * @view: a view widget for showing symbol menus.
+ * @view_handlers: signal handlers that hook the view.
* @symbol_menu: data related to menus.
*/
struct _LdLibraryToolbarPrivate
{
LdLibrary *library;
- LdCanvas *canvas;
- gulong canvas_handlers[CANVAS_HANDLER_COUNT];
+ LdDiagramView *view;
+ gulong view_handlers[VIEW_HANDLER_COUNT];
SymbolMenuData symbol_menu;
};
@@ -87,7 +87,7 @@ enum
{
PROP_0,
PROP_LIBRARY,
- PROP_CANVAS
+ PROP_VIEW
};
static void ld_library_toolbar_get_property (GObject *object, guint property_id,
@@ -106,16 +106,16 @@ static void emit_symbol_signal (LdLibraryToolbar *self,
static void on_category_toggle (GtkToggleButton *toggle_button,
gpointer user_data);
-static inline void block_canvas_handlers (LdLibraryToolbar *self);
-static inline void unblock_canvas_handlers (LdLibraryToolbar *self);
-static inline void disconnect_canvas_handlers (LdLibraryToolbar *self);
-static gboolean on_canvas_exposed (GtkWidget *widget,
+static inline void block_view_handlers (LdLibraryToolbar *self);
+static inline void unblock_view_handlers (LdLibraryToolbar *self);
+static inline void disconnect_view_handlers (LdLibraryToolbar *self);
+static gboolean on_view_exposed (GtkWidget *widget,
GdkEventExpose *event, gpointer user_data);
-static gboolean on_canvas_motion_notify (GtkWidget *widget,
+static gboolean on_view_motion_notify (GtkWidget *widget,
GdkEventMotion *event, gpointer user_data);
-static gboolean on_canvas_button_press (GtkWidget *widget,
+static gboolean on_view_button_press (GtkWidget *widget,
GdkEventButton *event, gpointer user_data);
-static gboolean on_canvas_button_release (GtkWidget *widget,
+static gboolean on_view_button_release (GtkWidget *widget,
GdkEventButton *event, gpointer user_data);
@@ -138,22 +138,22 @@ ld_library_toolbar_class_init (LdLibraryToolbarClass *klass)
/**
* LdLibraryToolbar:library:
*
- * The #LdLibrary that this canvas retrieves symbols from.
+ * The #LdLibrary that this toolbar retrieves symbols from.
*/
pspec = g_param_spec_object ("library", "Library",
- "The library that this canvas retrieves symbols from.",
+ "The library that this toolbar retrieves symbols from.",
LD_TYPE_LIBRARY, G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_LIBRARY, pspec);
/**
- * LdLibraryToolbar:canvas:
+ * LdLibraryToolbar:view:
*
- * The #LdCanvas misused for showing symbol menus.
+ * The #LdDiagramView widget misused for showing symbol menus.
*/
- pspec = g_param_spec_object ("canvas", "Canvas",
- "The canvas misused for showing symbol menus.",
- LD_TYPE_CANVAS, G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_CANVAS, pspec);
+ pspec = g_param_spec_object ("view", "View",
+ "The view widget misused for showing symbol menus.",
+ LD_TYPE_DIAGRAM_VIEW, G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_VIEW, pspec);
/**
* LdLibraryToolbar::symbol-chosen:
@@ -218,7 +218,7 @@ ld_library_toolbar_dispose (GObject *gobject)
self = LD_LIBRARY_TOOLBAR (gobject);
ld_library_toolbar_set_library (self, NULL);
- ld_library_toolbar_set_canvas (self, NULL);
+ ld_library_toolbar_set_view (self, NULL);
/* Chain up to the parent class. */
G_OBJECT_CLASS (ld_library_toolbar_parent_class)->dispose (gobject);
@@ -236,8 +236,8 @@ ld_library_toolbar_get_property (GObject *object, guint property_id,
case PROP_LIBRARY:
g_value_set_object (value, ld_library_toolbar_get_library (self));
break;
- case PROP_CANVAS:
- g_value_set_object (value, ld_library_toolbar_get_canvas (self));
+ case PROP_VIEW:
+ g_value_set_object (value, ld_library_toolbar_get_view (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -257,9 +257,9 @@ ld_library_toolbar_set_property (GObject *object, guint property_id,
ld_library_toolbar_set_library (self,
LD_LIBRARY (g_value_get_object (value)));
break;
- case PROP_CANVAS:
- ld_library_toolbar_set_canvas (self,
- LD_CANVAS (g_value_get_object (value)));
+ case PROP_VIEW:
+ ld_library_toolbar_set_view (self,
+ LD_DIAGRAM_VIEW (g_value_get_object (value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -281,7 +281,7 @@ ld_library_toolbar_new (void)
/**
* ld_library_toolbar_set_library:
* @self: an #LdLibraryToolbar object.
- * @library: (allow-none): the #LdLibrary to be assigned to the toolbar.
+ * @library: (allow-none): the library to be assigned to the toolbar.
*
* Assign an #LdLibrary object to the toolbar.
*/
@@ -315,8 +315,8 @@ ld_library_toolbar_set_library (LdLibraryToolbar *self, LdLibrary *library)
* ld_library_toolbar_get_library:
* @self: an #LdLibraryToolbar object.
*
- * Get the #LdLibrary object assigned to this toolbar.
- * The reference count on the library is not incremented.
+ * Return value: (transfer: none): the #LdLibrary object
+ * assigned to the toolbar.
*/
LdLibrary *
ld_library_toolbar_get_library (LdLibraryToolbar *self)
@@ -326,59 +326,59 @@ ld_library_toolbar_get_library (LdLibraryToolbar *self)
}
/**
- * ld_library_toolbar_set_canvas:
+ * ld_library_toolbar_set_view:
* @self: an #LdLibraryToolbar object.
- * @canvas: (allow-none): the #LdCanvas to be assigned to the toolbar.
+ * @view: (allow-none): the widget to be assigned to the toolbar.
*
- * Assign an #LdCanvas object to the toolbar.
+ * Assign an #LdDiagramView widget to the toolbar.
*/
void
-ld_library_toolbar_set_canvas (LdLibraryToolbar *self, LdCanvas *canvas)
+ld_library_toolbar_set_view (LdLibraryToolbar *self, LdDiagramView *view)
{
g_return_if_fail (LD_IS_LIBRARY_TOOLBAR (self));
- g_return_if_fail (LD_IS_CANVAS (canvas) || canvas == NULL);
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (view) || view == NULL);
- if (self->priv->canvas)
+ if (self->priv->view)
{
- disconnect_canvas_handlers (self);
- g_object_unref (self->priv->canvas);
+ disconnect_view_handlers (self);
+ g_object_unref (self->priv->view);
}
- self->priv->canvas = canvas;
+ self->priv->view = view;
- if (canvas)
+ if (view)
{
- self->priv->canvas_handlers[CANVAS_HANDLER_EXPOSE]
- = g_signal_connect (canvas, "expose-event",
- G_CALLBACK (on_canvas_exposed), self);
- self->priv->canvas_handlers[CANVAS_HANDLER_MOTION_NOTIFY]
- = g_signal_connect (canvas, "motion-notify-event",
- G_CALLBACK (on_canvas_motion_notify), self);
- self->priv->canvas_handlers[CANVAS_HANDLER_BUTTON_PRESS]
- = g_signal_connect (canvas, "button-press-event",
- G_CALLBACK (on_canvas_button_press), self);
- self->priv->canvas_handlers[CANVAS_HANDLER_BUTTON_RELEASE]
- = g_signal_connect (canvas, "button-release-event",
- G_CALLBACK (on_canvas_button_release), self);
-
- block_canvas_handlers (self);
- g_object_ref (canvas);
+ self->priv->view_handlers[VIEW_HANDLER_EXPOSE]
+ = g_signal_connect (view, "expose-event",
+ G_CALLBACK (on_view_exposed), self);
+ self->priv->view_handlers[VIEW_HANDLER_MOTION_NOTIFY]
+ = g_signal_connect (view, "motion-notify-event",
+ G_CALLBACK (on_view_motion_notify), self);
+ self->priv->view_handlers[VIEW_HANDLER_BUTTON_PRESS]
+ = g_signal_connect (view, "button-press-event",
+ G_CALLBACK (on_view_button_press), self);
+ self->priv->view_handlers[VIEW_HANDLER_BUTTON_RELEASE]
+ = g_signal_connect (view, "button-release-event",
+ G_CALLBACK (on_view_button_release), self);
+
+ block_view_handlers (self);
+ g_object_ref (view);
}
- g_object_notify (G_OBJECT (self), "canvas");
+ g_object_notify (G_OBJECT (self), "view");
}
/**
- * ld_library_toolbar_get_canvas:
+ * ld_library_toolbar_get_view:
* @self: an #LdLibraryToolbar object.
*
- * Get the #LdLibrary object assigned to this toolbar.
- * The reference count on the canvas is not incremented.
+ * Return value: (transfer: none): the #LdDiagramView widget
+ * assigned to the toolbar.
*/
-LdCanvas *
-ld_library_toolbar_get_canvas (LdLibraryToolbar *self)
+LdDiagramView *
+ld_library_toolbar_get_view (LdLibraryToolbar *self)
{
g_return_val_if_fail (LD_IS_LIBRARY_TOOLBAR (self), NULL);
- return self->priv->canvas;
+ return self->priv->view;
}
static void
@@ -439,7 +439,7 @@ load_category_cb (gpointer data, gpointer user_data)
gtk_container_add (GTK_CONTAINER (button), img);
gtk_container_add (GTK_CONTAINER (item), button);
- /* Don't steal focus from the canvas. */
+ /* Don't steal focus from the view. */
g_object_set (button, "can-focus", FALSE, NULL);
/* Assign the category to the toggle button. */
@@ -498,7 +498,7 @@ redraw_symbol_menu (LdLibraryToolbar *self)
g_return_if_fail (LD_IS_LIBRARY_TOOLBAR (self));
data = &self->priv->symbol_menu;
- gtk_widget_queue_draw_area (GTK_WIDGET (self->priv->canvas),
+ gtk_widget_queue_draw_area (GTK_WIDGET (self->priv->view),
0, data->menu_y - 1, data->menu_width + 2, data->menu_height + 2);
}
@@ -541,7 +541,7 @@ on_category_toggle (GtkToggleButton *toggle_button, gpointer user_data)
{
gint i;
- block_canvas_handlers (self);
+ block_view_handlers (self);
g_object_unref (data->active_button);
data->active_button = NULL;
@@ -559,7 +559,7 @@ on_category_toggle (GtkToggleButton *toggle_button, gpointer user_data)
g_free (data->items);
data->items = NULL;
- gtk_grab_remove (GTK_WIDGET (self->priv->canvas));
+ gtk_grab_remove (GTK_WIDGET (self->priv->view));
}
else
{
@@ -568,12 +568,12 @@ on_category_toggle (GtkToggleButton *toggle_button, gpointer user_data)
gint x, y, menu_width;
g_return_if_fail (gtk_widget_translate_coordinates (GTK_WIDGET
- (toggle_button), GTK_WIDGET (priv->canvas), 0, 0, &x, &y));
+ (toggle_button), GTK_WIDGET (priv->view), 0, 0, &x, &y));
data->menu_y = y;
data->menu_height = GTK_WIDGET (toggle_button)->allocation.height;
- unblock_canvas_handlers (self);
+ unblock_view_handlers (self);
data->active_button = toggle_button;
g_object_ref (data->active_button);
@@ -616,28 +616,28 @@ on_category_toggle (GtkToggleButton *toggle_button, gpointer user_data)
}
data->menu_width = menu_width;
- gtk_grab_add (GTK_WIDGET (self->priv->canvas));
+ gtk_grab_add (GTK_WIDGET (self->priv->view));
}
redraw_symbol_menu (self);
}
-#define DEFINE_CANVAS_HANDLER_FUNC(name) \
+#define DEFINE_VIEW_HANDLER_FUNC(name) \
static inline void \
-name ## _canvas_handlers (LdLibraryToolbar *self) \
+name ## _view_handlers (LdLibraryToolbar *self) \
{ \
gint i; \
- g_return_if_fail (LD_IS_CANVAS (self->priv->canvas)); \
- for (i = 0; i < CANVAS_HANDLER_COUNT; i++) \
- g_signal_handler_ ## name (self->priv->canvas, \
- self->priv->canvas_handlers[i]); \
+ g_return_if_fail (LD_IS_DIAGRAM_VIEW (self->priv->view)); \
+ for (i = 0; i < VIEW_HANDLER_COUNT; i++) \
+ g_signal_handler_ ## name (self->priv->view, \
+ self->priv->view_handlers[i]); \
}
-DEFINE_CANVAS_HANDLER_FUNC (block)
-DEFINE_CANVAS_HANDLER_FUNC (unblock)
-DEFINE_CANVAS_HANDLER_FUNC (disconnect)
+DEFINE_VIEW_HANDLER_FUNC (block)
+DEFINE_VIEW_HANDLER_FUNC (unblock)
+DEFINE_VIEW_HANDLER_FUNC (disconnect)
static gboolean
-on_canvas_exposed (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+on_view_exposed (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
cairo_t *cr;
LdLibraryToolbar *self;
@@ -702,7 +702,7 @@ on_canvas_exposed (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
}
static gboolean
-on_canvas_motion_notify (GtkWidget *widget, GdkEventMotion *event,
+on_view_motion_notify (GtkWidget *widget, GdkEventMotion *event,
gpointer user_data)
{
LdLibraryToolbar *self;
@@ -715,7 +715,7 @@ on_canvas_motion_notify (GtkWidget *widget, GdkEventMotion *event,
if (widget->window != event->window
|| event->x < 0 || event->y < data->menu_y
|| event->y >= data->menu_y + data->menu_height)
- goto on_canvas_motion_notify_end;
+ goto on_view_motion_notify_end;
for (x = i = 0; i < data->n_items; i++)
{
@@ -727,7 +727,7 @@ on_canvas_motion_notify (GtkWidget *widget, GdkEventMotion *event,
}
}
-on_canvas_motion_notify_end:
+on_view_motion_notify_end:
if (data->active_item != at_cursor)
{
emit_symbol_signal (self, LD_LIBRARY_TOOLBAR_GET_CLASS (self)
@@ -743,7 +743,7 @@ on_canvas_motion_notify_end:
}
static gboolean
-on_canvas_button_press (GtkWidget *widget, GdkEventButton *event,
+on_view_button_press (GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
LdLibraryToolbar *self;
@@ -764,7 +764,7 @@ on_canvas_button_press (GtkWidget *widget, GdkEventButton *event,
}
static gboolean
-on_canvas_button_release (GtkWidget *widget, GdkEventButton *event,
+on_view_button_release (GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
LdLibraryToolbar *self;
diff --git a/liblogdiag/ld-library-toolbar.h b/liblogdiag/ld-library-toolbar.h
index 96b9750..b1dc77a 100644
--- a/liblogdiag/ld-library-toolbar.h
+++ b/liblogdiag/ld-library-toolbar.h
@@ -59,9 +59,9 @@ GtkWidget *ld_library_toolbar_new (void);
void ld_library_toolbar_set_library (LdLibraryToolbar *self,
LdLibrary *library);
LdLibrary *ld_library_toolbar_get_library (LdLibraryToolbar *self);
-void ld_library_toolbar_set_canvas (LdLibraryToolbar *self,
- LdCanvas *canvas);
-LdCanvas *ld_library_toolbar_get_canvas (LdLibraryToolbar *self);
+void ld_library_toolbar_set_view (LdLibraryToolbar *self,
+ LdDiagramView *view);
+LdDiagramView *ld_library_toolbar_get_view (LdLibraryToolbar *self);
G_END_DECLS
diff --git a/liblogdiag/ld-symbol.c b/liblogdiag/ld-symbol.c
index bcc68d9..f353334 100644
--- a/liblogdiag/ld-symbol.c
+++ b/liblogdiag/ld-symbol.c
@@ -15,9 +15,9 @@
/**
* SECTION:ld-symbol
* @short_description: A symbol
- * @see_also: #LdDiagramSymbol, #LdCanvas
+ * @see_also: #LdDiagramSymbol, #LdDiagramView
*
- * #LdSymbol represents a symbol to be drawn onto a #LdCanvas.
+ * #LdSymbol represents a symbol to be drawn by #LdDiagramView.
*
* All implementations of this abstract class are required to use
* cairo_save() and cairo_restore() when drawing to store the state.
diff --git a/liblogdiag/liblogdiag.h b/liblogdiag/liblogdiag.h
index 0e17e92..2ec48bc 100644
--- a/liblogdiag/liblogdiag.h
+++ b/liblogdiag/liblogdiag.h
@@ -27,7 +27,7 @@
#include "ld-diagram-connection.h"
#include "ld-diagram.h"
-#include "ld-canvas.h"
+#include "ld-diagram-view.h"
#include "ld-library-toolbar.h"
#include "ld-lua.h"
--
cgit v1.2.3-70-g09d2