diff options
Diffstat (limited to 'liblogdiag')
-rw-r--r-- | liblogdiag/ld-canvas.c | 1417 | ||||
-rw-r--r-- | liblogdiag/ld-canvas.h | 91 | ||||
-rw-r--r-- | liblogdiag/ld-diagram-object.c | 186 | ||||
-rw-r--r-- | liblogdiag/ld-diagram-object.h | 65 | ||||
-rw-r--r-- | liblogdiag/ld-diagram-symbol.c | 171 | ||||
-rw-r--r-- | liblogdiag/ld-diagram-symbol.h | 64 | ||||
-rw-r--r-- | liblogdiag/ld-diagram.c | 550 | ||||
-rw-r--r-- | liblogdiag/ld-diagram.h | 98 | ||||
-rw-r--r-- | liblogdiag/ld-library.c | 524 | ||||
-rw-r--r-- | liblogdiag/ld-library.h | 73 | ||||
-rw-r--r-- | liblogdiag/ld-lua-private.h | 26 | ||||
-rw-r--r-- | liblogdiag/ld-lua-symbol-private.h | 40 | ||||
-rw-r--r-- | liblogdiag/ld-lua-symbol.c | 138 | ||||
-rw-r--r-- | liblogdiag/ld-lua-symbol.h | 60 | ||||
-rw-r--r-- | liblogdiag/ld-lua.c | 808 | ||||
-rw-r--r-- | liblogdiag/ld-lua.h | 70 | ||||
-rw-r--r-- | liblogdiag/ld-marshal.c | 88 | ||||
-rw-r--r-- | liblogdiag/ld-marshal.h | 20 | ||||
-rw-r--r-- | liblogdiag/ld-marshal.list | 1 | ||||
-rw-r--r-- | liblogdiag/ld-symbol-category.c | 339 | ||||
-rw-r--r-- | liblogdiag/ld-symbol-category.h | 79 | ||||
-rw-r--r-- | liblogdiag/ld-symbol.c | 232 | ||||
-rw-r--r-- | liblogdiag/ld-symbol.h | 74 | ||||
-rw-r--r-- | liblogdiag/ld-types.c | 221 | ||||
-rw-r--r-- | liblogdiag/ld-types.h | 101 | ||||
-rw-r--r-- | liblogdiag/liblogdiag.h | 34 |
26 files changed, 5570 insertions, 0 deletions
diff --git a/liblogdiag/ld-canvas.c b/liblogdiag/ld-canvas.c new file mode 100644 index 0000000..9523d9d --- /dev/null +++ b/liblogdiag/ld-canvas.c @@ -0,0 +1,1417 @@ +/* + * 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 <math.h> +#include <string.h> +#include <gdk/gdkkeysyms.h> + +#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 with mouse wheel. */ +#define ZOOM_WHEEL_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 +}; + +typedef struct _AddObjectData AddObjectData; + +struct _AddObjectData +{ + LdDiagramObject *object; + gboolean visible; +}; + +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. + * @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_highlighted; + + gint operation; + union + { + AddObjectData add_object; + } + 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 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); + +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 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 void move_object_to_coords (LdCanvas *self, LdDiagramObject *object, + gdouble x, gdouble y); +static LdDiagramObject *get_object_at_coords (LdCanvas *self, + gdouble x, gdouble y); +static gboolean is_object_selected (LdCanvas *self, LdDiagramObject *object); +static LdSymbol *resolve_diagram_symbol (LdCanvas *self, + LdDiagramSymbol *diagram_symbol); +static gboolean get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol, + LdRectangle *rect); +static gboolean get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol, + LdRectangle *rect); +static gboolean get_object_area (LdCanvas *self, LdDiagramObject *object, + LdRectangle *rect); +static gboolean object_hit_test (LdCanvas *self, LdDiagramObject *object, + gdouble x, gdouble y); +static void check_terminals (LdCanvas *self, gdouble x, gdouble y); +static void hide_terminals (LdCanvas *self); +static void queue_draw (LdCanvas *self, LdRectangle *rect); +static void queue_object_draw (LdCanvas *self, LdDiagramObject *object); +static void queue_terminal_draw (LdCanvas *self, LdPoint *terminal); + +static void ld_canvas_real_cancel_operation (LdCanvas *self); +static void ld_canvas_add_object_end (LdCanvas *self); + +static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event, + gpointer user_data); +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); + + +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; + + binding_set = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, + "cancel-operation", 0); + +/** + * 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: + * @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, + g_cclosure_user_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT); + +/** + * LdCanvas::cancel-operation: + * + * Cancel any current operation. + */ + klass->cancel_operation_signal = g_signal_new + ("cancel-operation", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (LdCanvasClass, cancel_operation), NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (LdCanvasPrivate)); +} + +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), 0, 0, 1, 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); + } +} + + +/* ===== Generic interface etc. ============================================ */ + +/** + * ld_canvas_new: + * + * Create an instance. + */ +LdCanvas * +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"); +} + + +/* ===== Operations ======================================================== */ + +static void +ld_canvas_real_cancel_operation (LdCanvas *self) +{ + g_return_if_fail (LD_IS_CANVAS (self)); + + if (self->priv->operation) + { + if (self->priv->operation_end) + self->priv->operation_end (self); + self->priv->operation = OPER_0; + self->priv->operation_end = NULL; + } +} + +/** + * ld_canvas_add_object_begin: + * @self: An #LdCanvas object. + * @object: (transfer full): The object to be added to the diagram. + * + * Begin an operation for adding an object into the diagram. + */ +void +ld_canvas_add_object_begin (LdCanvas *self, LdDiagramObject *object) +{ + AddObjectData *data; + + g_return_if_fail (LD_IS_CANVAS (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + ld_canvas_real_cancel_operation (self); + + self->priv->operation = OPER_ADD_OBJECT; + self->priv->operation_end = ld_canvas_add_object_end; + + data = &OPER_DATA (self, add_object); + data->object = object; +} + +static void +ld_canvas_add_object_end (LdCanvas *self) +{ + AddObjectData *data; + + data = &OPER_DATA (self, add_object); + if (data->object) + { + queue_object_draw (self, data->object); + g_object_unref (data->object); + data->object = NULL; + } +} + + +/* ===== Events, rendering ================================================= */ + +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 void +move_object_to_coords (LdCanvas *self, LdDiagramObject *object, + gdouble x, gdouble y) +{ + gdouble dx, dy; + + ld_canvas_widget_to_diagram_coords (self, x, y, &dx, &dy); + ld_diagram_object_set_x (object, floor (dx + 0.5)); + ld_diagram_object_set_y (object, floor (dy + 0.5)); +} + +static LdDiagramObject * +get_object_at_coords (LdCanvas *self, gdouble x, gdouble y) +{ + GList *objects, *iter; + + /* Iterate from the top object downwards. */ + 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 (object_hit_test (self, object, x, y)) + return object; + } + return NULL; +} + +static gboolean +is_object_selected (LdCanvas *self, LdDiagramObject *object) +{ + return g_list_find (ld_diagram_get_selection (self->priv->diagram), + object) != NULL; +} + +static LdSymbol * +resolve_diagram_symbol (LdCanvas *self, LdDiagramSymbol *diagram_symbol) +{ + if (!self->priv->library) + return NULL; + + return ld_library_find_symbol (self->priv->library, + ld_diagram_symbol_get_class (diagram_symbol)); +} + +static gboolean +get_symbol_area (LdCanvas *self, LdDiagramSymbol *symbol, LdRectangle *rect) +{ + LdDiagramObject *object; + gdouble object_x, object_y; + LdSymbol *library_symbol; + LdRectangle area; + gdouble x1, x2; + gdouble y1, y2; + + object = LD_DIAGRAM_OBJECT (symbol); + object_x = ld_diagram_object_get_x (object); + object_y = ld_diagram_object_get_y (object); + + library_symbol = resolve_diagram_symbol (self, symbol); + if (library_symbol) + ld_symbol_get_area (library_symbol, &area); + else + return FALSE; + + /* TODO: Rotate the rectangle for other orientations. */ + ld_canvas_diagram_to_widget_coords (self, + object_x + area.x, + object_y + area.y, + &x1, &y1); + ld_canvas_diagram_to_widget_coords (self, + object_x + area.x + area.width, + object_y + area.y + area.height, + &x2, &y2); + + rect->x = x1; + rect->y = y1; + rect->width = x2 - x1; + rect->height = y2 - y1; + return TRUE; +} + +static gboolean +get_symbol_clip_area (LdCanvas *self, LdDiagramSymbol *symbol, + LdRectangle *rect) +{ + LdRectangle object_rect; + + if (!get_object_area (self, LD_DIAGRAM_OBJECT (symbol), &object_rect)) + return FALSE; + + *rect = object_rect; + ld_rectangle_extend (rect, SYMBOL_CLIP_TOLERANCE); + return TRUE; +} + +static gboolean +get_object_area (LdCanvas *self, LdDiagramObject *object, LdRectangle *rect) +{ + if (LD_IS_DIAGRAM_SYMBOL (object)) + return get_symbol_area (self, LD_DIAGRAM_SYMBOL (object), rect); + return FALSE; +} + +static gboolean +object_hit_test (LdCanvas *self, LdDiagramObject *object, gdouble x, gdouble y) +{ + LdRectangle rect; + + if (!get_object_area (self, object, &rect)) + return FALSE; + ld_rectangle_extend (&rect, OBJECT_BORDER_TOLERANCE); + return ld_rectangle_contains (&rect, x, y); +} + +static void +check_terminals (LdCanvas *self, gdouble x, gdouble y) +{ + 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)) + { + LdDiagramObject *diagram_object; + gdouble object_x, object_y; + LdDiagramSymbol *diagram_symbol; + LdSymbol *symbol; + const LdPointArray *terminals; + gint i; + + if (!LD_IS_DIAGRAM_SYMBOL (iter->data)) + continue; + + diagram_symbol = LD_DIAGRAM_SYMBOL (iter->data); + symbol = resolve_diagram_symbol (self, diagram_symbol); + if (!symbol) + continue; + + diagram_object = LD_DIAGRAM_OBJECT (iter->data); + object_x = ld_diagram_object_get_x (diagram_object); + object_y = ld_diagram_object_get_y (diagram_object); + + terminals = ld_symbol_get_terminals (symbol); + + for (i = 0; i < terminals->num_points; i++) + { + LdPoint cur_term; + gdouble distance; + + cur_term = terminals->points[i]; + cur_term.x += object_x; + cur_term.y += object_y; + ld_canvas_diagram_to_widget_coords (self, + cur_term.x, cur_term.y, &cur_term.x, &cur_term.y); + + distance = ld_point_distance (&cur_term, x, y); + if (distance <= closest_distance) + { + closest_symbol = diagram_symbol; + closest_distance = distance; + closest_terminal = cur_term; + } + } + } + + hide_terminals (self); + + if (closest_symbol) + { + self->priv->terminal_highlighted = TRUE; + self->priv->terminal = closest_terminal; + queue_terminal_draw (self, &closest_terminal); + } +} + +static void +hide_terminals (LdCanvas *self) +{ + if (self->priv->terminal_highlighted) + { + self->priv->terminal_highlighted = FALSE; + queue_terminal_draw (self, &self->priv->terminal); + } +} + +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) +{ + if (LD_IS_DIAGRAM_SYMBOL (object)) + { + LdRectangle rect; + + if (!get_symbol_clip_area (self, LD_DIAGRAM_SYMBOL (object), &rect)) + return; + queue_draw (self, &rect); + } +} + +static void +queue_terminal_draw (LdCanvas *self, LdPoint *terminal) +{ + LdRectangle rect; + + rect.x = terminal->x - TERMINAL_RADIUS; + rect.y = terminal->y - TERMINAL_RADIUS; + rect.width = 2 * TERMINAL_RADIUS; + rect.height = 2 * TERMINAL_RADIUS; + queue_draw (self, &rect); +} + +static 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) +{ + LdCanvas *self; + + self = LD_CANVAS (widget); + switch (self->priv->operation) + { + AddObjectData *data; + + case OPER_ADD_OBJECT: + data = &OPER_DATA (self, add_object); + data->visible = TRUE; + + queue_object_draw (self, data->object); + move_object_to_coords (self, data->object, event->x, event->y); + queue_object_draw (self, data->object); + break; + case OPER_0: + check_terminals (self, event->x, event->y); + 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) +{ + LdCanvas *self; + + if (!gtk_widget_has_focus (widget)) + gtk_widget_grab_focus (widget); + + self = LD_CANVAS (widget); + switch (self->priv->operation) + { + AddObjectData *data; + + case OPER_ADD_OBJECT: + data = &OPER_DATA (self, add_object); + + queue_object_draw (self, data->object); + move_object_to_coords (self, data->object, event->x, event->y); + + if (self->priv->diagram) + ld_diagram_insert_object (self->priv->diagram, data->object, 0); + + /* XXX: "cancel" causes confusion. */ + ld_canvas_real_cancel_operation (self); + break; + case OPER_0: + if (self->priv->diagram) + { + LdDiagramObject *object; + + if (event->state != GDK_SHIFT_MASK) + ld_diagram_unselect_all (self->priv->diagram); + + object = get_object_at_coords (self, event->x, event->y); + if (object) + ld_diagram_selection_add (self->priv->diagram, object, 0); + } + break; + } + return FALSE; +} + +static gboolean +on_button_release (GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + return FALSE; +} + +static gboolean +on_scroll (GtkWidget *widget, GdkEventScroll *event, gpointer user_data) +{ + gdouble prev_x, prev_y; + gdouble new_x, new_y; + LdCanvas *self; + + 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_set_zoom (self, self->priv->zoom * ZOOM_WHEEL_STEP); + break; + case GDK_SCROLL_DOWN: + ld_canvas_set_zoom (self, self->priv->zoom / ZOOM_WHEEL_STEP); + 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, event->x, event->y); + 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); + + cairo_destroy (data.cr); + return FALSE; +} + +static void +draw_grid (GtkWidget *widget, DrawData *data) +{ + gdouble grid_step; + gdouble x_init, y_init; + gdouble x, y; + + grid_step = data->scale; + while (grid_step < 5) + grid_step *= 5; + + 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_ROUND); + + /* 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); + ld_canvas_diagram_to_widget_coords (data->self, + ceil (x_init), ceil (y_init), &x_init, &y_init); + + /* Iterate over all the points. */ + for (x = x_init; x <= data->exposed_rect.x + data->exposed_rect.width; + x += grid_step) + { + for (y = y_init; y <= data->exposed_rect.y + data->exposed_rect.height; + y += grid_step) + { + cairo_move_to (data->cr, x, y); + cairo_line_to (data->cr, x, y); + } + } + cairo_stroke (data->cr); +} + +static void +draw_terminal (GtkWidget *widget, DrawData *data) +{ + LdCanvasPrivate *priv; + + priv = data->self->priv; + if (!priv->terminal_highlighted) + return; + + ld_canvas_color_apply (COLOR_GET (data->self, COLOR_TERMINAL), data->cr); + cairo_set_line_width (data->cr, 1); + + cairo_new_path (data->cr); + cairo_arc (data->cr, priv->terminal.x, priv->terminal.y, + 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 = g_list_last (objects); iter; iter = g_list_previous (iter)) + draw_object (LD_DIAGRAM_OBJECT (iter->data), data); + + switch (data->self->priv->operation) + { + AddObjectData *op_data; + + case OPER_ADD_OBJECT: + op_data = &OPER_DATA (data->self, add_object); + if (op_data->visible) + draw_object (op_data->object, data); + break; + } + + cairo_restore (data->cr); +} + +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); +} + +static void +draw_symbol (LdDiagramSymbol *diagram_symbol, DrawData *data) +{ + LdSymbol *symbol; + LdRectangle clip_rect; + gdouble x, y; + + symbol = resolve_diagram_symbol (data->self, diagram_symbol); + + /* TODO: Resolve this better; draw a cross or whatever. */ + if (!symbol) + { + g_warning ("Cannot find symbol %s in the library.", + ld_diagram_symbol_get_class (diagram_symbol)); + 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); + + /* TODO: Rotate the space for other orientations. */ + ld_canvas_diagram_to_widget_coords (data->self, + ld_diagram_object_get_x (LD_DIAGRAM_OBJECT (diagram_symbol)), + ld_diagram_object_get_y (LD_DIAGRAM_OBJECT (diagram_symbol)), + &x, &y); + cairo_translate (data->cr, x, y); + cairo_scale (data->cr, data->scale, data->scale); + ld_symbol_draw (symbol, data->cr); + + cairo_restore (data->cr); +} diff --git a/liblogdiag/ld-canvas.h b/liblogdiag/ld-canvas.h new file mode 100644 index 0000000..702f2fe --- /dev/null +++ b/liblogdiag/ld-canvas.h @@ -0,0 +1,91 @@ +/* + * ld-canvas.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. 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; + +/*< public >*/ +}; + +struct _LdCanvasClass +{ +/*< private >*/ + GtkDrawingAreaClass parent_class; + + guint cancel_operation_signal; + + void (*set_scroll_adjustments) (LdCanvas *self, + GtkAdjustment *horizontal, GtkAdjustment *vertical); + void (*cancel_operation) (LdCanvas *self); +}; + + +/** + * LD_CANVAS_BASE_UNIT: + * + * Length of the base unit in milimetres. + */ +#define LD_CANVAS_BASE_UNIT_LENGTH 2.5 + + +GType ld_canvas_get_type (void) G_GNUC_CONST; + +LdCanvas *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); + +void ld_canvas_add_object_begin (LdCanvas *self, LdDiagramObject *object); + +/* TODO: The rest of the interface. */ + + +G_END_DECLS + +#endif /* ! __LD_CANVAS_H__ */ diff --git a/liblogdiag/ld-diagram-object.c b/liblogdiag/ld-diagram-object.c new file mode 100644 index 0000000..f43e620 --- /dev/null +++ b/liblogdiag/ld-diagram-object.c @@ -0,0 +1,186 @@ +/* + * ld-diagram-object.c + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#include "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-diagram-object + * @short_description: A diagram object. + * @see_also: #LdDiagram, #LdCanvas + * + * #LdDiagramObject represents an object in an #LdDiagram. + */ + +/* + * LdDiagramObjectPrivate: + * @x: The X coordinate of this object. + * @y: The Y coordinate of this object. + */ +struct _LdDiagramObjectPrivate +{ + gdouble x; + gdouble y; +}; + +enum +{ + PROP_0, + PROP_X, + PROP_Y +}; + +static void ld_diagram_object_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +static void ld_diagram_object_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); + + +G_DEFINE_ABSTRACT_TYPE (LdDiagramObject, ld_diagram_object, G_TYPE_OBJECT); + +static void +ld_diagram_object_class_init (LdDiagramObjectClass *klass) +{ + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = ld_diagram_object_get_property; + object_class->set_property = ld_diagram_object_set_property; + +/** + * LdDiagramObject:x: + * + * The X coordinate of the object. + */ + pspec = g_param_spec_double ("x", "X", + "The X coordinate of this object.", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_X, pspec); + +/** + * LdDiagramObject:y: + * + * The Y coordinate of the object. + */ + pspec = g_param_spec_double ("y", "Y", + "The Y coordinate of this object.", + -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_Y, pspec); + + g_type_class_add_private (klass, sizeof (LdDiagramObjectPrivate)); +} + +static void +ld_diagram_object_init (LdDiagramObject *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_DIAGRAM_OBJECT, LdDiagramObjectPrivate); +} + +static void +ld_diagram_object_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LdDiagramObject *self; + + self = LD_DIAGRAM_OBJECT (object); + switch (property_id) + { + case PROP_X: + g_value_set_double (value, ld_diagram_object_get_x (self)); + break; + case PROP_Y: + g_value_set_double (value, ld_diagram_object_get_y (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_diagram_object_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + LdDiagramObject *self; + + self = LD_DIAGRAM_OBJECT (object); + switch (property_id) + { + case PROP_X: + ld_diagram_object_set_x (self, g_value_get_double (value)); + break; + case PROP_Y: + ld_diagram_object_set_y (self, g_value_get_double (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + + +/** + * ld_diagram_object_get_x: + * @self: An #LdDiagramObject object. + * + * Return value: The X coordinate of the object. + */ +gdouble +ld_diagram_object_get_x (LdDiagramObject *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM_OBJECT (self), 0); + return self->priv->x; +} + +/** + * ld_diagram_object_get_y: + * @self: An #LdDiagramObject object. + * + * Return value: The Y coordinate of the object. + */ +gdouble +ld_diagram_object_get_y (LdDiagramObject *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM_OBJECT (self), 0); + return self->priv->y; +} + +/** + * ld_diagram_object_set_x: + * @self: An #LdDiagramObject object. + * @x: The new X coordinate. + * + * Set the X coordinate of the object. + */ +void +ld_diagram_object_set_x (LdDiagramObject *self, gdouble x) +{ + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (self)); + self->priv->x = x; + + g_object_notify (G_OBJECT (self), "x"); +} + +/** + * ld_diagram_object_set_y: + * @self: An #LdDiagramObject object. + * @y: The new Y coordinate. + * + * Set the Y coordinate of the object. + */ +void +ld_diagram_object_set_y (LdDiagramObject *self, gdouble y) +{ + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (self)); + self->priv->y = y; + + g_object_notify (G_OBJECT (self), "y"); +} diff --git a/liblogdiag/ld-diagram-object.h b/liblogdiag/ld-diagram-object.h new file mode 100644 index 0000000..c727602 --- /dev/null +++ b/liblogdiag/ld-diagram-object.h @@ -0,0 +1,65 @@ +/* + * ld-diagram-object.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_DIAGRAM_OBJECT_H__ +#define __LD_DIAGRAM_OBJECT_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_DIAGRAM_OBJECT (ld_diagram_object_get_type ()) +#define LD_DIAGRAM_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_DIAGRAM_OBJECT, LdDiagramObject)) +#define LD_DIAGRAM_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_DIAGRAM_OBJECT, LdDiagramObjectClass)) +#define LD_IS_DIAGRAM_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_DIAGRAM_OBJECT)) +#define LD_IS_DIAGRAM_OBJECT_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_DIAGRAM_OBJECT)) +#define LD_DIAGRAM_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_DIAGRAM_OBJECT, LdDiagramObjectClass)) + +typedef struct _LdDiagramObject LdDiagramObject; +typedef struct _LdDiagramObjectPrivate LdDiagramObjectPrivate; +typedef struct _LdDiagramObjectClass LdDiagramObjectClass; + + +/** + * LdDiagramObject: + */ +struct _LdDiagramObject +{ +/*< private >*/ + GObject parent_instance; + LdDiagramObjectPrivate *priv; +}; + +/** + * LdDiagramObjectClass: + */ +struct _LdDiagramObjectClass +{ +/*< private >*/ + GObjectClass parent_class; +}; + + +GType ld_diagram_object_get_type (void) G_GNUC_CONST; + +gdouble ld_diagram_object_get_x (LdDiagramObject *self); +gdouble ld_diagram_object_get_y (LdDiagramObject *self); +void ld_diagram_object_set_x (LdDiagramObject *self, gdouble x); +void ld_diagram_object_set_y (LdDiagramObject *self, gdouble y); + + +G_END_DECLS + +#endif /* ! __LD_DIAGRAM_OBJECT_H__ */ + diff --git a/liblogdiag/ld-diagram-symbol.c b/liblogdiag/ld-diagram-symbol.c new file mode 100644 index 0000000..3308dbb --- /dev/null +++ b/liblogdiag/ld-diagram-symbol.c @@ -0,0 +1,171 @@ +/* + * ld-diagram-symbol.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 "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-diagram-symbol + * @short_description: A symbol object. + * @see_also: #LdDiagramObject + * + * #LdDiagramSymbol is an implementation of #LdDiagramObject. + */ + +/* + * LdDiagramSymbolPrivate: + * @klass: The class of this symbol. + */ +struct _LdDiagramSymbolPrivate +{ + gchar *klass; +}; + +enum +{ + PROP_0, + PROP_CLASS +}; + +static void ld_diagram_symbol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +static void ld_diagram_symbol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); +static void ld_diagram_symbol_finalize (GObject *gobject); + + +G_DEFINE_TYPE (LdDiagramSymbol, ld_diagram_symbol, LD_TYPE_DIAGRAM_OBJECT); + +static void +ld_diagram_symbol_class_init (LdDiagramSymbolClass *klass) +{ + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = ld_diagram_symbol_get_property; + object_class->set_property = ld_diagram_symbol_set_property; + object_class->finalize = ld_diagram_symbol_finalize; + +/** + * LdDiagramSymbol:class: + * + * The class of this symbol. + */ + pspec = g_param_spec_string ("class", "Class", + "The class of this symbol.", + "", G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_CLASS, pspec); + + g_type_class_add_private (klass, sizeof (LdDiagramSymbolPrivate)); +} + +static void +ld_diagram_symbol_init (LdDiagramSymbol *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_DIAGRAM_SYMBOL, LdDiagramSymbolPrivate); +} + +static void +ld_diagram_symbol_finalize (GObject *gobject) +{ + LdDiagramSymbol *self; + + self = LD_DIAGRAM_SYMBOL (gobject); + + if (self->priv->klass) + g_free (self->priv->klass); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_diagram_symbol_parent_class)->finalize (gobject); +} + +static void +ld_diagram_symbol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LdDiagramSymbol *self; + + self = LD_DIAGRAM_SYMBOL (object); + switch (property_id) + { + case PROP_CLASS: + g_value_set_string (value, ld_diagram_symbol_get_class (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_diagram_symbol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + LdDiagramSymbol *self; + + self = LD_DIAGRAM_SYMBOL (object); + switch (property_id) + { + case PROP_CLASS: + ld_diagram_symbol_set_class (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + + +/** + * ld_diagram_symbol_new: + * @klass: The class of the new symbol. + * + * Return value: A new #LdDiagramSymbol object. + */ +LdDiagramSymbol * +ld_diagram_symbol_new (const gchar *klass) +{ + LdDiagramSymbol *self; + + self = g_object_new (LD_TYPE_DIAGRAM_SYMBOL, NULL); + ld_diagram_symbol_set_class (self, klass); + return self; +} + +/** + * ld_diagram_symbol_get_class: + * @self: An #LdDiagramSymbol object. + * + * Return value: The class of the symbol. + */ +const gchar * +ld_diagram_symbol_get_class (LdDiagramSymbol *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM_SYMBOL (self), NULL); + return self->priv->klass; +} + +/** + * ld_diagram_symbol_get_class: + * @self: An #LdDiagramSymbol object. + * @klass: The class. + * + * Set the class of the symbol. + */ +void +ld_diagram_symbol_set_class (LdDiagramSymbol *self, const gchar *klass) +{ + g_return_if_fail (LD_IS_DIAGRAM_SYMBOL (self)); + + if (self->priv->klass) + g_free (self->priv->klass); + self->priv->klass = g_strdup (klass); +} diff --git a/liblogdiag/ld-diagram-symbol.h b/liblogdiag/ld-diagram-symbol.h new file mode 100644 index 0000000..09d8739 --- /dev/null +++ b/liblogdiag/ld-diagram-symbol.h @@ -0,0 +1,64 @@ +/* + * ld-diagram-symbol.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_DIAGRAM_SYMBOL_H__ +#define __LD_DIAGRAM_SYMBOL_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_DIAGRAM_SYMBOL (ld_diagram_symbol_get_type ()) +#define LD_DIAGRAM_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_DIAGRAM_SYMBOL, LdDiagramSymbol)) +#define LD_DIAGRAM_SYMBOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_DIAGRAM_SYMBOL, LdDiagramSymbolClass)) +#define LD_IS_DIAGRAM_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_DIAGRAM_SYMBOL)) +#define LD_IS_DIAGRAM_SYMBOL_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_DIAGRAM_SYMBOL)) +#define LD_DIAGRAM_SYMBOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_DIAGRAM_SYMBOL, LdDiagramSymbolClass)) + +typedef struct _LdDiagramSymbol LdDiagramSymbol; +typedef struct _LdDiagramSymbolPrivate LdDiagramSymbolPrivate; +typedef struct _LdDiagramSymbolClass LdDiagramSymbolClass; + + +/** + * LdDiagramSymbol: + */ +struct _LdDiagramSymbol +{ +/*< private >*/ + LdDiagramObject parent_instance; + LdDiagramSymbolPrivate *priv; +}; + +/** + * LdDiagramSymbolClass: + */ +struct _LdDiagramSymbolClass +{ +/*< private >*/ + LdDiagramObjectClass parent_class; +}; + + +GType ld_diagram_symbol_get_type (void) G_GNUC_CONST; + +LdDiagramSymbol *ld_diagram_symbol_new (const gchar *klass); +const gchar *ld_diagram_symbol_get_class (LdDiagramSymbol *self); +void ld_diagram_symbol_set_class (LdDiagramSymbol *self, const gchar *klass); + + +G_END_DECLS + +#endif /* ! __LD_DIAGRAM_SYMBOL_H__ */ + diff --git a/liblogdiag/ld-diagram.c b/liblogdiag/ld-diagram.c new file mode 100644 index 0000000..0129e2b --- /dev/null +++ b/liblogdiag/ld-diagram.c @@ -0,0 +1,550 @@ +/* + * ld-diagram.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 "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-diagram + * @short_description: A diagram object. + * @see_also: #LdCanvas + * + * #LdDiagram is a model used for storing diagrams. + */ + +/* + * LdDiagramPrivate: + * @modified: Whether the diagram has been modified. + * @objects: All objects in the diagram. + * @selection: All currently selected objects. + * @connections: Connections between objects. + */ +struct _LdDiagramPrivate +{ + gboolean modified; + + GList *objects; + GList *selection; + GList *connections; +}; + +enum +{ + PROP_0, + PROP_MODIFIED +}; + +static void ld_diagram_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +static void ld_diagram_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); +static void ld_diagram_dispose (GObject *gobject); +static void ld_diagram_finalize (GObject *gobject); + +static gboolean write_signature (GOutputStream *stream, GError **error); + +static void ld_diagram_real_changed (LdDiagram *self); +static void ld_diagram_clear_internal (LdDiagram *self); +static void ld_diagram_unselect_all_internal (LdDiagram *self); + + +G_DEFINE_TYPE (LdDiagram, ld_diagram, G_TYPE_OBJECT); + +static void +ld_diagram_class_init (LdDiagramClass *klass) +{ + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = ld_diagram_get_property; + object_class->set_property = ld_diagram_set_property; + object_class->dispose = ld_diagram_dispose; + object_class->finalize = ld_diagram_finalize; + + klass->changed = ld_diagram_real_changed; + +/** + * LdDiagram:modified: + * + * Whether the diagram has been modified. + */ + pspec = g_param_spec_boolean ("modified", "Modified", + "Whether the diagram has been modified.", + FALSE, G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MODIFIED, pspec); + +/** + * LdDiagram::changed: + * @diagram: The diagram object. + * + * Contents of the diagram have changed. + */ + klass->changed_signal = g_signal_new + ("changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LdDiagramClass, changed), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + +/** + * LdDiagram::selection-changed: + * @diagram: The diagram object. + * + * The current selection has changed. + */ + klass->selection_changed_signal = g_signal_new + ("selection-changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (LdDiagramClass, selection_changed), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (LdDiagramPrivate)); +} + +static void +ld_diagram_init (LdDiagram *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_DIAGRAM, LdDiagramPrivate); +} + +static void +ld_diagram_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LdDiagram *self; + + self = LD_DIAGRAM (object); + switch (property_id) + { + case PROP_MODIFIED: + g_value_set_boolean (value, ld_diagram_get_modified (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_diagram_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + LdDiagram *self; + + self = LD_DIAGRAM (object); + switch (property_id) + { + case PROP_MODIFIED: + ld_diagram_set_modified (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_diagram_dispose (GObject *gobject) +{ + LdDiagram *self; + + self = LD_DIAGRAM (gobject); + ld_diagram_clear_internal (self); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_diagram_parent_class)->dispose (gobject); +} + +static void +ld_diagram_finalize (GObject *gobject) +{ + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_diagram_parent_class)->finalize (gobject); +} + +static void +ld_diagram_real_changed (LdDiagram *self) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + + ld_diagram_set_modified (self, TRUE); +} + + +/** + * ld_diagram_new: + * + * Create an instance. + */ +LdDiagram * +ld_diagram_new (void) +{ + return g_object_new (LD_TYPE_DIAGRAM, NULL); +} + +/** + * ld_diagram_clear: + * @self: An #LdDiagram object. + * + * Clear the whole diagram with it's objects and selection. + */ +void +ld_diagram_clear (LdDiagram *self) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + + ld_diagram_clear_internal (self); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->changed_signal, 0); +} + +/* + * ld_diagram_clear_internal: + * @self: An #LdDiagram object. + * + * Do the same as ld_diagram_clear() does but don't emit signals. + */ +static void +ld_diagram_clear_internal (LdDiagram *self) +{ + ld_diagram_unselect_all (self); + + g_list_free (self->priv->connections); + self->priv->connections = NULL; + + g_list_foreach (self->priv->objects, (GFunc) g_object_unref, NULL); + g_list_free (self->priv->objects); + self->priv->objects = NULL; +} + +/** + * ld_diagram_load_from_file: + * @self: An #LdDiagram object. + * @filename: A filename. + * @error: Return location for a GError, or NULL. + * + * Load a file into the diagram. + * + * Return value: TRUE if the file could be loaded, FALSE otherwise. + */ +gboolean +ld_diagram_load_from_file (LdDiagram *self, + const gchar *filename, GError **error) +{ + JsonParser *parser; + GError *json_error; + + g_return_val_if_fail (LD_IS_DIAGRAM (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + /* TODO: Implement loading for real. This is just a stub. */ + parser = json_parser_new (); + + json_error = NULL; + json_parser_load_from_file (parser, filename, &json_error); + if (json_error) + { + g_propagate_error (error, json_error); + g_object_unref (parser); + return FALSE; + } + + ld_diagram_clear (self); + g_object_unref (parser); + return TRUE; +} + +/** + * ld_diagram_save_to_file: + * @self: An #LdDiagram object. + * @filename: A filename. + * @error: Return location for a GError, or NULL. + * + * Save the diagram into a file. + * + * Return value: TRUE if the diagram could be saved, FALSE otherwise. + */ +gboolean +ld_diagram_save_to_file (LdDiagram *self, + const gchar *filename, GError **error) +{ + GFile *file; + GFileOutputStream *file_stream; + JsonGenerator *generator; + JsonNode *root; + GError *local_error; + + g_return_val_if_fail (LD_IS_DIAGRAM (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + file = g_file_new_for_path (filename); + + local_error = NULL; + file_stream = g_file_replace (file, NULL, FALSE, + G_FILE_CREATE_NONE, NULL, &local_error); + g_object_unref (file); + + if (local_error) + { + g_propagate_error (error, local_error); + return FALSE; + } + + local_error = NULL; + write_signature (G_OUTPUT_STREAM (file_stream), &local_error); + if (local_error) + { + g_object_unref (file_stream); + g_propagate_error (error, local_error); + return FALSE; + } + + /* TODO: Implement saving for real. This is just a stub. */ + generator = json_generator_new (); + g_object_set (generator, "pretty", TRUE, NULL); + + /* XXX: json-glib dislikes empty objects. */ + root = json_node_new (JSON_NODE_OBJECT); + json_generator_set_root (generator, root); + json_node_free (root); + + local_error = NULL; + json_generator_to_stream (generator, G_OUTPUT_STREAM (file_stream), + NULL, &local_error); + g_object_unref (file_stream); + g_object_unref (generator); + + if (local_error) + { + g_propagate_error (error, local_error); + return FALSE; + } + return TRUE; +} + +static gboolean +write_signature (GOutputStream *stream, GError **error) +{ + static const gchar signature[] = "/* logdiag diagram */\n"; + GError *local_error = NULL; + + g_output_stream_write (stream, signature, sizeof (signature) - 1, + NULL, &local_error); + if (local_error) + { + g_propagate_error (error, local_error); + return FALSE; + } + return TRUE; +} + +/** + * ld_diagram_get_modified: + * @self: An #LdDiagram object. + * + * Return value: The modification status of diagram. + */ +gboolean +ld_diagram_get_modified (LdDiagram *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM (self), FALSE); + return self->priv->modified; +} + +/** + * ld_diagram_set_modified: + * @self: An #LdDiagram object. + * @value: Whether the diagram has been modified. + * + * Set the modification status of diagram. + */ +void +ld_diagram_set_modified (LdDiagram *self, gboolean value) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + self->priv->modified = value; + + g_object_notify (G_OBJECT (self), "modified"); +} + +/** + * ld_diagram_get_objects: + * @self: An #LdDiagram object. + * + * Get a list of objects in the diagram. Do not modify. + */ +GList * +ld_diagram_get_objects (LdDiagram *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM (self), NULL); + return self->priv->objects; +} + +/** + * ld_diagram_insert_object: + * @self: An #LdDiagram object. + * @object: The object to be inserted. + * @pos: The position at which the object is to be inserted. + * Negative values will append to the end. + * + * Insert an object into the diagram. + */ +void +ld_diagram_insert_object (LdDiagram *self, LdDiagramObject *object, gint pos) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + if (!g_list_find (self->priv->objects, object)) + { + self->priv->objects = + g_list_insert (self->priv->objects, object, pos); + g_object_ref (object); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->changed_signal, 0); + } +} + +/** + * ld_diagram_remove_object: + * @self: An #LdDiagram object. + * @object: The object to be removed. + * + * Remove an object from the diagram. + */ +void +ld_diagram_remove_object (LdDiagram *self, LdDiagramObject *object) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + if (g_list_find (self->priv->objects, object)) + { + ld_diagram_selection_remove (self, object); + + self->priv->objects = g_list_remove (self->priv->objects, object); + g_object_unref (object); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->changed_signal, 0); + } +} + +/** + * ld_diagram_get_selection: + * @self: An #LdDiagram object. + * + * Get a list of objects that are currently selected in the diagram. + * Do not modify. + */ +GList * +ld_diagram_get_selection (LdDiagram *self) +{ + g_return_val_if_fail (LD_IS_DIAGRAM (self), NULL); + return self->priv->selection; +} + +/** + * ld_diagram_selection_add: + * @self: An #LdDiagram object. + * @object: The object to be added to the selection. + * @pos: The position at which the object is to be inserted. + * Negative values will append to the end. + * + * Add an object to selection. + */ +void +ld_diagram_selection_add (LdDiagram *self, LdDiagramObject *object, gint pos) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + g_return_if_fail (g_list_find (self->priv->objects, object) != NULL); + + if (!g_list_find (self->priv->selection, object)) + { + self->priv->selection = + g_list_insert (self->priv->selection, object, pos); + g_object_ref (object); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->selection_changed_signal, 0); + } +} + +/** + * ld_diagram_selection_remove: + * @self: An #LdDiagram object. + * @object: The object to be removed from the selection. + * + * Remove an object from the selection. + */ +void +ld_diagram_selection_remove (LdDiagram *self, LdDiagramObject *object) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + g_return_if_fail (LD_IS_DIAGRAM_OBJECT (object)); + + if (g_list_find (self->priv->selection, object)) + { + self->priv->selection = g_list_remove (self->priv->selection, object); + g_object_unref (object); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->selection_changed_signal, 0); + } +} + +/** + * ld_diagram_select_all: + * @self: An #LdDiagram object. + * + * Include all objects in the document to the selection. + */ +void +ld_diagram_select_all (LdDiagram *self) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + + ld_diagram_unselect_all_internal (self); + + self->priv->selection = g_list_copy (self->priv->objects); + g_list_foreach (self->priv->selection, (GFunc) g_object_ref, NULL); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->selection_changed_signal, 0); +} + +/** + * ld_diagram_unselect_all: + * @self: An #LdDiagram object. + * + * Remove all objects from the current selection. + */ +void +ld_diagram_unselect_all (LdDiagram *self) +{ + g_return_if_fail (LD_IS_DIAGRAM (self)); + + ld_diagram_unselect_all_internal (self); + + g_signal_emit (self, + LD_DIAGRAM_GET_CLASS (self)->selection_changed_signal, 0); +} + +static void +ld_diagram_unselect_all_internal (LdDiagram *self) +{ + g_list_foreach (self->priv->selection, (GFunc) g_object_unref, NULL); + g_list_free (self->priv->selection); + self->priv->selection = NULL; +} diff --git a/liblogdiag/ld-diagram.h b/liblogdiag/ld-diagram.h new file mode 100644 index 0000000..f364189 --- /dev/null +++ b/liblogdiag/ld-diagram.h @@ -0,0 +1,98 @@ +/* + * ld-diagram.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_H__ +#define __LD_DIAGRAM_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_DIAGRAM (ld_diagram_get_type ()) +#define LD_DIAGRAM(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_DIAGRAM, LdDiagram)) +#define LD_DIAGRAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_DIAGRAM, LdDiagramClass)) +#define LD_IS_DIAGRAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_DIAGRAM)) +#define LD_IS_DIAGRAM_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_DIAGRAM)) +#define LD_DIAGRAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_DIAGRAM, LdDiagramClass)) + +typedef struct _LdDiagram LdDiagram; +typedef struct _LdDiagramClass LdDiagramClass; +typedef struct _LdDiagramPrivate LdDiagramPrivate; + + +/** + * LdDiagram: + * + * A diagram object. + */ +struct _LdDiagram +{ +/*< private >*/ + GObject parent_instance; + LdDiagramPrivate *priv; +}; + +struct _LdDiagramClass +{ +/*< private >*/ + GObjectClass parent_class; + + guint changed_signal; + guint selection_changed_signal; + + void (*changed) (LdDiagram *self); + void (*selection_changed) (LdDiagram *self); +}; + + +GType ld_diagram_get_type (void) G_GNUC_CONST; + +LdDiagram *ld_diagram_new (void); +void ld_diagram_clear (LdDiagram *self); +gboolean ld_diagram_load_from_file (LdDiagram *self, + const gchar *filename, GError **error); +gboolean ld_diagram_save_to_file (LdDiagram *self, + const gchar *filename, GError **error); + +gboolean ld_diagram_get_modified (LdDiagram *self); +void ld_diagram_set_modified (LdDiagram *self, gboolean value); + +GList *ld_diagram_get_objects (LdDiagram *self); +void ld_diagram_insert_object (LdDiagram *self, + LdDiagramObject *object, gint pos); +void ld_diagram_remove_object (LdDiagram *self, + LdDiagramObject *object); + +GList *ld_diagram_get_selection (LdDiagram *self); +void ld_diagram_selection_add (LdDiagram *self, + LdDiagramObject *object, gint pos); +void ld_diagram_selection_remove (LdDiagram *self, + LdDiagramObject *object); + +void ld_diagram_select_all (LdDiagram *self); +void ld_diagram_unselect_all (LdDiagram *self); + +/* +GList *ld_diagram_get_connections (LdDiagram *self); +void ld_diagram_connection_add (LdDiagram *self, + LdConnection *connection, gint pos); +void ld_diagram_connection_remove (LdDiagram *self, + LdConnection *connection); +*/ + + +G_END_DECLS + +#endif /* ! __LD_DIAGRAM_H__ */ + diff --git a/liblogdiag/ld-library.c b/liblogdiag/ld-library.c new file mode 100644 index 0000000..37e2bc9 --- /dev/null +++ b/liblogdiag/ld-library.c @@ -0,0 +1,524 @@ +/* + * ld-library.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 <string.h> + +#include "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-library + * @short_description: A symbol library. + * @see_also: #LdSymbol, #LdSymbolCategory + * + * #LdLibrary is used for loading symbols from their files. + */ + +/* + * LdLibraryPrivate: + * @lua: State of the scripting language. + * @children: Child objects of the library. + */ +struct _LdLibraryPrivate +{ + LdLua *lua; + GSList *children; +}; + +static void ld_library_finalize (GObject *gobject); + +static LdSymbolCategory *load_category (LdLibrary *self, + const gchar *path, const gchar *name); +static gboolean load_category_cb (const gchar *base, + const gchar *filename, gpointer userdata); +static void load_category_symbol_cb (LdSymbol *symbol, gpointer user_data); + +static gchar *read_human_name_from_file (const gchar *filename); + +static gboolean foreach_dir (const gchar *path, + gboolean (*callback) (const gchar *, const gchar *, gpointer), + gpointer userdata, GError **error); +static gboolean ld_library_load_cb + (const gchar *base, const gchar *filename, gpointer userdata); + + +G_DEFINE_TYPE (LdLibrary, ld_library, G_TYPE_OBJECT); + +static void +ld_library_class_init (LdLibraryClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ld_library_finalize; + +/** + * LdLibrary::changed: + * @library: The library object. + * + * Contents of the library have changed. + */ + klass->changed_signal = g_signal_new + ("changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (LdLibraryPrivate)); +} + +static void +ld_library_init (LdLibrary *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_LIBRARY, LdLibraryPrivate); + + self->priv->lua = ld_lua_new (); + self->priv->children = NULL; +} + +static void +ld_library_finalize (GObject *gobject) +{ + LdLibrary *self; + + self = LD_LIBRARY (gobject); + + g_object_unref (self->priv->lua); + + g_slist_foreach (self->priv->children, (GFunc) g_object_unref, NULL); + g_slist_free (self->priv->children); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_library_parent_class)->finalize (gobject); +} + +/** + * ld_library_new: + * + * Create an instance. + */ +LdLibrary * +ld_library_new (void) +{ + return g_object_new (LD_TYPE_LIBRARY, NULL); +} + +/* + * foreach_dir: + * + * Call a user-defined function for each file within a directory. + */ +static gboolean +foreach_dir (const gchar *path, + gboolean (*callback) (const gchar *, const gchar *, gpointer), + gpointer userdata, GError **error) +{ + GDir *dir; + const gchar *item; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + dir = g_dir_open (path, 0, error); + if (!dir) + return FALSE; + + while ((item = g_dir_read_name (dir))) + { + gchar *filename; + + filename = g_build_filename (path, item, NULL); + if (!callback (item, filename, userdata)) + break; + g_free (filename); + } + g_dir_close (dir); + return TRUE; +} + +/* + * LoadCategoryData: + * + * Data shared between load_category() and load_category_cb(). + */ +typedef struct +{ + LdLibrary *self; + LdSymbolCategory *cat; +} +LoadCategoryData; + +/* + * load_category: + * @self: An #LdLibrary object. + * @path: The path to the category. + * @name: The default name of the category. + * + * Loads a category into the library. + */ +static LdSymbolCategory * +load_category (LdLibrary *self, const gchar *path, const gchar *name) +{ + LdSymbolCategory *cat; + gchar *icon_file, *category_file; + gchar *human_name; + LoadCategoryData data; + + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) + goto load_category_fail_1; + + icon_file = g_build_filename (path, "icon.svg", NULL); + if (!g_file_test (icon_file, G_FILE_TEST_IS_REGULAR)) + { + g_warning ("The category in %s has no icon.", path); + goto load_category_fail_2; + } + + category_file = g_build_filename (path, "category.json", NULL); + human_name = read_human_name_from_file (category_file); + if (!human_name) + human_name = g_strdup (name); + + cat = ld_symbol_category_new (name, human_name); + ld_symbol_category_set_image_path (cat, icon_file); + + data.self = self; + data.cat = cat; + foreach_dir (path, load_category_cb, &data, NULL); + + g_free (human_name); + g_free (category_file); + g_free (icon_file); + return cat; + +load_category_fail_2: + g_free (icon_file); +load_category_fail_1: + return NULL; +} + +/* + * load_category_cb: + * + * Load script files from a directory into a symbol category. + */ +static gboolean +load_category_cb (const gchar *base, const gchar *filename, gpointer userdata) +{ + LoadCategoryData *data; + + g_return_val_if_fail (base != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (userdata != NULL, FALSE); + + data = (LoadCategoryData *) userdata; + + if (ld_lua_check_file (data->self->priv->lua, filename)) + ld_lua_load_file (data->self->priv->lua, filename, + load_category_symbol_cb, data->cat); + return TRUE; +} + +/* + * load_category_symbol_cb: + * + * Insert newly registered symbols into the category. + */ +static void +load_category_symbol_cb (LdSymbol *symbol, gpointer user_data) +{ + const gchar *name; + LdSymbolCategory *cat; + const GSList *children, *iter; + + g_return_if_fail (LD_IS_SYMBOL (symbol)); + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (user_data)); + + cat = LD_SYMBOL_CATEGORY (user_data); + name = ld_symbol_get_name (symbol); + + /* Check for name collisions with other symbols. */ + children = ld_symbol_category_get_children (cat); + for (iter = children; iter; iter = iter->next) + { + if (!LD_IS_SYMBOL (iter->data)) + continue; + if (!strcmp (name, ld_symbol_get_name (LD_SYMBOL (iter->data)))) + { + g_warning ("Attempted to insert multiple '%s' symbols into" + " category '%s'.", name, ld_symbol_category_get_name (cat)); + return; + } + } + ld_symbol_category_insert_child (cat, G_OBJECT (symbol), -1); +} + +/* + * read_human_name_from_file: + * @filename: Location of the JSON file. + * + * Read the human name of the processed category. + */ +static gchar * +read_human_name_from_file (const gchar *filename) +{ + const gchar *const *lang; + JsonParser *parser; + JsonNode *root; + JsonObject *object; + GError *error; + + g_return_val_if_fail (filename != NULL, NULL); + + parser = json_parser_new (); + error = NULL; + if (!json_parser_load_from_file (parser, filename, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + goto read_human_name_from_file_end; + } + + root = json_parser_get_root (parser); + if (!JSON_NODE_HOLDS_OBJECT (root)) + { + g_warning ("Failed to parse '%s': %s", filename, + "The root node is not an object."); + goto read_human_name_from_file_end; + } + + object = json_node_get_object (root); + for (lang = g_get_language_names (); *lang; lang++) + { + const gchar *member; + + if (!json_object_has_member (object, *lang)) + continue; + member = json_object_get_string_member (object, *lang); + + if (member != NULL) + { + gchar *result; + + result = g_strdup (member); + g_object_unref (parser); + return result; + } + } + +read_human_name_from_file_end: + g_object_unref (parser); + return NULL; +} + +/* + * LibraryLoadData: + * + * Data shared between ld_library_load() and ld_library_load_cb(). + */ +typedef struct +{ + LdLibrary *self; + gboolean changed; +} +LibraryLoadData; + +/** + * ld_library_load: + * @self: An #LdLibrary object. + * @directory: A directory to be loaded. + * + * Load the contents of a directory into the library. + */ +gboolean +ld_library_load (LdLibrary *self, const gchar *directory) +{ + LibraryLoadData data; + + g_return_val_if_fail (LD_IS_LIBRARY (self), FALSE); + g_return_val_if_fail (directory != NULL, FALSE); + + data.self = self; + data.changed = FALSE; + foreach_dir (directory, ld_library_load_cb, &data, NULL); + + if (data.changed) + g_signal_emit (self, LD_LIBRARY_GET_CLASS (self)->changed_signal, 0); + + return TRUE; +} + +/* + * ld_library_load_cb: + * + * A callback that's called for each file in the root directory. + */ +static gboolean +ld_library_load_cb (const gchar *base, const gchar *filename, gpointer userdata) +{ + LdSymbolCategory *cat; + LibraryLoadData *data; + + g_return_val_if_fail (base != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (userdata != NULL, FALSE); + + data = (LibraryLoadData *) userdata; + + cat = load_category (data->self, filename, base); + if (cat) + ld_library_insert_child (data->self, G_OBJECT (cat), -1); + + data->changed = TRUE; + return TRUE; +} + +/** + * ld_library_find_symbol: + * @self: An #LdLibrary object. + * @identifier: An identifier of the symbol to be searched for. + * + * Search for a symbol in the library. + * + * Return value: A symbol object if found, NULL otherwise. + */ +/* XXX: With this level of indentation, this function is really ugly. */ +LdSymbol * +ld_library_find_symbol (LdLibrary *self, const gchar *identifier) +{ + gchar **id_el_start, **id_el; + const GSList *list, *list_el; + + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + g_return_val_if_fail (identifier != NULL, NULL); + + id_el_start = g_strsplit (identifier, LD_LIBRARY_IDENTIFIER_SEPARATOR, 0); + if (!id_el_start) + return NULL; + + list = ld_library_get_children (self); + for (id_el = id_el_start; id_el[0]; id_el++) + { + LdSymbolCategory *cat; + LdSymbol *symbol; + gboolean found = FALSE; + + for (list_el = list; list_el; list_el = g_slist_next (list_el)) + { + /* If the current identifier element is a category (not last) + * and this list element is a category. + */ + if (id_el[1] && LD_IS_SYMBOL_CATEGORY (list_el->data)) + { + cat = LD_SYMBOL_CATEGORY (list_el->data); + if (strcmp (id_el[0], ld_symbol_category_get_name (cat))) + continue; + + list = ld_symbol_category_get_children (cat); + found = TRUE; + break; + } + /* If the current identifier element is a symbol (last) + * and this list element is a symbol. + */ + else if (!id_el[1] && LD_IS_SYMBOL (list_el->data)) + { + symbol = LD_SYMBOL (list_el->data); + if (strcmp (id_el[0], ld_symbol_get_name (symbol))) + continue; + + g_strfreev (id_el_start); + return symbol; + } + } + + if (!found) + break; + } + g_strfreev (id_el_start); + return NULL; +} + +/** + * ld_library_clear: + * @self: An #LdLibrary object. + * + * Clear all the contents. + */ +void +ld_library_clear (LdLibrary *self) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + + g_slist_foreach (self->priv->children, (GFunc) g_object_unref, NULL); + g_slist_free (self->priv->children); + self->priv->children = NULL; + + g_signal_emit (self, + LD_LIBRARY_GET_CLASS (self)->changed_signal, 0); +} + +/** + * ld_library_insert_child: + * @self: An #LdLibrary object. + * @child: The child to be inserted. + * @pos: The position at which the child will be inserted. + * Negative values will append to the end of list. + * + * Insert a child into the library. + */ +void +ld_library_insert_child (LdLibrary *self, GObject *child, gint pos) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + g_object_ref (child); + self->priv->children = g_slist_insert (self->priv->children, child, pos); +} + +/** + * ld_library_remove_child: + * @self: An #LdLibrary object. + * @child: The child to be removed. + * + * Remove a child from the library. + */ +void +ld_library_remove_child (LdLibrary *self, GObject *child) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + if (g_slist_find (self->priv->children, child)) + { + g_object_unref (child); + self->priv->children = g_slist_remove (self->priv->children, child); + } +} + +/** + * ld_library_get_children: + * @self: An #LdLibrary object. + * + * Return value: The internal list of children. Do not modify. + */ +const GSList * +ld_library_get_children (LdLibrary *self) +{ + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + return self->priv->children; +} + diff --git a/liblogdiag/ld-library.h b/liblogdiag/ld-library.h new file mode 100644 index 0000000..26521ed --- /dev/null +++ b/liblogdiag/ld-library.h @@ -0,0 +1,73 @@ +/* + * ld-library.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_LIBRARY_H__ +#define __LD_LIBRARY_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_LIBRARY (ld_library_get_type ()) +#define LD_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_LIBRARY, LdLibrary)) +#define LD_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_LIBRARY, LdLibraryClass)) +#define LD_IS_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_LIBRARY)) +#define LD_IS_LIBRARY_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_LIBRARY)) +#define LD_LIBRARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_LIBRARY, LdLibraryClass)) + +typedef struct _LdLibrary LdLibrary; +typedef struct _LdLibraryPrivate LdLibraryPrivate; +typedef struct _LdLibraryClass LdLibraryClass; + + +struct _LdLibrary +{ +/*< private >*/ + GObject parent_instance; + LdLibraryPrivate *priv; +}; + +struct _LdLibraryClass +{ +/*< private >*/ + GObjectClass parent_class; + + guint changed_signal; +}; + + +/** + * LD_LIBRARY_IDENTIFIER_SEPARATOR: + * + * Defines a string that separates categories and symbols in identifiers. + */ +#define LD_LIBRARY_IDENTIFIER_SEPARATOR "/" + + +GType ld_library_get_type (void) G_GNUC_CONST; + +LdLibrary *ld_library_new (void); +gboolean ld_library_load (LdLibrary *self, const gchar *directory); +LdSymbol *ld_library_find_symbol (LdLibrary *self, const gchar *identifier); +void ld_library_clear (LdLibrary *self); + +void ld_library_insert_child (LdLibrary *self, GObject *child, gint pos); +void ld_library_remove_child (LdLibrary *self, GObject *child); +const GSList *ld_library_get_children (LdLibrary *self); + + +G_END_DECLS + +#endif /* ! __LD_LIBRARY_H__ */ + diff --git a/liblogdiag/ld-lua-private.h b/liblogdiag/ld-lua-private.h new file mode 100644 index 0000000..7d6943a --- /dev/null +++ b/liblogdiag/ld-lua-private.h @@ -0,0 +1,26 @@ +/* + * ld-lua-private.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_LUA_PRIVATE_H__ +#define __LD_LUA_PRIVATE_H__ + +G_BEGIN_DECLS + + +/*< private_header >*/ + +void ld_lua_private_unregister (LdLua *self, LdLuaSymbol *symbol); +void ld_lua_private_draw (LdLua *self, LdLuaSymbol *symbol, cairo_t *cr); + + +G_END_DECLS + +#endif /* ! __LD_LUA_PRIVATE_H__ */ + diff --git a/liblogdiag/ld-lua-symbol-private.h b/liblogdiag/ld-lua-symbol-private.h new file mode 100644 index 0000000..83bc646 --- /dev/null +++ b/liblogdiag/ld-lua-symbol-private.h @@ -0,0 +1,40 @@ +/* + * ld-lua-symbol-private.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_LUA_SYMBOL_PRIVATE_H__ +#define __LD_LUA_SYMBOL_PRIVATE_H__ + +G_BEGIN_DECLS + + +/*< private_header >*/ + +/* + * LdLuaSymbolPrivate: + * @lua: Parent #LdLua object. + * @name: Name of this symbol. + * @human_name: Localized human name of this symbol. + * @area: Area of this symbol. + * @terminals: Terminals of this symbol. + */ +struct _LdLuaSymbolPrivate +{ + LdLua *lua; + gchar *name; + gchar *human_name; + LdRectangle area; + LdPointArray *terminals; +}; + + +G_END_DECLS + +#endif /* ! __LD_LUA_SYMBOL_PRIVATE_H__ */ + diff --git a/liblogdiag/ld-lua-symbol.c b/liblogdiag/ld-lua-symbol.c new file mode 100644 index 0000000..27a6279 --- /dev/null +++ b/liblogdiag/ld-lua-symbol.c @@ -0,0 +1,138 @@ +/* + * ld-lua-symbol.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 "liblogdiag.h" +#include "config.h" + +#include "ld-lua-private.h" +#include "ld-lua-symbol-private.h" + + +/** + * SECTION:ld-lua-symbol + * @short_description: A symbol. + * @see_also: #LdSymbol + * + * #LdLuaSymbol is an implementation of #LdSymbol. + */ + +static void ld_lua_symbol_finalize (GObject *gobject); + +static const gchar *ld_lua_symbol_real_get_name (LdSymbol *symbol); +static const gchar *ld_lua_symbol_real_get_human_name (LdSymbol *symbol); +static void ld_lua_symbol_real_get_area (LdSymbol *symbol, LdRectangle *area); +static const LdPointArray *ld_lua_symbol_real_get_terminals (LdSymbol *symbol); +static void ld_lua_symbol_real_draw (LdSymbol *symbol, cairo_t *cr); + + +G_DEFINE_TYPE (LdLuaSymbol, ld_lua_symbol, LD_TYPE_SYMBOL); + +static void +ld_lua_symbol_class_init (LdLuaSymbolClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ld_lua_symbol_finalize; + + klass->parent_class.get_name = ld_lua_symbol_real_get_name; + klass->parent_class.get_human_name = ld_lua_symbol_real_get_human_name; + klass->parent_class.get_area = ld_lua_symbol_real_get_area; + klass->parent_class.get_terminals = ld_lua_symbol_real_get_terminals; + klass->parent_class.draw = ld_lua_symbol_real_draw; + + g_type_class_add_private (klass, sizeof (LdLuaSymbolPrivate)); +} + +static void +ld_lua_symbol_init (LdLuaSymbol *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_LUA_SYMBOL, LdLuaSymbolPrivate); +} + +static void +ld_lua_symbol_finalize (GObject *gobject) +{ + LdLuaSymbol *self; + + self = LD_LUA_SYMBOL (gobject); + + if (self->priv->lua) + { + ld_lua_private_unregister (self->priv->lua, self); + g_object_unref (self->priv->lua); + } + + if (self->priv->name) + g_free (self->priv->name); + if (self->priv->human_name) + g_free (self->priv->human_name); + + if (self->priv->terminals) + ld_point_array_free (self->priv->terminals); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_lua_symbol_parent_class)->finalize (gobject); +} + + +static const gchar * +ld_lua_symbol_real_get_name (LdSymbol *symbol) +{ + g_return_val_if_fail (LD_IS_LUA_SYMBOL (symbol), NULL); + return LD_LUA_SYMBOL (symbol)->priv->name; +} + +static const gchar * +ld_lua_symbol_real_get_human_name (LdSymbol *symbol) +{ + g_return_val_if_fail (LD_IS_LUA_SYMBOL (symbol), NULL); + return LD_LUA_SYMBOL (symbol)->priv->human_name; +} + +static void +ld_lua_symbol_real_get_area (LdSymbol *symbol, LdRectangle *area) +{ + LdLuaSymbol *self; + + g_return_if_fail (LD_IS_LUA_SYMBOL (symbol)); + g_return_if_fail (area != NULL); + + self = LD_LUA_SYMBOL (symbol); + *area = self->priv->area; +} + +static const LdPointArray * +ld_lua_symbol_real_get_terminals (LdSymbol *symbol) +{ + LdLuaSymbol *self; + + g_return_val_if_fail (LD_IS_LUA_SYMBOL (symbol), NULL); + + self = LD_LUA_SYMBOL (symbol); + return self->priv->terminals; +} + +static void +ld_lua_symbol_real_draw (LdSymbol *symbol, cairo_t *cr) +{ + LdLuaSymbol *self; + + g_return_if_fail (LD_IS_LUA_SYMBOL (symbol)); + g_return_if_fail (cr != NULL); + + self = LD_LUA_SYMBOL (symbol); + + cairo_save (cr); + ld_lua_private_draw (self->priv->lua, self, cr); + cairo_restore (cr); +} + diff --git a/liblogdiag/ld-lua-symbol.h b/liblogdiag/ld-lua-symbol.h new file mode 100644 index 0000000..5f68b59 --- /dev/null +++ b/liblogdiag/ld-lua-symbol.h @@ -0,0 +1,60 @@ +/* + * ld-lua-symbol.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_LUA_SYMBOL_H__ +#define __LD_LUA_SYMBOL_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_LUA_SYMBOL (ld_lua_symbol_get_type ()) +#define LD_LUA_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_LUA_SYMBOL, LdLuaSymbol)) +#define LD_LUA_SYMBOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_LUA_SYMBOL, LdLuaSymbolClass)) +#define LD_IS_LUA_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_LUA_SYMBOL)) +#define LD_IS_LUA_SYMBOL_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_LUA_SYMBOL)) +#define LD_LUA_SYMBOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_LUA_SYMBOL, LdLuaSymbolClass)) + +typedef struct _LdLuaSymbol LdLuaSymbol; +typedef struct _LdLuaSymbolPrivate LdLuaSymbolPrivate; +typedef struct _LdLuaSymbolClass LdLuaSymbolClass; + + +/** + * LdLuaSymbol: + */ +struct _LdLuaSymbol +{ +/*< private >*/ + LdSymbol parent_instance; + LdLuaSymbolPrivate *priv; +}; + +/** + * LdLuaSymbolClass: + * @parent_class: The parent class. + */ +struct _LdLuaSymbolClass +{ + LdSymbolClass parent_class; +}; + + +GType ld_lua_symbol_get_type (void) G_GNUC_CONST; + + +G_END_DECLS + +#endif /* ! __LD_LUA_SYMBOL_H__ */ + diff --git a/liblogdiag/ld-lua.c b/liblogdiag/ld-lua.c new file mode 100644 index 0000000..47a41b5 --- /dev/null +++ b/liblogdiag/ld-lua.c @@ -0,0 +1,808 @@ +/* + * ld-lua.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 <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include "liblogdiag.h" +#include "config.h" + +#include "ld-lua-private.h" +#include "ld-lua-symbol-private.h" + + +/** + * SECTION:ld-lua + * @short_description: Lua symbol engine. + * @see_also: #LdLuaSymbol + * + * #LdLua is a symbol engine that uses Lua scripts to manage symbols. + */ + +/* + * LdLuaPrivate: + * @L: Lua state. + * + * The library contains the real function for rendering. + */ +struct _LdLuaPrivate +{ + lua_State *L; +}; + +/* registry.logdiag_symbols + * -> A table indexed by pointers to LdLuaSymbol objects + * registry.logdiag_symbols.object.render(cr) + * -> The rendering function + */ + +#define LD_LUA_LIBRARY_NAME "logdiag" +#define LD_LUA_DATA_INDEX LD_LUA_LIBRARY_NAME "_data" +#define LD_LUA_SYMBOLS_INDEX LD_LUA_LIBRARY_NAME "_symbols" + +/* + * LdLuaData: + * @self: A reference to self. + * @load_callback: A callback for newly registered symbols. + * @load_user_data: User data to be passed to the callback. + * + * Full user data to be stored in Lua registry. + */ +typedef struct _LdLuaData LdLuaData; + +struct _LdLuaData +{ + LdLua *self; + LdLuaLoadCallback load_callback; + gpointer load_user_data; +}; + +typedef struct _LdLuaDrawData LdLuaDrawData; + +struct _LdLuaDrawData +{ + LdLuaSymbol *symbol; + cairo_t *cr; + unsigned save_count; +}; + +static void ld_lua_finalize (GObject *gobject); + +static void *ld_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize); + +static int ld_lua_private_draw_cb (lua_State *L); +static int ld_lua_private_unregister_cb (lua_State *L); + +static int ld_lua_logdiag_register (lua_State *L); +static int process_registration (lua_State *L); +static gchar *get_translation (lua_State *L, int index); +static gboolean read_symbol_area (lua_State *L, int index, LdRectangle *area); +static gboolean read_terminals (lua_State *L, int index, + LdPointArray **terminals); + +static void push_cairo_object (lua_State *L, LdLuaDrawData *draw_data); +static gdouble get_cairo_scale (cairo_t *cr); +static int ld_lua_cairo_save (lua_State *L); +static int ld_lua_cairo_restore (lua_State *L); +static int ld_lua_cairo_get_line_width (lua_State *L); +static int ld_lua_cairo_set_line_width (lua_State *L); +static int ld_lua_cairo_move_to (lua_State *L); +static int ld_lua_cairo_line_to (lua_State *L); +static int ld_lua_cairo_curve_to (lua_State *L); +static int ld_lua_cairo_arc (lua_State *L); +static int ld_lua_cairo_arc_negative (lua_State *L); +static int ld_lua_cairo_new_path (lua_State *L); +static int ld_lua_cairo_new_sub_path (lua_State *L); +static int ld_lua_cairo_close_path (lua_State *L); +static int ld_lua_cairo_stroke (lua_State *L); +static int ld_lua_cairo_stroke_preserve (lua_State *L); +static int ld_lua_cairo_fill (lua_State *L); +static int ld_lua_cairo_fill_preserve (lua_State *L); +static int ld_lua_cairo_clip (lua_State *L); +static int ld_lua_cairo_clip_preserve (lua_State *L); + + +static luaL_Reg ld_lua_logdiag_lib[] = +{ + {"register", ld_lua_logdiag_register}, + {NULL, NULL} +}; + +static luaL_Reg ld_lua_cairo_table[] = +{ + {"save", ld_lua_cairo_save}, + {"restore", ld_lua_cairo_restore}, + {"get_line_width", ld_lua_cairo_get_line_width}, + {"set_line_width", ld_lua_cairo_set_line_width}, + {"move_to", ld_lua_cairo_move_to}, + {"line_to", ld_lua_cairo_line_to}, + {"curve_to", ld_lua_cairo_curve_to}, + {"arc", ld_lua_cairo_arc}, + {"arc_negative", ld_lua_cairo_arc_negative}, + {"new_path", ld_lua_cairo_new_path}, + {"new_sub_path", ld_lua_cairo_new_sub_path}, + {"close_path", ld_lua_cairo_close_path}, + {"stroke", ld_lua_cairo_stroke}, + {"stroke_preserve", ld_lua_cairo_stroke_preserve}, + {"fill", ld_lua_cairo_fill}, + {"fill_preserve", ld_lua_cairo_fill_preserve}, + {"clip", ld_lua_cairo_clip}, + {"clip_preserve", ld_lua_cairo_clip_preserve}, + {NULL, NULL} +}; + + +/* ===== Generic =========================================================== */ + +G_DEFINE_TYPE (LdLua, ld_lua, G_TYPE_OBJECT); + +static void +ld_lua_class_init (LdLuaClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ld_lua_finalize; + + g_type_class_add_private (klass, sizeof (LdLuaPrivate)); +} + +static void +ld_lua_init (LdLua *self) +{ + lua_State *L; + LdLuaData *ud; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_LUA, LdLuaPrivate); + + L = self->priv->L = lua_newstate (ld_lua_alloc, NULL); + g_return_if_fail (L != NULL); + + /* TODO: lua_atpanic () */ + + /* Load some safe libraries. */ + lua_pushcfunction (L, luaopen_string); + lua_call (L, 0, 0); + + lua_pushcfunction (L, luaopen_table); + lua_call (L, 0, 0); + + lua_pushcfunction (L, luaopen_math); + lua_call (L, 0, 0); + + /* Load the application library. */ + luaL_register (L, LD_LUA_LIBRARY_NAME, ld_lua_logdiag_lib); + + /* Store user data to the registry. */ + ud = lua_newuserdata (L, sizeof (LdLuaData)); + ud->self = self; + ud->load_callback = NULL; + ud->load_user_data = NULL; + + lua_setfield (L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX); + + /* Create an empty symbol table. */ + lua_newtable (L); + lua_setfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX); +} + +static void +ld_lua_finalize (GObject *gobject) +{ + LdLua *self; + + self = LD_LUA (gobject); + lua_close (self->priv->L); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_lua_parent_class)->finalize (gobject); +} + +/** + * ld_lua_new: + * + * Create an instance of #LdLua. + */ +LdLua * +ld_lua_new (void) +{ + return g_object_new (LD_TYPE_LUA, NULL); +} + +static void * +ld_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) +{ + if (!nsize) + { + g_free (ptr); + return NULL; + } + else + return g_try_realloc (ptr, nsize); +} + +/** + * ld_lua_check_file: + * @self: An #LdLua object. + * @filename: The file to be checked. + * + * Check if the given filename can be loaded by #LdLua. + */ +gboolean +ld_lua_check_file (LdLua *self, const gchar *filename) +{ + g_return_val_if_fail (LD_IS_LUA (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + + return g_str_has_suffix (filename, ".lua") + && g_file_test (filename, G_FILE_TEST_IS_REGULAR); +} + +/** + * ld_lua_load_file: + * @self: An #LdLua object. + * @filename: The file to be loaded. + * @callback: A callback for newly registered symbols. + * The callee is responsible for referencing the symbol. + * @user_data: User data to be passed to the callback. + * + * Loads a file and creates #LdLuaSymbol objects for contained symbols. + * + * Returns: TRUE if no error has occured, FALSE otherwise. + */ +gboolean +ld_lua_load_file (LdLua *self, const gchar *filename, + LdLuaLoadCallback callback, gpointer user_data) +{ + gint retval; + LdLuaData *ud; + + g_return_val_if_fail (LD_IS_LUA (self), FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + /* XXX: If something from the following fails, Lua will call exit(). */ + lua_getfield (self->priv->L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX); + ud = lua_touserdata (self->priv->L, -1); + lua_pop (self->priv->L, 1); + g_return_val_if_fail (ud != NULL, FALSE); + + ud->load_callback = callback; + ud->load_user_data = user_data; + + retval = luaL_loadfile (self->priv->L, filename); + if (retval) + goto ld_lua_lftc_fail; + + retval = lua_pcall (self->priv->L, 0, 0, 0); + if (retval) + goto ld_lua_lftc_fail; + + ud->load_callback = NULL; + ud->load_user_data = NULL; + return TRUE; + +ld_lua_lftc_fail: + g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1)); + lua_remove (self->priv->L, -1); + + ud->load_callback = NULL; + ud->load_user_data = NULL; + return FALSE; +} + +/* ===== LdLuaSymbol callbacks ============================================= */ + +/** + * ld_lua_private_draw: + * @self: An #LdLua object. + * @symbol: A symbol to be drawn. + * @cr: A Cairo context to be drawn onto. + * + * Draw a symbol onto a Cairo context. + */ +void +ld_lua_private_draw (LdLua *self, LdLuaSymbol *symbol, cairo_t *cr) +{ + LdLuaDrawData data; + + g_return_if_fail (LD_IS_LUA (self)); + g_return_if_fail (LD_IS_LUA_SYMBOL (symbol)); + g_return_if_fail (cr != NULL); + + data.symbol = symbol; + data.cr = cr; + data.save_count = 0; + + if (lua_cpcall (self->priv->L, ld_lua_private_draw_cb, &data)) + { + g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1)); + lua_pop (self->priv->L, 1); + } + + while (data.save_count--) + cairo_restore (cr); +} + +static int +ld_lua_private_draw_cb (lua_State *L) +{ + LdLuaDrawData *data; + + data = lua_touserdata (L, -1); + + /* Retrieve the function for rendering from the registry. */ + lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX); + lua_pushlightuserdata (L, data->symbol); + lua_gettable (L, -2); + + luaL_checktype (L, -1, LUA_TTABLE); + lua_getfield (L, -1, "render"); + luaL_checktype (L, -1, LUA_TFUNCTION); + + /* Call the function do draw the symbol. */ + push_cairo_object (L, data); + lua_pcall (L, 1, 0, 0); + return 0; +} + +/** + * ld_lua_private_unregister: + * @self: An #LdLua object. + * @symbol: A symbol to be unregistered. + * + * Unregister a symbol from the internal Lua state. + */ +void +ld_lua_private_unregister (LdLua *self, LdLuaSymbol *symbol) +{ + g_return_if_fail (LD_IS_LUA (self)); + g_return_if_fail (LD_IS_LUA_SYMBOL (symbol)); + + if (lua_cpcall (self->priv->L, ld_lua_private_unregister_cb, symbol)) + { + g_warning ("Lua error: %s", lua_tostring (self->priv->L, -1)); + lua_pop (self->priv->L, 1); + } +} + +static int +ld_lua_private_unregister_cb (lua_State *L) +{ + /* Set the entry in the symbol table to nil. */ + lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX); + lua_insert (L, -2); + lua_pushnil (L); + lua_settable (L, -3); + return 0; +} + +/* ===== Application library =============================================== */ + +static int +ld_lua_logdiag_register (lua_State *L) +{ + LdLuaData *ud; + LdLuaSymbol *symbol; + + lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_DATA_INDEX); + ud = lua_touserdata (L, -1); + lua_pop (L, 1); + g_return_val_if_fail (ud != NULL, 0); + + /* Use a protected environment, so script errors won't cause leaking + * of the symbol object. Only a failure of the last three function calls + * before lua_pcall() may cause the symbol to leak. + */ + lua_checkstack (L, 3); + symbol = g_object_new (LD_TYPE_LUA_SYMBOL, NULL); + + lua_pushlightuserdata (L, symbol); + lua_pushcclosure (L, process_registration, 1); + lua_insert (L, 1); + + /* On the stack, there are function arguments plus the function itself. */ + if (lua_pcall (L, lua_gettop (L) - 1, 0, 0)) + { + luaL_where (L, 1); + lua_insert (L, -2); + lua_concat (L, 2); + + g_warning ("Lua symbol registration failed: %s", + lua_tostring (L, -1)); + lua_pushboolean (L, FALSE); + } + else + { + /* We don't want an extra LdLua reference either. */ + symbol->priv->lua = ud->self; + g_object_ref (ud->self); + + ud->load_callback (LD_SYMBOL (symbol), ud->load_user_data); + lua_pushboolean (L, TRUE); + } + g_object_unref (symbol); + + return 1; +} + +/* + * process_registration: + * @L: A Lua state. + * + * Parse arguments, write them to a symbol object and register the object. + */ +static int +process_registration (lua_State *L) +{ + LdLuaSymbol *symbol; + gchar *human_name; + + int i, type, types[] = + {LUA_TSTRING, LUA_TTABLE, LUA_TTABLE, LUA_TTABLE, LUA_TFUNCTION}; + int n_args_needed = sizeof (types) / sizeof (int); + + if (lua_gettop (L) < n_args_needed) + return luaL_error (L, "Too few arguments."); + + for (i = 0; i < n_args_needed; i++) + if ((type = lua_type (L, i + 1)) != types[i]) + return luaL_error (L, "Bad type of argument #%d." + " Expected %s, got %s.", i + 1, + lua_typename (L, types[i]), lua_typename (L, type)); + + symbol = LD_LUA_SYMBOL (lua_touserdata (L, lua_upvalueindex (1))); + symbol->priv->name = g_strdup (lua_tostring (L, 1)); + + human_name = get_translation (L, 2); + if (!human_name) + human_name = g_strdup (symbol->priv->name); + symbol->priv->human_name = human_name; + + if (!read_symbol_area (L, 3, &symbol->priv->area)) + return luaL_error (L, "Malformed symbol area array."); + if (!read_terminals (L, 4, &symbol->priv->terminals)) + return luaL_error (L, "Malformed terminals array."); + + lua_getfield (L, LUA_REGISTRYINDEX, LD_LUA_SYMBOLS_INDEX); + lua_pushlightuserdata (L, symbol); + + lua_newtable (L); + lua_pushvalue (L, 5); + lua_setfield (L, -2, "render"); + + lua_settable (L, -3); + return 0; +} + +/* + * get_translation: + * @L: A Lua state. + * @index: Stack index of the table. + * + * Select an applicable translation from a table. + * The return value has to be freed with g_free(). + * + * Return value: The translation, if found. If none was found, returns NULL. + */ +static gchar * +get_translation (lua_State *L, int index) +{ + const gchar *const *lang; + gchar *result; + + for (lang = g_get_language_names (); *lang; lang++) + { + lua_getfield (L, 2, *lang); + if (lua_isstring (L, -1)) + { + result = g_strdup (lua_tostring (L, -1)); + lua_pop (L, 1); + return result; + } + lua_pop (L, 1); + } + return NULL; +} + +/* + * read_symbol_area: + * @L: A Lua state. + * @index: Stack index of the table. + * @area: Where the area will be returned. + * + * Read a symbol area from a Lua table. + * + * Return value: TRUE on success, FALSE on failure. + */ +static gboolean +read_symbol_area (lua_State *L, int index, LdRectangle *area) +{ + lua_Number x1, x2, y1, y2; + + if (lua_objlen (L, index) != 4) + return FALSE; + + lua_rawgeti (L, index, 1); + if (!lua_isnumber (L, -1)) + return FALSE; + x1 = lua_tonumber (L, -1); + + lua_rawgeti (L, index, 2); + if (!lua_isnumber (L, -1)) + return FALSE; + y1 = lua_tonumber (L, -1); + + lua_rawgeti (L, index, 3); + if (!lua_isnumber (L, -1)) + return FALSE; + x2 = lua_tonumber (L, -1); + + lua_rawgeti (L, index, 4); + if (!lua_isnumber (L, -1)) + return FALSE; + y2 = lua_tonumber (L, -1); + + area->x = MIN (x1, x2); + area->y = MIN (y1, y2); + area->width = ABS (x2 - x1); + area->height = ABS (y2 - y1); + + lua_pop (L, 4); + return TRUE; +} + +/* + * read_terminals: + * @L: A Lua state. + * @index: Stack index of the table. + * @area: Where the point array will be returned. + * + * Read symbol terminals from a Lua table. + * + * Return value: TRUE on success, FALSE on failure. + */ +static gboolean +read_terminals (lua_State *L, int index, LdPointArray **terminals) +{ + LdPointArray *points; + size_t num_points; + unsigned i = 0; + + num_points = lua_objlen (L, index); + points = ld_point_array_new (num_points); + + lua_pushnil (L); + while (lua_next (L, index) != 0) + { + g_assert (i < num_points); + + if (!lua_istable (L, -1) || lua_objlen (L, -1) != 2) + goto read_terminals_fail; + + lua_rawgeti (L, -1, 1); + if (!lua_isnumber (L, -1)) + goto read_terminals_fail; + points->points[i].x = lua_tonumber (L, -1); + lua_pop (L, 1); + + lua_rawgeti (L, -1, 2); + if (!lua_isnumber (L, -1)) + goto read_terminals_fail; + points->points[i].y = lua_tonumber (L, -1); + + lua_pop (L, 2); + i++; + } + *terminals = points; + return TRUE; + +read_terminals_fail: + ld_point_array_free (points); + *terminals = NULL; + return FALSE; +} + + +/* ===== Cairo ============================================================= */ + +static void +push_cairo_object (lua_State *L, LdLuaDrawData *draw_data) +{ + luaL_Reg *fn; + + /* Create a table. */ + lua_newtable (L); + + /* Add methods. */ + /* XXX: The light user data pointer gets invalid after the end of + * "render" function invocation. If the script stores the "cr" object + * in some global variable and then tries to reuse it the next time, + * the application may go SIGSEGV. + * + * The solution is creating a full user data instead, referencing + * the cairo object and dereferencing it upon garbage collection + * of the user data object. + */ + for (fn = ld_lua_cairo_table; fn->name; fn++) + { + lua_pushlightuserdata (L, draw_data); + lua_pushcclosure (L, fn->func, 1); + lua_setfield (L, -2, fn->name); + } +} + +static gdouble +get_cairo_scale (cairo_t *cr) +{ + double dx = 1, dy = 0; + + cairo_user_to_device_distance (cr, &dx, &dy); + return dx; +} + +#define LD_LUA_CAIRO_TRIVIAL(name) \ +static int \ +ld_lua_cairo_ ## name (lua_State *L) \ +{ \ + LdLuaDrawData *data; \ + data = lua_touserdata (L, lua_upvalueindex (1)); \ + cairo_ ## name (data->cr); \ + return 0; \ +} + +LD_LUA_CAIRO_TRIVIAL (new_path) +LD_LUA_CAIRO_TRIVIAL (new_sub_path) +LD_LUA_CAIRO_TRIVIAL (close_path) + +LD_LUA_CAIRO_TRIVIAL (stroke) +LD_LUA_CAIRO_TRIVIAL (stroke_preserve) +LD_LUA_CAIRO_TRIVIAL (fill) +LD_LUA_CAIRO_TRIVIAL (fill_preserve) +LD_LUA_CAIRO_TRIVIAL (clip) +LD_LUA_CAIRO_TRIVIAL (clip_preserve) + +static int +ld_lua_cairo_save (lua_State *L) +{ + LdLuaDrawData *data; + + data = lua_touserdata (L, lua_upvalueindex (1)); + if (data->save_count + 1) + { + data->save_count++; + cairo_save (data->cr); + } + return 0; +} + +static int +ld_lua_cairo_restore (lua_State *L) +{ + LdLuaDrawData *data; + + data = lua_touserdata (L, lua_upvalueindex (1)); + if (data->save_count) + { + data->save_count--; + cairo_restore (data->cr); + } + return 0; +} + +static int +ld_lua_cairo_get_line_width (lua_State *L) +{ + LdLuaDrawData *data; + + data = lua_touserdata (L, lua_upvalueindex (1)); + lua_pushnumber (L, cairo_get_line_width (data->cr) + * get_cairo_scale (data->cr)); + return 1; +} + +static int +ld_lua_cairo_set_line_width (lua_State *L) +{ + LdLuaDrawData *data; + + data = lua_touserdata (L, lua_upvalueindex (1)); + cairo_set_line_width (data->cr, luaL_checknumber (L, 1) + / get_cairo_scale (data->cr)); + return 0; +} + +static int +ld_lua_cairo_move_to (lua_State *L) +{ + LdLuaDrawData *data; + lua_Number x, y; + + data = lua_touserdata (L, lua_upvalueindex (1)); + + x = luaL_checknumber (L, 1); + y = luaL_checknumber (L, 2); + + cairo_move_to (data->cr, x, y); + return 0; +} + +static int +ld_lua_cairo_line_to (lua_State *L) +{ + LdLuaDrawData *data; + lua_Number x, y; + + data = lua_touserdata (L, lua_upvalueindex (1)); + + x = luaL_checknumber (L, 1); + y = luaL_checknumber (L, 2); + + cairo_line_to (data->cr, x, y); + return 0; +} + +static int +ld_lua_cairo_curve_to (lua_State *L) +{ + LdLuaDrawData *data; + lua_Number x1, y1, x2, y2, x3, y3; + + data = lua_touserdata (L, lua_upvalueindex (1)); + + x1 = luaL_checknumber (L, 1); + y1 = luaL_checknumber (L, 2); + x2 = luaL_checknumber (L, 3); + y2 = luaL_checknumber (L, 4); + x3 = luaL_checknumber (L, 5); + y3 = luaL_checknumber (L, 6); + + cairo_curve_to (data->cr, x1, y1, x2, y2, x3, y3); + return 0; +} + +static int +ld_lua_cairo_arc (lua_State *L) +{ + LdLuaDrawData *data; + lua_Number xc, yc, radius, angle1, angle2; + + data = lua_touserdata (L, lua_upvalueindex (1)); + + xc = luaL_checknumber (L, 1); + yc = luaL_checknumber (L, 2); + radius = luaL_checknumber (L, 3); + angle1 = luaL_checknumber (L, 4); + angle2 = luaL_checknumber (L, 5); + + cairo_arc (data->cr, xc, yc, radius, angle1, angle2); + return 0; +} + +static int +ld_lua_cairo_arc_negative (lua_State *L) +{ + LdLuaDrawData *data; + lua_Number xc, yc, radius, angle1, angle2; + + data = lua_touserdata (L, lua_upvalueindex (1)); + + xc = luaL_checknumber (L, 1); + yc = luaL_checknumber (L, 2); + radius = luaL_checknumber (L, 3); + angle1 = luaL_checknumber (L, 4); + angle2 = luaL_checknumber (L, 5); + + cairo_arc_negative (data->cr, xc, yc, radius, angle1, angle2); + return 0; +} + diff --git a/liblogdiag/ld-lua.h b/liblogdiag/ld-lua.h new file mode 100644 index 0000000..b207d66 --- /dev/null +++ b/liblogdiag/ld-lua.h @@ -0,0 +1,70 @@ +/* + * ld-lua.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_LUA_H__ +#define __LD_LUA_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_LUA (ld_lua_get_type ()) +#define LD_LUA(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_LUA, LdLua)) +#define LD_LUA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_LUA, LdLuaClass)) +#define LD_IS_LUA(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_LUA)) +#define LD_IS_LUA_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_LUA)) +#define LD_LUA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_LUA, LdLuaClass)) + +typedef struct _LdLua LdLua; +typedef struct _LdLuaPrivate LdLuaPrivate; +typedef struct _LdLuaClass LdLuaClass; + + +struct _LdLua +{ +/*< private >*/ + GObject parent_instance; + LdLuaPrivate *priv; +}; + +/* TODO: A virtual superclass, so other engines can be used. */ +struct _LdLuaClass +{ +/*< private >*/ + GObjectClass parent_class; +}; + + +/** + * LdLuaLoadCallback: + * @symbol: The symbol that has been created. + * @user_data: User data passed to ld_lua_load_file(). + * + * A callback function that is called when a symbol is created. + */ +typedef void (*LdLuaLoadCallback) (LdSymbol *symbol, gpointer user_data); + + +GType ld_lua_get_type (void) G_GNUC_CONST; + +LdLua *ld_lua_new (void); +gboolean ld_lua_check_file (LdLua *self, const gchar *filename); +gboolean ld_lua_load_file (LdLua *self, const gchar *filename, + LdLuaLoadCallback callback, gpointer user_data); + + +G_END_DECLS + +#endif /* ! __LD_LUA_H__ */ + diff --git a/liblogdiag/ld-marshal.c b/liblogdiag/ld-marshal.c new file mode 100644 index 0000000..ac88836 --- /dev/null +++ b/liblogdiag/ld-marshal.c @@ -0,0 +1,88 @@ + +#include <glib-object.h> + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:OBJECT,OBJECT (ld-marshal.list:1) */ +void +g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_object (param_values + 1), + g_marshal_value_peek_object (param_values + 2), + data2); +} + diff --git a/liblogdiag/ld-marshal.h b/liblogdiag/ld-marshal.h new file mode 100644 index 0000000..545735a --- /dev/null +++ b/liblogdiag/ld-marshal.h @@ -0,0 +1,20 @@ + +#ifndef __g_cclosure_user_marshal_MARSHAL_H__ +#define __g_cclosure_user_marshal_MARSHAL_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* VOID:OBJECT,OBJECT (ld-marshal.list:1) */ +extern void g_cclosure_user_marshal_VOID__OBJECT_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __g_cclosure_user_marshal_MARSHAL_H__ */ + diff --git a/liblogdiag/ld-marshal.list b/liblogdiag/ld-marshal.list new file mode 100644 index 0000000..38076d6 --- /dev/null +++ b/liblogdiag/ld-marshal.list @@ -0,0 +1 @@ +VOID:OBJECT,OBJECT diff --git a/liblogdiag/ld-symbol-category.c b/liblogdiag/ld-symbol-category.c new file mode 100644 index 0000000..e4b86a9 --- /dev/null +++ b/liblogdiag/ld-symbol-category.c @@ -0,0 +1,339 @@ +/* + * ld-symbol-category.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 "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-symbol-category + * @short_description: A category of symbols. + * @see_also: #LdSymbol, #LdLibrary + * + * #LdSymbolCategory represents a category of #LdSymbol objects. + */ + +/* + * LdSymbolCategoryPrivate: + * @name: The name of this category. + * @image_path: Path to the image for this category. + * @children: Children of this category. + */ +struct _LdSymbolCategoryPrivate +{ + gchar *name; + gchar *human_name; + gchar *image_path; + GSList *children; +}; + +enum +{ + PROP_0, + PROP_NAME, + PROP_HUMAN_NAME, + PROP_IMAGE_PATH +}; + +static void ld_symbol_category_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +static void ld_symbol_category_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); +static void ld_symbol_category_finalize (GObject *gobject); + + +G_DEFINE_TYPE (LdSymbolCategory, ld_symbol_category, G_TYPE_OBJECT); + +static void +ld_symbol_category_class_init (LdSymbolCategoryClass *klass) +{ + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = ld_symbol_category_get_property; + object_class->set_property = ld_symbol_category_set_property; + object_class->finalize = ld_symbol_category_finalize; + +/** + * LdSymbolCategory:name: + * + * The name of this symbol category. + */ + pspec = g_param_spec_string ("name", "Name", + "The name of this symbol category.", + "", G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_NAME, pspec); + +/** + * LdSymbolCategory:human-name: + * + * The localized human name of this symbol category. + */ + pspec = g_param_spec_string ("human-name", "Human name", + "The localized human name of this symbol category.", + "", G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_HUMAN_NAME, pspec); + +/** + * LdSymbolCategory:image-path: + * + * Path to an image file representing this category. + */ + pspec = g_param_spec_string ("image-path", "Image path", + "Path to an image file representing this category.", + "", G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_IMAGE_PATH, pspec); + + g_type_class_add_private (klass, sizeof (LdSymbolCategoryPrivate)); +} + +static void +ld_symbol_category_init (LdSymbolCategory *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_SYMBOL_CATEGORY, LdSymbolCategoryPrivate); +} + +static void +ld_symbol_category_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LdSymbolCategory *self; + + self = LD_SYMBOL_CATEGORY (object); + switch (property_id) + { + case PROP_NAME: + g_value_set_string (value, ld_symbol_category_get_name (self)); + break; + case PROP_HUMAN_NAME: + g_value_set_string (value, ld_symbol_category_get_human_name (self)); + break; + case PROP_IMAGE_PATH: + g_value_set_string (value, ld_symbol_category_get_image_path (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_symbol_category_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + LdSymbolCategory *self; + + self = LD_SYMBOL_CATEGORY (object); + switch (property_id) + { + case PROP_NAME: + ld_symbol_category_set_name (self, g_value_get_string (value)); + break; + case PROP_HUMAN_NAME: + ld_symbol_category_set_human_name (self, g_value_get_string (value)); + break; + case PROP_IMAGE_PATH: + ld_symbol_category_set_image_path (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_symbol_category_finalize (GObject *gobject) +{ + LdSymbolCategory *self; + + self = LD_SYMBOL_CATEGORY (gobject); + + if (self->priv->name) + g_free (self->priv->name); + if (self->priv->human_name) + g_free (self->priv->human_name); + if (self->priv->image_path) + g_free (self->priv->image_path); + + g_slist_foreach (self->priv->children, (GFunc) g_object_unref, NULL); + g_slist_free (self->priv->children); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_symbol_category_parent_class)->finalize (gobject); +} + + +/** + * ld_symbol_category_new: + * @name: The name of the new category. + * @human_name: The localized human name of the new category. + * + * Create an instance. + */ +LdSymbolCategory * +ld_symbol_category_new (const gchar *name, const gchar *human_name) +{ + LdSymbolCategory *cat; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (human_name != NULL, NULL); + + cat = g_object_new (LD_TYPE_SYMBOL_CATEGORY, NULL); + cat->priv->name = g_strdup (name); + cat->priv->human_name = g_strdup (human_name); + + return cat; +} + +/** + * ld_symbol_category_set_name: + * @self: An #LdSymbolCategory object. + * @name: The new name for this category. + */ +void +ld_symbol_category_set_name (LdSymbolCategory *self, const gchar *name) +{ + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (self)); + g_return_if_fail (name != NULL); + + if (self->priv->name) + g_free (self->priv->name); + self->priv->name = g_strdup (name); + + g_object_notify (G_OBJECT (self), "name"); +} + +/** + * ld_symbol_category_get_name: + * @self: An #LdSymbolCategory object. + * + * Return the name of this category. + */ +const gchar * +ld_symbol_category_get_name (LdSymbolCategory *self) +{ + g_return_val_if_fail (LD_IS_SYMBOL_CATEGORY (self), NULL); + return self->priv->name; +} + +/** + * ld_symbol_category_set_human_name: + * @self: An #LdSymbolCategory object. + * @human_name: The new localized human name for this category. + */ +void +ld_symbol_category_set_human_name (LdSymbolCategory *self, + const gchar *human_name) +{ + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (self)); + g_return_if_fail (human_name != NULL); + + if (self->priv->human_name) + g_free (self->priv->human_name); + self->priv->human_name = g_strdup (human_name); + + g_object_notify (G_OBJECT (self), "human-name"); +} + +/** + * ld_symbol_category_get_human_name: + * @self: An #LdSymbolCategory object. + * + * Return the localized human name of this category. + */ +const gchar * +ld_symbol_category_get_human_name (LdSymbolCategory *self) +{ + g_return_val_if_fail (LD_IS_SYMBOL_CATEGORY (self), NULL); + return self->priv->human_name; +} + +/** + * ld_symbol_category_set_image_path: + * @self: An #LdSymbolCategory object. + * @image_path: The new path to the image for this category. May be NULL. + */ +void +ld_symbol_category_set_image_path (LdSymbolCategory *self, + const gchar *image_path) +{ + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (self)); + + if (self->priv->image_path) + g_free (self->priv->image_path); + self->priv->image_path = g_strdup (image_path); + + g_object_notify (G_OBJECT (self), "image-path"); +} + +/** + * ld_symbol_category_get_image_path: + * @self: An #LdSymbolCategory object. + * + * Return the filesystem path to the image for this category. May be NULL. + */ +const gchar * +ld_symbol_category_get_image_path (LdSymbolCategory *self) +{ + g_return_val_if_fail (LD_IS_SYMBOL_CATEGORY (self), NULL); + return self->priv->image_path; +} + +/** + * ld_symbol_category_insert_child: + * @self: An #LdSymbolCategory object. + * @child: The child to be inserted. + * @pos: The position at which the child will be inserted. + * Negative values will append to the end of list. + * + * Insert a child into the category. + */ +void +ld_symbol_category_insert_child (LdSymbolCategory *self, + GObject *child, gint pos) +{ + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + g_object_ref (child); + self->priv->children = g_slist_insert (self->priv->children, child, pos); +} + +/** + * ld_symbol_category_remove_child: + * @self: An #LdSymbolCategory object. + * @child: The child to be removed. + * + * Removes a child from the category. + */ +void +ld_symbol_category_remove_child (LdSymbolCategory *self, + GObject *child) +{ + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + g_object_unref (child); + self->priv->children = g_slist_remove (self->priv->children, child); +} + +/** + * ld_symbol_category_get_children: + * @self: An #LdSymbolCategory object. + * + * Return value: The internal list of children. Do not modify. + */ +const GSList * +ld_symbol_category_get_children (LdSymbolCategory *self) +{ + g_return_val_if_fail (LD_IS_SYMBOL_CATEGORY (self), NULL); + return self->priv->children; +} + diff --git a/liblogdiag/ld-symbol-category.h b/liblogdiag/ld-symbol-category.h new file mode 100644 index 0000000..3b1f05b --- /dev/null +++ b/liblogdiag/ld-symbol-category.h @@ -0,0 +1,79 @@ +/* + * ld-symbol-category.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LD_SYMBOL_CATEGORY_H__ +#define __LD_SYMBOL_CATEGORY_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_SYMBOL_CATEGORY (ld_symbol_category_get_type ()) +#define LD_SYMBOL_CATEGORY(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_SYMBOL_CATEGORY, LdSymbolCategory)) +#define LD_SYMBOL_CATEGORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_SYMBOL_CATEGORY, LdSymbolCategoryClass)) +#define LD_IS_SYMBOL_CATEGORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_SYMBOL_CATEGORY)) +#define LD_IS_SYMBOL_CATEGORY_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_SYMBOL_CATEGORY)) +#define LD_SYMBOL_CATEGORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_SYMBOL_CATEGORY, LdSymbolCategoryClass)) + +typedef struct _LdSymbolCategory LdSymbolCategory; +typedef struct _LdSymbolCategoryPrivate LdSymbolCategoryPrivate; +typedef struct _LdSymbolCategoryClass LdSymbolCategoryClass; + + +/** + * LdSymbolCategory: + */ +struct _LdSymbolCategory +{ +/*< private >*/ + GObject parent_instance; + LdSymbolCategoryPrivate *priv; +}; + +/* TODO: If required sometime, categories (and maybe symbols) should implement + * a "changed" signal. This can be somewhat tricky. The library might be + * a good candidate for what they call a proxy. See GtkUIManager. + */ +struct _LdSymbolCategoryClass +{ +/*< private >*/ + GObjectClass parent_class; +}; + + +GType ld_symbol_category_get_type (void) G_GNUC_CONST; + +LdSymbolCategory *ld_symbol_category_new (const gchar *name, + const gchar *human_name); + +void ld_symbol_category_set_name (LdSymbolCategory *self, const gchar *name); +const gchar *ld_symbol_category_get_name (LdSymbolCategory *self); +void ld_symbol_category_set_human_name (LdSymbolCategory *self, + const gchar *human_name); +const gchar *ld_symbol_category_get_human_name (LdSymbolCategory *self); +void ld_symbol_category_set_image_path (LdSymbolCategory *self, + const gchar *image_path); +const gchar *ld_symbol_category_get_image_path (LdSymbolCategory *self); + +void ld_symbol_category_insert_child (LdSymbolCategory *self, + GObject *child, gint pos); +void ld_symbol_category_remove_child (LdSymbolCategory *self, + GObject *child); +const GSList *ld_symbol_category_get_children (LdSymbolCategory *self); + + +G_END_DECLS + +#endif /* ! __LD_SYMBOL_CATEGORY_H__ */ + diff --git a/liblogdiag/ld-symbol.c b/liblogdiag/ld-symbol.c new file mode 100644 index 0000000..fafa9ab --- /dev/null +++ b/liblogdiag/ld-symbol.c @@ -0,0 +1,232 @@ +/* + * ld-symbol.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 "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-symbol + * @short_description: A symbol. + * @see_also: #LdDiagramSymbol, #LdCanvas + * + * #LdSymbol represents a symbol to be drawn onto a #LdCanvas. + * + * All implementations of this abstract class are required to use + * cairo_save() and cairo_restore() when drawing to store the state. + */ + +enum +{ + PROP_0, + PROP_NAME, + PROP_HUMAN_NAME, + PROP_AREA, + PROP_TERMINALS +}; + +static void ld_symbol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec); +static void ld_symbol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec); + + +G_DEFINE_ABSTRACT_TYPE (LdSymbol, ld_symbol, G_TYPE_OBJECT); + +static void +ld_symbol_class_init (LdSymbolClass *klass) +{ + GObjectClass *object_class; + GParamSpec *pspec; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = ld_symbol_get_property; + object_class->set_property = ld_symbol_set_property; + +/** + * LdSymbol:name: + * + * The name of this symbol. + */ + pspec = g_param_spec_string ("name", "Name", + "The name of this symbol.", + "", G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_NAME, pspec); + +/** + * LdSymbol:human-name: + * + * The localized human name of this symbol. + */ + pspec = g_param_spec_string ("human-name", "Human name", + "The localized human name of this symbol.", + "", G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_HUMAN_NAME, pspec); + +/** + * LdSymbol:area: + * + * The area of this symbol. + */ + pspec = g_param_spec_boxed ("area", "Area", + "The area of this symbol.", + LD_TYPE_RECTANGLE, G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_AREA, pspec); + +/** + * LdSymbol:terminals: + * + * A point array that specifies terminals of this symbol. + */ + pspec = g_param_spec_boxed ("terminals", "Terminals", + "A point array that specifies terminals of this symbol.", + LD_TYPE_POINT_ARRAY, G_PARAM_READABLE); + g_object_class_install_property (object_class, PROP_TERMINALS, pspec); +} + +static void +ld_symbol_init (LdSymbol *self) +{ +} + +static void +ld_symbol_get_property (GObject *object, guint property_id, + GValue *value, GParamSpec *pspec) +{ + LdSymbol *self; + + self = LD_SYMBOL (object); + switch (property_id) + { + case PROP_NAME: + g_value_set_string (value, ld_symbol_get_name (self)); + break; + case PROP_HUMAN_NAME: + g_value_set_string (value, ld_symbol_get_human_name (self)); + break; + case PROP_AREA: + { + LdRectangle area; + + ld_symbol_get_area (self, &area); + g_value_set_boxed (value, &area); + } + break; + case PROP_TERMINALS: + g_value_set_boxed (value, ld_symbol_get_terminals (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ld_symbol_set_property (GObject *object, guint property_id, + const GValue *value, GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + + +/** + * ld_symbol_get_name: + * @self: An #LdSymbol object. + * + * Return value: The name of the symbol. + */ +const gchar * +ld_symbol_get_name (LdSymbol *self) +{ + LdSymbolClass *klass; + + g_return_val_if_fail (LD_IS_SYMBOL (self), NULL); + + klass = LD_SYMBOL_GET_CLASS (self); + g_return_val_if_fail (klass->get_name != NULL, NULL); + return klass->get_name (self); +} + +/** + * ld_symbol_get_human_name: + * @self: An #LdSymbol object. + * + * Return value: The localised human name of the symbol. + */ +const gchar * +ld_symbol_get_human_name (LdSymbol *self) +{ + LdSymbolClass *klass; + + g_return_val_if_fail (LD_IS_SYMBOL (self), NULL); + + klass = LD_SYMBOL_GET_CLASS (self); + g_return_val_if_fail (klass->get_human_name != NULL, NULL); + return klass->get_human_name (self); +} + +/** + * ld_symbol_get_area: + * @self: An #LdSymbol object. + * @area: Where the area of the symbol will be returned. + * + * Get the area of the symbol. + */ +void +ld_symbol_get_area (LdSymbol *self, LdRectangle *area) +{ + LdSymbolClass *klass; + + g_return_if_fail (LD_IS_SYMBOL (self)); + g_return_if_fail (area != NULL); + + klass = LD_SYMBOL_GET_CLASS (self); + g_return_if_fail (klass->get_area != NULL); + klass->get_area (self, area); +} + +/** + * ld_symbol_get_terminals: + * @self: An #LdSymbol object. + * + * Get a list of symbol terminals. + * + * Return value: An #LdPointArray structure. + */ +const LdPointArray * +ld_symbol_get_terminals (LdSymbol *self) +{ + LdSymbolClass *klass; + + g_return_val_if_fail (LD_IS_SYMBOL (self), NULL); + + klass = LD_SYMBOL_GET_CLASS (self); + g_return_val_if_fail (klass->get_terminals != NULL, NULL); + return klass->get_terminals (self); +} + +/** + * ld_symbol_draw: + * @self: An #LdSymbol object. + * @cr: A cairo surface to be drawn on. + * + * Draw the symbol onto a Cairo surface. + */ +void +ld_symbol_draw (LdSymbol *self, cairo_t *cr) +{ + LdSymbolClass *klass; + + g_return_if_fail (LD_IS_SYMBOL (self)); + g_return_if_fail (cr != NULL); + + klass = LD_SYMBOL_GET_CLASS (self); + g_return_if_fail (klass->draw != NULL); + klass->draw (self, cr); +} diff --git a/liblogdiag/ld-symbol.h b/liblogdiag/ld-symbol.h new file mode 100644 index 0000000..409eba5 --- /dev/null +++ b/liblogdiag/ld-symbol.h @@ -0,0 +1,74 @@ +/* + * ld-symbol.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_SYMBOL_H__ +#define __LD_SYMBOL_H__ + +G_BEGIN_DECLS + + +#define LD_TYPE_SYMBOL (ld_symbol_get_type ()) +#define LD_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), LD_TYPE_SYMBOL, LdSymbol)) +#define LD_SYMBOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST \ + ((klass), LD_TYPE_SYMBOL, LdSymbolClass)) +#define LD_IS_SYMBOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), LD_TYPE_SYMBOL)) +#define LD_IS_SYMBOL_CLASS(klass) (G_TYPE_CHECK_INSTANCE_TYPE \ + ((klass), LD_TYPE_SYMBOL)) +#define LD_SYMBOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), LD_SYMBOL, LdSymbolClass)) + +typedef struct _LdSymbol LdSymbol; +typedef struct _LdSymbolPrivate LdSymbolPrivate; +typedef struct _LdSymbolClass LdSymbolClass; + + +struct _LdSymbol +{ +/*< private >*/ + GObject parent_instance; + LdSymbolPrivate *priv; +}; + +/** + * LdSymbolClass: + * @parent_class: The parent class. + * @get_name: Get the name of the symbol. + * @get_human_name: Get the localized human name of the symbol. + * @get_area: Get the area of the symbol. + * @get_terminals: Get a list of symbol terminals. + * @draw: Draw the symbol on a Cairo surface. + */ +struct _LdSymbolClass +{ + GObjectClass parent_class; + + const gchar *(*get_name) (LdSymbol *self); + const gchar *(*get_human_name) (LdSymbol *self); + void (*get_area) (LdSymbol *self, LdRectangle *area); + const LdPointArray *(*get_terminals) (LdSymbol *self); + void (*draw) (LdSymbol *self, cairo_t *cr); +}; + + +GType ld_symbol_get_type (void) G_GNUC_CONST; + +const gchar *ld_symbol_get_name (LdSymbol *self); +const gchar *ld_symbol_get_human_name (LdSymbol *self); +void ld_symbol_get_area (LdSymbol *self, LdRectangle *area); +const LdPointArray *ld_symbol_get_terminals (LdSymbol *self); +void ld_symbol_draw (LdSymbol *self, cairo_t *cr); + + +G_END_DECLS + +#endif /* ! __LD_SYMBOL_H__ */ + diff --git a/liblogdiag/ld-types.c b/liblogdiag/ld-types.c new file mode 100644 index 0000000..cde3da6 --- /dev/null +++ b/liblogdiag/ld-types.c @@ -0,0 +1,221 @@ +/* + * ld-types.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 <math.h> + +#include "liblogdiag.h" +#include "config.h" + + +#define DEFINE_BOXED_TYPE(TypeName, type_name) \ +GType \ +type_name ## _get_type (void) \ +{ \ + static GType our_type = 0; \ + if (our_type == 0) \ + our_type = g_boxed_type_register_static \ + (g_intern_static_string (#TypeName), \ + (GBoxedCopyFunc) type_name ## _copy, \ + (GBoxedFreeFunc) type_name ## _free); \ + return our_type; \ +} + +DEFINE_BOXED_TYPE (LdPoint, ld_point) +DEFINE_BOXED_TYPE (LdPointArray, ld_point_array) +DEFINE_BOXED_TYPE (LdRectangle, ld_rectangle) + +#define DEFINE_BOXED_TRIVIAL_COPY(TypeName, type_name) \ +TypeName * \ +type_name ## _copy (const TypeName *self) \ +{ \ + TypeName *new_copy; \ + g_return_val_if_fail (self != NULL, NULL); \ + new_copy = g_slice_new (TypeName); \ + *new_copy = *self; \ + return new_copy; \ +} + +#define DEFINE_BOXED_TRIVIAL_FREE(TypeName, type_name) \ +void \ +type_name ## _free (TypeName *self) \ +{ \ + g_return_if_fail (self != NULL); \ + g_slice_free (TypeName, self); \ +} + +/** + * ld_point_copy: + * @self: An #LdPoint structure. + * + * Makes a copy of the structure. + * The result must be freed by ld_point_free(). + * + * Return value: A copy of @self. + */ +DEFINE_BOXED_TRIVIAL_COPY (LdPoint, ld_point) + +/** + * ld_point_free: + * @self: An #LdPoint structure. + * + * Frees the structure created with ld_point_copy(). + */ +DEFINE_BOXED_TRIVIAL_FREE (LdPoint, ld_point) + +/** + * ld_point_distance: + * @self: An #LdPoint structure. + * @x: The X coordinate of the second point. + * @y: The Y coordinate of the second point. + * + * Compute the distance between two points. + */ +gdouble +ld_point_distance (LdPoint *self, gdouble x, gdouble y) +{ + gdouble dx, dy; + + g_return_val_if_fail (self != NULL, -1); + + dx = self->x - x; + dy = self->y - y; + return sqrt (dx * dx + dy * dy); +} + +/** + * ld_point_array_new: + * @num_points: The number of points the array can store. + * + * Create a new array of points and initialize. + * + * Return value: An #LdPointArray structure. + */ +LdPointArray * +ld_point_array_new (gint num_points) +{ + LdPointArray *new_array; + + g_return_val_if_fail (num_points >= 1, NULL); + + new_array = g_slice_new (LdPointArray); + new_array->num_points = num_points; + new_array->points = g_malloc0 (num_points * sizeof (LdPoint)); + return new_array; +} + +/** + * ld_point_array_copy: + * @self: An #LdPointArray structure. + * + * Makes a copy of the structure. + * The result must be freed by ld_point_array_free(). + * + * Return value: A copy of @self. + */ +LdPointArray * +ld_point_array_copy (const LdPointArray *self) +{ + LdPointArray *new_array; + + g_return_val_if_fail (self != NULL, NULL); + + new_array = g_slice_new (LdPointArray); + new_array->num_points = self->num_points; + new_array->points = g_memdup (self->points, + self->num_points * sizeof (LdPoint)); + return new_array; +} + +/** + * ld_point_array_free: + * @self: An #LdPointArray structure. + * + * Frees the structure created with ld_point_array_copy(). + */ +void +ld_point_array_free (LdPointArray *self) +{ + g_return_if_fail (self != NULL); + + g_free (self->points); + g_slice_free (LdPointArray, self); +} + +/** + * ld_rectangle_copy: + * @self: An #LdRectangle structure. + * + * Makes a copy of the structure. + * The result must be freed by ld_rectangle_free(). + * + * Return value: A copy of @self. + */ +DEFINE_BOXED_TRIVIAL_COPY (LdRectangle, ld_rectangle) + +/** + * ld_rectangle_free: + * @self: An #LdRectangle structure. + * + * Frees the structure created with ld_rectangle_copy(). + */ +DEFINE_BOXED_TRIVIAL_FREE (LdRectangle, ld_rectangle) + +/** + * ld_rectangle_contains: + * @self: An #LdRectangle structure. + * @x: The X coordinate of the point to be checked. + * @y: The Y coordinate of the point to be checked. + * + * Return value: TRUE if the rectangle contains the specified point. + */ +gboolean +ld_rectangle_contains (LdRectangle *self, gdouble x, gdouble y) +{ + g_return_val_if_fail (self != NULL, FALSE); + return (x >= self->x && x <= self->x + self->width + && y >= self->y && y <= self->y + self->height); +} + +/** + * ld_rectangle_intersects: + * @self: An #LdRectangle structure. + * @rect: An #LdRectangle to be checked for intersection. + * + * Return value: TRUE if the two rectangles intersect. + */ +gboolean +ld_rectangle_intersects (LdRectangle *self, LdRectangle *rect) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (rect != NULL, FALSE); + + return !(self->x > rect->x + rect->width + || self->y > rect->y + rect->height + || self->x + self->width < rect->x + || self->y + self->height < rect->y); +} + +/** + * ld_rectangle_extend: + * @self: An #LdRectangle structure. + * @border: The border by which the rectangle should be extended. + * + * Extend a rectangle on all sides. + */ +void +ld_rectangle_extend (LdRectangle *self, gdouble border) +{ + g_return_if_fail (self != NULL); + + self->x -= border; + self->y -= border; + self->width += 2 * border; + self->height += 2 * border; +} diff --git a/liblogdiag/ld-types.h b/liblogdiag/ld-types.h new file mode 100644 index 0000000..61a1a7d --- /dev/null +++ b/liblogdiag/ld-types.h @@ -0,0 +1,101 @@ +/* + * ld-types.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_TYPES_H__ +#define __LD_TYPES_H__ + +G_BEGIN_DECLS + + +/** + * SECTION:ld-types + * @short_description: Simple data types. + * + * #LdPoint defines coordinates of a point. + * + * #LdRectangle defines the position and size of a rectangle. + */ + +#define LD_TYPE_POINT (ld_point_get_type ()) +#define LD_TYPE_POINT_ARRAY (ld_point_array_get_type ()) +#define LD_TYPE_RECTANGLE (ld_rectangle_get_type ()) + +typedef struct _LdPoint LdPoint; +typedef struct _LdPointArray LdPointArray; +typedef struct _LdRectangle LdRectangle; + + +/** + * LdPoint: + * @x: The X coordinate. + * @y: The Y coordinate. + * + * Defines a point. + */ +struct _LdPoint +{ + gdouble x, y; +}; + +GType ld_point_get_type (void) G_GNUC_CONST; + +LdPoint *ld_point_copy (const LdPoint *self); +void ld_point_free (LdPoint *self); +gdouble ld_point_distance (LdPoint *self, gdouble x, gdouble y); + + +/** + * LdPointArray: + * @points: An array of #LdPoint structures. + * @num_points: Count of points in @points. + * + * Moves quickly. + */ +struct _LdPointArray +{ + LdPoint *points; + gint num_points; +}; + +GType ld_point_array_get_type (void) G_GNUC_CONST; + +LdPointArray *ld_point_array_new (gint num_points); +LdPointArray *ld_point_array_copy (const LdPointArray *self); +void ld_point_array_free (LdPointArray *self); + + +/** + * LdRectangle: + * @x: Left-top X coordinate. + * @y: Left-top Y coordinate. + * @width: Width of the area, must be positive. + * @height: Height of the area, must be positive. + * + * Defines a rectangle. + */ +struct _LdRectangle +{ + gdouble x, y; + gdouble width, height; +}; + +GType ld_rectangle_get_type (void) G_GNUC_CONST; + +LdRectangle *ld_rectangle_copy (const LdRectangle *self); +void ld_rectangle_free (LdRectangle *self); +gboolean ld_rectangle_contains (LdRectangle *self, gdouble x, gdouble y); +gboolean ld_rectangle_intersects (LdRectangle *self, LdRectangle *rect); +void ld_rectangle_extend (LdRectangle *self, gdouble border); + + +G_END_DECLS + +#endif /* ! __LD_TYPES_H__ */ + diff --git a/liblogdiag/liblogdiag.h b/liblogdiag/liblogdiag.h new file mode 100644 index 0000000..800826f --- /dev/null +++ b/liblogdiag/liblogdiag.h @@ -0,0 +1,34 @@ +/* + * liblogdiag.h + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2011. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#ifndef __LIBLOGDIAG_H__ +#define __LIBLOGDIAG_H__ + +#include <gtk/gtk.h> +#include <json-glib/json-glib.h> + +#include "ld-marshal.h" +#include "ld-types.h" + +#include "ld-symbol.h" +#include "ld-symbol-category.h" +#include "ld-library.h" + +#include "ld-diagram-object.h" +#include "ld-diagram-symbol.h" +#include "ld-diagram.h" + +#include "ld-canvas.h" + +#include "ld-lua.h" +#include "ld-lua-symbol.h" + +#endif /* ! __LIBLOGDIAG_H__ */ + |