diff options
author | Maximiliano Curia <maxy@debian.org> | 2012-01-04 00:00:53 +0100 |
---|---|---|
committer | Maximiliano Curia <maxy@debian.org> | 2012-01-04 00:00:53 +0100 |
commit | 825fd5668026f70071c7a5eef277b0bf5c8f02d2 (patch) | |
tree | 4d3a9112d9887ea16ab5dda07a7a791f15d3278f /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.am | 35 | ||||
-rw-r--r-- | src/model/item-data.c | 541 | ||||
-rw-r--r-- | src/model/item-data.h | 148 | ||||
-rw-r--r-- | src/model/node-store.c | 1227 | ||||
-rw-r--r-- | src/model/node-store.h | 99 | ||||
-rw-r--r-- | src/model/node.c | 334 | ||||
-rw-r--r-- | src/model/node.h | 92 | ||||
-rw-r--r-- | src/model/part-label.h | 39 | ||||
-rw-r--r-- | src/model/part-private.h | 48 | ||||
-rw-r--r-- | src/model/part-property.c | 319 | ||||
-rw-r--r-- | src/model/part-property.h | 42 | ||||
-rw-r--r-- | src/model/part.c | 1099 | ||||
-rw-r--r-- | src/model/part.h | 90 | ||||
-rw-r--r-- | src/model/schematic-print-context.h | 45 | ||||
-rw-r--r-- | src/model/schematic.c | 1114 | ||||
-rw-r--r-- | src/model/schematic.h | 126 | ||||
-rw-r--r-- | src/model/sheet-pos.h | 39 | ||||
-rw-r--r-- | src/model/textbox.c | 468 | ||||
-rw-r--r-- | src/model/textbox.h | 68 | ||||
-rw-r--r-- | src/model/wire-private.h | 45 | ||||
-rw-r--r-- | src/model/wire.c | 640 | ||||
-rw-r--r-- | src/model/wire.h | 87 |
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 |