summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorMaximiliano Curia <maxy@debian.org>2012-01-04 00:00:53 +0100
committerMaximiliano Curia <maxy@debian.org>2012-01-04 00:00:53 +0100
commit825fd5668026f70071c7a5eef277b0bf5c8f02d2 (patch)
tree4d3a9112d9887ea16ab5dda07a7a791f15d3278f /src/model
Import oregano_0.70.orig.tar.gz
[dgit import orig oregano_0.70.orig.tar.gz]
Diffstat (limited to 'src/model')
-rw-r--r--src/model/Makefile.am35
-rw-r--r--src/model/item-data.c541
-rw-r--r--src/model/item-data.h148
-rw-r--r--src/model/node-store.c1227
-rw-r--r--src/model/node-store.h99
-rw-r--r--src/model/node.c334
-rw-r--r--src/model/node.h92
-rw-r--r--src/model/part-label.h39
-rw-r--r--src/model/part-private.h48
-rw-r--r--src/model/part-property.c319
-rw-r--r--src/model/part-property.h42
-rw-r--r--src/model/part.c1099
-rw-r--r--src/model/part.h90
-rw-r--r--src/model/schematic-print-context.h45
-rw-r--r--src/model/schematic.c1114
-rw-r--r--src/model/schematic.h126
-rw-r--r--src/model/sheet-pos.h39
-rw-r--r--src/model/textbox.c468
-rw-r--r--src/model/textbox.h68
-rw-r--r--src/model/wire-private.h45
-rw-r--r--src/model/wire.c640
-rw-r--r--src/model/wire.h87
22 files changed, 6745 insertions, 0 deletions
diff --git a/src/model/Makefile.am b/src/model/Makefile.am
new file mode 100644
index 0000000..5fd7f67
--- /dev/null
+++ b/src/model/Makefile.am
@@ -0,0 +1,35 @@
+oreganodir = $(datadir)/oregano
+INCLUDES = \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(includedir) $(GNOME_INCLUDEDIR) \
+ -DOREGANO_GLADEDIR=\""$(oreganodir)/glade"\" \
+ -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \
+ -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DGETTEXT_PACKAGE=\""oregano\"" \
+ $(OREGANO_CFLAGS) -I$(top_srcdir)/src -I$(top_srcdir)/src/sheet \
+ -I$(top_srcdir)/src/engines
+
+noinst_LIBRARIES = libmodel.a
+libmodel_a_SOURCES = \
+ item-data.c \
+ item-data.h \
+ node.c \
+ node.h \
+ node-store.c \
+ node-store.h \
+ part.c \
+ part.h \
+ part-property.c \
+ part-property.h \
+ schematic.c \
+ schematic.h \
+ sheet-pos.h \
+ textbox.c \
+ textbox.h \
+ wire.c \
+ wire.h \
+ wire-private.h \
+ part-private.h
+#libmodel_a_LIBADD = @LIBMODEL_LIBADD@
+libmodel_a_LIBADD = libmodel.a
diff --git a/src/model/item-data.c b/src/model/item-data.c
new file mode 100644
index 0000000..c8d71c7
--- /dev/null
+++ b/src/model/item-data.c
@@ -0,0 +1,541 @@
+/*
+ * item-data.c
+ *
+ * ItemData object: part and wire model superclass.
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <gnome.h>
+#include "item-data.h"
+#include "node-store.h"
+
+static void item_data_class_init(ItemDataClass *klass);
+static void item_data_init(ItemData *item_data);
+static void item_data_set_gproperty(GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+static void item_data_get_gproperty(GObject *object, guint prop_id,
+ GValue *value, GParamSpec *spec);
+static void item_data_copy(ItemData *dest, ItemData *src);
+static void item_data_moved(ItemData *item_data, SheetPos *delta);
+
+enum {
+ ARG_0,
+ ARG_STORE,
+ ARG_POS
+};
+
+enum {
+ MOVED,
+ ROTATED,
+ FLIPPED,
+ HIGHLIGHT,
+ LAST_SIGNAL
+};
+
+struct _ItemDataPriv {
+ NodeStore *store;
+ SheetPos pos;
+
+ /*
+ * Bounding box.
+ */
+ SheetPos b1, b2;
+};
+
+static guint item_data_signals [LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+GType
+item_data_get_type (void)
+{
+ static GType item_data_type = 0;
+
+ if (!item_data_type) {
+ static const GTypeInfo item_data_info = {
+ sizeof(ItemDataClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) item_data_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof(ItemData),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)item_data_init,
+ NULL
+ };
+
+ item_data_type = g_type_register_static(G_TYPE_OBJECT, "ItemData",
+ &item_data_info, 0);
+ }
+
+ return item_data_type;
+}
+
+static void
+item_data_dispose(GObject *object)
+{
+ /*
+ * Remove the item from the sheet node store if there.
+ */
+ if (ITEM_DATA(object)->priv->store) {
+ item_data_unregister(ITEM_DATA(object));
+ }
+
+ parent_class->dispose(object);
+}
+
+
+static void
+item_data_finalize(GObject *object)
+{
+ parent_class->finalize(object);
+}
+
+
+static void
+item_data_class_init (ItemDataClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ /* Esta asignacion debe estar antes de las
+ * llamadas a g_object_class_install_property
+ */
+ object_class->set_property = item_data_set_gproperty;
+ object_class->get_property = item_data_get_gproperty;
+
+ g_object_class_install_property(
+ object_class,
+ ARG_STORE,
+ g_param_spec_pointer("store", "ItemData::store",
+ "the store data", G_PARAM_READWRITE));
+
+ g_object_class_install_property(
+ object_class,
+ ARG_POS,
+ g_param_spec_pointer("pos", "ItemData::pos",
+ "the pos data", G_PARAM_READWRITE));
+
+ object_class->dispose = item_data_dispose;
+ object_class->finalize = item_data_finalize;
+ item_data_signals [MOVED] = g_signal_new ("moved",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(ItemDataClass, moved),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ item_data_signals [ROTATED] = g_signal_new ("rotated",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ item_data_signals [FLIPPED] = g_signal_new ("flipped",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ item_data_signals [HIGHLIGHT] = g_signal_new ("highlight",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /*
+ * Methods.
+ */
+ klass->clone = NULL;
+ klass->copy = item_data_copy;
+ klass->rotate = NULL;
+ klass->flip = NULL;
+ klass->reg = NULL;
+ klass->unreg = NULL;
+
+ /*
+ * Signals.
+ */
+ klass->moved = item_data_moved; /* FIXME: remove this field.*/
+}
+
+static void
+item_data_init (ItemData *item_data)
+{
+ ItemDataPriv *priv;
+
+ priv = g_new0 (ItemDataPriv, 1);
+
+ priv->pos.x = 0;
+ priv->pos.y = 0;
+ priv->b1.x = priv->b1.y = priv->b2.x = priv->b2.y = 0.0;
+
+ item_data->priv = priv;
+}
+
+ItemData *
+item_data_new (void)
+{
+ ItemData *item_data;
+
+ item_data = ITEM_DATA(g_object_new(item_data_get_type(), NULL));
+
+ return item_data;
+}
+
+
+static void
+item_data_set_gproperty(GObject *object, guint prop_id, const GValue *value,
+ GParamSpec *spec)
+{
+ ItemData *item_data = ITEM_DATA(object);
+
+ switch (prop_id) {
+ case ARG_STORE:
+ item_data->priv->store = g_value_get_pointer(value);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+item_data_get_gproperty(GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec)
+{
+ ItemData *item_data = ITEM_DATA (object);
+
+ switch (prop_id) {
+ case ARG_STORE:
+ g_value_set_pointer(value, item_data->priv->store);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(item_data, prop_id, spec);
+ }
+}
+
+void
+item_data_get_pos (ItemData *item_data, SheetPos *pos)
+{
+ g_return_if_fail (item_data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (item_data));
+ g_return_if_fail (pos != NULL);
+
+ *pos = item_data->priv->pos;
+}
+
+void
+item_data_set_pos (ItemData *item_data, SheetPos *pos)
+{
+ ItemDataPriv *priv;
+ SheetPos delta;
+
+ g_return_if_fail (pos != NULL);
+ g_return_if_fail (item_data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (item_data));
+
+ if (pos == NULL)
+ return;
+
+ priv = item_data->priv;
+
+ delta.x = pos->x - priv->pos.x;
+ delta.y = pos->y - priv->pos.y;
+
+ priv->pos.x = pos->x;
+ priv->pos.y = pos->y;
+
+ g_signal_emit_by_name(G_OBJECT(item_data), "moved", &delta);
+}
+
+void
+item_data_move (ItemData *item_data, SheetPos *delta)
+{
+ ItemDataPriv *priv;
+
+ g_return_if_fail (item_data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (item_data));
+
+ if (delta == NULL)
+ return;
+
+ priv = item_data->priv;
+ priv->pos.x += delta->x;
+ priv->pos.y += delta->y;
+
+ g_signal_emit_by_name(G_OBJECT(item_data), "moved", delta);
+}
+
+static void
+item_data_moved (ItemData *item_data, SheetPos *delta)
+{
+/* g_print ("mooooooved\n");*/
+}
+
+gpointer /*NodeStore * */
+item_data_get_store (ItemData *item_data)
+{
+ g_return_val_if_fail (item_data != NULL, NULL);
+ g_return_val_if_fail (IS_ITEM_DATA (item_data), NULL);
+
+ return item_data->priv->store;
+}
+
+ItemData *
+item_data_clone (ItemData *src)
+{
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (IS_ITEM_DATA (src), NULL);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (src));
+ if (id_class->clone == NULL)
+ return NULL;
+
+ return id_class->clone (src);
+}
+
+static void
+item_data_copy (ItemData *dest, ItemData *src)
+{
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (IS_ITEM_DATA (dest));
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (IS_ITEM_DATA (src));
+
+ dest->priv->pos = src->priv->pos;
+ dest->priv->store = NULL;
+}
+
+void
+item_data_get_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2)
+{
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ if (p1) {
+ p1->x = data->priv->b1.x;
+ p1->y = data->priv->b1.y;
+ }
+
+ if (p2) {
+ p2->x = data->priv->b2.x;
+ p2->y = data->priv->b2.y;
+ }
+}
+
+void
+item_data_get_absolute_bbox (ItemData *data, SheetPos *p1, SheetPos *p2)
+{
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ item_data_get_relative_bbox (data, p1, p2);
+
+ if (p1) {
+ p1->x += data->priv->pos.x;
+ p1->y += data->priv->pos.y;
+ }
+
+ if (p2) {
+ p2->x += data->priv->pos.x;
+ p2->y += data->priv->pos.y;
+ }
+}
+
+void
+item_data_set_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2)
+{
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ if (p1) {
+ data->priv->b1.x = p1->x;
+ data->priv->b1.y = p1->y;
+ }
+
+ if (p2) {
+ data->priv->b2.x = p2->x;
+ data->priv->b2.y = p2->y;
+ }
+}
+
+void
+item_data_list_get_absolute_bbox (GList *item_data_list, SheetPos *p1,
+ SheetPos *p2)
+{
+ GList *list;
+ SheetPos b1, b2;
+
+ if (item_data_list == NULL)
+ return;
+
+ item_data_get_absolute_bbox (item_data_list->data, p1, p2);
+
+ for (list = item_data_list; list; list = list->next) {
+ item_data_get_absolute_bbox (list->data, &b1, &b2);
+
+ if (p1) {
+ p1->x = MIN (p1->x, b1.x);
+ p1->y = MIN (p1->y, b1.y);
+ }
+
+ if (p2) {
+ p2->x = MAX (p2->x, b2.x);
+ p2->y = MAX (p2->y, b2.y);
+ }
+ }
+}
+
+void
+item_data_rotate (ItemData *data, int angle, SheetPos *center)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->rotate) {
+ id_class->rotate (data, angle, center);
+ }
+}
+
+void
+item_data_flip (ItemData *data, gboolean horizontal, SheetPos *center)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->flip) {
+ id_class->flip (data, horizontal, center);
+ }
+}
+
+void
+item_data_unregister (ItemData *data)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->unreg) {
+ id_class->unreg (data);
+ }
+}
+
+int
+item_data_register (ItemData *data)
+{
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (data != NULL, -1);
+ g_return_val_if_fail (IS_ITEM_DATA (data), -1);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->reg) {
+ return id_class->reg (data);
+ }
+ return -1;
+}
+
+char *
+item_data_get_refdes_prefix (ItemData *data)
+{
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (IS_ITEM_DATA (data), NULL);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->get_refdes_prefix) {
+ return id_class->get_refdes_prefix (data);
+ }
+
+ return NULL;
+}
+
+void
+item_data_set_property (ItemData *data, char *property, char *value)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->set_property) {
+ id_class->set_property (data, property, value);
+ return;
+ }
+}
+
+gboolean
+item_data_has_properties (ItemData *data)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->has_properties) {
+ return id_class->has_properties (data);
+ }
+ return FALSE;
+}
+
+void
+item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx)
+{
+ ItemDataClass *id_class;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (cr != NULL);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data));
+ if (id_class->print) {
+ id_class->print (data, cr, ctx);
+ }
+}
+
diff --git a/src/model/item-data.h b/src/model/item-data.h
new file mode 100644
index 0000000..cbb68f0
--- /dev/null
+++ b/src/model/item-data.h
@@ -0,0 +1,148 @@
+/*
+ * item-data.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __ITEM_DATA_H
+#define __ITEM_DATA_H
+
+/** \file
+ *
+ * Base class for schematic model.
+ *
+ */
+
+#include <cairo/cairo.h>
+#include "sheet-pos.h"
+#include "schematic-print-context.h"
+
+#define TYPE_ITEM_DATA (item_data_get_type())
+#define ITEM_DATA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_ITEM_DATA, ItemData))
+#define ITEM_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_ITEM_DATA, ItemDataClass))
+#define IS_ITEM_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_ITEM_DATA))
+#define IS_ITEM_DATA_CLASS(klass) (G_TYPE_CHECK_INSTANCE_GET_CLASS((klass), TYPE_ITEM_DATA, ItemDataClass))
+
+typedef struct _ItemData ItemData;
+typedef struct _ItemDataClass ItemDataClass;
+typedef struct _ItemDataPriv ItemDataPriv;
+
+typedef enum {
+ ID_FLIP_NONE = 0,
+ ID_FLIP_HORIZ = 1 << 0,
+ ID_FLIP_VERT = 1 << 1
+} IDFlip;
+
+struct _ItemData {
+ GObject parent;
+ ItemDataPriv *priv;
+};
+
+struct _ItemDataClass
+{
+ GObjectClass parent_class;
+
+ /* Signals. */
+ void (*moved) (ItemData *data, SheetPos *delta);
+
+ /* Methods. */
+ ItemData *(*clone) (ItemData *src);
+ void (*copy) (ItemData *dest, ItemData *src);
+ void (*rotate) (ItemData *data, int angle, SheetPos *center);
+ void (*flip) (ItemData *data, gboolean horizontal, SheetPos *center);
+ void (*unreg) (ItemData *data);
+ int (*reg) (ItemData *data);
+
+ char* (*get_refdes_prefix) (ItemData *data);
+ void (*set_property) (ItemData *data, char *property, char *value);
+ gboolean (*has_properties) (ItemData *data);
+
+ void (*print) (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx);
+};
+
+GType item_data_get_type (void);
+/** Create a new ItemData object */
+ItemData *item_data_new (void);
+/** Clone an ItemData
+ *
+ * \param src A valid ItemData
+ */
+ItemData *item_data_clone (ItemData *src);
+/** Get Item position */
+void item_data_get_pos (ItemData *item_data, SheetPos *pos);
+/** Set Item position */
+void item_data_set_pos (ItemData *item_data, SheetPos *pos);
+/** Move an ItemData
+ *
+ * \param delta offset to move the item
+ */
+void item_data_move (ItemData *item_data, SheetPos *delta);
+/** Get the bounding box of an item data
+ *
+ * Retrieve the bounding box of the item relative to position
+ * \param p1 Where to store the upper-left point
+ * \param p2 Where to store the lower-right point
+ */
+void item_data_get_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2);
+/** Set the relative bounding box */
+void item_data_set_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2);
+/** Get absolute bounding box
+ *
+ * This function is like item_data_get_relative_bbox but it add
+ * the item position to the bbox vertex
+ */
+void item_data_get_absolute_bbox (ItemData *data, SheetPos *p1, SheetPos *p2);
+/** Get the absolute bounding box of a list of items
+ *
+ * This return a bbox that enclose all item in a list
+ */
+void item_data_list_get_absolute_bbox (GList *item_data_list, SheetPos *p1, SheetPos *p2);
+/** Rotate an item */
+void item_data_rotate (ItemData *data, int angle, SheetPos *center);
+/** Flip an item */
+void item_data_flip (ItemData *data, gboolean horizontal, SheetPos *center);
+/** Get the Store associated for this item
+ *
+ * Store is a class that hold all items in a schematic
+ */
+gpointer item_data_get_store (ItemData *item_data);
+/** Unregister item in his Store */
+void item_data_unregister (ItemData *data);
+/** Register item in his Store */
+int item_data_register (ItemData *data);
+/** Tell us what it does :-D FIXME */
+char *item_data_get_refdes_prefix (ItemData *data);
+gboolean item_data_has_properties (ItemData *date);
+/** Set property */
+void item_data_set_property (ItemData *data, char *property, char *value);
+/** Print Item data
+ *
+ * This method implement the Cairo stuff for schematic print of an item.
+ */
+void item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx);
+
+
+#endif
diff --git a/src/model/node-store.c b/src/model/node-store.c
new file mode 100644
index 0000000..2154b8b
--- /dev/null
+++ b/src/model/node-store.c
@@ -0,0 +1,1227 @@
+/*
+ * node-store.c
+ *
+ * This is where the circuit elements (pins/wires) are stored.
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <glib.h>
+#include <glib-object.h>
+#include "node-store.h"
+#include "node.h"
+#include "part.h"
+#include "wire.h"
+#include "wire-private.h"
+#include "item-data.h"
+
+/*
+ * NODE_EPSILON is used to check for intersection.
+ * HASH_EPSILON is used in the hash equality check function.
+ */
+#define NODE_EPSILON 1e-10
+#define HASH_EPSILON 1e-3
+
+/* Share an endpoint? */
+#define SEP(p1x,p1y,p2x,p2y) (IS_EQ(p1x, p2x) && IS_EQ(p1y, p2y))
+
+/* Equals? */
+#define IS_EQ(a,b) (fabs ((a) - (b)) < NODE_EPSILON)
+
+#define ON_THE_WIRE(p1,start,end) ( fabs((end.y-start.y)*(p1.x-start.x)-(end.x-start.x)*(p1.y-start.y))<NODE_EPSILON )
+
+static void node_store_class_init (NodeStoreClass *klass);
+static void node_store_init (NodeStore *store);
+static guint node_hash (gconstpointer key);
+static int node_equal (gconstpointer a, gconstpointer b);
+static GSList *wires_intersect (NodeStore *store, double x1, double y1,
+ double x2, double y2);
+static GSList *wire_intersect_parts (NodeStore *store, Wire *wire);
+static int is_wire_at_pos (double x1, double y1, double x2, double y2,
+ SheetPos pos);
+static GSList *wires_at_pos (NodeStore *store, SheetPos pos);
+static int do_wires_intersect (double Ax, double Ay, double Bx, double By,
+ double Cx, double Cy, double Dx, double Dy, SheetPos *pos);
+static void node_store_finalize(GObject *self);
+static void node_store_dispose(GObject *self);
+
+typedef struct {
+ Wire *wire;
+ SheetPos pos;
+} IntersectionPoint;
+
+enum {
+ DOT_ADDED,
+ DOT_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint node_store_signals [LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+GType
+node_store_get_type (void)
+{
+ static GType node_store_type = 0;
+
+ if (!node_store_type) {
+ static const GTypeInfo node_store_info = {
+ sizeof (NodeStoreClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) node_store_class_init,
+ NULL,
+ NULL,
+ sizeof (NodeStore),
+ 0,
+ (GInstanceInitFunc) node_store_init,
+ NULL
+ };
+
+ node_store_type = g_type_register_static (G_TYPE_OBJECT, "NodeStore",
+ &node_store_info, 0);
+ }
+
+ return node_store_type;
+}
+
+static void
+node_store_finalize(GObject *object)
+{
+ NodeStore *self = NODE_STORE(object);
+
+ if (self->nodes) {
+ g_hash_table_destroy (self->nodes);
+ self->nodes = NULL;
+ }
+
+ if (self->wires) {
+ g_list_free (self->wires);
+ self->wires = NULL;
+ }
+ if (self->parts) {
+ g_list_free (self->parts);
+ self->parts = NULL;
+ }
+ if (self->items) {
+ g_list_free (self->items);
+ self->items = NULL;
+ }
+
+ parent_class->finalize(object);
+}
+
+static void
+node_store_dispose(GObject *self)
+{
+ parent_class->dispose(self);
+}
+
+static void
+node_store_class_init (NodeStoreClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS(klass);
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = node_store_finalize;
+ gobject_class->dispose = node_store_dispose;
+
+ node_store_signals [DOT_ADDED] =
+ g_signal_new ("dot_added",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NodeStoreClass, dot_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+
+ node_store_signals [DOT_REMOVED] =
+ g_signal_new ("dot_removed",
+ TYPE_NODE_STORE,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NodeStoreClass, dot_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+}
+
+static void
+node_store_init (NodeStore *self)
+{
+ self->nodes = g_hash_table_new (node_hash, node_equal);
+ self->wires = NULL;
+ self->parts = NULL;
+ self->items = NULL;
+ self->textbox = NULL;
+}
+
+NodeStore *
+node_store_new (void)
+{
+ return NODE_STORE(g_object_new (TYPE_NODE_STORE, NULL));
+}
+
+static void
+dot_added_callback (Node *node, SheetPos *pos, NodeStore *store)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_signal_emit_by_name(G_OBJECT(store), "dot_added", pos);
+}
+
+static void
+dot_removed_callback (Node *node, SheetPos *pos, NodeStore *store)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_signal_emit_by_name(G_OBJECT(store), "dot_removed", pos);
+}
+
+Node *
+node_store_get_or_create_node (NodeStore *self, SheetPos pos)
+{
+ Node *node;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (self), NULL);
+
+ node = g_hash_table_lookup (self->nodes, &pos);
+
+ if (!node) {
+ /*
+ * Create a node at (x, y) and return it.
+ */
+ node = node_new (pos);
+
+ g_signal_connect_object(
+ G_OBJECT (node),
+ "dot_added",
+ G_CALLBACK (dot_added_callback),
+ G_OBJECT (self),
+ 0);
+
+ g_signal_connect_object (
+ G_OBJECT (node),
+ "dot_removed",
+ G_CALLBACK (dot_removed_callback),
+ G_OBJECT (self),
+ 0);
+
+ g_hash_table_insert (self->nodes, &node->key, node);
+ }
+
+ /*
+ * If there was a previously stored node here, just
+ * return that node.
+ */
+ return node;
+}
+
+int
+node_store_add_part (NodeStore *self, Part *part)
+{
+ GSList *wire_list, *list;
+ Node *node;
+ SheetPos lookup_key;
+ SheetPos part_pos;
+ gdouble x, y;
+ int i, num_pins;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ num_pins = part_get_num_pins (part);
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ for (i = 0; i < num_pins; i++) {
+ Pin *pins;
+
+ pins = part_get_pins (part);
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ /*
+ * Use the position of the pin as hash key.
+ */
+ lookup_key.x = x;
+ lookup_key.y = y;
+
+ /*
+ * Retrieve a node for this position.
+ */
+ node = node_store_get_or_create_node (self, lookup_key);
+
+ /*
+ * Add all the wires that intersect this pin to the node store.
+ */
+ wire_list = wires_at_pos (self, lookup_key);
+
+ for (list = wire_list; list; list = list->next) {
+ Wire *wire = list->data;
+
+ /* g_print ("Add pin to wire.\n"); */
+
+ node_add_wire (node, wire);
+ wire_add_node (wire, node);
+ }
+
+ g_slist_free (wire_list);
+
+ node_add_pin (node, &pins[i]);
+ }
+
+ g_object_set (G_OBJECT (part), "store", self, NULL);
+ self->parts = g_list_prepend (self->parts, part);
+ self->items = g_list_prepend (self->items, part);
+
+ return TRUE;
+}
+
+int
+node_store_remove_part (NodeStore *self, Part *part)
+{
+ Node *node;
+ SheetPos lookup_key;
+ SheetPos pos;
+ gdouble x, y;
+ int i, num_pins;
+ Pin *pins;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ self->parts = g_list_remove (self->parts, part);
+ self->items = g_list_remove (self->items, part);
+
+ num_pins = part_get_num_pins (part);
+ item_data_get_pos (ITEM_DATA (part), &pos);
+
+ pins = part_get_pins (part);
+ for (i = 0; i < num_pins; i++) {
+ x = pos.x + pins[i].offset.x;
+ y = pos.y + pins[i].offset.y;
+
+ /*
+ * Use the position of the pin as lookup key.
+ */
+ lookup_key.x = x;
+ lookup_key.y = y;
+
+ node = g_hash_table_lookup (self->nodes, &lookup_key);
+ if (node) {
+ if (!node_remove_pin (node, &pins[i])) {
+ g_warning ("Couldn't remove pin.");
+ return FALSE;
+ }
+
+ /*
+ * If the node is empty after removing the pin,
+ * remove the node as well.
+ */
+ if (node_is_empty (node)) {
+ g_hash_table_remove (self->nodes, &lookup_key);
+ g_object_unref(G_OBJECT(node));
+ }
+ } else {
+ /* FIXME: Fix this or just silently return if the part is
+ non-existant? */
+ g_warning ("No node to remove part from.");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+int
+node_store_add_textbox (NodeStore *self, Textbox *text)
+{
+ g_object_set (G_OBJECT (text), "store", self, NULL);
+ self->textbox = g_list_prepend (self->textbox, text);
+
+ return TRUE;
+}
+
+int
+node_store_remove_textbox (NodeStore *self, Textbox *text)
+{
+ self->textbox = g_list_remove (self->textbox, text);
+
+ return TRUE;
+}
+
+int
+node_store_add_wire (NodeStore *store, Wire *wire)
+{
+ gdouble x1, y1, x2, y2;
+ GSList *ip_list, *list;
+ IntersectionPoint *ipoint;
+ Node *node;
+ WirePriv *priv;
+ SheetPos pos, length;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+/* if (wire_get_store (wire) != NULL) {
+ g_warning ("Trying to add already stored wire.");
+ return FALSE;
+ }
+*/
+
+/* g_print ("ADD WIRE\n");*/
+
+ priv = wire->priv;
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ x1 = pos.x;
+ y1 = pos.y;
+ x2 = x1 + length.x;
+ y2 = y1 + length.y;
+
+ /*
+ * Check for intersection with other wires.
+ */
+ ip_list = wires_intersect (store, x1, y1, x2, y2);
+
+ for (list = ip_list; list; list = list->next) {
+ ipoint = list->data;
+
+
+ //g_print ("(%g %g) (%g %g), ip (%g %g)\n", x1, y1, x2, y2, ipoint->pos.x, ipoint->pos.y);
+
+
+ if (IS_EQ (x1, x2) && ((ipoint->pos.y == y1) || (ipoint->pos.y == y2))) {
+ SheetPos w_pos, w_length;
+ gboolean can_join;
+ GSList *nodes;
+
+ wire_get_pos_and_length (ipoint->wire, &w_pos, &w_length);
+ gdouble _x1, _x2, _y1, _y2;
+
+ _x1 = w_pos.x;
+ _y1 = w_pos.y;
+ _x2 = _x1 + w_length.x;
+ _y2 = _y1 + w_length.y;
+
+ can_join = TRUE;
+ nodes = wire_get_nodes (wire);
+ for (; nodes; nodes = nodes->next) {
+ SheetPos p1;
+ Node *node = (Node *)nodes->data;
+
+ p1.x = _x1;
+ p1.y = _y1;
+ if ((node->key.x == p1.x) && (node->key.y == p1.y)) {
+ can_join = FALSE;
+ break;
+ }
+ p1.x = _x2;
+ p1.y = _y2;
+ if ((node->key.x == p1.x) && (node->key.y == p1.y)) {
+ can_join = FALSE;
+ break;
+ }
+ }
+
+ if (IS_EQ(_x1, _x2) && can_join) {
+ if (w_pos.x < pos.x) pos.x = w_pos.x;
+ if (w_pos.y < pos.y) pos.y = w_pos.y;
+ length.x += w_length.x;
+ length.y += w_length.y;
+
+ /* Update the new size and pos of the wire */
+ item_data_unregister (ITEM_DATA (ipoint->wire));
+ wire_set_length (ipoint->wire, &length);
+ item_data_set_pos (ITEM_DATA (ipoint->wire), &pos);
+ wire_update_bbox (ipoint->wire);
+ item_data_register (ITEM_DATA (ipoint->wire));
+
+ /* Done!, return -1 so wire si deleted */
+ return -1;
+ }
+ } else if (IS_EQ (y1, y2) && ((ipoint->pos.x == x1) || (ipoint->pos.x == x2))) {
+ SheetPos w_pos, w_length;
+ gboolean can_join;
+ GSList *nodes;
+
+ wire_get_pos_and_length (ipoint->wire, &w_pos, &w_length);
+ gdouble _x1, _x2, _y1, _y2;
+
+ _x1 = w_pos.x;
+ _y1 = w_pos.y;
+ _x2 = _x1 + w_length.x;
+ _y2 = _y1 + w_length.y;
+
+ can_join = TRUE;
+ nodes = wire_get_nodes (wire);
+ for (; nodes; nodes = nodes->next) {
+ SheetPos p;
+ Node *node = (Node *)nodes->data;
+
+ p.x = _x1;
+ p.y = _y1;
+ if ((node->key.x == p.x) && (node->key.y == p.y)) {
+ can_join = FALSE;
+ break;
+ }
+ p.x = _x2;
+ p.y = _y2;
+ if ((node->key.x == p.x) && (node->key.y == p.y)) {
+ can_join = FALSE;
+ break;
+ }
+ }
+
+ if (IS_EQ(_y1, _y2) && can_join) {
+ if (w_pos.x < pos.x) pos.x = w_pos.x;
+ if (w_pos.y < pos.y) pos.y = w_pos.y;
+ length.x += w_length.x;
+ length.y += w_length.y;
+
+ /* Update the new size and pos of the wire */
+ item_data_unregister (ITEM_DATA (ipoint->wire));
+ wire_set_length (ipoint->wire, &length);
+ item_data_set_pos (ITEM_DATA (ipoint->wire), &pos);
+ wire_update_bbox (ipoint->wire);
+ item_data_register (ITEM_DATA (ipoint->wire));
+
+ /* Done!, return -1 so wire si deleted */
+ return -1;
+ }
+ }
+
+ node = node_store_get_or_create_node (store, ipoint->pos);
+
+ /*
+ * Add the wire, and also the wire that is intersected.
+ */
+ node_add_wire (node, wire);
+ node_add_wire (node, ipoint->wire);
+
+ wire_add_node (wire, node);
+ wire_add_node (ipoint->wire, node);
+
+/* g_print ("Add wire to wire.\n");*/
+
+ g_free (ipoint);
+ }
+ g_slist_free (ip_list);
+
+ /*
+ * Check for intersection with parts (pins).
+ */
+ ip_list = wire_intersect_parts (store, wire);
+
+ for (list = ip_list; list; list = list->next) {
+ node = list->data;
+
+ /*
+ * Add the wire to the node (pin) that it intersected.
+ */
+ node_add_wire (node, wire);
+ wire_add_node (wire, node);
+
+/* g_print ("Add wire to pin.\n");*/
+ }
+
+ g_slist_free (ip_list);
+
+ g_object_set (G_OBJECT (wire), "store", store, NULL);
+ store->wires = g_list_prepend (store->wires, wire);
+ store->items = g_list_prepend (store->items, wire);
+
+ return TRUE;
+}
+
+int
+node_store_remove_wire (NodeStore *store, Wire *wire)
+{
+ gdouble x1, y1, x2, y2;
+ GSList *list;
+ SheetPos lookup_key, pos, length;
+ WirePriv *priv;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ priv = wire->priv;
+
+ if (item_data_get_store (ITEM_DATA (wire)) == NULL) {
+ g_warning ("Trying to remove non-stored wire.");
+ return FALSE;
+ }
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ x1 = pos.x;
+ y1 = pos.y;
+ x2 = x1 + length.x;
+ y2 = y1 + length.y;
+
+ store->wires = g_list_remove (store->wires, wire);
+ store->items = g_list_remove (store->items, wire);
+
+ /*
+ * If the nodes that this wire passes through will be
+ * empty when the wire is removed, remove the node as well.
+ */
+
+ /*
+ * We must work on a copy of the nodes list, since it
+ * changes as we remove nodes.
+ */
+ list = g_slist_copy (wire_get_nodes (wire));
+
+ for (; list; list = list->next) {
+ Node *node = list->data;
+
+ lookup_key = node->key;
+
+ node_remove_wire (node, wire);
+
+ wire_remove_node (wire, node);
+
+ if (node_is_empty (node))
+ g_hash_table_remove (store->nodes, &lookup_key);
+ }
+
+ g_slist_free (list);
+
+ return TRUE;
+}
+
+static GSList *
+wire_intersect_parts (NodeStore *store, Wire *wire)
+{
+ GList *list;
+ GSList *ip_list;
+ Node *node;
+ SheetPos lookup_pos;
+ SheetPos part_pos, wire_pos, wire_length;
+ Part *part;
+ double x, y, wire_x1, wire_y1, wire_x2, wire_y2;
+ int i, num_pins;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ ip_list = NULL;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+
+ wire_x1 = wire_pos.x;
+ wire_x2 = wire_pos.x + wire_length.x;
+ wire_y1 = wire_pos.y;
+ wire_y2 = wire_pos.y + wire_length.y;
+
+ /*
+ * Go through all the parts and see which of their
+ * pins that intersect the wire.
+ */
+ for (list = store->parts; list; list = list->next) {
+ part = list->data;
+
+ num_pins = part_get_num_pins (part);
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ for (i = 0; i < num_pins; i++) {
+ Pin *pins;
+
+ pins = part_get_pins (part);
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ lookup_pos.x = x;
+ lookup_pos.y = y;
+
+ /*
+ * If there is a wire at this pin's position,
+ * add it to the return list.
+ */
+ if (is_wire_at_pos (wire_x1, wire_y1, wire_x2, wire_y2, lookup_pos)) {
+ node = node_store_get_node (store, lookup_pos);
+
+ if (node == NULL)
+ g_warning ("Bug: Found no node at pin at (%g %g).\n", x, y);
+ else
+ ip_list = g_slist_prepend (ip_list, node);
+ }
+ }
+ }
+
+ return ip_list;
+}
+
+static GSList *
+wires_at_pos (NodeStore *store, SheetPos pos)
+{
+ GList *list;
+ GSList *wire_list;
+ Wire *wire;
+ SheetPos wire_pos, wire_length;
+ double x1, y1, x2, y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ wire_list = NULL;
+
+ for (list = store->wires; list; list = list->next) {
+ wire = list->data;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+ x1 = wire_pos.x;
+ y1 = wire_pos.y;
+ x2 = x1 + wire_length.x;
+ y2 = y1 + wire_length.y;
+
+ if (is_wire_at_pos (x1, y1, x2, y2, pos))
+ wire_list = g_slist_prepend (wire_list, wire);
+ }
+
+ return wire_list;
+}
+
+int
+node_store_is_wire_at_pos (NodeStore *store, SheetPos pos)
+{
+ GList *list;
+ Wire *wire;
+ SheetPos wire_pos, wire_length;
+ double x1, y1, x2, y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ for (list = store->wires; list; list = list->next) {
+ wire = list->data;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+ x1 = wire_pos.x;
+ y1 = wire_pos.y;
+ x2 = x1 + wire_length.x;
+ y2 = y1 + wire_length.y;
+
+ if (is_wire_at_pos (x1, y1, x2, y2, pos))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GSList *
+wires_intersect (NodeStore *store, double x1, double y1, double x2, double y2)
+{
+ GList *list;
+ GSList *ip_list;
+ Wire *wire2;
+ SheetPos pos, wire2_pos, wire2_length;
+ SheetPos wire1_start_pos, wire1_end_pos;
+ double wire2_x1, wire2_y1, wire2_x2, wire2_y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ wire1_start_pos.x = x1;
+ wire1_start_pos.y = y1;
+ wire1_end_pos.x = x2;
+ wire1_end_pos.y = y2;
+
+ /*
+ * Search through all the wires. Is there a better way?
+ */
+ ip_list = NULL;
+ for (list = store->wires; list; list = list->next) {
+ wire2 = list->data;
+
+ wire_get_pos_and_length (wire2, &wire2_pos, &wire2_length);
+ wire2_x1 = wire2_pos.x;
+ wire2_y1 = wire2_pos.y;
+ wire2_x2 = wire2_x1 + wire2_length.x;
+ wire2_y2 = wire2_y1 + wire2_length.y;
+
+ if (do_wires_intersect (x1, y1, x2, y2, wire2_x1, wire2_y1,
+ wire2_x2, wire2_y2, &pos)) {
+ IntersectionPoint *ip;
+
+ ip = g_new0 (IntersectionPoint, 1);
+
+ ip->wire = wire2;
+ ip->pos = pos;
+ ip_list = g_slist_prepend (ip_list, ip);
+ }
+ }
+
+ return ip_list;
+}
+
+/*
+ * node_store_get_node
+ *
+ * Find the node that has an element at a certain position.
+ */
+Node *
+node_store_get_node (NodeStore *store, SheetPos pos)
+{
+ Node *node;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ node = g_hash_table_lookup (store->nodes, &pos);
+
+/* if (!node)
+ g_print ("No node at (%g, %g)\n", pos.x, pos.y);
+ else
+ g_print ("Found node at (%g, %g)\n", pos.x, pos.y);
+*/
+ return node;
+}
+
+static guint
+node_hash (gconstpointer key)
+{
+ SheetPos *sp = (SheetPos *) key;
+ int x, y;
+
+ /* hasha på varannan bit? */
+ /* Can anybody translate this? */
+
+ x = (int)rint (sp->x) % 256;
+ y = (int)rint (sp->y) % 256;
+
+ return (y << 8) | x;
+}
+
+static int
+node_equal (gconstpointer a, gconstpointer b)
+{
+ SheetPos *spa, *spb;
+
+ spa = (SheetPos *) a;
+ spb = (SheetPos *) b;
+
+ if (fabs (spa->y - spb->y) > HASH_EPSILON) {
+/* if (fabs (spa->y - spb->y) < 2.0)
+ g_print ("y mellan EPSILON och 2.0!\n");
+*/
+ return 0;
+ }
+
+ if (fabs (spa->x - spb->x) > HASH_EPSILON) {
+/* if (fabs (spa->x - spb->x) < 5.0)
+ g_print ("x mellan EPSILON och 2.0!\n");
+*/
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_hash_table_foreach (store->nodes, (gpointer)func, user_data);
+}
+
+static int
+is_wire_at_pos (double x1, double y1, double x2, double y2, SheetPos pos)
+{
+ double k, x0, y0;
+
+ x0 = pos.x;
+ y0 = pos.y;
+
+ if (!IS_EQ (x1, x2) && !IS_EQ (y1, y2)) {
+ k = ((y2 - y1)) / ((x2 - x1));
+ if (IS_EQ (y0, (y1 + k * (x0 - x1)))) {
+ return TRUE;
+ }
+ } else if (IS_EQ (x2, x1) && IS_EQ (x1, x0)) {
+ if (y0 >= y1 && y0 <= y2) {
+ return TRUE;
+ }
+ } else if (IS_EQ (y2, y1) && IS_EQ (y1, y0)) {
+ if (x0 >= x1 && x0 <= x2) {
+ return TRUE;
+ }
+ }
+
+// g_print ("no match: (%g %g) -> (%g %g), (%g %g)\n", x1, y1, x2, y2, pos.x, pos.y);
+
+ return FALSE;
+}
+
+/*
+ * Decides if two wires intersect. Note that wires that share an
+ * endpoint are considered intersecting each other. Intersection point
+ * is returned in pos.
+ */
+static int
+do_wires_intersect (double Ax, double Ay, double Bx, double By, double Cx,
+ double Cy, double Dx, double Dy, SheetPos *pos)
+{
+ double r, s, d;
+
+ /*
+ * Wires don't intersect if they share an endpoint. NOTE: they do here...
+ */
+ if (SEP (Ax, Ay, Cx, Cy)) { /* same starting point */
+ pos->x = Ax;
+ pos->y = Ay;
+ return TRUE;
+ } else if (SEP (Ax, Ay, Dx, Dy)) { /* 1st start == 2nd end */
+ pos->x = Ax;
+ pos->y = Ay;
+ return TRUE;
+ } else if (SEP (Bx, By, Cx, Cy)) { /* 1st end == 2nd start */
+ pos->x = Bx;
+ pos->y = By;
+ return TRUE;
+ } else if (SEP (Bx, By, Dx, Dy)) { /* 1st end == 2nd end */
+ pos->x = Bx;
+ pos->y = By;
+ return TRUE;
+ }
+
+ /*
+ * Calculate the denominator.
+ */
+ d = ((Bx - Ax) * (Dy - Cy) - (By - Ay) * (Dx - Cx));
+
+ /*
+ * We have two parallell wires if d = 0.
+ */
+ if (fabs (d) < NODE_EPSILON) {
+ return FALSE;
+ }
+
+ r = ((Ay - Cy) * (Dx - Cx) - (Ax - Cx) * (Dy - Cy));
+
+ /*
+ * Colinear wires, if r = 0. FIXME: check for intersection?
+ * not needed since we already checked for starts and ends
+ */
+ if (fabs (r) < NODE_EPSILON) {
+
+ }
+
+ r = r / d;
+ s = ((Ay - Cy) * (Bx - Ax) - (Ax - Cx) * (By - Ay)) / d;
+
+ /*
+ * Check for intersection, which we have for values of
+ * r and s in [0, 1].
+ */
+ if (r >= 0 &&
+ (r - 1.0) < NODE_EPSILON &&
+ s >= 0 &&
+ (s - 1.0) < NODE_EPSILON) {
+
+ /*
+ * Calculate the intersection point.
+ */
+ pos->x = Ax + r * (Bx - Ax);
+ pos->y = Ay + r * (By - Ay);
+
+ /*
+ to be accepted only if it coincides with the start or end
+ of any of the wires
+ */
+ if ( SEP(pos->x,pos->y,Ax,Ay) ||
+ SEP(pos->x,pos->y,Bx,By) ||
+ SEP(pos->x,pos->y,Cx,Cy) ||
+ SEP(pos->x,pos->y,Dx,Dy)
+ )
+
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+GList *
+node_store_get_parts (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->parts;
+}
+
+GList *
+node_store_get_wires (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->wires;
+}
+
+GList *
+node_store_get_items (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->items;
+}
+
+/*
+ * Debugging code.
+ */
+
+void
+node_store_dump_wires (NodeStore *store)
+{
+ GList *wires;
+
+ g_print ("\n------------------- Dump wires -------------------\n");
+
+ for (wires = store->wires; wires; wires = wires->next) {
+ Wire *wire;
+ SheetPos start, length;
+
+ wire = wires->data;
+ wire_get_pos_and_length (wire, &start, &length);
+
+ g_print ("(%g %g) -> (%g %g): %d nodes.\n",
+ start.x, start.y,
+ start.x + length.x, start.y + length.y,
+ g_slist_length (wire->priv->nodes));
+ }
+
+ g_print ("\n");
+}
+
+static void
+add_node (gpointer key, Node *node, GList **list)
+{
+ *list = g_list_prepend (*list, node);
+}
+
+static void
+add_node_position (gpointer key, Node *node, GList **list)
+{
+ if (node_needs_dot (node))
+ *list = g_list_prepend (*list, key);
+}
+
+GList *
+node_store_get_node_positions (NodeStore *store)
+{
+ GList *result;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ result = NULL;
+ g_hash_table_foreach (store->nodes, (GHFunc) add_node_position, &result);
+
+ return result;
+}
+
+GList *
+node_store_get_nodes (NodeStore *store)
+{
+ GList *result;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ result = NULL;
+ g_hash_table_foreach (store->nodes, (GHFunc) add_node, &result);
+
+ return result;
+}
+
+void
+node_store_get_bounds (NodeStore *store, ArtDRect *rect)
+{
+ GList *list;
+ SheetPos p1, p2;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+ g_return_if_fail (rect != NULL);
+
+ rect->x0 = G_MAXDOUBLE;
+ rect->y0 = G_MAXDOUBLE;
+ rect->x1 = -G_MAXDOUBLE;
+ rect->y1 = -G_MAXDOUBLE;
+
+ for (list = store->items; list; list = list->next) {
+ item_data_get_absolute_bbox (ITEM_DATA (list->data), &p1, &p2);
+
+ rect->x0 = MIN (rect->x0, p1.x);
+ rect->y0 = MIN (rect->y0, p1.y);
+ rect->x1 = MAX (rect->x1, p2.x);
+ rect->y1 = MAX (rect->y1, p2.y);
+ }
+}
+
+gint
+node_store_count_items (NodeStore *store, ArtDRect *rect)
+{
+ GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+ gint n;
+
+ g_return_val_if_fail (store != NULL, 0);
+ g_return_val_if_fail (IS_NODE_STORE (store), 0);
+
+ if (rect == NULL)
+ return g_list_length (store->items);
+
+ for (list = store->items, n = 0; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_get_absolute_bbox (data, &p1, &p2);
+ if (p1.x <= rect->x1 && p1.y <= rect->y1 &&
+ p2.x >= rect->x0 && p2.y >= rect->y0) {
+ n++;
+/*
+ if (p1.x >= rect->x0 && p1.y >= rect->y0 && p2.x <= rect->x1 && p2.y <= rect->y1) {
+*/
+ }
+ }
+
+ return n;
+}
+
+static void
+draw_dot (SheetPos *key, Node *value, cairo_t *cr)
+{
+ if (node_needs_dot (value)) {
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_translate (cr, key->x, key->y);
+ cairo_scale (cr, 1.0, 1.0);
+ cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI);
+ cairo_fill (cr);
+ cairo_restore (cr);
+ }
+}
+
+void
+node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ctx)
+{
+ GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ for (list = store->items; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_print (data, cr, ctx);
+ }
+
+ g_hash_table_foreach (store->nodes, (GHFunc)draw_dot, cr);
+}
+
+void
+node_store_print_labels (NodeStore *store, cairo_t *opc, ArtDRect *rect)
+{
+/* GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+ g_return_if_fail (rect != NULL);
+
+ for (list = store->textbox; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_get_absolute_bbox (data, &p1, &p2);
+// if (p1.x >= rect->x0 && p1.y >= rect->y0 && p2.x <= rect->x1 && p2.y <= rect->y1) {
+ if ((p1.x <= rect->x1) && (p1.y <= rect->y1) &&
+ (p2.x >= rect->x0) && (p2.y >= rect->y0)) {
+ item_data_print (data, opc);
+ }
+ }
+*/
+}
+
+int
+node_store_is_pin_at_pos (NodeStore *store, SheetPos pos)
+{
+ int num_pins;
+ SheetPos part_pos;
+ GList *p;
+ Part *part;
+ Pin *pins;
+ int i;
+ gdouble x, y;
+
+ for (p = store->parts; p; p = p->next) {
+ part = PART (p->data);
+
+ num_pins = part_get_num_pins (part);
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ pins = part_get_pins (part);
+ for (i = 0; i < num_pins; i++) {
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ if ((x == pos.x) && (y == pos.y)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/src/model/node-store.h b/src/model/node-store.h
new file mode 100644
index 0000000..857b94c
--- /dev/null
+++ b/src/model/node-store.h
@@ -0,0 +1,99 @@
+/*
+ * node-store.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __NODE_STORE_H
+#define __NODE_STORE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "sheet-pos.h"
+
+#define TYPE_NODE_STORE node_store_get_type ()
+#define NODE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NODE_STORE, NodeStore))
+#define NODE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_NODE_STORE, NodeStoreClass))
+#define IS_NODE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NODE_STORE))
+#define NODE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_NODE_STORE, NodeStoreClass))
+
+
+typedef struct _NodeStore NodeStore;
+typedef struct _NodeStoreClass NodeStoreClass;
+
+#include "schematic-print-context.h"
+#include "node.h"
+#include "wire.h"
+#include "part.h"
+#include "textbox.h"
+
+struct _NodeStore {
+ GObject parent;
+
+ GHashTable *nodes;
+ GList *items;
+ GList *wires;
+ GList *parts;
+ GList *textbox;
+};
+
+struct _NodeStoreClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+
+ void (*dot_added) (NodeStore*);
+ void (*dot_removed) (NodeStore*);
+};
+
+GType node_store_get_type (void);
+NodeStore *node_store_new (void);
+Node *node_store_get_node (NodeStore *store, SheetPos pos);
+int node_store_add_part (NodeStore *store, Part *part);
+int node_store_remove_part (NodeStore *store, Part *part);
+int node_store_add_wire (NodeStore *store, Wire *wire);
+int node_store_remove_wire (NodeStore *store, Wire *wire);
+int node_store_add_textbox (NodeStore *self, Textbox *text);
+int node_store_remove_textbox (NodeStore *self, Textbox *text);
+void node_store_node_foreach (NodeStore *store, GHFunc *func,
+ gpointer user_data);
+int node_store_is_wire_at_pos (NodeStore *store, SheetPos pos);
+int node_store_is_pin_at_pos (NodeStore *store, SheetPos pos);
+GList *node_store_get_parts (NodeStore *store);
+GList *node_store_get_wires (NodeStore *store);
+GList *node_store_get_items (NodeStore *store);
+GList *node_store_get_node_positions (NodeStore *store);
+GList *node_store_get_nodes (NodeStore *store);
+void node_store_dump_wires (NodeStore *store);
+void node_store_get_bounds (NodeStore *store, ArtDRect *rect);
+gint node_store_count_items (NodeStore *store, ArtDRect *rect);
+void node_store_print_items (NodeStore *store, cairo_t *opc, SchematicPrintContext *ctx);
+void node_store_print_labels (NodeStore *store,
+ cairo_t *opc, ArtDRect *rect);
+Node *node_store_get_or_create_node (NodeStore *store, SheetPos pos);
+
+#endif
diff --git a/src/model/node.c b/src/model/node.c
new file mode 100644
index 0000000..3045b92
--- /dev/null
+++ b/src/model/node.c
@@ -0,0 +1,334 @@
+/*
+ * node.c
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gnome.h>
+#include <math.h>
+#include "node.h"
+#include "part.h"
+
+static void node_class_init (NodeClass *klass);
+static void node_init (Node *node);
+
+enum {
+ DOT_ADDED,
+ DOT_REMOVED,
+ VOLTAGE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint node_signals [LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+GType
+node_get_type (void)
+{
+ static GType node_type = 0;
+
+ if (!node_type) {
+ static const GTypeInfo node_info = {
+ sizeof (NodeClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) node_class_init,
+ NULL,
+ NULL,
+ sizeof (Node),
+ 0,
+ (GInstanceInitFunc) node_init,
+ NULL
+ };
+
+ node_type = g_type_register_static(G_TYPE_OBJECT, "Node", &node_info, 0);
+ }
+
+ return node_type;
+}
+
+static void
+node_class_init (NodeClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *)klass;
+ parent_class = g_type_class_peek_parent(klass);
+
+ node_signals [DOT_ADDED] =
+ g_signal_new ("dot_added",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ node_signals [DOT_REMOVED] =
+ g_signal_new ("dot_removed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ node_signals [VOLTAGE_CHANGED] =
+ g_signal_new ("voltage_changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+node_init (Node *node)
+{
+ node->pin_count = 0;
+ node->wire_count = 0;
+ node->pins = NULL;
+ node->wires = NULL;
+ node->visited = FALSE;
+}
+
+Node *
+node_new (SheetPos pos)
+{
+ Node *node;
+
+ node = NODE(g_object_new(node_get_type(), NULL));
+
+ node->key = pos;
+
+ return node;
+}
+
+#define SEP(p1,p2) ((p1.x == p2.x) && (p1.y == p2.y))
+#define ON_THE_WIRE(p1,start,end) ( fabs((end.y-start.y)*(p1.x-start.x)-(end.x-start.x)*(p1.y-start.y))<1.e-5 )
+gboolean
+node_needs_dot (Node *node)
+{
+ Wire *wire1, *wire2;
+ SheetPos start_pos1, length1, end_pos1;
+ SheetPos start_pos2, length2, end_pos2;
+
+ if ((node->pin_count > 2) || (node->wire_count > 2))
+ return TRUE;
+ else if ((node->pin_count + node->wire_count) > 2)
+ return TRUE;
+ else if (node->wire_count == 2) {
+ /*
+ * Check that we don't have two wire endpoints.
+ */
+
+
+ wire1 = node->wires->data;
+ wire2 = node->wires->next->data;
+
+ wire_get_pos_and_length (wire1, &start_pos1, &length1);
+ wire_get_pos_and_length (wire2, &start_pos2, &length2);
+
+ end_pos1.x = start_pos1.x + length1.x;
+ end_pos1.y = start_pos1.y + length1.y;
+ end_pos2.x = start_pos2.x + length2.x;
+ end_pos2.y = start_pos2.y + length2.y;
+
+ if (!(SEP (start_pos1, start_pos2) ||
+ SEP (start_pos1, end_pos2) ||
+ SEP (end_pos1, end_pos2) ||
+ SEP (end_pos1, start_pos2))) {
+
+ /*
+ The dot is only needed when the end/start-point of
+ one of the wires in on the other wire.
+ */
+ if ( ON_THE_WIRE(start_pos1,start_pos2,end_pos2) ||
+ ON_THE_WIRE( end_pos1,start_pos2,end_pos2) ||
+ ON_THE_WIRE(start_pos2,start_pos1,end_pos1) ||
+ ON_THE_WIRE( end_pos2,start_pos1,end_pos1)
+ ) {
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ return FALSE;
+ } else if (node->pin_count == 1 && node->wire_count == 1) {
+ /*
+ * Check if we have one wire with a pin in the 'middle'.
+ */
+ wire1 = node->wires->data;
+ wire_get_pos_and_length (wire1, &start_pos1, &length1);
+ end_pos1.x = start_pos1.x + length1.x;
+ end_pos1.y = start_pos1.y + length1.y;
+
+ if (!SEP (node->key, start_pos1) && !SEP (node->key, end_pos1))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gint
+node_add_pin (Node *node, Pin *pin)
+{
+ gboolean dot;
+
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+ g_return_val_if_fail (pin != NULL, FALSE);
+
+ if (g_slist_find (node->pins, pin)) {
+/* g_print ("node_add_pin: pin already there.\n");*/
+ return FALSE;
+ }
+
+ dot = node_needs_dot (node);
+
+ node->pins = g_slist_prepend (node->pins, pin);
+ node->pin_count++;
+
+ if (!dot && node_needs_dot (node))
+ g_signal_emit_by_name(G_OBJECT(node),"dot_added", &node->key);
+
+ return TRUE;
+}
+
+gint
+node_remove_pin (Node *node, Pin *pin)
+{
+ gboolean dot;
+
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+ g_return_val_if_fail (pin != NULL, FALSE);
+
+ if (node->pin_count == 0)
+ return FALSE;
+
+ dot = node_needs_dot (node);
+
+ node->pins = g_slist_remove (node->pins, pin);
+ node->pin_count--;
+
+ if (dot && !node_needs_dot (node))
+ g_signal_emit_by_name(G_OBJECT (node), "dot_removed", &node->key);
+
+ return TRUE;
+}
+
+gint
+node_add_wire (Node *node, Wire *wire)
+{
+ gboolean dot;
+
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ if (g_slist_find (node->wires, wire)) {
+/* g_print ("node_add_wire: wire already there.\n");*/
+ return FALSE;
+ }
+
+ dot = node_needs_dot (node);
+
+ node->wires = g_slist_prepend (node->wires, wire);
+ node->wire_count++;
+
+ if (!dot && node_needs_dot (node))
+ g_signal_emit_by_name(G_OBJECT(node), "dot_added", &node->key);
+
+ return TRUE;
+}
+
+gint
+node_remove_wire (Node *node, Wire *wire)
+{
+ gboolean dot;
+
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ if (node->wire_count == 0)
+ return FALSE;
+
+ if (!g_slist_find (node->wires, wire)) {
+ g_print ("node_remove_wire: not there.\n");
+ return FALSE;
+ }
+
+ dot = node_needs_dot (node);
+
+ node->wires = g_slist_remove (node->wires, wire);
+ node->wire_count--;
+
+ if (dot && (!node_needs_dot (node)))
+ g_signal_emit_by_name(G_OBJECT(node), "dot_removed", &node->key);
+
+ return TRUE;
+}
+
+gint
+node_is_empty (Node *node)
+{
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+
+ if ((node->wire_count == 0) && (node->pin_count == 0))
+ return TRUE;
+
+ return FALSE;
+}
+
+gint
+node_is_visited (Node *node)
+{
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+
+ return node->visited;
+}
+
+void
+node_set_visited (Node *node, gboolean is_visited)
+{
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (IS_NODE (node));
+
+ node->visited = is_visited;
+}
+
diff --git a/src/model/node.h b/src/model/node.h
new file mode 100644
index 0000000..077676c
--- /dev/null
+++ b/src/model/node.h
@@ -0,0 +1,92 @@
+/*
+ * node.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __NODE_H
+#define __NODE_H
+
+#include <gtk/gtk.h>
+#include "sheet-pos.h"
+#include "part.h"
+
+#define TYPE_NODE (node_get_type ())
+#define NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NODE, Node))
+#define NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_NODE, NodeClass))
+#define IS_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NODE))
+#define IS_NODE_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS((klass), TYPE_NODE, NodeClass))
+
+typedef struct _Node Node;
+typedef struct _NodeClass NodeClass;
+
+#include "wire.h"
+
+struct _Node {
+ GObject parent;
+
+ /*
+ * Used for traversing all nodes in the netlist generation.
+ */
+ guint visited : 1;
+
+ char *netlist_node_name;
+
+ /*
+ * The number of wires and pins in this node.
+ */
+ guint16 pin_count;
+ guint16 wire_count;
+
+ GSList *pins;
+ GSList *wires;
+
+ SheetPos key;
+};
+
+struct _NodeClass
+{
+ GObjectClass parent_class;
+
+/* void (*something) (Node *node);*/
+};
+
+GType node_get_type (void);
+Node *node_new (SheetPos pos);
+gint node_is_empty (Node *node);
+
+gint node_add_pin (Node *node, Pin *pin);
+gint node_remove_pin (Node *node, Pin *pin);
+
+gint node_add_wire (Node *node, Wire *wire);
+gint node_remove_wire (Node *node, Wire *wire);
+
+gint node_is_visited (Node *node);
+void node_set_visited (Node *node, gboolean is_visited);
+
+gboolean node_needs_dot (Node *node);
+
+#endif
diff --git a/src/model/part-label.h b/src/model/part-label.h
new file mode 100644
index 0000000..bec7392
--- /dev/null
+++ b/src/model/part-label.h
@@ -0,0 +1,39 @@
+/*
+ * part-label.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __PART_LABEL_H
+#define __PART_LABEL_H
+
+typedef struct {
+ gchar *name;
+ gchar *text;
+ SheetPos pos;
+} PartLabel;
+
+#endif
diff --git a/src/model/part-private.h b/src/model/part-private.h
new file mode 100644
index 0000000..54194f2
--- /dev/null
+++ b/src/model/part-private.h
@@ -0,0 +1,48 @@
+/*
+ * part-private.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __PART_PRIVATE_H
+#define __PART_PRIVATE_H
+
+struct _PartPriv {
+ guint16 num_pins : 16;
+ guint16 rotation : 16;
+ IDFlip flip : 8;
+
+ gchar *name;
+ GSList *properties;
+ GSList *labels;
+
+ gchar *symbol_name;
+ Library *library;
+
+ Pin *pins; /* Array of pins. */
+};
+
+#endif
diff --git a/src/model/part-property.c b/src/model/part-property.c
new file mode 100644
index 0000000..5394880
--- /dev/null
+++ b/src/model/part-property.c
@@ -0,0 +1,319 @@
+/*
+ * part-property.c
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <glib.h>
+#include <string.h>
+#include "part.h"
+#include "part-property.h"
+
+/**
+ * Gets the name of a macro variable.
+ *
+ * @param str str
+ * @param cls1 returns first conditional clause
+ * @param cls2 returns second clause
+ * @param sz returns number of characters parsed
+ * @return the name of a macro variable
+ */
+static char *get_macro_name (const char *str, char **cls1,
+ char **cls2, size_t *sz)
+{
+ char separators[] = { ",.;/|()" };
+ GString *out;
+ const char *q, *qend;
+ char *csep = NULL;
+ size_t sln;
+ int rc = 0;
+ char *ret;
+
+ sln = strlen (str) + 1;
+ *sz = 0;
+ *cls1 = *cls2 = NULL;
+ qend = str + sln;
+ out = g_string_sized_new (sln);
+
+ /* Get the name */
+ for (q = str; (*q) && (*q != ' ') && !(csep = strchr (separators, *q)); q++) {
+ if ( q > qend ) {
+ g_warning ("Expand macro error.");
+ rc = 1;
+ break;
+ }
+ out = g_string_append_c (out, *q);
+ }
+
+ /* if error found, return here */
+ if (rc)
+ goto error;
+
+ /* Look for conditional clauses */
+ if (csep) {
+ /* get the first one */
+ GString *aux;
+ q++; /* skip the separator and store the clause in tmp */
+ aux = g_string_new ("");
+ for (; (*q) && (*q != *csep); q++)
+ g_string_append_c (aux, *q);
+
+ if (!*q) {
+ g_string_free (aux, TRUE);
+ goto error;
+ }
+
+ *cls1 = aux->str;
+ q++; /* skip the end-of-clause separator */
+ g_string_free (aux, FALSE);
+
+ /* Check for the second one */
+ if ( (*q) && (csep = strchr (separators, *q))) {
+ q++; /* skip the separator and store in tmp*/
+ aux = g_string_new ("");
+ for (; (*q) && (*q != *csep); q++)
+ g_string_append_c (aux, *q);
+
+ if (!(*q)) {
+ g_free (*cls1);
+ *cls1 = NULL;
+ goto error;
+ }
+
+ *cls2 = aux->str;
+ q++; /* skip the end-of-clause separator */
+ g_string_free (aux, FALSE);
+ }
+ }
+
+ *sz = out->len;
+ ret = NULL;
+ if (out->len > 0) {
+ out = g_string_append_c (out, '\0');
+ ret = g_strdup (out->str);
+ }
+ g_string_free (out, TRUE);
+
+ return ret;
+
+ error:
+ g_string_free (out, TRUE);
+ return NULL;
+}
+
+char *
+part_property_expand_macros (Part *part, char *string)
+{
+ static char mcode[] = {"@?~#&"};
+ char *value;
+ char *tmp0, *temp, *qn, *q0, *qend, *t0;
+ char *cls1, *cls2;
+ GString *out;
+ size_t sln;
+ char *ret;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (IS_PART (part), NULL);
+ g_return_val_if_fail (string != NULL, NULL);
+
+ cls1 = cls2 = q0 = NULL;
+ /* Rules:
+ @<id> value of <id>. If no value, error
+ &<id> value of <id> if <id> is defined
+ ?<id>s...s text between s...s separators if <id> defined
+ ?<id>s...ss...s text between 1st s...s separators if <id> defined
+ else 2nd s...s clause
+ ~<id>s...s text between s...s separators if <id> undefined
+ ~<id>s...ss...s text between 1st s...s separators if <id> undefined
+ else 2nd s...s clause
+ #<id>s...s text between s...s separators if <id> defined, but
+ delete rest of tempalte if <id> undefined
+
+ Separatos can be any of (, . ; / |) For an opening-closing pair of
+ separators the same character ahs to be used.
+
+ Examples: R^@refdes %1 %2 @value
+ V^@refdes %+ %- SIN(@offset @ampl @freq 0 0)
+ ?DC|DC @DC|
+ */
+ tmp0 = temp = g_strdup (string);
+ qend = temp + strlen (temp);
+
+ out = g_string_new ("");
+
+ for (temp = string; *temp;) {
+ /*
+ * Look for any of the macro char codes.
+ */
+ if (strchr (mcode, *temp)) {
+ qn = get_macro_name (temp + 1, &cls1, &cls2, &sln);
+ if (qn == NULL)
+ return NULL;
+ value = part_get_property (part, qn);
+ if ((*temp == '@' || *temp == '&') && value) {
+ out = g_string_append (out, value);
+ } else if (*temp =='&' && !value) {
+ g_warning ( "expand macro error: macro %s undefined", qn);
+ g_free (qn);
+ return NULL;
+ } else if (*temp == '?' || *temp == '~') {
+ if (cls1 == NULL) {
+ g_warning ("error in template: %s", temp);
+ g_free (qn);
+ return NULL;
+ }
+ q0 = (value
+ ? (*temp == '?' ? cls1 : cls2)
+ : (*temp == '?' ? cls2 : cls1)
+ );
+ if (q0) {
+ t0 = part_property_expand_macros (part, q0);
+ if (!t0) {
+ g_warning ( "error in template: %s", temp);
+ g_free (qn);
+ } else {
+ out = g_string_append (out, t0);
+ g_free (t0);
+ }
+ }
+ } else if (*temp=='#') {
+ if (value) {
+ t0 = part_property_expand_macros (part, value);
+ if (!t0) {
+ g_warning ( "error in template: %s", temp);
+ g_free (qn);
+ } else {
+ out = g_string_append (out, t0);
+ g_free (t0);
+ }
+ } else
+ *(temp + sln) = 0;
+ }
+ temp += 1;
+ temp += sln;
+ if (qn) g_free (qn);
+ if (cls1) g_free (cls1);
+ if (cls2) g_free (cls2);
+ } else {
+ if ( *temp== '\\' ) {
+ temp++;
+ switch (*temp) {
+ case 'n':
+ out = g_string_append_c (out, '\n');
+ break;
+ case 't':
+ out = g_string_append_c (out, '\t');
+ break;
+ case 'r':
+ out = g_string_append_c (out, '\r');
+ break;
+ case 'f':
+ out = g_string_append_c (out, '\f');
+ }
+ temp++;
+ } else {
+ out = g_string_append_c (out, *temp);
+ temp++;
+ }
+ }
+ }
+
+ if (tmp0) g_free (tmp0);
+
+ out = g_string_append_c (out, '\0');
+ ret = g_strdup (out->str);
+ g_string_free (out, TRUE);
+
+ return ret;
+}
+
+
+#if 0
+//---------------------
+char *
+part_property_expand_macros (Part *part, char *string)
+{
+ char *name;
+ char *value;
+ char *temp;
+ char buffer[512];
+ gint in_index = 0, out_index = 0;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (IS_PART (part), NULL);
+ g_return_val_if_fail (string != NULL, NULL);
+
+ /* Examples: R^@refdes %1 %2 @value
+ * V^@refdes %+ %- SIN(@offset @ampl @freq 0 0)
+ */
+
+ temp = string;
+ in_index = 0;
+ out_index = 0;
+
+ while (*temp != 0) {
+ /* Is it a macro? */
+ if (temp[0] == '@'){
+ int i = 0;
+ /* Find the end of the macro. */
+ while (1){
+ if (temp[i] == ' ' || temp[i] == '(' ||
+ temp[i] == ')' || temp[i] == 0)
+ break;
+ else {
+ i++;
+ if (i > strlen (string)){
+ g_warning (
+ "expand macro error.");
+ break;
+ }
+ }
+ }
+
+ /* Perform a lookup on the macro. */
+ name = g_strndup (temp + 1, i - 1);
+ value = part_get_property (part, name);
+ g_free (name);
+
+ if (value) {
+ snprintf (buffer + out_index, 16,
+ "%s ", value);
+ out_index += strlen (value);
+ in_index += i + 1;
+ }
+ temp += i;
+ } else{
+ buffer[out_index] = *temp;
+ out_index++;
+ in_index++;
+ temp++;
+ }
+ }
+
+ buffer[out_index] = '\0';
+ return g_strdup (buffer);
+}
+#endif
diff --git a/src/model/part-property.h b/src/model/part-property.h
new file mode 100644
index 0000000..1e9f350
--- /dev/null
+++ b/src/model/part-property.h
@@ -0,0 +1,42 @@
+/*
+ * part-property.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __PART_PROPERTY_H
+#define __PART_PROPERTY_H
+
+#include "part.h"
+
+typedef struct {
+ gchar *name;
+ gchar *value;
+} PartProperty;
+
+gchar *part_property_expand_macros (Part *part, gchar *string);
+
+#endif
diff --git a/src/model/part.c b/src/model/part.c
new file mode 100644
index 0000000..a8fe18d
--- /dev/null
+++ b/src/model/part.c
@@ -0,0 +1,1099 @@
+/*
+ * part.c
+ *
+ * Part object: represents a schematic part
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <ctype.h>
+#include <gnome.h>
+#include <math.h>
+#include <string.h>
+#include "part.h"
+#include "item-data.h"
+#include "part-property.h"
+#include "part-label.h"
+#include "node-store.h"
+#include "load-common.h"
+#include "load-library.h"
+#include "part-private.h"
+#include "schematic-print-context.h"
+
+static void part_class_init (PartClass *klass);
+
+static void part_init (Part *part);
+
+static void part_set_gproperty (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+
+static void part_get_gproperty (GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec);
+
+static int part_set_properties (Part *part, GSList *properties);
+static gboolean part_has_properties (ItemData *part);
+
+static int part_set_labels (Part *part, GSList *labels);
+
+static void part_copy (ItemData *dest, ItemData *src);
+
+static ItemData *part_clone (ItemData *src);
+
+static void part_rotate (ItemData *data, int angle, SheetPos *center);
+
+static void part_flip (ItemData *data, gboolean horizontal, SheetPos *center);
+
+static void part_update_bbox (Part *part);
+
+static void part_unregister (ItemData *data);
+
+static int part_register (ItemData *data);
+
+static void part_set_property (ItemData *data, char *property, char *value);
+
+static char *part_get_refdes_prefix (ItemData *data);
+static void part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx);
+
+enum {
+ ARG_0,
+ ARG_PROPERTIES,
+ ARG_LABELS,
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint part_signals [LAST_SIGNAL] = { 0 };
+static ItemDataClass *parent_class = NULL;
+
+GType
+part_get_type (void)
+{
+ static GType part_type = 0;
+
+ if (!part_type) {
+ static const GTypeInfo part_info = {
+ sizeof(PartClass),
+ NULL, /* Base Init */
+ NULL, /* Base Finalize */
+ (GClassInitFunc)part_class_init, /* Class Init */
+ NULL, /* Class Finalize */
+ NULL, /* Class Data */
+ sizeof(Part),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)part_init, /* Instance init */
+ NULL
+ };
+ part_type = g_type_register_static(TYPE_ITEM_DATA,
+ "Part", &part_info, 0);
+ }
+
+ return part_type;
+}
+
+static void
+part_finalize(GObject *object)
+{
+ Part *part;
+ PartPriv *priv;
+ GSList *list;
+
+ part = PART (object);
+ priv = part->priv;
+
+ if (priv) {
+ g_free (priv->name);
+
+ for (list = priv->properties; list; list = list->next) {
+ PartProperty *property = list->data;
+
+ g_free (property->name);
+ g_free (property->value);
+ g_free (property);
+ }
+ g_slist_free (priv->properties);
+
+ for (list = priv->labels; list; list = list->next) {
+ PartLabel *label = list->data;
+
+ g_free (label->name);
+ g_free (label->text);
+ g_free (label);
+ }
+ g_slist_free (priv->labels);
+
+ g_free (priv->pins);
+ g_free (priv->symbol_name);
+ g_free (priv);
+ part->priv = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+part_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static void
+part_class_init (PartClass *klass)
+{
+ GObjectClass *object_class;
+ ItemDataClass *item_data_class;
+
+ parent_class = g_type_class_peek(TYPE_ITEM_DATA);
+
+ object_class = G_OBJECT_CLASS(klass);
+ item_data_class = ITEM_DATA_CLASS(klass);
+
+ object_class->set_property = part_set_gproperty;
+ object_class->get_property = part_get_gproperty;
+ object_class->dispose = part_dispose;
+ object_class->finalize = part_finalize;
+
+ g_object_class_install_property ( object_class, ARG_PROPERTIES,
+ g_param_spec_pointer("properties", "properties",
+ "the properties", G_PARAM_READWRITE));
+ g_object_class_install_property(object_class, ARG_LABELS,
+ g_param_spec_pointer("labels", "labels", "the labels",
+ G_PARAM_READWRITE));
+
+ item_data_class->clone = part_clone;
+ item_data_class->copy = part_copy;
+ item_data_class->rotate = part_rotate;
+ item_data_class->flip = part_flip;
+ item_data_class->unreg = part_unregister;
+ item_data_class->reg = part_register;
+
+ item_data_class->get_refdes_prefix = part_get_refdes_prefix;
+ item_data_class->set_property = part_set_property;
+ item_data_class->has_properties = part_has_properties;
+ item_data_class->print = part_print;
+
+ part_signals[CHANGED] =
+ g_signal_new ("changed", G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (PartClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+// ? g_object_class_add_signals (object_class, part_signals, LAST_SIGNAL);
+}
+
+static void
+part_init (Part *part)
+{
+ PartPriv *priv;
+
+ priv = g_new0(PartPriv, 1);
+
+ part->priv = priv;
+}
+
+Part *
+part_new (void)
+{
+ Part *part;
+
+ part = PART(g_object_new(TYPE_PART, NULL));
+
+ return part;
+}
+
+Part *
+part_new_from_library_part (LibraryPart *library_part)
+{
+ Part *part;
+ GSList *pins;
+ PartPriv *priv;
+ LibrarySymbol *symbol;
+
+ g_return_val_if_fail (library_part != NULL, NULL);
+
+ part = part_new();
+ if (!part)
+ return NULL;
+
+ priv = part->priv;
+
+ symbol = library_get_symbol (library_part->symbol_name);
+ if (symbol == NULL){
+ g_warning ("Couldn't find the requested symbol %s for part %s in library.\n",
+ library_part->symbol_name,
+ library_part->name);
+ return NULL;
+ }
+
+ pins = symbol->connections;
+
+ if ( pins )
+ part_set_pins (part, pins);
+
+
+ g_object_set (
+ G_OBJECT (part),
+ "Part::properties", library_part->properties,
+ "Part::labels", library_part->labels,
+ NULL);
+
+ /* FIXME: */
+ priv->name = g_strdup (library_part->name);
+ priv->symbol_name = g_strdup (library_part->symbol_name);
+ priv->library = library_part->library;
+
+ part_update_bbox (part);
+
+ return part;
+}
+
+static void
+part_set_gproperty(GObject *object, guint prop_id, const GValue *value,
+ GParamSpec *spec)
+{
+ GSList *list;
+ Part *part = PART (object);
+
+ switch (prop_id) {
+ case ARG_PROPERTIES:
+ list = g_value_get_pointer(value);
+ part_set_properties (part, list);
+ break;
+ case ARG_LABELS:
+ list = g_value_get_pointer(value);
+ part_set_labels (part, list);
+ break;
+ }
+}
+
+static void
+part_get_gproperty(GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec)
+{
+ Part *part = PART (object);
+ PartPriv *priv = part->priv;
+
+ switch (prop_id) {
+ case ARG_LABELS:
+ g_value_set_pointer(value, priv->labels);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(part, prop_id, spec);
+ }
+}
+
+int
+part_get_num_pins (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, 0);
+ g_return_val_if_fail (IS_PART (part), 0);
+
+ priv = part->priv;
+ return priv->num_pins;
+}
+
+int
+part_get_rotation (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, 0);
+ g_return_val_if_fail (IS_PART (part), 0);
+
+ priv = part->priv;
+ return priv->rotation;
+}
+
+IDFlip
+part_get_flip (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, 0);
+ g_return_val_if_fail (IS_PART (part), 0);
+
+ priv = part->priv;
+ return priv->flip;
+}
+
+Pin *
+part_get_pins (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (IS_PART (part), NULL);
+
+ priv = part->priv;
+ return priv->pins;
+}
+
+static gboolean
+part_has_properties (ItemData *item)
+{
+ Part *part = PART (item);
+
+ return part->priv->properties != NULL;
+}
+
+static int
+part_set_properties (Part *part, GSList *properties)
+{
+ PartPriv *priv;
+ GSList *list;
+
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ priv = part->priv;
+
+ if (priv->properties != NULL)
+ g_warning ("Properties already set!");
+
+ /*
+ * Copy the properties list to the part.
+ */
+ for (list = properties; list; list = list->next) {
+ PartProperty *prop_new, *prop;
+
+ prop = list->data;
+ prop_new = g_new0 (PartProperty, 1);
+ prop_new->name = g_strdup (prop->name);
+ prop_new->value = g_strdup (prop->value);
+
+ priv->properties = g_slist_prepend (priv->properties, prop_new);
+ }
+
+ return TRUE;
+}
+
+GSList *
+part_get_properties (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ priv = part->priv;
+
+ return priv->properties;
+}
+
+char *
+part_get_property (Part *part, char *name)
+{
+ PartPriv *priv;
+ GSList *props;
+ PartProperty *prop;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (IS_PART (part), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ priv = part->priv;
+
+ for (props = priv->properties; props; props = props->next) {
+ prop = props->data;
+ if (g_strcasecmp (prop->name, name) == 0) {
+ return g_strdup (prop->value);
+ }
+ }
+ return NULL;
+}
+
+static int
+part_set_labels (Part *part, GSList *labels)
+{
+ PartPriv *priv;
+ GSList *list;
+
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ priv = part->priv;
+
+ if (priv->labels != NULL) {
+ g_warning ("Part already has labels.");
+ for (list = priv->labels; list; list = list->next) {
+ PartLabel *label = list->data;
+ g_free (label->name);
+ g_free (label->text);
+ g_free (label);
+ }
+ g_slist_free (priv->labels);
+ priv->labels = NULL;
+ }
+
+ for (list = labels; list; list = list->next) {
+ PartLabel *label, *label_copy;
+
+ label = list->data;
+
+ label_copy = g_new0 (PartLabel, 1);
+ label_copy->name = g_strdup (label->name);
+ label_copy->text = g_strdup (label->text);
+ label_copy->pos.x = label->pos.x;
+ label_copy->pos.y = label->pos.y;
+ priv->labels = g_slist_prepend (priv->labels, label_copy);
+ }
+
+ return TRUE;
+}
+
+int
+part_set_pins (Part *part, GSList *pins)
+{
+ PartPriv *priv;
+ GSList *list;
+ int num_pins, i;
+
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+ g_return_val_if_fail (pins != NULL, FALSE);
+
+ priv = part->priv;
+
+ num_pins = g_slist_length (pins);
+
+ if (priv->pins)
+ g_free (priv->pins);
+
+ priv->pins = g_new0 (Pin, num_pins);
+ priv->num_pins = num_pins;
+
+ for (list = pins, i = 0; list; list = list->next, i++) {
+ /*
+ * Note that this is slightly hackish. The list contains
+ * Connections which only have the SheetPos field.
+ */
+ Pin *pin = list->data;
+
+ priv->pins[i].pin_nr = i;
+ priv->pins[i].node_nr= 0;
+ priv->pins[i].offset.x = pin->offset.x;
+ priv->pins[i].offset.y = pin->offset.y;
+ priv->pins[i].part = part;
+ }
+
+ return TRUE;
+}
+
+GSList *
+part_get_labels (Part *part)
+{
+ PartPriv *priv;
+
+ g_return_val_if_fail (part != NULL, NULL);
+ g_return_val_if_fail (IS_PART (part), NULL);
+
+ priv = part->priv;
+
+ return priv->labels;
+}
+
+static void
+part_rotate (ItemData *data, int angle, SheetPos *center)
+{
+ double affine[6], dx, dy;
+ ArtPoint src, dst;
+ Part *part;
+ PartPriv *priv;
+ int i, tot_rotation;
+ SheetPos b1, b2, part_center_before, part_center_after, delta;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_PART (data));
+
+ if (angle == 0)
+ return;
+
+ part = PART (data);
+
+ priv = part->priv;
+
+ tot_rotation = (priv->rotation + angle)%360;
+
+ priv->rotation = tot_rotation;
+
+ art_affine_rotate (affine, angle);
+
+ /*
+ * Rotate the pins.
+ */
+ for (i = 0; i < priv->num_pins; i++) {
+ src.x = priv->pins[i].offset.x;
+ src.y = priv->pins[i].offset.y;
+ art_affine_point (&dst, &src, affine);
+
+ if (fabs (dst.x) < 1e-2)
+ dst.x = 0.0;
+ if (fabs (dst.y) < 1e-2)
+ dst.y = 0.0;
+
+ priv->pins[i].offset.x = dst.x;
+ priv->pins[i].offset.y = dst.y;
+ }
+
+ item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2);
+ part_center_before.x = b1.x + (b2.x - b1.x) / 2;
+ part_center_before.y = b1.y + (b2.y - b1.y) / 2;
+
+ /*
+ * Rotate the bounding box.
+ */
+ src.x = b1.x;
+ src.y = b1.y;
+ art_affine_point (&dst, &src, affine);
+ b1.x = dst.x;
+ b1.y = dst.y;
+
+ src.x = b2.x;
+ src.y = b2.y;
+ art_affine_point (&dst, &src, affine);
+ b2.x = dst.x;
+ b2.y = dst.y;
+
+ item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2);
+
+ if (center) {
+ SheetPos part_pos;
+
+ part_center_after.x = b1.x + (b2.x - b1.x) / 2;
+ part_center_after.y = b1.y + (b2.y - b1.y) / 2;
+
+ dx = part_center_before.x - part_center_after.x;
+ dy = part_center_before.y - part_center_after.y;
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ src.x = part_center_before.x - center->x + part_pos.x;
+ src.y = part_center_before.y - center->y + part_pos.y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = dx - src.x + dst.x;
+ delta.y = dy - src.y + dst.y;
+
+ item_data_move (ITEM_DATA (part), &delta);
+ }
+
+ /*
+ * Let the views (canvas items) know about the rotation.
+ */
+ g_signal_emit_by_name (G_OBJECT (part), "rotated", angle);
+
+}
+
+static void
+part_flip (ItemData *data, gboolean horizontal, SheetPos *center)
+{
+ Part *part;
+ PartPriv *priv;
+ SheetPos b1, b2;
+ int i;
+ double affine[6];
+ ArtPoint src, dst;
+ SheetPos part_center_before, part_center_after;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_PART (data));
+
+ part = PART (data);
+ priv = part->priv;
+
+ if (horizontal && !(priv->flip & ID_FLIP_HORIZ))
+ priv->flip = priv->flip | ID_FLIP_HORIZ;
+ else if (horizontal && (priv->flip & ID_FLIP_HORIZ))
+ priv->flip = priv->flip & ~ID_FLIP_HORIZ;
+ else if (!horizontal && !(priv->flip & ID_FLIP_VERT))
+ priv->flip = priv->flip | ID_FLIP_VERT;
+ else if (!horizontal && (priv->flip & ID_FLIP_VERT))
+ priv->flip = priv->flip & ~ID_FLIP_VERT;
+
+ if (horizontal)
+ art_affine_scale (affine, -1, 1);
+ else
+ art_affine_scale (affine, 1, -1);
+
+ /*
+ * Flip the pins.
+ */
+ item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2);
+ for (i = 0; i < priv->num_pins; i++) {
+ src.x = priv->pins[i].offset.x;
+ src.y = priv->pins[i].offset.y;
+ art_affine_point (&dst, &src, affine);
+
+ if (fabs (dst.x) < 1e-2)
+ dst.x = 0;
+ if (fabs (dst.y) < 1e-2)
+ dst.y = 0;
+
+ priv->pins[i].offset.x = dst.x;
+ priv->pins[i].offset.y = dst.y;
+ }
+
+ /*
+ * Tell the views.
+ */
+ g_signal_emit_by_name (G_OBJECT (part), "flipped", horizontal);
+
+ if (center) {
+ item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2);
+ part_center_before.x = b1.x + (b2.x - b1.x) / 2;
+ part_center_before.y = b1.y + (b2.y - b1.y) / 2;
+ }
+
+ /*
+ * Flip the bounding box.
+ */
+ src.x = b1.x;
+ src.y = b1.y;
+ art_affine_point (&dst, &src, affine);
+ b1.x = dst.x;
+ b1.y = dst.y;
+
+ src.x = b2.x;
+ src.y = b2.y;
+ art_affine_point (&dst, &src, affine);
+ b2.x = dst.x;
+ b2.y = dst.y;
+
+ item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2);
+
+ if (center) {
+ SheetPos part_pos, delta;
+ double dx, dy;
+
+ part_center_after.x = b1.x + (b2.x - b1.x) / 2;
+ part_center_after.y = b1.y + (b2.y - b1.y) / 2;
+
+ dx = part_center_before.x - part_center_after.x;
+ dy = part_center_before.y - part_center_after.y;
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ src.x = part_center_before.x - center->x + part_pos.x;
+ src.y = part_center_before.y - center->y + part_pos.y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = dx - src.x + dst.x;
+ delta.y = dy - src.y + dst.y;
+
+ item_data_move (ITEM_DATA (part), &delta);
+ }
+}
+
+static ItemData *
+part_clone (ItemData *src)
+{
+ Part *src_part, *new_part;
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (IS_PART (src), NULL);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS(src));
+ if (id_class->copy == NULL)
+ return NULL;
+
+ src_part = PART (src);
+ new_part = PART(g_object_new(TYPE_PART, NULL));
+ new_part->priv->pins = g_new0 (Pin, src_part->priv->num_pins);
+ id_class->copy (ITEM_DATA (new_part), src);
+
+ return ITEM_DATA (new_part);
+}
+
+static void
+part_copy (ItemData *dest, ItemData *src)
+{
+ Part *dest_part, *src_part;
+ GSList *list;
+ int i;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (IS_PART (dest));
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (IS_PART (src));
+
+ if (parent_class->copy != NULL)
+ parent_class->copy (dest, src);
+
+ dest_part = PART (dest);
+ src_part = PART (src);
+
+ dest_part->priv->rotation = src_part->priv->rotation;
+ dest_part->priv->flip = src_part->priv->flip;
+ dest_part->priv->num_pins = src_part->priv->num_pins;
+ dest_part->priv->library = src_part->priv->library;
+ dest_part->priv->name = g_strdup (src_part->priv->name);
+ dest_part->priv->symbol_name = g_strdup (src_part->priv->symbol_name);
+
+ memcpy (dest_part->priv->pins, src_part->priv->pins,
+ src_part->priv->num_pins * sizeof (Pin));
+ for (i = 0; i < dest_part->priv->num_pins; i++)
+ dest_part->priv->pins[i].part = dest_part;
+
+ /*
+ * Copy properties and labels.
+ */
+ dest_part->priv->properties =
+ g_slist_copy (src_part->priv->properties);
+ for (list = dest_part->priv->properties; list; list = list->next) {
+ PartProperty *prop, *new_prop;
+
+ new_prop = g_new0 (PartProperty, 1);
+ prop = list->data;
+ new_prop->name = g_strdup (prop->name);
+ new_prop->value = g_strdup (prop->value);
+ list->data = new_prop;
+ }
+
+ dest_part->priv->labels = g_slist_copy (src_part->priv->labels);
+ for (list = dest_part->priv->labels; list; list = list->next) {
+ PartLabel *label, *new_label;
+
+ new_label = g_new0 (PartLabel, 1);
+ label = list->data;
+ new_label->name = g_strdup (label->name);
+ new_label->text = g_strdup (label->text);
+ new_label->pos = label->pos;
+ list->data = new_label;
+ }
+}
+
+static void
+part_update_bbox (Part *part)
+{
+ GSList *objects;
+ LibrarySymbol *symbol;
+ SymbolObject *object;
+ GnomeCanvasPoints *points;
+ int i;
+
+ SheetPos b1, b2;
+
+ symbol = library_get_symbol (part->priv->symbol_name);
+ if (symbol == NULL) {
+ g_warning ("Couldn't find the requested symbol.");
+ return;
+ }
+
+ b1.x = b1.y = b2.x = b2.y = 0.0;
+
+ for (objects = symbol->symbol_objects; objects;
+ objects = objects->next){
+ object = objects->data;
+ switch (object->type){
+ case SYMBOL_OBJECT_LINE:
+ points = object->u.uline.line;
+
+ for (i = 0; i < points->num_points; i++) {
+ b1.x = MIN (points->coords[i * 2], b1.x);
+ b1.y = MIN (points->coords[i * 2 + 1], b1.y);
+
+ b2.x = MAX (points->coords[i * 2], b2.x);
+ b2.y = MAX (points->coords[i * 2 + 1], b2.y);
+ }
+ break;
+
+ case SYMBOL_OBJECT_ARC:
+ b1.x = MIN (object->u.arc.x1, b1.x);
+ b1.y = MIN (object->u.arc.y1, b1.y);
+
+ b2.x = MAX (object->u.arc.x1, b2.x);
+ b2.y = MAX (object->u.arc.y1, b2.y);
+
+ b1.x = MIN (object->u.arc.x2, b1.x);
+ b1.y = MIN (object->u.arc.y2, b1.y);
+
+ b2.x = MAX (object->u.arc.x2, b2.x);
+ b2.y = MAX (object->u.arc.y2, b2.y);
+
+ break;
+
+ case SYMBOL_OBJECT_TEXT:
+ {
+ /*GdkFont *font = gdk_font_load ("Sans 10");
+ b1.x = b1.y = 0;
+ b2.x = 2*object->u.text.x +
+ gdk_string_width (font, object->u.text.str );
+ b2.y = 2*object->u.text.y +
+ gdk_string_height (font,object->u.text.str );
+ */
+ }
+ break;
+
+
+ default:
+ g_warning ("Unknown symbol object.\n");
+ continue;
+ }
+ }
+
+ item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2);
+}
+
+static void
+part_unregister (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_if_fail (IS_PART (data));
+
+ store = item_data_get_store (data);
+ node_store_remove_part (store, PART (data));
+}
+
+static int
+part_register (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_val_if_fail (IS_PART (data), -1);
+
+ store = item_data_get_store (data);
+ node_store_add_part (store, PART (data));
+
+ return 0;
+}
+
+static char *
+part_get_refdes_prefix (ItemData *data)
+{
+ Part *part;
+ char *refdes;
+ int i, length;
+
+ g_return_val_if_fail (IS_PART (data), NULL);
+
+ part = PART (data);
+
+ refdes = part_get_property (part, "refdes");
+ if (refdes == NULL)
+ return NULL;
+
+ /*
+ * Get the 'prefix' i.e R for resistors.
+ */
+ length = strlen (refdes);
+ for (i = 0; i < length; i++) {
+ if (isdigit (refdes[length-i -1])) {
+ refdes[length -i -1] = '\0';
+ }
+ else break;
+ }
+ return g_strdup (refdes);
+}
+
+static void
+part_set_property (ItemData *data, char *property, char *value)
+{
+ Part *part;
+ PartPriv *priv;
+ GSList *props;
+ PartProperty *prop;
+
+ part = PART (data);
+
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (IS_PART (part));
+ g_return_if_fail (property != NULL);
+
+ priv = part->priv;
+
+ for (props = priv->properties; props; props = props->next) {
+ prop = props->data;
+ if (g_strcasecmp (prop->name, property) == 0) {
+ g_free (prop->value);
+ if (value != NULL)
+ prop->value = g_strdup (value);
+ else
+ prop->value = NULL;
+ break;
+ }
+ }
+}
+
+static void
+part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx)
+{
+ GSList *objects, *labels;
+ SymbolObject *object;
+ LibrarySymbol *symbol;
+ double x0, y0;
+ int i, rotation;
+ Part *part;
+ PartPriv *priv;
+ SheetPos pos;
+ IDFlip flip;
+ GnomeCanvasPoints *line;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_PART (data));
+
+ part = PART (data);
+ priv = part->priv;
+
+ symbol = library_get_symbol (priv->symbol_name);
+ if (symbol == NULL) {
+ return;
+ }
+
+ item_data_get_pos (ITEM_DATA (part), &pos);
+ x0 = pos.x;
+ y0 = pos.y;
+
+ cairo_save (cr);
+
+ gdk_cairo_set_source_color (cr, &ctx->colors.components);
+ rotation = part_get_rotation (part);
+ if (rotation != 0) {
+ cairo_translate (cr, x0, y0);
+
+ flip = part_get_flip (part);
+ if (flip) {
+ if ((flip & ID_FLIP_HORIZ) && !(flip & ID_FLIP_VERT))
+ cairo_scale (cr, -1, 1);
+ if (!(flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT))
+ cairo_scale (cr, 1, -1);
+ if ((flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT))
+ rotation+=180;
+ }
+
+ cairo_rotate (cr, rotation*M_PI/180);
+ cairo_translate (cr, -x0, -y0);
+ } else {
+ flip = part_get_flip (part);
+ if (flip) {
+ cairo_translate (cr, x0, y0);
+ if ((flip & ID_FLIP_HORIZ) && !(flip & ID_FLIP_VERT))
+ cairo_scale (cr, -1, 1);
+ if (!(flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT))
+ cairo_scale (cr, 1, -1);
+ if ((flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT))
+ cairo_scale(cr,-1,-1);
+ cairo_translate (cr, -x0, -y0);
+ }
+ }
+
+ for (objects = symbol->symbol_objects; objects; objects = objects->next) {
+ object = (SymbolObject *)(objects->data);
+
+ switch (object->type) {
+ case SYMBOL_OBJECT_LINE:
+ line = object->u.uline.line;
+ for (i = 0; i < line->num_points; i++) {
+ double x, y;
+
+ x = line->coords[i * 2];
+ y = line->coords[i * 2 + 1];
+
+ if (i == 0)
+ cairo_move_to (cr, x0 + x, y0 + y);
+ else
+ cairo_line_to (cr, x0 + x, y0 + y);
+ }
+ break;
+ case SYMBOL_OBJECT_ARC: {
+ gdouble x1 = object->u.arc.x1;
+ gdouble y1 = object->u.arc.y1;
+ gdouble x2 = object->u.arc.x2;
+ gdouble y2 = object->u.arc.y2;
+ gdouble width, height, x, y;
+
+ x = (x2 - x1) / 2 + x1;
+ y = (y2 - y1) / 2 + y1;
+ width = x2 - x1;
+ height = y2 - y1;
+
+ cairo_save (cr);
+ cairo_translate (cr, x0 + x, y0 + y);
+ cairo_scale (cr, width / 2.0, height / 2.0);
+ cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI);
+ cairo_restore (cr);
+ }
+ break;
+ default:
+ g_warning (
+ "Print part: Part %s contains unknown object.",
+ priv->name
+ );
+ continue;
+ }
+
+ cairo_stroke (cr);
+ }
+
+ /* We don't want to rotate labels text, only the (x,y)
+ * coordinate
+ */
+ gdk_cairo_set_source_color (cr, &ctx->colors.labels);
+ for (labels = part_get_labels (part); labels; labels = labels->next) {
+ gdouble x, y;
+ PartLabel *label = (PartLabel *)labels->data;
+ gchar *text;
+ /* gint text_width, text_height; */
+
+ x = label->pos.x + x0;
+ y = label->pos.y + y0;
+
+ text = part_property_expand_macros (part, label->text);
+ /* Align the label.
+ switch (rotation) {
+ case 90:
+ y += text_height*opc->scale;
+ break;
+ case 180:
+ break;
+ case 270:
+ x -= text_width*opc->scale;
+ break;
+ case 0:
+ default:
+ break;
+ } */
+
+ cairo_save (cr);
+ cairo_move_to (cr, x, y);
+ cairo_show_text (cr, text);
+ cairo_restore (cr);
+ g_free (text);
+ }
+ cairo_restore (cr);
+}
+
+
diff --git a/src/model/part.h b/src/model/part.h
new file mode 100644
index 0000000..df157b3
--- /dev/null
+++ b/src/model/part.h
@@ -0,0 +1,90 @@
+/*
+ * part.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __PART_H
+#define __PART_H
+
+#include <gnome.h>
+#include "sheet-pos.h"
+#include "clipboard.h"
+#include "load-common.h"
+
+#define TYPE_PART (part_get_type())
+#define PART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_PART, Part))
+#define PART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_PART, PartClass))
+#define IS_PART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_PART))
+#define IS_PART_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS((klass), TYPE_PART, PartClass))
+
+typedef struct _Part Part;
+typedef struct _PartClass PartClass;
+typedef struct _Pin Pin;
+typedef struct _PartPriv PartPriv;
+
+#include "item-data.h"
+
+struct _Pin {
+ SheetPos offset;
+ guint pin_nr;
+ gint node_nr; /* Numero de nodo en la netlist */
+ Part *part;
+};
+
+#define PIN(x) ((Pin *)(x))
+
+struct _Part {
+ ItemData parent;
+
+ PartPriv *priv;
+};
+
+struct _PartClass
+{
+ ItemDataClass parent_class;
+
+ void (*changed) ();
+};
+
+GType part_get_type (void);
+Part *part_new (void);
+Part *part_new_from_library_part (LibraryPart *library_part);
+int part_get_num_pins (Part *part);
+Pin *part_get_pins (Part *part);
+int part_set_pins (Part *part, GSList *connections);
+int part_get_rotation (Part *part);
+IDFlip part_get_flip (Part *part);
+void part_labels_rotate (Part *part, int rotation);
+
+char *part_get_property (Part *part, char *name);
+GSList *part_get_properties (Part *part);
+GSList *part_get_labels (Part *part);
+
+ClipboardData *part_clipboard_dup (Part *part);
+
+#endif
diff --git a/src/model/schematic-print-context.h b/src/model/schematic-print-context.h
new file mode 100644
index 0000000..cb0eccf
--- /dev/null
+++ b/src/model/schematic-print-context.h
@@ -0,0 +1,45 @@
+/*
+ * schematic.c
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Web page: http://oregano.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef _SCHEMATIC_PRINT_CONTEXT_
+#define _SCHEMATIC_PRINT_CONTEXT_
+
+#include <gdk/gdk.h>
+
+typedef struct _SchematicColors {
+ GdkColor components;
+ GdkColor labels;
+ GdkColor wires;
+ GdkColor text;
+ GdkColor background;
+} SchematicColors;
+
+typedef struct _SchematicPrintContext {
+ SchematicColors colors;
+} SchematicPrintContext;
+
+#endif
+
diff --git a/src/model/schematic.c b/src/model/schematic.c
new file mode 100644
index 0000000..9736398
--- /dev/null
+++ b/src/model/schematic.c
@@ -0,0 +1,1114 @@
+/*
+ * schematic.c
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glade/glade.h>
+#include "schematic.h"
+#include "node-store.h"
+#include "file-manager.h"
+#include "settings.h"
+#include "sim-settings.h"
+#include "simulation.h"
+#include "errors.h"
+#include "schematic-print-context.h"
+
+typedef struct _SchematicsPrintOptions {
+ GtkColorButton *components;
+ GtkColorButton *labels;
+ GtkColorButton *wires;
+ GtkColorButton *text;
+ GtkColorButton *background;
+} SchematicPrintOptions;
+
+struct _SchematicPriv {
+ char *title;
+ char *filename;
+ char *author;
+ char *comments;
+ char *netlist_filename;
+
+ SchematicColors colors;
+ SchematicPrintOptions *printoptions;
+
+ /*
+ * Data for various dialogs.
+ */
+ gpointer settings;
+ gpointer sim_settings;
+ gpointer simulation;
+
+ GList *items;
+
+ NodeStore *store;
+ GHashTable *symbols;
+ GHashTable *refdes_values;
+
+ double zoom;
+
+ gboolean dirty;
+
+ GtkTextBuffer *log;
+ GtkTextTag *tag_error;
+};
+
+typedef enum {
+ REFDATA_SINGLE,
+ REFDATA_RANGE,
+ REFDATA_MIN,
+ REFDATA_MAX
+} RefDataType;
+
+typedef struct {
+ RefDataType type;
+ union {
+ int nr;
+ struct { int min; int max; } range;
+ } u;
+} RefData;
+
+enum {
+ TITLE_CHANGED,
+ ITEM_DATA_ADDED,
+ LOG_UPDATED,
+ DOT_ADDED,
+ DOT_REMOVED,
+ LAST_SCHEMATIC_DESTROYED,
+ LAST_SIGNAL
+};
+
+static void schematic_init(Schematic *schematic);
+static void schematic_class_init(SchematicClass *klass);
+static void schematic_finalize(GObject *object);
+static void schematic_dispose(GObject *object);
+static void item_data_destroy_callback (gpointer s, GObject *data);
+static void part_moved_callback (ItemData *data, SheetPos *pos, Schematic *sm);
+
+static int schematic_get_lowest_available_refdes (Schematic *schematic,
+ char *prefix);
+static void schematic_set_lowest_available_refdes (Schematic *schematic,
+ char *prefix, int num);
+
+static GObjectClass *parent_class = NULL;
+static guint schematic_signals[LAST_SIGNAL] = { 0 };
+
+static GList *schematic_list = NULL;
+static int schematic_count_ = 0;
+
+GType
+schematic_get_type (void)
+{
+ static GType schematic_type = 0;
+
+ if (!schematic_type) {
+ static const GTypeInfo schematic_info = {
+ sizeof (SchematicClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) schematic_class_init,
+ NULL,
+ NULL,
+ sizeof (Schematic),
+ 0,
+ (GInstanceInitFunc) schematic_init,
+ NULL
+ };
+
+ schematic_type =
+ g_type_register_static (G_TYPE_OBJECT, "Schematic",
+ &schematic_info,0);
+ }
+
+ return schematic_type;
+}
+
+static void
+schematic_class_init (SchematicClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ parent_class = g_type_class_peek_parent(klass);
+
+ schematic_signals[TITLE_CHANGED] = g_signal_new ("title_changed",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,title_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING );
+
+ schematic_signals[LAST_SCHEMATIC_DESTROYED] = g_signal_new ("last_schematic_destroyed",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,last_schematic_destroyed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ schematic_signals[ITEM_DATA_ADDED] = g_signal_new ("item_data_added",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,item_data_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ schematic_signals[DOT_ADDED] = g_signal_new ("dot_added",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,dot_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ schematic_signals[DOT_REMOVED] = g_signal_new ("dot_removed",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,dot_removed),
+ NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ schematic_signals[LOG_UPDATED] = g_signal_new ("log_updated",
+ TYPE_SCHEMATIC,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SchematicClass,log_updated),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,0);
+
+ object_class->finalize = schematic_finalize;
+ object_class->dispose = schematic_dispose;
+}
+
+static void
+dot_added_callback (NodeStore *store, SheetPos *pos, Schematic *schematic)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ g_signal_emit_by_name (G_OBJECT(schematic), "dot_added", pos);
+}
+
+static void
+dot_removed_callback (NodeStore *store, SheetPos *pos, Schematic *schematic)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ g_signal_emit_by_name (G_OBJECT(schematic), "dot_removed", pos);
+}
+
+static void
+schematic_init(Schematic *schematic)
+{
+ SchematicPriv *priv;
+
+ priv = schematic->priv = g_new0 (SchematicPriv, 1);
+
+ priv->printoptions = NULL;
+ /* Colors */
+ priv->colors.components.red = 65535;
+ priv->colors.components.green = 0;
+ priv->colors.components.blue = 0;
+ priv->colors.labels.red = 0;
+ priv->colors.labels.green = 35723;
+ priv->colors.labels.blue = 35723;
+ priv->colors.wires.red = 0;
+ priv->colors.wires.green = 0;
+ priv->colors.wires.blue = 65535;
+ priv->colors.text.red = 0;
+ priv->colors.text.green = 0;
+ priv->colors.text.blue = 0;
+
+ priv->symbols = g_hash_table_new (g_str_hash, g_str_equal);
+ /* FIXME: use own str_equal (lib::sym)*/
+ priv->refdes_values = g_hash_table_new (g_str_hash, g_str_equal);
+ priv->store = node_store_new ();
+ priv->dirty = FALSE;
+ priv->log = gtk_text_buffer_new (NULL);
+ priv->tag_error = gtk_text_buffer_create_tag (priv->log, "error",
+ "foreground", "red", "weight", PANGO_WEIGHT_BOLD, NULL);
+
+ g_signal_connect_object (
+ G_OBJECT (priv->store),
+ "dot_added",
+ G_CALLBACK(dot_added_callback),
+ G_OBJECT (schematic),
+ G_CONNECT_AFTER);
+
+ g_signal_connect_object (
+ G_OBJECT (priv->store),
+ "dot_removed",
+ G_CALLBACK(dot_removed_callback),
+ G_OBJECT (schematic),
+ G_CONNECT_AFTER);
+
+ priv->sim_settings = sim_settings_new (schematic);
+ priv->settings = settings_new (schematic);
+ priv->simulation = simulation_new (schematic);
+
+ priv->filename = NULL;
+ priv->netlist_filename = NULL;
+ priv->author = g_strdup ("");
+ priv->comments = g_strdup ("");
+}
+
+Schematic *
+schematic_new (void)
+{
+ Schematic *schematic;
+
+ schematic = SCHEMATIC(g_object_new (TYPE_SCHEMATIC, NULL));
+
+ schematic_count_++;
+ schematic_list = g_list_prepend (schematic_list, schematic);
+
+ return schematic;
+}
+
+static void
+schematic_dispose (GObject *object)
+{
+ Schematic *schematic;
+ GList *list;
+
+ schematic = SCHEMATIC(object);
+
+ /* Disconnect weak item signal */
+ for(list=schematic->priv->items; list; list=list->next)
+ g_object_weak_unref(G_OBJECT(list->data),
+ item_data_destroy_callback, G_OBJECT(schematic));
+
+ g_object_unref (G_OBJECT (schematic->priv->log));
+
+ schematic_count_--;
+ schematic_list = g_list_remove (schematic_list, schematic);
+
+ if (schematic_count_ == 0) {
+ g_signal_emit_by_name(G_OBJECT (schematic),
+ "last_schematic_destroyed", NULL);
+ }
+
+ G_OBJECT_CLASS(parent_class)->dispose(G_OBJECT(schematic));
+}
+
+static void
+schematic_finalize(GObject *object)
+{
+ Schematic *sm;
+
+ sm = SCHEMATIC(object);
+ if (sm->priv) {
+ g_free(sm->priv->simulation);
+ g_hash_table_destroy(sm->priv->symbols);
+ g_hash_table_destroy(sm->priv->refdes_values);
+ if (sm->priv->netlist_filename)
+ g_free (sm->priv->netlist_filename);
+
+ g_free(sm->priv);
+ sm->priv = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(sm));
+}
+
+/*
+ * Get/set functions.
+ */
+
+char *
+schematic_get_title (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->title;
+}
+
+char *
+schematic_get_author (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->author;
+}
+
+char *
+schematic_get_comments (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->comments;
+}
+
+void
+schematic_set_title (Schematic *schematic, const gchar *title)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (!title) return;
+
+ if (schematic->priv->title)
+ g_free (schematic->priv->title);
+ schematic->priv->title = g_strdup (title);
+
+ g_signal_emit_by_name (G_OBJECT (schematic),
+ "title_changed", schematic->priv->title);
+}
+
+void
+schematic_set_author (Schematic *schematic, const gchar *author)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (!author) return;
+
+ if (schematic->priv->author)
+ g_free (schematic->priv->author);
+ schematic->priv->author = g_strdup (author);
+}
+
+void
+schematic_set_comments (Schematic *schematic, const gchar *comments)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (schematic->priv->comments)
+ g_free (schematic->priv->comments);
+ schematic->priv->comments = g_strdup (comments);
+}
+
+char *
+schematic_get_filename (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->filename;
+}
+
+void
+schematic_set_filename (Schematic *schematic, const gchar *filename)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ g_free (schematic->priv->filename);
+ schematic->priv->filename = g_strdup (filename);
+}
+
+char *
+schematic_get_netlist_filename (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->netlist_filename;
+}
+
+void
+schematic_set_netlist_filename (Schematic *schematic, char *filename)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (schematic->priv->netlist_filename)
+ g_free (schematic->priv->netlist_filename);
+
+ schematic->priv->netlist_filename = g_strdup (filename);
+}
+
+double
+schematic_get_zoom (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, 1.0);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), 1.0);
+
+ return schematic->priv->zoom;
+}
+
+/*
+ * FIXME: different zoom level on views...
+ */
+void
+schematic_set_zoom (Schematic *schematic, double zoom)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ schematic->priv->zoom = zoom;
+}
+
+NodeStore *
+schematic_get_store (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->store;
+}
+
+gpointer
+schematic_get_settings (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->settings;
+}
+
+gpointer
+schematic_get_sim_settings (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->sim_settings;
+}
+
+gpointer
+schematic_get_simulation (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->simulation;
+}
+
+void
+schematic_log_append (Schematic *schematic, const char *message)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ gtk_text_buffer_insert_at_cursor (
+ schematic->priv->log,
+ message, strlen (message));
+}
+
+void
+schematic_log_append_error (Schematic *schematic, const char *message)
+{
+ GtkTextIter iter;
+ SchematicPriv *priv;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ priv = schematic->priv;
+
+ gtk_text_buffer_get_end_iter (priv->log, &iter);
+ gtk_text_buffer_insert_with_tags (priv->log, &iter, message,
+ -1, priv->tag_error, NULL);
+}
+
+void
+schematic_log_show (Schematic *schematic)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ g_signal_emit_by_name (G_OBJECT(schematic),
+ "log_updated", schematic->priv->log);
+}
+
+void
+schematic_log_clear (Schematic *schematic)
+{
+ GList *log;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ gtk_text_buffer_set_text (
+ schematic->priv->log,
+ "", -1);
+}
+
+GtkTextBuffer*
+schematic_get_log_text (Schematic *schematic)
+{
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ return schematic->priv->log;
+}
+
+int
+schematic_count (void)
+{
+ return schematic_count_;
+}
+
+Schematic *
+schematic_read (char *name, GError **out_error)
+{
+ Schematic *new_sm;
+ int ret;
+ char *fname;
+ GError *error = NULL;
+ FileType *ft;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ fname = g_filename_from_uri (name, NULL, &error);
+
+ if (!fname) {
+ fname = name;
+ if (error) {
+ g_error_free (error);
+ error = NULL;
+ }
+ }
+
+ if (!g_file_test (fname, G_FILE_TEST_EXISTS)) {
+ g_set_error (out_error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
+ _("File %s does not exists."), fname);
+ return NULL;
+ }
+
+ /* Get File Handler */
+ ft = file_manager_get_handler (fname);
+ if (ft == NULL) {
+ g_set_error (out_error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
+ _("Unknown file format for %s."), fname);
+ return NULL;
+ }
+
+ new_sm = schematic_new ();
+
+ /* TODO : Add GError-like error reporting! */
+ ret = ft->load_func (new_sm, fname, &error);
+
+ if (error != NULL) {
+ g_propagate_error (out_error, error);
+ g_object_unref(G_OBJECT(new_sm));
+ return NULL;
+ }
+
+ if (ret) {
+ g_object_unref(G_OBJECT(new_sm));
+ new_sm = NULL;
+ g_set_error (out_error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
+ _("Load fails!."), fname);
+ } else
+ schematic_set_dirty (new_sm, FALSE);
+
+ return new_sm;
+}
+
+gint
+schematic_save_file (Schematic *sm, GError **error)
+{
+ FileType *ft;
+ GError *internal_error = NULL;
+
+ g_return_val_if_fail (sm != NULL, FALSE);
+
+ ft = file_manager_get_handler (schematic_get_filename (sm));
+
+ if (ft == NULL) {
+ g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND,
+ _("Unknown file format for %s."), schematic_get_filename (sm));
+ return FALSE;
+ }
+
+ if (ft->save_func (sm, &internal_error)) {
+ schematic_set_title (sm, g_path_get_basename (sm->priv->filename));
+ schematic_set_dirty (sm, FALSE);
+ return TRUE;
+ }
+
+ g_propagate_error (error, internal_error);
+
+ g_error_free (internal_error);
+ return FALSE; // Save fails!
+}
+
+void
+schematic_add_item (Schematic *sm, ItemData *data)
+{
+ NodeStore *store;
+ char *prefix, *refdes;
+ int num;
+
+ g_return_if_fail (sm != NULL);
+ g_return_if_fail (IS_SCHEMATIC (sm));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ store = sm->priv->store;
+ g_object_set(G_OBJECT(data), "store", store, NULL);
+ if (item_data_register(data) == -1) {
+ /* Item does not be added */
+ g_object_unref (G_OBJECT (data));
+ return;
+ }
+
+ /*
+ * Some items need a reference designator. Find a good one.
+ */
+ prefix = item_data_get_refdes_prefix (data);
+ if (prefix != NULL) {
+ num = schematic_get_lowest_available_refdes (sm, prefix);
+ refdes = g_strdup_printf ("%s%d", prefix, num);
+ item_data_set_property (data, "refdes", refdes);
+
+ schematic_set_lowest_available_refdes (sm, prefix, num + 1);
+
+ g_free (prefix);
+ g_free (refdes);
+ }
+
+ sm->priv->items = g_list_prepend(sm->priv->items, data);
+ g_object_weak_ref(G_OBJECT(data), item_data_destroy_callback, G_OBJECT(sm));
+
+ g_signal_connect_object(G_OBJECT(data),
+ "moved",
+ G_CALLBACK(part_moved_callback),
+ sm,
+ 0);
+
+ sm->priv->dirty = TRUE;
+
+ g_signal_emit_by_name(G_OBJECT(sm), "item_data_added", data);
+}
+
+void
+schematic_parts_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data)
+{
+ GList *list;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (func == NULL)
+ return;
+
+ for (list = node_store_get_parts (schematic->priv->store); list; list = list->next) {
+ func (list->data, user_data);
+ }
+}
+
+void
+schematic_wires_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data)
+{
+ GList *list;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (func == NULL)
+ return;
+
+ for (list = node_store_get_wires (schematic->priv->store); list; list = list->next) {
+ func (list->data, user_data);
+ }
+}
+
+void
+schematic_items_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data)
+{
+ GList *list;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+
+ if (func == NULL)
+ return;
+
+ for (list = schematic->priv->items; list; list = list->next) {
+ func (list->data, user_data);
+ }
+}
+
+GList *
+schematic_get_items (Schematic *sm)
+{
+ g_return_val_if_fail (sm != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (sm), NULL);
+
+ return sm->priv->items;
+}
+
+static void
+item_data_destroy_callback (gpointer s, GObject *data)
+{
+ Schematic *sm = SCHEMATIC(s);
+ schematic_set_dirty (sm, TRUE);
+ if (sm->priv) {
+ sm->priv->items = g_list_remove (sm->priv->items, data);
+ }
+}
+
+static int
+schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix)
+{
+ gpointer key, value;
+
+ g_return_val_if_fail (schematic != NULL, -1);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), -1);
+ g_return_val_if_fail (prefix != NULL, -1);
+
+ if ( g_hash_table_lookup_extended (schematic->priv->refdes_values,
+ prefix, &key, &value) ) {
+ return GPOINTER_TO_INT (value);
+ } else {
+ return 1;
+ }
+}
+
+static void
+schematic_set_lowest_available_refdes (Schematic *schematic,
+ char *prefix, int num)
+{
+ gpointer key, value;
+
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+ g_return_if_fail (prefix != NULL);
+
+ /* If there already is a key, use it, otherwise copy the prefix and
+ * use as key.
+ */
+ if (!g_hash_table_lookup_extended (schematic->priv->refdes_values,
+ prefix, &key, &value))
+ key = g_strdup (prefix);
+
+ g_hash_table_insert (schematic->priv->refdes_values,
+ key, GINT_TO_POINTER (num));
+}
+
+gboolean
+schematic_is_dirty(Schematic *sm)
+{
+ g_return_val_if_fail (sm != NULL, FALSE);
+ g_return_val_if_fail (IS_SCHEMATIC (sm), FALSE);
+
+ return sm->priv->dirty;
+}
+
+void
+schematic_set_dirty(Schematic *sm, gboolean b)
+{
+ g_return_if_fail (sm != NULL);
+ g_return_if_fail (IS_SCHEMATIC (sm));
+
+ sm->priv->dirty = b;
+}
+
+static void
+part_moved_callback (ItemData *data, SheetPos *pos, Schematic *sm)
+{
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+
+ schematic_set_dirty (sm, TRUE);
+}
+
+static void
+schematic_render (Schematic *sm, cairo_t *cr)
+{
+ NodeStore *store;
+
+ SchematicPrintContext schematic_print_context;
+ schematic_print_context.colors = sm->priv->colors;
+ store = schematic_get_store (sm);
+
+ node_store_print_items (store, cr, &schematic_print_context);
+ node_store_print_labels (store, cr, NULL);
+}
+
+GdkColor
+convert_to_grayscale(GdkColor *source)
+{
+ GdkColor color;
+ int factor;
+
+ factor = (source->red + source->green + source->blue)/3;
+ color.red = factor;
+ color.green = factor;
+ color.blue = factor;
+
+ return color;
+}
+
+void
+schematic_export (Schematic *sm, const gchar *filename,
+ gint img_w, gint img_h, int bg, int color, int format)
+{
+ ArtDRect bbox;
+ NodeStore *store;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ gdouble w, h;
+ gdouble graph_w, graph_h;
+ gdouble scale, scalew, scaleh;
+ SchematicColors colors;
+
+ if (!color) {
+ colors = sm->priv->colors;
+ sm->priv->colors.components = convert_to_grayscale (&sm->priv->colors.components);
+ sm->priv->colors.labels = convert_to_grayscale (&sm->priv->colors.labels);
+ sm->priv->colors.wires = convert_to_grayscale (&sm->priv->colors.wires);
+ sm->priv->colors.text = convert_to_grayscale (&sm->priv->colors.text);
+ sm->priv->colors.background = convert_to_grayscale (&sm->priv->colors.background);
+ }
+
+ store = schematic_get_store (sm);
+ node_store_get_bounds (store, &bbox);
+
+ w = bbox.x1 - bbox.x0;
+ h = bbox.y1 - bbox.y0;
+
+ switch (format) {
+#ifdef CAIRO_HAS_SVG_SURFACE
+ case 0:
+ surface = cairo_svg_surface_create (filename, img_w, img_h);
+ break;
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+ case 1:
+ surface = cairo_pdf_surface_create (filename, img_w, img_h);
+ break;
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+ case 2:
+ surface = cairo_ps_surface_create (filename, img_w, img_h);
+ break;
+#endif
+ default:
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, img_w, img_h);
+ }
+ cr = cairo_create (surface);
+
+ /* Background */
+ switch (bg) {
+ case 1: /* White */
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr, 0, 0, img_w, img_h);
+ cairo_fill (cr);
+ break;
+ case 2: /* Black */
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_rectangle (cr, 0, 0, img_w, img_h);
+ cairo_fill (cr);
+ }
+
+ graph_w = img_w * 0.8;
+ graph_h = img_h * 0.8;
+ scalew = graph_w / (bbox.x1 - bbox.x0);
+ scaleh = graph_h / (bbox.y1 - bbox.y0);
+ if (scalew < scaleh)
+ scale = scalew;
+ else
+ scale = scaleh;
+
+ /* Preparing ...*/
+ cairo_save (cr);
+ cairo_translate (cr, (img_w - graph_w)/2.0, (img_h - graph_h) / 2.0);
+ cairo_scale (cr, scale, scale);
+ cairo_translate (cr, -bbox.x0, -bbox.y0);
+ cairo_set_line_width (cr, 0.5);
+
+ /* Render ... */
+ schematic_render (sm, cr);
+
+ cairo_restore (cr);
+ cairo_show_page (cr);
+
+ /* Saving ... */
+ if (format >= 3)
+ cairo_surface_write_to_png (surface, filename);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ // Restore color information
+ if (!color) {
+ sm->priv->colors = colors;
+ }
+}
+
+static void
+draw_rotule (Schematic *sm, cairo_t *cr)
+{
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_set_line_width (cr, 0.5);
+ cairo_rectangle (cr, 0, 0, 180, 20);
+ cairo_rectangle (cr, 0, 20, 180, 10);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+}
+
+static void
+draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context, int page_nr, Schematic *sm)
+{
+ PangoLayout *layout;
+ PangoFontDescription *desc;
+ NodeStore *store;
+ ArtDRect bbox;
+ gdouble page_w, page_h;
+ gdouble circuit_w, circuit_h;
+
+ page_w = gtk_print_context_get_width (context);
+ page_h = gtk_print_context_get_height (context);
+
+ circuit_w = page_w * 0.8;
+ circuit_h = page_h * 0.8;
+
+ cairo_t *cr = gtk_print_context_get_cairo_context (context);
+
+ /* Draw a red rectangle, as wide as the paper (inside the margins) */
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_set_line_width (cr, 0.5);
+ cairo_rectangle (cr, 20, 10, page_w-30, page_h-20);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ cairo_save (cr);
+ cairo_translate (cr, page_w-190, page_h-40);
+ draw_rotule (sm, cr);
+ cairo_restore (cr);
+
+ store = schematic_get_store (sm);
+
+ node_store_get_bounds (store, &bbox);
+
+ cairo_save (cr);
+ cairo_set_line_width (cr, 0.5);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_translate (cr, page_w * 0.1, page_h * 0.1);
+ /* 0.4 is the convert factor between Model unit and
+ * milimeters, unit used in printing
+ */
+ cairo_scale (cr, 0.4, 0.4);
+ cairo_translate (cr, -bbox.x0, -bbox.y0);
+ schematic_render (sm, cr);
+ cairo_restore (cr);
+}
+
+static GObject*
+print_options (GtkPrintOperation *operation, Schematic *sm)
+{
+ GladeXML *gui;
+
+ if (!g_file_test (OREGANO_GLADEDIR "/print-options.glade", G_FILE_TEST_EXISTS)) {
+ return G_OBJECT (gtk_label_new (_("Error loading print-options.glade")));
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/print-options.glade",
+ "widget", GETTEXT_PACKAGE);
+ if (!gui) {
+ return G_OBJECT (gtk_label_new (_("Error loading print-options.glade")));
+ }
+
+ if (sm->priv->printoptions)
+ g_free (sm->priv->printoptions);
+ sm->priv->printoptions = g_new0(SchematicPrintOptions, 1);
+
+ sm->priv->printoptions->components = GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_components"));
+ sm->priv->printoptions->labels = GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_labels"));
+ sm->priv->printoptions->wires = GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_wires"));
+ sm->priv->printoptions->text = GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_text"));
+ sm->priv->printoptions->background = GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_background"));
+
+ /* Set default colors */
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_components")),
+ &sm->priv->colors.components);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_labels")),
+ &sm->priv->colors.labels);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_wires")),
+ &sm->priv->colors.wires);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_text")),
+ &sm->priv->colors.text);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (glade_xml_get_widget (gui, "color_background")),
+ &sm->priv->colors.background);
+
+ return (GObject *) (glade_xml_get_widget (gui, "widget"));
+}
+
+static void
+read_print_options(GtkPrintOperation *operation, GtkWidget *widget, Schematic *sm)
+{
+ SchematicPrintOptions *colors = sm->priv->printoptions;
+
+ gtk_color_button_get_color (colors->components, &sm->priv->colors.components);
+ gtk_color_button_get_color (colors->labels, &sm->priv->colors.labels);
+ gtk_color_button_get_color (colors->wires, &sm->priv->colors.wires);
+ gtk_color_button_get_color (colors->text, &sm->priv->colors.text);
+ gtk_color_button_get_color (colors->background, &sm->priv->colors.background);
+
+ g_free (sm->priv->printoptions);
+ sm->priv->printoptions = NULL;
+}
+
+void
+schematic_print (Schematic *sm, GtkPageSetup *page, GtkPrintSettings *settings, gboolean preview)
+{
+ GtkPrintOperation *op;
+ GtkPrintOperationResult res;
+
+ op = gtk_print_operation_new ();
+
+ gtk_print_operation_set_print_settings (op, settings);
+ gtk_print_operation_set_default_page_setup (op, page);
+ gtk_print_operation_set_n_pages (op, 1);
+ gtk_print_operation_set_unit (op, GTK_UNIT_MM);
+ gtk_print_operation_set_use_full_page (op, TRUE);
+
+ g_signal_connect (op, "create-custom-widget", G_CALLBACK (print_options), sm);
+ g_signal_connect (op, "custom-widget-apply", G_CALLBACK (read_print_options), sm);
+ g_signal_connect (op, "draw_page", G_CALLBACK (draw_page), sm);
+
+ gtk_print_operation_set_custom_tab_label (op, _("Schematic"));
+
+ if (preview)
+ res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PREVIEW,
+ NULL, NULL);
+ else
+ res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ NULL, NULL);
+
+ if (res == GTK_PRINT_OPERATION_RESULT_CANCEL) {
+ }
+}
diff --git a/src/model/schematic.h b/src/model/schematic.h
new file mode 100644
index 0000000..29678fb
--- /dev/null
+++ b/src/model/schematic.h
@@ -0,0 +1,126 @@
+/*
+ * schematic.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SCHEMATIC_H__
+#define __SCHEMATIC_H__
+
+#include <gtk/gtk.h>
+#include <cairo/cairo.h>
+#include <cairo/cairo-features.h>
+#ifdef CAIRO_HAS_SVG_SURFACE
+#include <cairo/cairo-svg.h>
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+#include <cairo/cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#include <cairo/cairo-ps.h>
+#endif
+
+#include "item-data.h"
+#include "part.h"
+#include "wire.h"
+#include "node-store.h"
+
+#define TYPE_SCHEMATIC (schematic_get_type ())
+#define SCHEMATIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SCHEMATIC, Schematic))
+#define SCHEMATIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SCHEMATIC, SchematicClass))
+#define IS_SCHEMATIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SCHEMATIC))
+#define IS_SCHEMATIC_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SCHEMATIC, SchematicClass))
+
+typedef struct _Schematic Schematic;
+typedef struct _SchematicClass SchematicClass;
+typedef struct _SchematicPriv SchematicPriv;
+
+typedef void (*ForeachItemDataFunc) (ItemData *item_data, gpointer user_data);
+
+struct _Schematic {
+ GObject parent;
+
+ gpointer corba_server;
+ SchematicPriv *priv;
+};
+
+struct _SchematicClass {
+ GObjectClass parent_class;
+
+ /* signals */
+ void (*title_changed) (Schematic* ,gchar*);
+ void (*item_data_added) (Schematic*, gpointer*);
+ void (*log_updated) (gpointer);
+ void (*dot_added) (Schematic*);
+ void (*dot_removed) (Schematic*, gpointer*);
+ void (*last_schematic_destroyed) (Schematic*);
+};
+
+GType schematic_get_type (void);
+Schematic *schematic_new (void);
+char *schematic_get_title (Schematic *schematic);
+void schematic_set_title (Schematic *schematic, const gchar *title);
+char *schematic_get_author (Schematic *schematic);
+void schematic_set_author (Schematic *schematic, const gchar *author);
+char *schematic_get_comments (Schematic *schematic);
+void schematic_set_comments (Schematic *schematic, const gchar *comments);
+char *schematic_get_filename (Schematic *schematic);
+void schematic_set_filename (Schematic *schematic, const gchar *filename);
+char *schematic_get_netlist_filename (Schematic *schematic);
+void schematic_set_netlist_filename (Schematic *schematic, char *filename);
+int schematic_count (void);
+double schematic_get_zoom (Schematic *schematic);
+void schematic_set_zoom (Schematic *schematic, double zoom);
+void schematic_add_item (Schematic *sm, ItemData *data);
+void schematic_parts_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data);
+void schematic_wires_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data);
+void schematic_items_foreach (Schematic *schematic,
+ ForeachItemDataFunc func, gpointer user_data);
+GList *schematic_get_items (Schematic *sm);
+NodeStore *schematic_get_store (Schematic *schematic);
+gpointer schematic_get_settings (Schematic *schematic);
+gpointer schematic_get_sim_settings (Schematic *schematic);
+gpointer schematic_get_simulation (Schematic *schematic);
+void schematic_log_clear (Schematic *schematic);
+void schematic_log_append (Schematic *schematic, const char *message);
+void schematic_log_append_error (Schematic *schematic, const char *message);
+void schematic_log_show (Schematic *schematic);
+GtkTextBuffer *schematic_get_log_text (Schematic *schematic);
+int schematic_count (void);
+gboolean schematic_is_dirty(Schematic *sm);
+void schematic_set_dirty(Schematic *sm, gboolean b);
+gint schematic_save_file (Schematic *sm, GError **error);
+Schematic *schematic_read (char *fname, GError **error);
+void schematic_print (Schematic *sm, GtkPageSetup *p, GtkPrintSettings *s, gboolean preview);
+void schematic_export (Schematic *sm, const gchar *filename, gint img_w, gint img_h, int bg, int color, int format);
+
+#endif /* __SCHEMATIC_H__ */
+
+
+
diff --git a/src/model/sheet-pos.h b/src/model/sheet-pos.h
new file mode 100644
index 0000000..66fa1bd
--- /dev/null
+++ b/src/model/sheet-pos.h
@@ -0,0 +1,39 @@
+/*
+ * sheet-pos.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __SHEET_POS_H
+#define __SHEET_POS_H
+
+typedef struct _SheetPos SheetPos;
+struct _SheetPos {
+ gdouble x;
+ gdouble y;
+};
+
+#endif
diff --git a/src/model/textbox.c b/src/model/textbox.c
new file mode 100644
index 0000000..4f163cb
--- /dev/null
+++ b/src/model/textbox.c
@@ -0,0 +1,468 @@
+/*
+ * textbox.c
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gnome.h>
+#include <math.h>
+#include "item-data.h"
+#include "textbox.h"
+#include "clipboard.h"
+#include "node-store.h"
+#include "schematic-print-context.h"
+
+/*
+#define TEXTBOX_DEFAULT_FONT "-*-helvetica-medium-r-*-*-*-100-*-*-*-*-*-*"
+*/
+#define TEXTBOX_DEFAULT_FONT "Arial 10"
+
+/*
+ * Private declarations
+ */
+
+static void textbox_class_init (TextboxClass *klass);
+static void textbox_init (Textbox *textbox);
+static void textbox_copy (ItemData *dest, ItemData *src);
+static ItemData *textbox_clone (ItemData *src);
+static void textbox_rotate (ItemData *data, int angle, SheetPos *center);
+static void textbox_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx);
+static int textbox_register (ItemData *data);
+static void textbox_unregister (ItemData *data);
+static gboolean textbox_has_properties (ItemData *data);
+
+static void textbox_flip (ItemData *data, gboolean horizontal,
+ SheetPos *center);
+
+
+enum {
+ TEXT_CHANGED,
+ FONT_CHANGED,
+ LAST_SIGNAL
+};
+
+static ItemDataClass *parent_class = NULL;
+static guint textbox_signals[LAST_SIGNAL] = { 0 };
+
+struct _TextboxPriv {
+ char *text;
+ char *font;
+};
+
+GType
+textbox_get_type (void)
+{
+ static GType textbox_type = 0;
+
+ if (!textbox_type) {
+ static const GTypeInfo textbox_info = {
+ sizeof (TextboxClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) textbox_class_init,
+ NULL,
+ NULL,
+ sizeof (Textbox),
+ 0,
+ (GInstanceInitFunc) textbox_init,
+ NULL
+ };
+
+ textbox_type = g_type_register_static(TYPE_ITEM_DATA,
+ "Textbox", &textbox_info, 0);
+ }
+
+ return textbox_type;
+}
+
+static void
+textbox_finalize(GObject *object)
+{
+ Textbox *textbox = TEXTBOX (object);
+ TextboxPriv *priv = textbox->priv;
+
+ g_free (priv);
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+textbox_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static void
+textbox_class_init (TextboxClass *klass)
+{
+ GObjectClass *object_class;
+ ItemDataClass *item_data_class;
+
+ parent_class = g_type_class_peek(TYPE_ITEM_DATA);
+ item_data_class = ITEM_DATA_CLASS(klass);
+ object_class = G_OBJECT_CLASS(klass);
+
+ textbox_signals[TEXT_CHANGED] =
+ g_signal_new ("text_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ textbox_signals[FONT_CHANGED] =
+ g_signal_new ("font_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ object_class->finalize= textbox_finalize;
+ object_class->dispose = textbox_dispose;
+
+ item_data_class->clone = textbox_clone;
+ item_data_class->copy = textbox_copy;
+ item_data_class->rotate = textbox_rotate;
+ item_data_class->flip = textbox_flip;
+ item_data_class->unreg = textbox_unregister;
+ item_data_class->reg = textbox_register;
+ item_data_class->has_properties = textbox_has_properties;
+ item_data_class->print = textbox_print;
+}
+
+static void
+textbox_init (Textbox *textbox)
+{
+ TextboxPriv *priv = g_new0 (TextboxPriv, 1);
+ textbox->priv = priv;
+}
+
+Textbox *
+textbox_new (char *font)
+{
+ Textbox *textbox;
+
+ textbox = TEXTBOX(g_object_new(TYPE_TEXTBOX, NULL));
+
+ if (font == NULL)
+ textbox->priv->font = g_strdup (TEXTBOX_DEFAULT_FONT);
+ else
+ textbox->priv->font = g_strdup (font);
+
+ return textbox;
+}
+
+static ItemData *
+textbox_clone (ItemData *src)
+{
+ Textbox *src_textbox, *new_textbox;
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (IS_TEXTBOX (src), NULL);
+
+ id_class = ITEM_DATA_CLASS(G_OBJECT_GET_CLASS(src));
+ if (id_class->copy == NULL)
+ return NULL;
+
+ src_textbox = TEXTBOX(src);
+ new_textbox = TEXTBOX(g_object_new(TYPE_TEXTBOX, NULL));
+ id_class->copy (ITEM_DATA (new_textbox), src);
+
+ return ITEM_DATA (new_textbox);
+}
+
+static void
+textbox_copy (ItemData *dest, ItemData *src)
+{
+ Textbox *dest_textbox, *src_textbox;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (IS_TEXTBOX (dest));
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (IS_TEXTBOX (src));
+
+ if (parent_class->copy != NULL)
+ parent_class->copy (dest, src);
+
+ dest_textbox = TEXTBOX (dest);
+ src_textbox = TEXTBOX (src);
+
+ dest_textbox->priv->text = src_textbox->priv->text;
+ dest_textbox->priv->font = src_textbox->priv->font;
+}
+
+static void
+textbox_rotate (ItemData *data, int angle, SheetPos *center)
+{
+ double affine[6];
+ ArtPoint src, dst;
+ Textbox *textbox;
+ TextboxPriv *priv;
+ SheetPos b1, b2;
+ SheetPos textbox_center, delta;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ if (angle == 0)
+ return;
+
+ textbox = TEXTBOX (data);
+
+ if (center) {
+ item_data_get_absolute_bbox (ITEM_DATA (textbox), &b1, &b2);
+ textbox_center.x = b1.x + (b2.x - b1.x) / 2;
+ textbox_center.y = b1.y + (b2.y - b1.y) / 2;
+ }
+
+ priv = textbox->priv;
+
+ art_affine_rotate (affine, angle);
+
+ /*
+ * Let the views (canvas items) know about the rotation.
+ */
+ g_signal_emit_by_name(G_OBJECT(textbox), "rotated", angle);
+
+ if (center) {
+ SheetPos textbox_pos;
+
+ item_data_get_pos (ITEM_DATA (textbox), &textbox_pos);
+
+ src.x = textbox_center.x - center->x;
+ src.y = textbox_center.y - center->y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = -src.x + dst.x;
+ delta.y = -src.y + dst.y;
+
+ item_data_move (ITEM_DATA (textbox), &delta);
+ }
+}
+
+static void
+textbox_flip (ItemData *data, gboolean horizontal, SheetPos *center)
+{
+ double affine[6];
+ ArtPoint src, dst;
+ Textbox *textbox;
+ TextboxPriv *priv;
+ SheetPos b1, b2;
+ SheetPos textbox_center, delta;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ textbox = TEXTBOX (data);
+
+ if (center) {
+ item_data_get_absolute_bbox (ITEM_DATA (textbox), &b1, &b2);
+ textbox_center.x = b1.x + (b2.x - b1.x) / 2;
+ textbox_center.y = b1.y + (b2.y - b1.y) / 2;
+ }
+
+ priv = textbox->priv;
+
+ if (horizontal)
+ art_affine_scale (affine, -1, 1);
+ else
+ art_affine_scale (affine, 1, -1);
+
+ /*
+ * Let the views (canvas items) know about the rotation.
+ */
+ g_signal_emit_by_name(G_OBJECT (textbox), "flipped", horizontal);
+
+ if (center) {
+ SheetPos textbox_pos;
+
+ item_data_get_pos (ITEM_DATA (textbox), &textbox_pos);
+
+ src.x = textbox_center.x - center->x;
+ src.y = textbox_center.y - center->y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = -src.x + dst.x;
+ delta.y = -src.y + dst.y;
+
+ item_data_move (ITEM_DATA (textbox), &delta);
+ }
+}
+
+/* static */
+void
+textbox_update_bbox (Textbox *textbox)
+{
+ PangoFontDescription *font;
+ /*
+ Unused variables
+ int width;
+ int rbearing;
+ int lbearing;
+ int ascent, descent;
+ */
+ SheetPos b1, b2;
+ TextboxPriv *priv;
+
+ priv = textbox->priv;
+
+ font = pango_font_description_from_string(priv->font);
+ /* TODO : Find out how to do this with Pango. */
+ /* gdk_string_extents (font,
+ priv->text,
+ &lbearing,
+ &rbearing,
+ &width,
+ &ascent,
+ &descent);
+ gdk_font_unref (font);
+ */
+ b1.x = 0.0;
+ b1.y = 0.0-5; // - font->ascent;
+ b2.x = 0.0+5; // + rbearing;
+ b2.y = 0.0+5; // + font->descent;
+
+ item_data_set_relative_bbox (ITEM_DATA (textbox), &b1, &b2);
+ pango_font_description_free(font);
+}
+
+static gboolean
+textbox_has_properties (ItemData *data)
+{
+ return TRUE;
+}
+
+void
+textbox_set_text (Textbox *textbox, const char *text)
+{
+ g_return_if_fail (textbox != NULL);
+ g_return_if_fail (IS_TEXTBOX (textbox));
+
+ g_free (textbox->priv->text);
+ textbox->priv->text = g_strdup (text);
+
+ textbox_update_bbox (textbox);
+
+ g_signal_emit_by_name (G_OBJECT(textbox), "text_changed", text);
+}
+
+char *
+textbox_get_text (Textbox *textbox)
+{
+ g_return_val_if_fail (textbox != NULL, NULL);
+ g_return_val_if_fail (IS_TEXTBOX (textbox), NULL);
+
+ return textbox->priv->text;
+}
+
+void
+textbox_set_font (Textbox *textbox, char *font)
+{
+ g_return_if_fail (textbox != NULL);
+ g_return_if_fail (IS_TEXTBOX (textbox));
+
+ g_free (textbox->priv->font);
+ if (font == NULL)
+ textbox->priv->font = g_strdup (TEXTBOX_DEFAULT_FONT);
+ else
+ textbox->priv->font = g_strdup (font);
+
+ textbox_update_bbox (textbox);
+
+ g_signal_emit_by_name(G_OBJECT (textbox),
+ "font_changed", textbox->priv->font);
+}
+
+char *
+textbox_get_font (Textbox *textbox)
+{
+ g_return_val_if_fail (textbox != NULL, NULL);
+ g_return_val_if_fail (IS_TEXTBOX (textbox), NULL);
+
+ return textbox->priv->font;
+}
+
+static void
+textbox_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx)
+{
+/* GnomeCanvasPoints *line;
+ double x0, y0;
+ ArtPoint dst, src;
+ double affine[6];
+ int i;
+ Textbox *textbox;
+ TextboxPriv *priv;
+ SheetPos pos;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ textbox = TEXTBOX (data);
+ priv = textbox->priv;
+
+ item_data_get_pos (ITEM_DATA (textbox), &pos);
+ src.x = pos.x;
+ src.y = pos.y;
+
+ art_affine_identity (affine);
+
+ gnome_print_setfont(opc->ctx,
+ gnome_font_face_get_font_default(
+ opc->label_font, 6)
+ );
+ print_draw_text (opc->ctx, priv->text, &src);
+ */
+}
+
+static void
+textbox_unregister (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ store = item_data_get_store (data);
+ node_store_remove_textbox (store, TEXTBOX (data));
+}
+
+static int
+textbox_register (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_val_if_fail (IS_TEXTBOX (data), 0);
+
+ store = item_data_get_store (data);
+ node_store_add_textbox (store, TEXTBOX (data));
+ return 0;
+}
diff --git a/src/model/textbox.h b/src/model/textbox.h
new file mode 100644
index 0000000..72fbb80
--- /dev/null
+++ b/src/model/textbox.h
@@ -0,0 +1,68 @@
+/*
+ * textbox.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __TEXTBOX_H
+#define __TEXTBOX_H
+
+#include <gtk/gtk.h>
+#include "clipboard.h"
+#include "item-data.h"
+
+#define TYPE_TEXTBOX (textbox_get_type ())
+#define TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TEXTBOX, Textbox))
+#define TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TEXTBOX, TextboxClass))
+#define IS_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TEXTBOX))
+#define IS_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_GET_CLASS ((klass), TYPE_TEXTBOX))
+
+typedef struct _Textbox Textbox;
+typedef struct _TextboxClass TextboxClass;
+typedef struct _TextboxPriv TextboxPriv;
+
+struct _Textbox {
+ ItemData parent;
+ TextboxPriv *priv;
+};
+
+struct _TextboxClass
+{
+ ItemDataClass parent_class;
+
+ Textbox *(*dup) (Textbox *textbox);
+};
+
+GType textbox_get_type (void);
+Textbox *textbox_new (char *font);
+void textbox_set_text (Textbox *textbox, const char *text);
+char *textbox_get_text (Textbox *textbox);
+void textbox_set_font (Textbox *textbox, char *font);
+char *textbox_get_font (Textbox *textbox);
+void textbox_update_bbox (Textbox *textbox);
+
+
+#endif
diff --git a/src/model/wire-private.h b/src/model/wire-private.h
new file mode 100644
index 0000000..28c1ce0
--- /dev/null
+++ b/src/model/wire-private.h
@@ -0,0 +1,45 @@
+/*
+ * wire-private.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __WIRE_PRIVATE_H
+#define __WIRE_PRIVATE_H
+
+struct _WirePriv {
+ /*
+ * Used to traverse the wires during netlist generation.
+ */
+ gboolean visited : 1;
+
+ GSList *nodes;
+
+ SheetPos length;
+ WireDir direction;
+};
+
+#endif
diff --git a/src/model/wire.c b/src/model/wire.c
new file mode 100644
index 0000000..672c410
--- /dev/null
+++ b/src/model/wire.c
@@ -0,0 +1,640 @@
+/*
+ * wire.c
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gnome.h>
+#include <math.h>
+#include "item-data.h"
+#include "node-store.h"
+#include "node.h"
+#include "item-data.h"
+#include "wire.h"
+#include "wire-private.h"
+#include "clipboard.h"
+#include "schematic-print-context.h"
+
+static void wire_class_init (WireClass *klass);
+static void wire_init (Wire *wire);
+static void wire_copy (ItemData *dest, ItemData *src);
+static ItemData *wire_clone (ItemData *src);
+static void wire_rotate (ItemData *data, int angle, SheetPos *center);
+static void wire_flip (ItemData *data, gboolean horizontal,
+ SheetPos *center);
+static void wire_unregister (ItemData *data);
+static int wire_register (ItemData *data);
+static gboolean wire_has_properties (ItemData *data);
+static void wire_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx);
+
+enum {
+ CHANGED,
+ DELETE,
+ LAST_SIGNAL
+};
+
+static guint wire_signals [LAST_SIGNAL] = { 0 };
+static ItemDataClass *parent_class = NULL;
+
+GType
+wire_get_type (void)
+{
+ static GType wire_type = 0;
+
+ if (!wire_type) {
+ static const GTypeInfo wire_info = {
+ sizeof (WireClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) wire_class_init,
+ NULL,
+ NULL,
+ sizeof (Wire),
+ 0,
+ (GInstanceInitFunc) wire_init,
+ NULL
+ };
+
+ wire_type = g_type_register_static(TYPE_ITEM_DATA,
+ "Wire", &wire_info, 0);
+ }
+
+ return wire_type;
+}
+
+static void
+wire_finalize(GObject *object)
+{
+ Wire *wire = WIRE (object);
+ WirePriv *priv = wire->priv;
+
+ if (priv) {
+ g_slist_free (priv->nodes);
+ g_free (priv);
+ wire->priv = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize (object);
+}
+
+static void
+wire_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+
+static void
+wire_class_init (WireClass *klass)
+{
+ GObjectClass *object_class;
+ ItemDataClass *item_data_class;
+
+ parent_class = g_type_class_peek (TYPE_ITEM_DATA);
+ item_data_class = ITEM_DATA_CLASS(klass);
+ object_class = G_OBJECT_CLASS(klass);
+
+ object_class->dispose = wire_dispose;
+ object_class->finalize = wire_finalize;
+
+ wire_signals [CHANGED] =
+ g_signal_new ("changed", G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (WireClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ wire_signals [DELETE] =
+ g_signal_new ("delete", G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (WireClass, delete),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ item_data_class->clone = wire_clone;
+ item_data_class->copy = wire_copy;
+ item_data_class->rotate = wire_rotate;
+ item_data_class->flip = wire_flip;
+ item_data_class->unreg = wire_unregister;
+ item_data_class->reg = wire_register;
+ item_data_class->has_properties = wire_has_properties;
+ item_data_class->print = wire_print;
+}
+
+static void
+wire_init (Wire *wire)
+{
+ WirePriv *priv = g_new0 (WirePriv, 1);
+
+ /*
+ * For debugging purposes.
+ */
+ priv->length.x = -1;
+ priv->length.y = -1;
+
+ priv->nodes = NULL;
+ priv->visited = FALSE;
+ priv->direction = WIRE_DIR_NONE;
+
+ wire->priv = priv;
+}
+
+Wire *
+wire_new (void)
+{
+ return WIRE(g_object_new(TYPE_WIRE, NULL));
+}
+
+gint
+wire_add_node (Wire *wire, Node *node)
+{
+ WirePriv *priv;
+
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+
+ priv = wire->priv;
+
+ if (g_slist_find (priv->nodes, node)) {
+ return FALSE;
+ }
+
+ priv->nodes = g_slist_prepend (priv->nodes, node);
+ return TRUE;
+}
+
+gint
+wire_remove_node (Wire *wire, Node *node)
+{
+ WirePriv *priv;
+
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE (node), FALSE);
+
+ priv = wire->priv;
+
+ if (!g_slist_find (priv->nodes, node)) {
+ return FALSE;
+ }
+
+ priv->nodes = g_slist_remove (priv->nodes, node);
+
+ return TRUE;
+}
+
+GSList *
+wire_get_nodes (Wire *wire)
+{
+ WirePriv *priv;
+
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ priv = wire->priv;
+
+ return priv->nodes;
+}
+
+void
+wire_get_start_pos (Wire *wire, SheetPos *pos)
+{
+ WirePriv *priv;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+ g_return_if_fail (pos != NULL);
+
+ priv = wire->priv;
+
+ item_data_get_pos (ITEM_DATA (wire), pos);
+}
+
+void
+wire_get_end_pos (Wire *wire, SheetPos *pos)
+{
+ WirePriv *priv;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+ g_return_if_fail (pos != NULL);
+
+ priv = wire->priv;
+
+ item_data_get_pos (ITEM_DATA (wire), pos);
+
+ pos->x += priv->length.x;
+ pos->y += priv->length.y;
+}
+
+void
+wire_get_pos_and_length (Wire *wire, SheetPos *pos, SheetPos *length)
+{
+ WirePriv *priv;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+ g_return_if_fail (pos != NULL);
+
+ priv = wire->priv;
+
+ item_data_get_pos (ITEM_DATA (wire), pos);
+ *length = priv->length;
+}
+
+void
+wire_set_length (Wire *wire, SheetPos *length)
+{
+ WirePriv *priv;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ priv = wire->priv;
+
+ priv->length = *length;
+
+ if (length->x == 0) {
+ wire->priv->direction = WIRE_DIR_VERT;
+ } else if (length->y == 0) {
+ wire->priv->direction = WIRE_DIR_HORIZ;
+ } else {
+ wire->priv->direction = WIRE_DIR_DIAG;
+ }
+
+ g_signal_emit_by_name (G_OBJECT (wire), "changed");
+}
+
+gint
+wire_is_visited (Wire *wire)
+{
+ WirePriv *priv;
+
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ priv = wire->priv;
+
+ return priv->visited;
+}
+
+void
+wire_set_visited (Wire *wire, gboolean is_visited)
+{
+ WirePriv *priv;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ priv = wire->priv;
+
+ priv->visited = is_visited;
+}
+
+static ItemData *
+wire_clone (ItemData *src)
+{
+ Wire *src_wire, *new_wire;
+ ItemDataClass *id_class;
+
+ g_return_val_if_fail (src != NULL, NULL);
+ g_return_val_if_fail (IS_WIRE (src), NULL);
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS(src));
+ if (id_class->copy == NULL)
+ return NULL;
+
+ src_wire = WIRE (src);
+ new_wire = g_object_new (TYPE_WIRE, NULL);
+ id_class->copy (ITEM_DATA (new_wire), src);
+
+ return ITEM_DATA (new_wire);
+}
+
+static void
+wire_copy (ItemData *dest, ItemData *src)
+{
+ Wire *dest_wire, *src_wire;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (IS_WIRE (dest));
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (IS_WIRE (src));
+
+ if (parent_class->copy != NULL)
+ parent_class->copy (dest, src);
+
+ dest_wire = WIRE (dest);
+ src_wire = WIRE (src);
+
+ dest_wire->priv->nodes = NULL;
+ dest_wire->priv->length = src_wire->priv->length;
+}
+
+/* static */ void wire_update_bbox (Wire *wire);
+
+static void
+wire_rotate (ItemData *data, int angle, SheetPos *center)
+{
+ double affine[6], dx, dy;
+ ArtPoint src, dst;
+ Wire *wire;
+ WirePriv *priv;
+ SheetPos b1, b2;
+ SheetPos wire_center_before, wire_center_after, delta;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_WIRE (data));
+
+ if (angle == 0)
+ return;
+
+ wire = WIRE (data);
+
+ if (center) {
+ item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2);
+ wire_center_before.x = b1.x + (b2.x - b1.x) / 2;
+ wire_center_before.y = b1.y + (b2.y - b1.y) / 2;
+ }
+
+ priv = wire->priv;
+
+ if (priv->direction == WIRE_DIR_VERT) {
+ priv->direction = WIRE_DIR_HORIZ;
+ } else if (priv->direction == WIRE_DIR_HORIZ) {
+ priv->direction = WIRE_DIR_VERT;
+ }
+
+ art_affine_rotate (affine, angle);
+
+ /*
+ * Rotate the wire's end point.
+ */
+ src.x = priv->length.x;
+ src.y = priv->length.y;
+
+ art_affine_point (&dst, &src, affine);
+
+ if (fabs (dst.x) < 1e-2)
+ dst.x = 0.0;
+ if (fabs (dst.y) < 1e-2)
+ dst.y = 0.0;
+
+ /*
+ * 'Normalize'.
+ */
+ if (dst.y < 0 ||
+ (dst.y == 0 && dst.x < 0)) {
+ priv->length.x = -dst.x;
+ priv->length.y = -dst.y;
+ delta.x = -dst.x;
+ delta.y = -dst.y;
+
+ item_data_move (ITEM_DATA (wire), &delta);
+ } else {
+ priv->length.x = dst.x;
+ priv->length.y = dst.y;
+ }
+
+ /*
+ * Let the views (canvas items) know about the rotation.
+ */
+ g_signal_emit_by_name (G_OBJECT (wire), "rotated", angle);
+
+ /*
+ * Update bounding box.
+ */
+ wire_update_bbox (wire);
+
+ if (center) {
+ SheetPos wire_pos;
+
+ item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2);
+
+ wire_center_after.x = b1.x + (b2.x - b1.x) / 2;
+ wire_center_after.y = b1.y + (b2.y - b1.y) / 2;
+
+ dx = wire_center_before.x - wire_center_after.x;
+ dy = wire_center_before.y - wire_center_after.y;
+
+ item_data_get_pos (ITEM_DATA (wire), &wire_pos);
+
+ src.x = wire_center_before.x - center->x;
+ src.y = wire_center_before.y - center->y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = dx - src.x + dst.x;
+ delta.y = dy - src.y + dst.y;
+
+ item_data_move (ITEM_DATA (wire), &delta);
+ }
+}
+
+static void
+wire_flip (ItemData *data, gboolean horizontal, SheetPos *center)
+{
+ Wire *wire;
+ WirePriv *priv;
+ SheetPos b1, b2, delta;
+ double affine[6];
+ ArtPoint src, dst;
+ SheetPos wire_center_before, wire_center_after;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_WIRE (data));
+
+ wire = WIRE (data);
+ priv = wire->priv;
+
+ if (horizontal)
+ art_affine_scale (affine, -1, 1);
+ else
+ art_affine_scale (affine, 1, -1);
+
+ /*
+ * Flip the wire's end point.
+ */
+ src.x = priv->length.x;
+ src.y = priv->length.y;
+
+ art_affine_point (&dst, &src, affine);
+
+ if (fabs (dst.x) < 1e-2)
+ dst.x = 0.0;
+ if (fabs (dst.y) < 1e-2)
+ dst.y = 0.0;
+
+ /*
+ * 'Normalize'.
+ */
+ if (dst.y < 0 ||
+ (dst.y == 0 && dst.x < 0)) {
+ priv->length.x = -dst.x;
+ priv->length.y = -dst.y;
+ delta.x = -dst.x;
+ delta.y = -dst.y;
+
+ item_data_move (ITEM_DATA (wire), &delta);
+ } else {
+ priv->length.x = dst.x;
+ priv->length.y = dst.y;
+ }
+
+ /*
+ * Tell the views.
+ */
+ g_signal_emit_by_name (G_OBJECT (wire), "flipped", horizontal);
+
+ if (center) {
+ item_data_get_relative_bbox (ITEM_DATA (wire), &b1, &b2);
+ wire_center_before.x = b1.x + (b2.x - b1.x) / 2;
+ wire_center_before.y = b1.y + (b2.y - b1.y) / 2;
+ }
+
+ /*
+ * Flip the bounding box.
+ */
+ src.x = b1.x;
+ src.y = b1.y;
+ art_affine_point (&dst, &src, affine);
+ b1.x = dst.x;
+ b1.y = dst.y;
+
+ src.x = b2.x;
+ src.y = b2.y;
+ art_affine_point (&dst, &src, affine);
+ b2.x = dst.x;
+ b2.y = dst.y;
+
+ item_data_set_relative_bbox (ITEM_DATA (wire), &b1, &b2);
+
+ if (center) {
+ SheetPos wire_pos, delta;
+ double dx, dy;
+
+ wire_center_after.x = b1.x + (b2.x - b1.x) / 2;
+ wire_center_after.y = b1.y + (b2.y - b1.y) / 2;
+
+ dx = wire_center_before.x - wire_center_after.x;
+ dy = wire_center_before.y - wire_center_after.y;
+
+ item_data_get_pos (ITEM_DATA (wire), &wire_pos);
+
+ src.x = wire_center_before.x - center->x + wire_pos.x;
+ src.y = wire_center_before.y - center->y + wire_pos.y;
+ art_affine_point (&dst, &src, affine);
+
+ delta.x = dx - src.x + dst.x;
+ delta.y = dy - src.y + dst.y;
+
+ item_data_move (ITEM_DATA (wire), &delta);
+ }
+}
+
+/* static */
+void
+wire_update_bbox (Wire *wire)
+{
+ SheetPos b1, b2, pos, length;
+ WirePriv *priv;
+
+ priv = wire->priv;
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ b1.x = b1.y = 0.0;
+ b2 = length;
+
+ item_data_set_relative_bbox (ITEM_DATA (wire), &b1, &b2);
+}
+
+static gboolean
+wire_has_properties (ItemData *data)
+{
+ return FALSE;
+}
+
+static void
+wire_unregister (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_if_fail (IS_WIRE (data));
+
+ store = item_data_get_store (data);
+ node_store_remove_wire (store, WIRE (data));
+}
+
+static int
+wire_register (ItemData *data)
+{
+ NodeStore *store;
+
+ g_return_val_if_fail (IS_WIRE (data), -1);
+
+ store = item_data_get_store (data);
+ return node_store_add_wire (store, WIRE (data));
+}
+
+static void
+wire_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx)
+{
+ SheetPos start_pos, end_pos;
+ Wire *wire;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_WIRE (data));
+
+ wire = WIRE (data);
+
+ wire_get_start_pos (wire, &start_pos);
+ wire_get_end_pos (wire, &end_pos);
+
+ cairo_save (cr);
+ gdk_cairo_set_source_color (cr, &ctx->colors.wires);
+ cairo_move_to (cr, start_pos.x, start_pos.y);
+ cairo_line_to (cr, end_pos.x, end_pos.y);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+}
+
+void wire_delete (Wire *wire)
+{
+ g_return_if_fail (IS_WIRE (wire));
+
+ g_signal_emit_by_name (G_OBJECT (wire), "delete");
+}
+
diff --git a/src/model/wire.h b/src/model/wire.h
new file mode 100644
index 0000000..b33731b
--- /dev/null
+++ b/src/model/wire.h
@@ -0,0 +1,87 @@
+/*
+ * wire.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __WIRE_H
+#define __WIRE_H
+
+#include <gnome.h>
+#include "sheet-pos.h"
+#include "clipboard.h"
+#include "item-data.h"
+
+#define TYPE_WIRE (wire_get_type ())
+#define WIRE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_WIRE, Wire))
+#define WIRE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_WIRE, WireClass))
+#define IS_WIRE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_WIRE))
+#define IS_WIRE_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((klass), TYPE_WIRE))
+
+typedef struct _Wire Wire;
+typedef struct _WireClass WireClass;
+typedef struct _WirePriv WirePriv;
+
+#include "node-store.h"
+#include "node.h"
+
+typedef enum {
+ WIRE_DIR_NONE = 0,
+ WIRE_DIR_HORIZ = 1,
+ WIRE_DIR_VERT = 2,
+ WIRE_DIR_DIAG = 3
+} WireDir;
+
+struct _Wire {
+ ItemData parent;
+ WirePriv *priv;
+};
+
+struct _WireClass
+{
+ ItemDataClass parent_class;
+
+ Wire *(*dup) (Wire *wire);
+ void (*changed) ();
+ void (*delete) ();
+};
+
+GType wire_get_type (void);
+Wire *wire_new (void);
+NodeStore *wire_get_store (Wire *wire);
+gint wire_set_store (Wire *wire, NodeStore *store);
+gint wire_add_node (Wire *wire, Node *node);
+gint wire_remove_node (Wire *wire, Node *node);
+GSList * wire_get_nodes (Wire *wire);
+void wire_get_start_pos (Wire *wire, SheetPos *pos);
+void wire_get_end_pos (Wire *wire, SheetPos *pos);
+void wire_get_pos_and_length (Wire *wire, SheetPos *pos, SheetPos *length);
+void wire_set_length (Wire *wire, SheetPos *length);
+gint wire_is_visited (Wire *wire);
+void wire_set_visited (Wire *wire, gboolean is_visited);
+void wire_delete (Wire *wire);
+
+#endif