diff options
Diffstat (limited to 'liblogdiag/ld-diagram.c')
-rw-r--r-- | liblogdiag/ld-diagram.c | 550 |
1 files changed, 550 insertions, 0 deletions
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; +} |