summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am102
-rw-r--r--src/clipboard.c130
-rw-r--r--src/clipboard.h45
-rw-r--r--src/create-wire.c690
-rw-r--r--src/create-wire.h57
-rw-r--r--src/cursors.c67
-rw-r--r--src/cursors.h51
-rw-r--r--src/dialogs.c183
-rw-r--r--src/dialogs.h43
-rw-r--r--src/engines/Makefile.am20
-rw-r--r--src/engines/engine.c178
-rw-r--r--src/engines/engine.h89
-rw-r--r--src/engines/gnucap.c767
-rw-r--r--src/engines/gnucap.h59
-rw-r--r--src/engines/netlist.c689
-rw-r--r--src/engines/netlist.h71
-rw-r--r--src/engines/ngspice.c633
-rw-r--r--src/engines/ngspice.h59
-rw-r--r--src/errors.c37
-rw-r--r--src/errors.h44
-rw-r--r--src/file-manager.c64
-rw-r--r--src/file-manager.h51
-rw-r--r--src/file.c415
-rw-r--r--src/file.h42
-rw-r--r--src/gplot/Makefile.am14
-rw-r--r--src/gplot/gplot.c839
-rw-r--r--src/gplot/gplot.h70
-rw-r--r--src/gplot/gplotfunction.c80
-rw-r--r--src/gplot/gplotfunction.h69
-rw-r--r--src/gplot/gplotlines.c294
-rw-r--r--src/gplot/gplotlines.h55
-rw-r--r--src/load-common.h75
-rw-r--r--src/load-library.c655
-rw-r--r--src/load-library.h78
-rw-r--r--src/load-schematic.c1024
-rw-r--r--src/load-schematic.h39
-rw-r--r--src/main.c252
-rw-r--r--src/main.h65
-rw-r--r--src/model/Makefile.am35
-rw-r--r--src/model/item-data.c541
-rw-r--r--src/model/item-data.h148
-rw-r--r--src/model/node-store.c1227
-rw-r--r--src/model/node-store.h99
-rw-r--r--src/model/node.c334
-rw-r--r--src/model/node.h92
-rw-r--r--src/model/part-label.h39
-rw-r--r--src/model/part-private.h48
-rw-r--r--src/model/part-property.c319
-rw-r--r--src/model/part-property.h42
-rw-r--r--src/model/part.c1099
-rw-r--r--src/model/part.h90
-rw-r--r--src/model/schematic-print-context.h45
-rw-r--r--src/model/schematic.c1114
-rw-r--r--src/model/schematic.h126
-rw-r--r--src/model/sheet-pos.h39
-rw-r--r--src/model/textbox.c468
-rw-r--r--src/model/textbox.h68
-rw-r--r--src/model/wire-private.h45
-rw-r--r--src/model/wire.c640
-rw-r--r--src/model/wire.h87
-rw-r--r--src/netlist-editor.c429
-rw-r--r--src/netlist-editor.h66
-rw-r--r--src/oregano-config.c173
-rw-r--r--src/oregano-config.h45
-rwxr-xr-xsrc/oregano-convert31
-rw-r--r--src/oregano-impl.c140
-rw-r--r--src/oregano-utils.c80
-rw-r--r--src/oregano-utils.h36
-rw-r--r--src/part-browser.c715
-rw-r--r--src/part-browser.h43
-rw-r--r--src/pixmaps/Makefile.am13
-rw-r--r--src/pixmaps/README2
-rw-r--r--src/pixmaps/log.xpm21
-rw-r--r--src/pixmaps/logo.pngbin0 -> 36100 bytes
-rw-r--r--src/pixmaps/logo.xpm2896
-rw-r--r--src/pixmaps/menu_zoom.xcfbin0 -> 1129 bytes
-rw-r--r--src/pixmaps/menu_zoom.xpm64
-rw-r--r--src/pixmaps/mini_icon.xpm31
-rw-r--r--src/pixmaps/mini_icon_plot.xcfbin0 -> 1472 bytes
-rw-r--r--src/pixmaps/mini_icon_plot.xpm33
-rw-r--r--src/pixmaps/plot.xpm72
-rw-r--r--src/pixmaps/tool_arrow.xpm26
-rw-r--r--src/pixmaps/tool_text.xpm27
-rw-r--r--src/pixmaps/tool_wire.xpm36
-rw-r--r--src/plot-add-function.c126
-rw-r--r--src/plot-add-function.h38
-rw-r--r--src/plot.c753
-rw-r--r--src/plot.h37
-rw-r--r--src/save-schematic.c561
-rw-r--r--src/save-schematic.h38
-rw-r--r--src/schematic-view-menu.h183
-rw-r--r--src/schematic-view.c2698
-rw-r--r--src/schematic-view.h127
-rw-r--r--src/settings.c274
-rw-r--r--src/settings.h42
-rw-r--r--src/sheet/Makefile.am28
-rw-r--r--src/sheet/grid.c394
-rw-r--r--src/sheet/grid.h61
-rw-r--r--src/sheet/node-item.c112
-rw-r--r--src/sheet/node-item.h64
-rw-r--r--src/sheet/part-item.c1632
-rw-r--r--src/sheet/part-item.h70
-rw-r--r--src/sheet/sheet-item-factory.c70
-rw-r--r--src/sheet/sheet-item-factory.h38
-rw-r--r--src/sheet/sheet-item.c1222
-rw-r--r--src/sheet/sheet-item.h129
-rw-r--r--src/sheet/sheet-private.h54
-rw-r--r--src/sheet/sheet.c469
-rw-r--r--src/sheet/sheet.h92
-rw-r--r--src/sheet/textbox-item.c699
-rw-r--r--src/sheet/textbox-item.h67
-rw-r--r--src/sheet/wire-item.c916
-rw-r--r--src/sheet/wire-item.h60
-rw-r--r--src/sim-settings.c1064
-rw-r--r--src/sim-settings.h169
-rw-r--r--src/simulation.c264
-rw-r--r--src/simulation.h136
-rw-r--r--src/smallicon.c66
-rw-r--r--src/smallicon.h33
-rw-r--r--src/spice2to3.awk294
-rw-r--r--src/splash.c118
-rw-r--r--src/splash.h46
-rw-r--r--src/stock.c92
-rw-r--r--src/stock.h53
-rw-r--r--src/stock/Makefile.am18
-rw-r--r--src/stock/arrow.xpm64
-rw-r--r--src/stock/clamp.xpm30
-rw-r--r--src/stock/grid.xcfbin0 -> 4022 bytes
-rw-r--r--src/stock/grid.xpm56
-rw-r--r--src/stock/part-browser.xcfbin0 -> 2857 bytes
-rw-r--r--src/stock/part-browser.xpm427
-rw-r--r--src/stock/plot.xcfbin0 -> 2704 bytes
-rw-r--r--src/stock/plot.xpm209
-rw-r--r--src/stock/plot2.xcfbin0 -> 2530 bytes
-rw-r--r--src/stock/rotate.xpm62
-rw-r--r--src/stock/sim-settings.xpm187
-rw-r--r--src/stock/text.xpm30
-rw-r--r--src/stock/vclamp.xpm88
-rw-r--r--src/stock/voltmeter.xpm30
-rw-r--r--src/stock/wire.xpm184
-rw-r--r--src/stock/zoom_in.xpm36
-rw-r--r--src/stock/zoom_out.xpm36
-rw-r--r--src/stock/zoom_pan.xpm270
-rw-r--r--src/stock/zoom_region.xpm229
-rw-r--r--src/xml-compat.h50
-rw-r--r--src/xml-helper.c254
-rw-r--r--src/xml-helper.h56
147 files changed, 35624 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..2222e14
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,102 @@
+SUBDIRS = \
+ gplot \
+ engines \
+ model \
+ sheet \
+ stock \
+ pixmaps
+
+oreganodir = $(datadir)/oregano
+
+INCLUDES = \
+ -DVERSION="\"$(VERSION)\"" \
+ -DPACKAGE=\""oregano\"" \
+ -DGETTEXT_PACKAGE=\""oregano\"" \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(includedir) $(GNOME_INCLUDEDIR) \
+ -DOREGANO_GLADEDIR=\""$(oreganodir)/glade"\" \
+ -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \
+ -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" \
+ -DOREGANO_LANGDIR=\""$(oreganodir)/language-specs"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -I./sheet \
+ -I./gplot \
+ -I./model \
+ -I./engines \
+ -I$(top_srcdir)/data/glade \
+ -I. \
+ $(OREGANO_CFLAGS)
+
+bin_PROGRAMS = oregano
+
+OREGANO_LIBS += gplot/libgplot.a \
+ engines/libengines.a \
+ model/libmodel.a \
+ sheet/libsheet.a
+
+oregano_PIXMAPS = \
+ plot.xpm \
+ tool_wire.xpm \
+ tool_part.xpm \
+ tool_arrow.xpm \
+ tool_text.xpm \
+ mini_icon.xpm \
+ logo.png
+
+oregano_SOURCES = \
+ clipboard.h \
+ clipboard.c \
+ create-wire.c \
+ create-wire.h \
+ cursors.c \
+ cursors.h \
+ dialogs.c \
+ dialogs.h \
+ errors.c \
+ errors.h \
+ file.c \
+ file.h \
+ file-manager.c \
+ file-manager.h \
+ load-common.h \
+ load-library.c \
+ load-library.h \
+ load-schematic.c \
+ load-schematic.h \
+ main.c \
+ main.h \
+ netlist-editor.c \
+ netlist-editor.h \
+ oregano-config.c \
+ oregano-config.h \
+ oregano-utils.c \
+ oregano-utils.h \
+ part-browser.c \
+ part-browser.h \
+ plot.c \
+ plot.h \
+ plot-add-function.c \
+ plot-add-function.h \
+ save-schematic.c \
+ save-schematic.h \
+ schematic-view.c \
+ schematic-view.h \
+ schematic-view-menu.h \
+ settings.c \
+ settings.h \
+ sim-settings.c \
+ sim-settings.h \
+ simulation.c \
+ simulation.h \
+ smallicon.c \
+ smallicon.h \
+ splash.h \
+ splash.c \
+ stock.c \
+ stock.h \
+ xml-compat.h \
+ xml-helper.c \
+ xml-helper.h
+
+oregano_LDADD = \
+ $(OREGANO_LIBS)
diff --git a/src/clipboard.c b/src/clipboard.c
new file mode 100644
index 0000000..baf815a
--- /dev/null
+++ b/src/clipboard.c
@@ -0,0 +1,130 @@
+/*
+ * clipboard.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 <glib.h>
+#include "main.h"
+#include "sheet-item.h"
+#include "item-data.h"
+#include "clipboard.h"
+
+struct _ClipboardData {
+ ItemData *item_data;
+ SheetItemClass *item_class;
+};
+
+void
+clipboard_empty (void)
+{
+ GSList *list;
+ ClipboardData *cb_data;
+
+ if (oregano.clipboard == NULL)
+ return;
+
+ for (list = oregano.clipboard; list; list = list->next) {
+ cb_data = list->data;
+
+ g_object_unref (G_OBJECT (cb_data->item_data));
+ g_free (cb_data);
+ }
+
+ g_slist_free (oregano.clipboard);
+ oregano.clipboard = NULL;
+}
+
+gboolean
+clipboard_is_empty (void)
+{
+ if (oregano.clipboard == NULL)
+ return TRUE;
+
+ return g_slist_length (oregano.clipboard) > 0 ? FALSE : TRUE;
+}
+
+void
+clipboard_foreach (ClipBoardFunction callback, gpointer user_data)
+{
+ GSList *list;
+ ClipboardData *data;
+
+ if (!oregano.clipboard)
+ return;
+
+ for (list = oregano.clipboard; list; list = list->next) {
+ data = list->data;
+ callback (data, user_data);
+ }
+}
+
+void
+clipboard_add_object (GObject *item)
+{
+ ItemDataClass *id_class;
+ ItemData *item_data, *clone;
+ ClipboardData *cb_data;
+
+ g_return_if_fail (item != NULL);
+
+ /* TODO: FIX sheet global access */
+ g_return_if_fail (IS_SHEET_ITEM (item));
+
+ item_data = sheet_item_get_data (SHEET_ITEM (item));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (item_data));
+ if (id_class->clone == NULL)
+ return;
+
+ /*
+ * Duplicate the data for the object and add to the clipboard.
+ */
+ clone = id_class->clone (item_data);
+
+ cb_data = g_new0 (ClipboardData, 1);
+ cb_data->item_data = clone;
+ cb_data->item_class = SHEET_ITEM_CLASS (G_OBJECT_GET_CLASS (item));
+
+ oregano.clipboard = g_slist_prepend (oregano.clipboard, cb_data);
+}
+
+GObject *
+clipboard_data_get_item_data (ClipboardData *data)
+{
+ g_return_val_if_fail (data != NULL, NULL);
+
+ return G_OBJECT (data->item_data);
+}
+
+GObjectClass *
+clipboard_data_get_item_class (ClipboardData *data)
+{
+ g_return_val_if_fail (data != NULL, NULL);
+
+ return G_OBJECT_CLASS (data->item_class);
+}
+
diff --git a/src/clipboard.h b/src/clipboard.h
new file mode 100644
index 0000000..54a4f43
--- /dev/null
+++ b/src/clipboard.h
@@ -0,0 +1,45 @@
+/*
+ * clipboard.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 __CLIPBOARD_H
+#define __CLIPBOARD_H
+
+typedef struct _ClipboardData ClipboardData;
+
+typedef void (*ClipBoardFunction) (ClipboardData *data, gpointer user_data);
+
+void clipboard_empty (void);
+gboolean clipboard_is_empty (void);
+void clipboard_foreach (ClipBoardFunction callback,
+ gpointer user_data);
+void clipboard_add_object (GObject *item);
+GObjectClass *clipboard_data_get_item_class (ClipboardData *data);
+GObject *clipboard_data_get_item_data (ClipboardData *data);
+
+#endif
diff --git a/src/create-wire.c b/src/create-wire.c
new file mode 100644
index 0000000..03968d7
--- /dev/null
+++ b/src/create-wire.c
@@ -0,0 +1,690 @@
+/*
+ * @file create-wire.c
+ *
+ * @author Richard Hult <rhult@hem.passagen.se>
+ * @author Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * @author Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * @brief Handles the user interaction when creating wires.
+ *
+ * The name is not really right. This part handles creation of wires and
+ * acts as glue between NodeStore/Wire and Sheet/WireItem.
+ *
+ * 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 "cursors.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "node-store.h"
+#include "item-data.h"
+#include "wire-item.h"
+#include "create-wire.h"
+#include "wire.h"
+#include "schematic-view.h"
+
+struct _CreateWireContext {
+ guint active : 1;
+ guint moved : 1;
+ guint oneshot : 1;
+ gint start_handler_id;
+ gint draw_handler_id;
+ gint sheet_cancel_id;
+
+ gdouble old_x, old_y;
+
+ SchematicView *schematic_view;
+
+ GnomeCanvasItem *dot_item;
+
+ CreateWire *create_wire;
+};
+
+static int create_wire_event (Sheet *sheet, const GdkEvent *event,
+ CreateWireContext *cwc);
+
+static int create_wire_pre_create_event (Sheet *sheet, const GdkEvent *event,
+ CreateWireContext *cwc);
+
+static int sheet_cancel (Sheet *sheet, CreateWireContext *cwc);
+
+static void fixate_wire (CreateWireContext *cwc, gboolean always_fixate_both,
+ int x, int y);
+
+static Wire *create_wire_and_place_item (SchematicView *sv, SheetPos start_pos,
+ SheetPos end_pos);
+
+static void cancel_wire (CreateWireContext *cwc);
+
+static void exit_wire_mode (CreateWireContext *cwc);
+
+/*
+ * Initiates wire creation by disconnecting the signal handler that
+ * starts the wire-drawing and then connecting the drawing handler.
+ * This is an event handler.
+ *
+ * @param sheet a sheet (the canvas)
+ * @param event the event
+ * @param cwc context
+ */
+static int
+create_wire_pre_create_event (Sheet *sheet, const GdkEvent *event,
+ CreateWireContext *cwc)
+{
+ GnomeCanvasPoints *points;
+ CreateWire *wire;
+ double new_x, new_y;
+ int x, y;
+
+ if (cwc->active)
+ return FALSE;
+
+ if (event->button.button == 2 || event->button.button > 3)
+ return FALSE;
+
+ if (event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS)
+ return FALSE;
+
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "button_press_event");
+
+ /*
+ * Button 3 resets the sheet mode.
+ */
+ if (event->button.button == 3) {
+ exit_wire_mode (cwc);
+ return TRUE;
+ }
+
+ /*
+ * Button 1 starts a new wire. Start by deselecting all objects.
+ */
+ schematic_view_select_all (cwc->schematic_view, FALSE);
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet),
+ event->button.x, event->button.y,
+ &new_x, &new_y);
+
+ snap_to_grid (sheet->grid, &new_x, &new_y);
+ x = new_x;
+ y = new_y;
+
+ points = gnome_canvas_points_new (3);
+ points->coords[0] = x;
+ points->coords[1] = y;
+ points->coords[2] = x;
+ points->coords[3] = y;
+ points->coords[4] = x;
+ points->coords[5] = y;
+
+ wire = g_new0 (CreateWire, 1);
+ cwc->create_wire = wire;
+ cwc->moved = FALSE;
+ cwc->oneshot = TRUE;
+ cwc->old_x = x;
+ cwc->old_y = y;
+
+ wire->line = GNOME_CANVAS_LINE (
+ gnome_canvas_item_new (
+ sheet->object_group,
+ gnome_canvas_line_get_type (),
+ "points", points,
+ "fill_color", "red",
+ "line_style", GDK_LINE_ON_OFF_DASH,
+ "width_pixels", 1,
+ NULL));
+
+ wire->points = points;
+ wire->direction = WIRE_DIR_NONE;
+
+ cwc->draw_handler_id = g_signal_connect (
+ G_OBJECT (sheet),
+ "event",
+ G_CALLBACK (create_wire_event),
+ cwc);
+
+ cwc->active = TRUE;
+
+ return TRUE;
+}
+
+/*
+ * This needs to be called in order to start a wire creation.
+ * It sets up the initial event handler that basically just
+ * takes care of the first button-1 press that starts the
+ * drawing mode.
+ */
+CreateWireContext *
+create_wire_initiate (SchematicView *sv)
+{
+ CreateWireContext *cwc;
+ Sheet *sheet;
+
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ sheet = schematic_view_get_sheet (sv);
+
+ cwc = g_new0 (CreateWireContext, 1);
+ cwc->schematic_view = sv;
+ cwc->active = FALSE;
+ cwc->dot_item = NULL;
+
+ cwc->start_handler_id = g_signal_connect (
+ G_OBJECT (sheet),
+ "button_press_event",
+ G_CALLBACK (create_wire_pre_create_event),
+ cwc);
+
+ cwc->sheet_cancel_id = g_signal_connect (
+ G_OBJECT (sheet),
+ "cancel",
+ G_CALLBACK (sheet_cancel),
+ cwc);
+
+ return cwc;
+}
+
+/*
+ * create_wire_event
+ */
+static int
+create_wire_event (Sheet *sheet, const GdkEvent *event, CreateWireContext *cwc)
+{
+ int snapped_x, snapped_y;
+ int x1, y1, x2, y2;
+ double new_x, new_y;
+ gboolean diagonal;
+ CreateWire *wire = cwc->create_wire;
+ Schematic *s;
+ NodeStore *store;
+ SheetPos pos;
+ int i, intersect;
+
+ s = schematic_view_get_schematic (cwc->schematic_view);
+ store = schematic_get_store (s);
+
+ if (event->type == GDK_2BUTTON_PRESS
+ || event->type == GDK_3BUTTON_PRESS) {
+ return FALSE;
+ }
+
+ switch (event->type){
+ case GDK_BUTTON_PRESS:
+
+ switch (event->button.button) {
+ case 2:
+ case 4:
+ case 5:
+ /*
+ * Don't care about middle button or mouse wheel.
+ */
+ return FALSE;
+ break;
+
+ case 1:
+ cwc->oneshot = FALSE;
+
+ /*
+ * Button-1 fixates the first segment of the wire,
+ * letting the user continue drawing wires, with the
+ * former second segment as the first segment of the
+ * new wire. There are a few exceptions though; if the
+ * button press happens at another wire or a pin of a
+ * part, then we fixate the wire and cancel the drawing
+ * mode.
+ */
+ g_signal_stop_emission_by_name (G_OBJECT (sheet),
+ "event");
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet),
+ event->button.x, event->button.y,
+ &new_x, &new_y);
+
+ snap_to_grid (sheet->grid, &new_x, &new_y);
+ snapped_x = new_x;
+ snapped_y = new_y;
+
+ fixate_wire (cwc, FALSE, snapped_x, snapped_y);
+ break;
+
+ case 3:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet),
+ "event");
+ cancel_wire (cwc);
+ break;
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+
+ switch (event->button.button) {
+ case 2:
+ case 4:
+ case 5:
+ /*
+ * Don't care about middle button or mouse wheel.
+ */
+ return FALSE;
+ break;
+
+ case 1:
+ /*
+ * If the button is released after moving the mouse
+ * pointer, then we finish the wire.
+ */
+ if (cwc->moved && cwc->oneshot) {
+ gnome_canvas_window_to_world (
+ GNOME_CANVAS (sheet),
+ event->button.x, event->button.y,
+ &new_x, &new_y);
+
+ snap_to_grid (sheet->grid, &new_x, &new_y);
+ snapped_x = new_x;
+ snapped_y = new_y;
+
+ fixate_wire (cwc, TRUE, snapped_x, snapped_y);
+ }
+ }
+ return TRUE;
+
+ case GDK_MOTION_NOTIFY:
+ if (!cwc->moved) {
+ double m, dx, dy;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet),
+ event->motion.x, event->motion.y,
+ &new_x, &new_y);
+
+ snap_to_grid (sheet->grid, &new_x, &new_y);
+ dx = fabs (cwc->old_x - new_x);
+ dy = fabs (cwc->old_y - new_y);
+ m = sqrt (dx*dx + dy*dy);
+
+ if (m > 20)
+ cwc->moved = TRUE;
+ }
+
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+
+ diagonal = event->button.state & GDK_SHIFT_MASK;
+ if (!diagonal && wire->direction == WIRE_DIR_DIAG)
+ wire->direction = WIRE_DIR_NONE;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet),
+ event->motion.x, event->motion.y,
+ &new_x, &new_y);
+
+ snap_to_grid (sheet->grid, &new_x, &new_y);
+ snapped_x = new_x;
+ snapped_y = new_y;
+
+ x1 = wire->points->coords[0];
+ y1 = wire->points->coords[1];
+
+ if (x1 == snapped_x && y1 == snapped_y) {
+ wire->direction = WIRE_DIR_NONE;
+ } else {
+ if (wire->direction == WIRE_DIR_NONE) {
+ if (abs (y1 - snapped_y) < abs (x1 - snapped_x)) {
+ wire->direction = WIRE_DIR_HORIZ;
+ } else {
+ wire->direction = WIRE_DIR_VERT;
+ }
+ }
+ }
+
+ x2 = snapped_x;
+ y2 = snapped_y;
+
+ if (diagonal) {
+ wire->direction = WIRE_DIR_DIAG;
+ x2 = x1;
+ y2 = y1;
+ }
+
+ if (wire->direction == WIRE_DIR_HORIZ) {
+ y2 = y1;
+ } else if (wire->direction == WIRE_DIR_VERT) {
+ x2 = x1;
+ }
+
+ if (wire->direction == WIRE_DIR_HORIZ && x2 == x1) {
+ x2 = snapped_x;
+ y2 = snapped_y;
+ } else if (wire->direction == WIRE_DIR_VERT && y2 == y1) {
+ x2 = snapped_x;
+ y2 = snapped_y;
+ }
+
+ /*
+ * This is a really hackish thing.
+ * The reason for the hack is that when you draw a
+ * straight line consisting of > 2 points, and some
+ * points have the same coordinates, then the line will
+ * be invisible.
+ */
+ if (x2 == snapped_x && y2 == snapped_y) {
+ wire->points->num_points = 2;
+ } else {
+ wire->points->num_points = 3;
+ }
+
+ wire->points->coords[2] = x2;
+ wire->points->coords[3] = y2;
+ wire->points->coords[4] = snapped_x;
+ wire->points->coords[5] = snapped_y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire->line),
+ "points", wire->points, NULL);
+
+ pos.x = new_x;
+ pos.y = new_y;
+ /* Check if the pre-wire intersect another wire, and
+ * draw a small red circle to indicate the connection
+ */
+ intersect = 0;
+ intersect = node_store_is_wire_at_pos (store, pos);
+ intersect += node_store_is_pin_at_pos (store, pos);
+ if (intersect) {
+ if (cwc->dot_item) {
+ gnome_canvas_item_set (
+ cwc->dot_item,
+ "x1", -3.0+new_x,
+ "y1", -3.0+new_y,
+ "x2", 3.0+new_x,
+ "y2", 3.0+new_y,
+ NULL
+ );
+
+ gnome_canvas_item_show (cwc->dot_item);
+ } else {
+ cwc->dot_item = gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (sheet->object_group),
+ gnome_canvas_ellipse_get_type (),
+ "x1", -3.0+new_x,
+ "y1", -3.0+new_y,
+ "x2", 3.0+new_x,
+ "y2", 3.0+new_y,
+ "fill_color", "red",
+ NULL
+ );
+ gnome_canvas_item_show (cwc->dot_item);
+ }
+ } else {
+ if (cwc->dot_item) gnome_canvas_item_hide (cwc->dot_item);
+ }
+
+ cwc->old_x = snapped_x;
+ cwc->old_y = snapped_y;
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+fixate_wire (CreateWireContext *cwc, gboolean always_fixate_both, int x, int y)
+{
+ SchematicView *sv;
+ CreateWire *create_wire = cwc->create_wire;
+ Wire *wire1, *wire2;
+ SheetPos p1, p2, start_pos, end_pos, start_pos2, end_pos2;
+ gboolean cancel = FALSE;
+ NodeStore *store;
+ Schematic *schematic;
+
+ g_return_if_fail (cwc != NULL);
+ g_return_if_fail (create_wire != NULL);
+
+ sv = cwc->schematic_view;
+ schematic = schematic_view_get_schematic (sv);
+
+ store = schematic_get_store (schematic);
+
+ p1.x = create_wire->points->coords[0];
+ p1.y = create_wire->points->coords[1];
+ p2.x = create_wire->points->coords[2];
+ p2.y = create_wire->points->coords[3];
+
+ if (create_wire->direction == WIRE_DIR_DIAG) {
+ p1.x = p2.x;
+ p1.y = p2.y;
+ p2.x = create_wire->points->coords[4];
+ p2.y = create_wire->points->coords[5];
+ create_wire->points->coords[2] = p2.x;
+ create_wire->points->coords[3] = p2.y;
+ create_wire->points->num_points = 2;
+ }
+
+ /*
+ * If the user clicks when wire length is zero, cancel the wire.
+ */
+ if (p1.x == p2.x && p1.y == p2.y) {
+ cancel_wire (cwc);
+ return;
+ }
+
+ if (create_wire->direction != WIRE_DIR_DIAG) {
+ start_pos.x = MIN (p1.x, p2.x);
+ start_pos.y = MIN (p1.y, p2.y);
+ end_pos.x = MAX (p1.x, p2.x);
+ end_pos.y = MAX (p1.y, p2.y);
+ } else {
+ start_pos.x = p1.x;
+ start_pos.y = p1.y;
+ end_pos.x = p2.x;
+ end_pos.y = p2.y;
+ }
+
+ /*
+ * If the wire connects to something, then fixate
+ * the second segment too and exit wire drawing mode.
+ *
+ * Also fixate both segments when explicitly asked to.
+ */
+ p2.x = x;
+ p2.y = y;
+ if (always_fixate_both ||
+ node_store_get_node (store, p2) ||
+ node_store_is_wire_at_pos (store, p2)) {
+ if (create_wire->points->num_points == 3) {
+ p1.x = create_wire->points->coords[2];
+ p1.y = create_wire->points->coords[3];
+
+ if (create_wire->direction != WIRE_DIR_DIAG) {
+ start_pos2.x = MIN (p1.x, p2.x);
+ start_pos2.y = MIN (p1.y, p2.y);
+ end_pos2.x = MAX (p1.x, p2.x);
+ end_pos2.y = MAX (p1.y, p2.y);
+ } else {
+ start_pos2.x = p1.x;
+ start_pos2.y = p1.y;
+ end_pos2.x = p2.x;
+ end_pos2.y = p2.y;
+ }
+
+ wire2 = create_wire_and_place_item (sv,
+ start_pos2, end_pos2);
+ }
+ cancel_wire (cwc);
+ cancel = TRUE;
+ }
+
+ wire1 = create_wire_and_place_item (sv, start_pos, end_pos);
+
+ if (cancel)
+ return;
+
+ /*
+ * Start a new "floating" wire, using the same CreateWire that was used
+ * for the old wire.
+ */
+ create_wire->points->coords[0] = create_wire->points->coords[2];
+ create_wire->points->coords[1] = create_wire->points->coords[3];
+ create_wire->points->coords[2] = x;
+ create_wire->points->coords[3] = y;
+ create_wire->points->num_points = 2;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (create_wire->line), "points",
+ create_wire->points, NULL);
+
+ /*
+ * If the finished wire's first segment was horizontal, you get the
+ * best "feeling" if the new wire is vertical. Based on the author's
+ * feeling =)
+ */
+ if (create_wire->direction == WIRE_DIR_HORIZ)
+ create_wire->direction = WIRE_DIR_VERT;
+ else
+ create_wire->direction = WIRE_DIR_HORIZ;
+
+ /*
+ * Raise the floting wire so that we can see it when it is overlapping
+ * other wires.
+ */
+ gnome_canvas_item_raise (GNOME_CANVAS_ITEM (create_wire->line), 1);
+}
+
+Wire *
+create_wire_and_place_item (SchematicView *sv, SheetPos start_pos,
+ SheetPos end_pos)
+{
+ Wire *wire;
+ NodeStore *store;
+ Sheet *sheet;
+ Schematic *schematic;
+ SheetPos length;
+
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ schematic = schematic_view_get_schematic (sv);
+ sheet = schematic_view_get_sheet (sv);
+ store = schematic_get_store (schematic);
+
+ wire = wire_new ();
+ item_data_set_pos (ITEM_DATA (wire), &start_pos);
+
+ length.x = end_pos.x - start_pos.x;
+ length.y = end_pos.y - start_pos.y;
+ wire_set_length (wire, &length);
+
+ schematic_add_item (schematic_view_get_schematic (sv), ITEM_DATA (wire));
+
+ return wire;
+}
+
+static void
+cancel_wire (CreateWireContext *cwc)
+{
+ CreateWire *create_wire;
+ Sheet *sheet;
+
+ create_wire = cwc->create_wire;
+ sheet = schematic_view_get_sheet (cwc->schematic_view);
+
+ g_return_if_fail (create_wire != NULL);
+
+ g_signal_handler_disconnect (G_OBJECT (sheet), cwc->draw_handler_id);
+ cwc->draw_handler_id = 0;
+ cwc->active = FALSE;
+
+ create_wire->points->num_points = 3;
+ gnome_canvas_points_free (create_wire->points);
+
+ if (cwc->dot_item) {
+ gtk_object_destroy (GTK_OBJECT (cwc->dot_item));
+ cwc->dot_item = NULL;
+ }
+ gtk_object_destroy (GTK_OBJECT (create_wire->line));
+
+ g_free (create_wire);
+
+ /* Setup the sheet for a new wire creation process. */
+}
+
+static void
+exit_wire_mode (CreateWireContext *cwc)
+{
+ Sheet *sheet;
+
+ sheet = schematic_view_get_sheet (cwc->schematic_view);
+
+ if (cwc->draw_handler_id != 0)
+ cancel_wire (cwc);
+
+ if (cwc->start_handler_id != 0) {
+ g_signal_handler_disconnect (G_OBJECT (sheet),
+ cwc->start_handler_id);
+ cwc->start_handler_id = 0;
+ }
+
+ if (cwc->sheet_cancel_id != 0) {
+ g_signal_handler_disconnect (G_OBJECT (sheet),
+ cwc->sheet_cancel_id);
+ cwc->sheet_cancel_id = 0;
+ }
+
+ g_signal_emit_by_name (G_OBJECT (sheet), "reset_tool");
+
+ g_free (cwc);
+}
+
+void
+create_wire_exit (CreateWireContext *cwc)
+{
+ Sheet *sheet;
+
+ sheet = schematic_view_get_sheet (cwc->schematic_view);
+
+ if (cwc->draw_handler_id != 0)
+ cancel_wire (cwc);
+
+ if (cwc->start_handler_id != 0) {
+ g_signal_handler_disconnect (G_OBJECT (sheet),
+ cwc->start_handler_id);
+ cwc->start_handler_id = 0;
+ }
+}
+
+/*
+ * Signal handler for the "cancel" signal that the sheet emits
+ * when <escape> is pressed.
+ */
+static int
+sheet_cancel (Sheet *sheet, CreateWireContext *cwc)
+{
+ g_return_val_if_fail (sheet != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET (sheet), FALSE);
+
+ if (cwc->active)
+ cancel_wire (cwc);
+ else
+ exit_wire_mode (cwc);
+
+ return TRUE;
+}
+
diff --git a/src/create-wire.h b/src/create-wire.h
new file mode 100644
index 0000000..f475396
--- /dev/null
+++ b/src/create-wire.h
@@ -0,0 +1,57 @@
+/*
+ * create-wire.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 __CREATE_WIRE_H
+#define __CREATE_WIRE_H
+
+#include <gnome.h>
+
+#include "sheet.h"
+#include "wire.h"
+#include "wire-item.h"
+#include "schematic-view.h"
+
+typedef struct _CreateWireContext CreateWireContext;
+
+typedef struct {
+ GnomeCanvasLine *line;
+ GnomeCanvasPoints *points;
+
+ WireDir direction; /* Direction of the first wire segment. */
+} CreateWire;
+
+CreateWireContext *create_wire_initiate (SchematicView *sv);
+
+void create_wire_from_file (SchematicView *sv, SheetPos start_pos,
+ SheetPos end_pos);
+void create_wire_exit (CreateWireContext *cwc);
+void paste_wire_item_from_wire (SchematicView *schematic_view,
+ Wire *wire);
+
+#endif
diff --git a/src/cursors.c b/src/cursors.c
new file mode 100644
index 0000000..e9d77db
--- /dev/null
+++ b/src/cursors.c
@@ -0,0 +1,67 @@
+/*
+ * cursors.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 "cursors.h"
+
+OreganoCursor oregano_cursors[] = {
+ { NULL, GDK_LEFT_PTR },
+ { NULL, GDK_TCROSS },
+ { NULL, GDK_PENCIL },
+ { NULL, GDK_XTERM },
+ { NULL, -1 }
+};
+
+void
+cursors_init (void)
+{
+ int i;
+
+ for (i = 0; oregano_cursors[i].type != -1; i++){
+ oregano_cursors[i].cursor = gdk_cursor_new (oregano_cursors[i].type);
+ }
+}
+
+void
+cursors_shutdown (void)
+{
+ int i;
+
+ for (i = 0; oregano_cursors[i].type != -1; i++)
+ gdk_cursor_unref(oregano_cursors[i].cursor);
+}
+
+void
+cursor_set_widget (GtkWidget *w, int name)
+{
+ if (w->window)
+ gdk_window_set_cursor (w->window, oregano_cursors[name].cursor);
+}
+
+
diff --git a/src/cursors.h b/src/cursors.h
new file mode 100644
index 0000000..6a52b7b
--- /dev/null
+++ b/src/cursors.h
@@ -0,0 +1,51 @@
+/*
+ * cursors.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 __CURSORS_H
+#define __CURSORS_H
+
+#include <gtk/gtk.h>
+
+#define OREGANO_CURSOR_LEFT_PTR 0
+#define OREGANO_CURSOR_CROSS 1
+#define OREGANO_CURSOR_PENCIL 2
+#define OREGANO_CURSOR_CARET 3
+
+typedef struct {
+ GdkCursor *cursor;
+ GdkCursorType type;
+} OreganoCursor;
+
+extern OreganoCursor oregano_cursors[];
+
+void cursors_init (void);
+void cursors_shutdown (void);
+void cursor_set_widget (GtkWidget *w, int name);
+
+#endif
diff --git a/src/dialogs.c b/src/dialogs.c
new file mode 100644
index 0000000..5facc0d
--- /dev/null
+++ b/src/dialogs.c
@@ -0,0 +1,183 @@
+/*
+ * Author:
+ * Richard Hult <rhult@codefactory.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 "dialogs.h"
+#include "main.h"
+
+#include "pixmaps/logo.xpm"
+
+static void about_destroy_event (void);
+static GtkWidget *about = NULL;
+
+void
+oregano_error (gchar *msg)
+{
+ oregano_error_with_title(msg, NULL);
+}
+
+void
+oregano_error_with_title (gchar *title, gchar *desc)
+{
+ GtkWidget *dialog;
+ gint result;
+
+ GString* span_msg;
+
+ span_msg = g_string_new("<span weight=\"bold\" size=\"large\">");
+ span_msg = g_string_append(span_msg, title);
+ span_msg = g_string_append(span_msg,"</span>");
+
+ if (desc && desc[0] != '\0') {
+ span_msg = g_string_append(span_msg,"\n\n");
+ span_msg = g_string_append(span_msg, desc);
+ }
+
+ dialog = gtk_message_dialog_new_with_markup (
+ NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ span_msg->str);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ g_string_free(span_msg, TRUE);
+ gtk_widget_destroy (dialog);
+}
+
+void
+oregano_warning (gchar *msg)
+{
+ oregano_warning_with_title(msg, NULL);
+}
+
+
+void
+oregano_warning_with_title (gchar *title, gchar *desc)
+{
+ GtkWidget *dialog;
+ gint result;
+
+ GString* span_msg;
+
+ span_msg = g_string_new("<span weight=\"bold\" size=\"large\">");
+ span_msg = g_string_append(span_msg, title);
+ span_msg = g_string_append(span_msg,"</span>");
+
+ if (desc && desc[0] != '\0') {
+ span_msg = g_string_append(span_msg,"\n\n");
+ span_msg = g_string_append(span_msg, desc);
+ }
+
+ dialog = gtk_message_dialog_new_with_markup (
+ NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_OK,
+ span_msg->str);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ g_string_free(span_msg, TRUE);
+ gtk_widget_destroy (dialog);
+}
+gint
+oregano_question (gchar *msg)
+{
+ GtkWidget *dialog;
+ gint ans;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ NULL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_OK,
+ GTK_BUTTONS_CANCEL,
+ msg);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+
+ ans = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ switch (ans) {
+ case GTK_RESPONSE_ACCEPT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+void
+dialog_about (void)
+{
+ GdkPixbuf *logo;
+
+ const gchar *authors[] = {
+ "Richard Hult",
+ "Margarita Manterola",
+ "Andres de Barbara",
+ "Gustavo M. Pereyra",
+ "Maximiliano Curia",
+ "Ricardo Markiewicz",
+ NULL
+ };
+
+ const char *docs[] = {
+ "Ricardo Markiewicz <rmarkie@fi.uba.ar> (es)",
+ "Jordi Mallach <tradgnome@softcatala.net> (ca)",
+ NULL
+ };
+
+ const gchar *copy = _("(c) 2003-2006 LUGFi\n(c) 1999-2001 Richard Hult");
+
+ /* Allow only one about box at a time. */
+ if (about){
+ gdk_window_raise (about->window);
+ return;
+ }
+
+ logo = gdk_pixbuf_new_from_xpm_data ((const char **) logo_xpm);
+
+ about = gtk_about_dialog_new ();
+ gtk_about_dialog_set_name (GTK_ABOUT_DIALOG (about), "Oregano");
+ gtk_about_dialog_set_version (GTK_ABOUT_DIALOG (about), VERSION);
+ gtk_about_dialog_set_copyright (GTK_ABOUT_DIALOG (about), copy);
+ gtk_about_dialog_set_comments (GTK_ABOUT_DIALOG (about), _("Schematic capture and circuit simulation.\n"));
+ gtk_about_dialog_set_license (GTK_ABOUT_DIALOG (about), "GNU General Public License");
+ gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (about), "http://arrakis.gforge.lug.fi.uba.ar");
+ gtk_about_dialog_set_authors (GTK_ABOUT_DIALOG (about), authors);
+ gtk_about_dialog_set_documenters (GTK_ABOUT_DIALOG (about), docs);
+ gtk_about_dialog_set_logo (GTK_ABOUT_DIALOG (about), logo);
+ gtk_dialog_run (GTK_DIALOG (about));
+ gtk_widget_destroy (about);
+
+}
diff --git a/src/dialogs.h b/src/dialogs.h
new file mode 100644
index 0000000..a56ac52
--- /dev/null
+++ b/src/dialogs.h
@@ -0,0 +1,43 @@
+/*
+ * dialogs.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 __DIALOGS_H
+#define __DIALOGS_H
+
+#include <libgnome/gnome-i18n.h>
+#include "schematic.h"
+
+void oregano_error (gchar *msg);
+void oregano_error_with_title (gchar *title, gchar *desc);
+void oregano_warning (gchar *msg);
+void oregano_warning_with_title (gchar *title, gchar *desc);
+gint oregano_question (gchar *msg);
+void dialog_about (void);
+
+#endif
diff --git a/src/engines/Makefile.am b/src/engines/Makefile.am
new file mode 100644
index 0000000..ccdf011
--- /dev/null
+++ b/src/engines/Makefile.am
@@ -0,0 +1,20 @@
+oreganodir = $(datadir)/oregano
+INCLUDES = \
+ $(OREGANO_CFLAGS) -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/model -I$(top_srcdir)/src/sheet \
+ -DOREGANO_XMLDIR=\""$(oreganodir)/xml"\" \
+ -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \
+ -DOREGANO_MODELDIR=\""$(oreganodir)/models"\"
+
+noinst_LIBRARIES = libengines.a
+libengines_a_SOURCES = \
+ engine.c \
+ engine.h \
+ gnucap.c \
+ gnucap.h \
+ netlist.c \
+ netlist.h \
+ ngspice.c \
+ ngspice.h
+
+libengines_a_LIBADD = libengines.a
diff --git a/src/engines/engine.c b/src/engines/engine.c
new file mode 100644
index 0000000..9958608
--- /dev/null
+++ b/src/engines/engine.c
@@ -0,0 +1,178 @@
+/*
+ * engine.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.
+ */
+
+#include "engine.h"
+#include "gnucap.h"
+#include "ngspice.h"
+
+static gchar *analysis_names[] = {
+ N_("Operating Point"),
+ N_("Transient Analysis"),
+ N_("DC transfer characteristic"),
+ N_("AC Analysis"),
+ N_("Transfer Function"),
+ N_("Distortion Analysis"),
+ N_("Noise Analysis"),
+ N_("Pole-Zero Analysis"),
+ N_("Sensitivity Analysis"),
+ N_("Unknown Analysis"),
+ NULL
+};
+
+/* Signals */
+enum {
+ DONE,
+ ABORTED,
+ LAST_SIGNAL
+};
+static guint engine_signals[LAST_SIGNAL] = { 0 };
+
+static void
+oregano_engine_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized) {
+ /* create interface signals here. */
+ engine_signals[DONE] = g_signal_new ("done", G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (OreganoEngineClass, done),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ engine_signals[ABORTED] = g_signal_new ("aborted", G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (OreganoEngineClass, abort),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ initialized = TRUE;
+ }
+}
+
+GType
+oregano_engine_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (OreganoEngineClass),
+ oregano_engine_base_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+ type = g_type_register_static (G_TYPE_INTERFACE, "OreganoEngine", &info, 0);
+ }
+ return type;
+}
+
+void
+oregano_engine_start (OreganoEngine *self)
+{
+ OREGANO_ENGINE_GET_CLASS (self)->start (self);
+}
+
+void
+oregano_engine_stop (OreganoEngine *self)
+{
+ OREGANO_ENGINE_GET_CLASS (self)->stop (self);
+}
+
+gboolean
+oregano_engine_has_warnings (OreganoEngine *self)
+{
+ return OREGANO_ENGINE_GET_CLASS (self)->has_warnings (self);
+}
+
+gboolean
+oregano_engine_is_available (OreganoEngine *self)
+{
+ return OREGANO_ENGINE_GET_CLASS (self)->is_available (self);
+}
+
+void
+oregano_engine_get_progress (OreganoEngine *self, double *p)
+{
+ OREGANO_ENGINE_GET_CLASS (self)->progress (self, p);
+}
+
+void
+oregano_engine_generate_netlist (OreganoEngine *self, const gchar *file, GError **error)
+{
+ OREGANO_ENGINE_GET_CLASS (self)->get_netlist (self, file, error);
+}
+
+GList*
+oregano_engine_get_results (OreganoEngine *self)
+{
+ return OREGANO_ENGINE_GET_CLASS (self)->get_results (self);
+}
+
+gchar*
+oregano_engine_get_current_operation (OreganoEngine *self)
+{
+ return OREGANO_ENGINE_GET_CLASS (self)->get_operation (self);
+}
+OreganoEngine*
+oregano_engine_factory_create_engine (gint type, Schematic *sm)
+{
+ OreganoEngine *engine;
+
+ switch (type) {
+ case OREGANO_ENGINE_GNUCAP:
+ engine = oregano_gnucap_new (sm);
+ break;
+ case OREGANO_ENGINE_NGSPICE:
+ engine = oregano_ngspice_new (sm);
+ break;
+ default:
+ engine = NULL;
+ }
+
+ return engine;
+}
+
+gchar *
+oregano_engine_get_analysis_name (SimulationData *sdat)
+{
+ if (sdat == NULL)
+ return g_strdup (_(analysis_names[ANALYSIS_UNKNOWN]));
+
+ return g_strdup (_(analysis_names[sdat->type]));
+}
diff --git a/src/engines/engine.h b/src/engines/engine.h
new file mode 100644
index 0000000..b5e6a9f
--- /dev/null
+++ b/src/engines/engine.h
@@ -0,0 +1,89 @@
+/*
+ * engine.h
+ *
+ * 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 __ENGINE_H
+#define __ENGINE_H 1
+
+#include <gtk/gtk.h>
+#include "sim-settings.h"
+#include "schematic.h"
+#include "simulation.h"
+
+#define OREGANO_TYPE_ENGINE (oregano_engine_get_type ())
+#define OREGANO_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OREGANO_TYPE_ENGINE, OreganoEngine))
+#define OREGANO_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass))
+#define OREGANO_IS_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OREGANO_TYPE_ENGINE))
+#define OREGANO_IS_ENGINE_CLASS(klass) (G_TYPE_CLASS_TYPE((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass))
+#define OREGANO_ENGINE_GET_CLASS(klass) (G_TYPE_INSTANCE_GET_INTERFACE((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass))
+
+typedef struct _OreganoEngine OreganoEngine;
+typedef struct _OreganoEngineClass OreganoEngineClass;
+
+struct _OreganoEngineClass {
+ GTypeInterface parent;
+
+ void (*start) (OreganoEngine *engine);
+ void (*stop) (OreganoEngine *engine);
+ void (*progress) (OreganoEngine *engine, double *p);
+ void (*get_netlist) (OreganoEngine *engine, const gchar *sm, GError **error);
+ GList* (*get_results) (OreganoEngine *engine);
+ gchar* (*get_operation) (OreganoEngine *engine);
+ gboolean (*has_warnings) (OreganoEngine *engine);
+ gboolean (*is_available) (OreganoEngine *engine);
+
+ /* Signals */
+ void (*done) ();
+ void (*abort) ();
+};
+
+/* Engines IDs */
+enum {
+ OREGANO_ENGINE_GNUCAP=0,
+ OREGANO_ENGINE_NGSPICE,
+ OREGANO_ENGINE_COUNT
+};
+
+/* Engines Titles */
+static const gchar*
+engines[] = {
+ "GnuCap",
+ "NgSpice"
+};
+
+OreganoEngine *oregano_engine_factory_create_engine (gint type, Schematic *sm);
+
+GType oregano_engine_get_type (void);
+void oregano_engine_start (OreganoEngine *engine);
+void oregano_engine_stop (OreganoEngine *engine);
+gboolean oregano_engine_has_warnings (OreganoEngine *engine);
+void oregano_engine_get_progress (OreganoEngine *engine, double *p);
+void oregano_engine_generate_netlist (OreganoEngine *engine, const gchar *file, GError **error);
+GList *oregano_engine_get_results (OreganoEngine *engine);
+gchar *oregano_engine_get_current_operation (OreganoEngine *);
+gboolean oregano_engine_is_available (OreganoEngine *);
+gchar *oregano_engine_get_analysis_name (SimulationData *id);
+
+#endif
diff --git a/src/engines/gnucap.c b/src/engines/gnucap.c
new file mode 100644
index 0000000..8e93ec6
--- /dev/null
+++ b/src/engines/gnucap.c
@@ -0,0 +1,767 @@
+/*
+ * gnucap.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.
+ */
+
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include "gnucap.h"
+#include "netlist.h"
+
+// TODO Move analisys data and result to another file
+#include "simulation.h"
+
+typedef enum {
+ STATE_IDLE,
+ IN_VARIABLES,
+ IN_VALUES,
+ STATE_ABORT
+} ParseDataState;
+
+struct analisys_tag {
+ gchar *name;
+ guint len;
+};
+
+static struct analisys_tag analisys_tags[] = {
+ {"#", 1}, /* OP */
+ {"#Time", 5}, /* Transient */
+ {"#DC", 3}, /* DC */
+ {"#Freq", 5}, /* AC */
+};
+
+#define IS_THIS_ITEM(str,item) (!strncmp(str,item.name,item.len))
+#define GNUCAP_TITLE '#'
+#define TAGS_COUNT (sizeof (analisys_tags) / sizeof (struct analisys_tag))
+
+/* Parser STATUS */
+struct _OreganoGnuCapPriv {
+ GPid child_pid;
+ gint child_stdout;
+ GIOChannel *child_iochannel;
+ gint child_iochannel_watch;
+ Schematic *schematic;
+
+ gboolean aborted;
+
+ GList *analysis;
+ gint num_analysis;
+ SimulationData *current;
+ double progress;
+ gboolean char_last_newline;
+ guint status;
+ guint buf_count;
+ gchar buf[256]; // FIXME later
+};
+
+static void gnucap_class_init (OreganoGnuCapClass *klass);
+static void gnucap_finalize (GObject *object);
+static void gnucap_dispose (GObject *object);
+static void gnucap_instance_init (GTypeInstance *instance, gpointer g_class);
+static void gnucap_interface_init (gpointer g_iface, gpointer iface_data);
+static gboolean gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCap *gnucap);
+static void gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap);
+
+static GObjectClass *parent_class = NULL;
+
+GType
+oregano_gnucap_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (OreganoGnuCapClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gnucap_class_init, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (OreganoGnuCap),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnucap_instance_init, /* instance_init */
+ NULL
+ };
+
+ static const GInterfaceInfo gnucap_info = {
+ (GInterfaceInitFunc) gnucap_interface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT, "OreganoGnuCap", &info, 0);
+ g_type_add_interface_static (type, OREGANO_TYPE_ENGINE, &gnucap_info);
+ }
+ return type;
+}
+
+static void
+gnucap_class_init (OreganoGnuCapClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gnucap_dispose;
+ object_class->finalize = gnucap_finalize;
+}
+
+static void
+gnucap_finalize (GObject *object)
+{
+ SimulationData *data;
+ OreganoGnuCap *gnucap;
+ GList *lst;
+ int i;
+
+ gnucap = OREGANO_GNUCAP (object);
+
+ lst = gnucap->priv->analysis;
+ while (lst) {
+ data = SIM_DATA (lst->data);
+ for (i=0; i<data->n_variables; i++) {
+ g_free (data->var_names[i]);
+ g_free (data->var_units[i]);
+ }
+ g_free (data->var_names);
+ g_free (data->var_units);
+ for (i=0; i<data->n_points; i++)
+ g_array_free (data->data[i], TRUE);
+ g_free (data->min_data);
+ g_free (data->max_data);
+
+ g_free (lst->data);
+ lst = lst->next;
+ }
+ g_list_free (gnucap->priv->analysis);
+ gnucap->priv->analysis = NULL;
+
+ parent_class->finalize (object);
+}
+
+static void
+gnucap_dispose (GObject *object)
+{
+ parent_class->dispose (object);
+}
+
+static gboolean
+gnucap_has_warnings (OreganoEngine *self)
+{
+ return FALSE;
+}
+
+static gboolean
+gnucap_is_available (OreganoEngine *self)
+{
+ gchar *exe;
+ exe = g_find_program_in_path ("gnucap");
+
+ if (!exe) return FALSE; // gnucap not found
+
+ g_free (exe);
+ return TRUE;
+}
+
+static void
+gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, GError **error)
+{
+ OreganoGnuCap *gnucap;
+ Netlist output;
+ SimOption *so;
+ GList *list;
+ FILE *file;
+ GError *local_error = NULL;
+
+ gnucap = OREGANO_GNUCAP (engine);
+
+ netlist_helper_create (gnucap->priv->schematic, &output, &local_error);
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ return;
+ }
+
+ file = fopen (filename, "w");
+ if (!file) {
+ g_print ("No se pudo crear %s\n", filename);
+ return;
+ }
+
+ list = sim_settings_get_options (output.settings);
+
+ /* Prints title */
+ fputs (output.title ? output.title : "Title: <unset>", file);
+ fputs ("\n"
+ "*----------------------------------------------"
+ "\n"
+ "*\tGNUCAP - NETLIST"
+ "\n", file);
+ /* Prints Options */
+ fputs (".options OUT=120 ",file);
+
+ while (list) {
+ so = list->data;
+ /* Prevent send NULL text */
+ if (so->value) {
+ if (strlen(so->value) > 0) {
+ fprintf (file,"%s=%s ",so->name,so->value);
+ }
+ }
+ list = list->next;
+ }
+ fputc ('\n',file);
+
+ /* Include of subckt models */
+ fputs ("*------------- Models -------------------------\n",file);
+ list = output.models;
+ while (list) {
+ gchar *model;
+ model = (gchar *)list->data;
+ fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model);
+ list = list->next;
+ }
+
+ fputs ("*------------- Circuit Description-------------\n",file);
+ fputs (output.template->str,file);
+ fputs ("\n*----------------------------------------------\n",file);
+
+ /* Prints Transient Analisis */
+ if (sim_settings_get_trans (output.settings)) {
+ gchar *tmp_str = netlist_helper_create_analisys_string (output.store, FALSE);
+ fprintf(file, ".print tran %s\n", tmp_str);
+ g_free (tmp_str);
+
+ fprintf (file, ".tran %g %g ",
+ sim_settings_get_trans_start(output.settings),
+ sim_settings_get_trans_stop(output.settings));
+ if (!sim_settings_get_trans_step_enable(output.settings))
+ /* FIXME Do something to get the right resolution */
+ fprintf(file,"%g",
+ (sim_settings_get_trans_stop(output.settings)-
+ sim_settings_get_trans_start(output.settings))/100);
+ else
+ fprintf(file,"%g", sim_settings_get_trans_step(output.settings));
+
+ if (sim_settings_get_trans_init_cond(output.settings)) {
+ fputs(" UIC\n", file);
+ } else {
+ fputs("\n", file);
+ }
+ }
+
+ /* Print dc Analysis */
+ if (sim_settings_get_dc (output.settings)) {
+ fprintf(file, ".print dc %s\n", netlist_helper_create_analisys_string (output.store, FALSE));
+ fputs(".dc ",file);
+
+ /* GNUCAP don t support nesting so the first or the second */
+ /* Maybe a error message must be show if both are on */
+
+ if ( sim_settings_get_dc_vsrc (output.settings,0) ) {
+ fprintf (file,"%s %g %g %g",
+ sim_settings_get_dc_vsrc(output.settings,0),
+ sim_settings_get_dc_start (output.settings,0),
+ sim_settings_get_dc_stop (output.settings,0),
+ sim_settings_get_dc_step (output.settings,0) );
+ }
+
+ else if ( sim_settings_get_dc_vsrc (output.settings,1) ) {
+ fprintf (file,"%s %g %g %g",
+ sim_settings_get_dc_vsrc(output.settings,1),
+ sim_settings_get_dc_start (output.settings,1),
+ sim_settings_get_dc_stop (output.settings,1),
+ sim_settings_get_dc_step (output.settings,1) );
+ };
+
+ fputc ('\n',file);
+ }
+
+ /* Prints ac Analysis*/
+ if ( sim_settings_get_ac (output.settings) ) {
+ double ac_start, ac_stop, ac_step;
+ /* GNUCAP dont support OCT or DEC */
+ /* Maybe a error message must be show if is set in that way */
+ ac_start = sim_settings_get_ac_start(output.settings) ;
+ ac_stop = sim_settings_get_ac_stop(output.settings);
+ ac_step = (ac_stop - ac_start) / sim_settings_get_ac_npoints(output.settings);
+ fprintf(file, ".print ac %s\n", netlist_helper_create_analisys_string (output.store, TRUE));
+ /* AC format : ac start stop step_size */
+ fprintf (file, ".ac %g %g %g\n", ac_start, ac_stop, ac_step);
+ }
+
+ /* Debug op analysis. */
+ fputs(".print op v(nodes)\n", file);
+ fputs(".op\n", file);
+ fputs(".end\n", file);
+ fclose (file);
+}
+
+static void
+gnucap_progress (OreganoEngine *self, double *d)
+{
+ OreganoGnuCap *gnucap = OREGANO_GNUCAP (self);
+
+ gnucap->priv->progress += 0.1;
+ (*d) = gnucap->priv->progress;
+}
+
+static void
+gnucap_stop (OreganoEngine *self)
+{
+ OreganoGnuCap *gnucap = OREGANO_GNUCAP (self);
+ g_io_channel_shutdown (gnucap->priv->child_iochannel, TRUE, NULL);
+ g_source_remove (gnucap->priv->child_iochannel_watch);
+ g_spawn_close_pid (gnucap->priv->child_pid);
+ close (gnucap->priv->child_stdout);
+}
+
+static void
+gnucap_watch_cb (GPid pid, gint status, OreganoGnuCap *gnucap)
+{
+ /* check for status, see man waitpid(2) */
+ if (WIFEXITED (status)) {
+ gchar *line;
+ gint status;
+ gsize len;
+ g_io_channel_read_to_end (gnucap->priv->child_iochannel, &line, &len, NULL);
+ if (len > 0)
+ gnucap_parse (line, len, gnucap);
+ g_free (line);
+
+ /* Free stuff */
+ g_io_channel_shutdown (gnucap->priv->child_iochannel, TRUE, NULL);
+ g_source_remove (gnucap->priv->child_iochannel_watch);
+ g_spawn_close_pid (gnucap->priv->child_pid);
+ close (gnucap->priv->child_stdout);
+
+ gnucap->priv->current = NULL;
+
+ if (gnucap->priv->num_analysis == 0) {
+ schematic_log_append_error (gnucap->priv->schematic, _("### Too few or none analysis found ###\n"));
+ gnucap->priv->aborted = TRUE;
+ g_signal_emit_by_name (G_OBJECT (gnucap), "aborted");
+ } else
+ g_signal_emit_by_name (G_OBJECT (gnucap), "done");
+ }
+}
+
+static gboolean
+gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCap *gnucap)
+{
+ gchar *line;
+ gsize len, terminator;
+ GIOStatus status;
+ GError *error = NULL;
+
+ status = g_io_channel_read_line (source, &line, &len, &terminator, &error);
+ if ((status & G_IO_STATUS_NORMAL) && (len > 0)) {
+ gnucap_parse (line, len, gnucap);
+ g_free (line);
+ }
+
+ /* Let UI update */
+ g_main_context_iteration (NULL, FALSE);
+ return TRUE;
+}
+
+static void
+gnucap_start (OreganoEngine *self)
+{
+ OreganoGnuCap *gnucap;
+ GError *error = NULL;
+ char *argv[] = {"gnucap", "-b", "/tmp/netlist.tmp", NULL};
+
+ gnucap = OREGANO_GNUCAP (self);
+ oregano_engine_generate_netlist (self, "/tmp/netlist.tmp", &error);
+ if (error != NULL) {
+ gnucap->priv->aborted = TRUE;
+ schematic_log_append_error (gnucap->priv->schematic, error->message);
+ g_signal_emit_by_name (G_OBJECT (gnucap), "aborted");
+ g_error_free (error);
+ return;
+ }
+
+ error = NULL;
+ if (g_spawn_async_with_pipes (
+ NULL, /* Working directory */
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &gnucap->priv->child_pid,
+ NULL, /* STDIN */
+ &gnucap->priv->child_stdout, /* STDOUT */
+ NULL, /* STDERR*/
+ &error
+ )) {
+ /* Add a watch for process status */
+ g_child_watch_add (gnucap->priv->child_pid, (GChildWatchFunc)gnucap_watch_cb, gnucap);
+ /* Add a GIOChannel to read from process stdout */
+ gnucap->priv->child_iochannel = g_io_channel_unix_new (gnucap->priv->child_stdout);
+ /* Watch the I/O Channel to read child strout */
+ gnucap->priv->child_iochannel_watch = g_io_add_watch (gnucap->priv->child_iochannel,
+ G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_NVAL, (GIOFunc)gnucap_child_stdout_cb, gnucap);
+ } else {
+ gnucap->priv->aborted = TRUE;
+ schematic_log_append_error (gnucap->priv->schematic, _("Unable to execute GnuCap."));
+ g_signal_emit_by_name (G_OBJECT (gnucap), "aborted");
+ }
+}
+
+static GList*
+gnucap_get_results (OreganoEngine *self)
+{
+ return OREGANO_GNUCAP (self)->priv->analysis;
+}
+
+const gchar*
+gnucap_get_operation (OreganoEngine *self)
+{
+ OreganoGnuCapPriv *priv = OREGANO_GNUCAP (self)->priv;
+
+ if (priv->current == NULL)
+ return _("None");
+
+ return oregano_engine_get_analysis_name (priv->current);
+}
+
+static void
+gnucap_interface_init (gpointer g_iface, gpointer iface_data)
+{
+ OreganoEngineClass *klass = (OreganoEngineClass *)g_iface;
+ klass->start = gnucap_start;
+ klass->stop = gnucap_stop;
+ klass->progress = gnucap_progress;
+ klass->get_netlist = gnucap_generate_netlist;
+ klass->has_warnings = gnucap_has_warnings;
+ klass->get_results = gnucap_get_results;
+ klass->get_operation = gnucap_get_operation;
+ klass->is_available = gnucap_is_available;
+}
+
+static void
+gnucap_instance_init (GTypeInstance *instance, gpointer g_class)
+{
+ OreganoGnuCap *self = OREGANO_GNUCAP (instance);
+
+ self->priv = g_new0 (OreganoGnuCapPriv, 1);
+ self->priv->progress = 0.0;
+ self->priv->char_last_newline = TRUE;
+ self->priv->status = 0;
+ self->priv->buf_count = 0;
+ self->priv->num_analysis = 0;
+ self->priv->analysis = NULL;
+ self->priv->current = NULL;
+ self->priv->aborted = FALSE;
+}
+
+OreganoEngine*
+oregano_gnucap_new (Schematic *sc)
+{
+ OreganoGnuCap *gnucap;
+
+ gnucap = OREGANO_GNUCAP (g_object_new (OREGANO_TYPE_GNUCAP, NULL));
+ gnucap->priv->schematic = sc;
+
+ return OREGANO_ENGINE (gnucap);
+}
+
+typedef struct {
+ gchar *name;
+ //gchar *unit;
+} GCap_Variable;
+
+GCap_Variable *_get_variables(gchar *str, gint *count)
+{
+ GCap_Variable *out;
+ /* FIXME Improve the code */
+ gchar *tmp[100];
+ gchar *ini, *fin;
+ gint i = 0;
+
+ // Don't USE!. Is not smarty !!
+ // It generate empty strings that really sucks all!!!
+ //arr = g_strsplit_set (str, " ", -1);
+
+ i = 0;
+ ini = str;
+ /* saco espacios adelante */
+ while (isspace(*ini)) ini++;
+ fin = ini;
+ while (*fin != '\0') {
+ if (isspace(*fin)) {
+ *fin = '\0';
+ tmp[i] = g_strdup(ini);
+ *fin = ' ';
+ i++;
+ ini = fin;
+ while (isspace(*ini)) ini++;
+ fin = ini;
+ } else fin++;
+ }
+
+ if (i == 0) {
+ g_warning ("NO COLUMNS FOUND\n");
+ return NULL;
+ }
+
+ out = g_new0 (GCap_Variable, i);
+ (*count) = i;
+ for ( i=0; i<(*count); i++ ) {
+ out[i].name = tmp[i];
+ }
+
+ return out;
+}
+
+void
+_free_variables(GCap_Variable *v, gint count)
+{
+ int i;
+ for(i=0; i<count; i++)
+ g_free(v[i].name);
+ g_free(v);
+}
+
+gdouble
+strtofloat (char *s) {
+ gdouble val;
+ char *error;
+
+ val = strtod(s, &error);
+ /* If the value looks like : 100.u, adjust it */
+ /* We need this because GNU Cap's or ngSpice float notation */
+ switch (error[0]) {
+ case 'u':
+ val /= 1000000;
+ break;
+ case 'n':
+ val /= 1000000;
+ val /= 1000;
+ break;
+ case 'p':
+ val /= 1000000;
+ val /= 1000000;
+ break;
+ case 'f':
+ val /= 100;
+ break;
+ case 'K':
+ val *= 1000;
+ break;
+ default:
+ if (strcmp(error, "Meg") == 0) val *= 1000000;
+ }
+
+ return val;
+}
+
+/* Main method. Here we'll transform GnuCap output
+ * into SimulationResults!
+ */
+static void
+gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap)
+{
+ static SimulationData *sdata;
+ static Analysis *data;
+ GCap_Variable *variables;
+ OreganoGnuCapPriv *priv = gnucap->priv;
+ gint i, j, n;
+ gdouble val;
+ gchar *s;
+
+ for (j=0; j < len; j++) {
+ if (raw[j] != '\n') {
+ priv->buf[priv->buf_count++] = raw[j];
+ continue;
+ }
+ priv->buf[priv->buf_count] = '\0';
+
+ //Got a complete line
+ s = priv->buf;
+ if (s[0] == GNUCAP_TITLE) {
+ SimSettings *sim_settings;
+ gdouble np1, np2;
+
+ sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic);
+
+ data = g_new0 (Analysis, 1);
+ priv->current = sdata = SIM_DATA (data);
+ priv->analysis = g_list_append (priv->analysis, sdata);
+ priv->num_analysis++;
+ sdata->state = STATE_IDLE;
+ sdata->type = ANALYSIS_UNKNOWN;
+ sdata->functions = NULL;
+
+ /* Calculates the quantity of variables */
+ variables = _get_variables(s, &n);
+
+ for (i = 0; i < TAGS_COUNT; i++)
+ if (IS_THIS_ITEM (variables[0].name, analisys_tags[i]))
+ sdata->type = i;
+
+ sdata->state = IN_VALUES;
+ sdata->n_variables = n;
+ sdata->got_points = 0;
+ sdata->got_var = 0;
+ sdata->var_names = (char**) g_new0 (gpointer, n);
+ sdata->var_units = (char**) g_new0 (gpointer, n);
+ sdata->data = (GArray**) g_new0 (gpointer, n);
+
+ for (i = 0; i < n; i++)
+ sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double));
+ sdata->min_data = g_new (double, n);
+ sdata->max_data = g_new (double, n);
+
+ for (i = 0; i < n; i++) {
+ sdata->min_data[i] = G_MAXDOUBLE;
+ sdata->max_data[i] = -G_MAXDOUBLE;
+ }
+ for (i = 0; i < n; i++) {
+ sdata->var_names[i] = g_strdup (variables[i].name);
+ switch (sdata->type) {
+ case TRANSIENT:
+ if (i==0)
+ sdata->var_units[i] = g_strdup (_("time"));
+ else {
+ if (strstr (sdata->var_names[i], "db") != NULL) {
+ sdata->var_units[i] = g_strdup ("db");
+ } else
+ sdata->var_units[i] = g_strdup (_("voltage"));
+ }
+ break;
+ case AC:
+ if (i == 0)
+ sdata->var_units[i] = g_strdup (_("frequency"));
+ else {
+ if (strstr (sdata->var_names[i], "db") != NULL) {
+ sdata->var_units[i] = g_strdup ("db");
+ } else
+ sdata->var_units[i] = g_strdup (_("voltage"));
+ }
+ break;
+ default:
+ sdata->var_units[i] = g_strdup ("");
+ }
+ }
+ sdata->n_variables = n;
+
+ switch (sdata->type) {
+ case TRANSIENT:
+ data->transient.sim_length =
+ sim_settings_get_trans_stop (sim_settings) -
+ sim_settings_get_trans_start (sim_settings);
+ data->transient.step_size =
+ sim_settings_get_trans_step (sim_settings);
+ break;
+ case AC:
+ data->ac.start = sim_settings_get_ac_start (sim_settings);
+ data->ac.stop = sim_settings_get_ac_stop (sim_settings);
+ data->ac.sim_length = sim_settings_get_ac_npoints (sim_settings);
+ break;
+ case OP_POINT:
+ case DC_TRANSFER:
+ np1 = np2 = 1.;
+ data->dc.start1 = sim_settings_get_dc_start (sim_settings,0);
+ data->dc.stop1 = sim_settings_get_dc_stop (sim_settings,0);
+ data->dc.step1 = sim_settings_get_dc_step (sim_settings,0);
+ data->dc.start2 = sim_settings_get_dc_start (sim_settings,1);
+ data->dc.stop2 = sim_settings_get_dc_stop (sim_settings,1);
+ data->dc.step2 = sim_settings_get_dc_step (sim_settings,1);
+ np1 = (data->dc.stop1 - data->dc.start1) / data->dc.step1;
+ if (data->dc.step2 != 0.0) {
+ np2 = (data->dc.stop2 - data->dc.start2) / data->dc.step2;
+ }
+ data->dc.sim_length = np1 * np2;
+ break;
+ case TRANSFER:
+ case DISTORTION:
+ case NOISE:
+ case POLE_ZERO:
+ case SENSITIVITY:
+ break;
+ case ANALYSIS_UNKNOWN:
+ g_error (_("Unknown analysis"));
+ break;
+ }
+ } else {
+ if ((priv->analysis == NULL) || (isalpha (s[0]))) {
+ if (priv->buf_count > 1) {
+ schematic_log_append (priv->schematic, s);
+ schematic_log_append (priv->schematic, "\n");
+ }
+ priv->buf_count = 0;
+ continue;
+ }
+
+ switch (sdata->state) {
+ case IN_VALUES:
+ val = strtofloat (s);
+ switch (sdata->type) {
+ case TRANSIENT:
+ priv->progress = val / data->transient.sim_length;
+ break;
+ case AC:
+ priv->progress = (val - data->ac.start) / data->ac.sim_length;
+ break;
+ case DC_TRANSFER:
+ priv->progress = val / data->ac.sim_length;
+ }
+ if (priv->progress > 1.0)
+ priv->progress = 1.0;
+
+ variables = _get_variables (s, &n);
+ for (i = 0; i < n; i++) {
+ val = strtofloat (variables[i].name);
+
+ sdata->data[i] = g_array_append_val (sdata->data[i], val);
+
+ /* Update the minimal and maximal values so far. */
+ if (val < sdata->min_data[i])
+ sdata->min_data[i] = val;
+ if (val > sdata->max_data[i])
+ sdata->max_data[i] = val;
+ }
+
+ _free_variables(variables, n);
+ sdata->got_points++;
+ sdata->got_var = n;
+ break;
+ default:
+ if (priv->buf_count > 1) {
+ if (strstr (s, _("abort")))
+ sdata->state = STATE_ABORT;
+ schematic_log_append_error (priv->schematic, s);
+ }
+ }
+ }
+ priv->buf_count = 0;
+ }
+}
+
diff --git a/src/engines/gnucap.h b/src/engines/gnucap.h
new file mode 100644
index 0000000..d65a33b
--- /dev/null
+++ b/src/engines/gnucap.h
@@ -0,0 +1,59 @@
+/*
+ * engine.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 __GNUCAP_ENGINE
+#define __GNUCAP_ENGINE
+
+#include <gtk/gtk.h>
+#include "engine.h"
+
+#define OREGANO_TYPE_GNUCAP (oregano_gnucap_get_type ())
+#define OREGANO_GNUCAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_GNUCAP, OreganoGnuCap))
+#define OREGANO_GNUCAP_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), OREGANO_TYPE_GNUCAP, OreganoGnuCapClass))
+#define OREGANO_IS_GNUCAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_GNUCAP))
+#define OREGANO_IS_GNUCAP_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), OREGANO_TYPE_GNUCAP))
+#define OREGANO_GNUCAP_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), OREGANO_TYPE_GNUCAP, OreganoGnuCapClass))
+
+typedef struct _OreganoGnuCap OreganoGnuCap;
+typedef struct _OreganoGnuCapPriv OreganoGnuCapPriv;
+typedef struct _OreganoGnuCapClass OreganoGnuCapClass;
+
+struct _OreganoGnuCap {
+ GObject parent;
+
+ OreganoGnuCapPriv *priv;
+};
+
+struct _OreganoGnuCapClass {
+ GObjectClass parent;
+};
+
+GType oregano_gnucap_get_type (void);
+OreganoEngine *oregano_gnucap_new (Schematic *sm);
+
+#endif
+
diff --git a/src/engines/netlist.c b/src/engines/netlist.c
new file mode 100644
index 0000000..05c370f
--- /dev/null
+++ b/src/engines/netlist.c
@@ -0,0 +1,689 @@
+/*
+ * netlist.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 <stdio.h>
+#include <string.h>
+#include <string.h>
+#include "main.h"
+#include "schematic.h"
+#include "node-store.h"
+#include "node.h"
+#include "wire.h"
+#include "part-private.h"
+#include "part-property.h"
+#include "netlist.h"
+#include "errors.h"
+
+static void netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data);
+static void netlist_helper_wire_traverse (Wire *wire, NetlistData *data);
+static void netlist_helper_node_traverse (Node *node, NetlistData *data);
+static void netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data);
+static gboolean foreach_model_free (gpointer key, gpointer model, gpointer user_data);
+static gboolean foreach_model_save (gpointer key, gpointer model, gpointer user_data);
+static char *linebreak (char *str);
+static void nl_node_traverse (Node *node, GSList **lst);
+
+static void
+nl_wire_traverse (Wire *wire, GSList **lst)
+{
+ GSList *nodes;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ if (wire_is_visited (wire))
+ return;
+
+ wire_set_visited (wire, TRUE);
+
+ for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) {
+ GSList *pins;
+ Part *part;
+ Node *node = nodes->data;
+
+ for(pins=node->pins; pins; pins=pins->next) {
+ char *template, *tmp;
+ char **template_split;
+
+ part = PART (((Pin *)pins->data)->part);
+
+ tmp = part_get_property (part, "template");
+
+ if (!tmp) continue;
+
+ template = part_property_expand_macros (part, tmp);
+
+ template_split = g_strsplit (template, " ", 0);
+
+ (*lst) = g_slist_prepend (*lst, g_strdup (template_split[0]));
+
+ g_strfreev (template_split);
+ g_free (tmp);
+ g_free (template);
+ }
+
+ nl_node_traverse (node, lst);
+ }
+}
+
+static void
+nl_node_traverse (Node *node, GSList **lst)
+{
+ GSList *wires;
+
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (IS_NODE (node));
+
+ if (node_is_visited (node))
+ return;
+
+ node_set_visited (node, TRUE);
+
+ for (wires = node->wires; wires; wires = wires->next) {
+ Wire *wire = wires->data;
+ nl_wire_traverse (wire, lst);
+ }
+}
+
+static GSList *
+netlist_helper_get_clamp_parts (NodeStore *store, Node *node)
+{
+ GList *wires;
+ GSList *lst;
+ Wire *wire;
+ GSList *ret=NULL;
+
+ if (!node) return NULL;
+
+ node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL);
+ for (wires = store->wires; wires; wires = wires->next) {
+ wire = wires->data;
+ wire_set_visited (wire, FALSE);
+ }
+
+ lst = node->wires;
+ while (lst) {
+ nl_wire_traverse (lst->data, &ret);
+ lst = lst->next;
+ }
+
+ return ret;
+}
+
+void
+netlist_helper_init_data (NetlistData *data)
+{
+ data->pins = g_hash_table_new (g_direct_hash, g_direct_equal);
+ data->models = g_hash_table_new (g_str_hash, g_str_equal);
+ data->node_nr = 1;
+ data->gnd_list = NULL;
+ data->clamp_list = NULL;
+ data->mark_list = NULL;
+ data->node_and_number_list = NULL;
+}
+
+void
+netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data)
+{
+ Node *node = value;
+
+ node_set_visited (node, FALSE);
+}
+
+void
+netlist_helper_wire_traverse (Wire *wire, NetlistData *data)
+{
+ GSList *nodes;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ if (wire_is_visited (wire))
+ return;
+
+ wire_set_visited (wire, TRUE);
+
+ for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) {
+ Node *node = nodes->data;
+
+ netlist_helper_node_traverse (node, data);
+ }
+}
+
+void
+netlist_helper_node_traverse (Node *node, NetlistData *data)
+{
+ GSList *wires, *pins;
+ gchar *prop;
+ NodeAndNumber *nan;
+
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (IS_NODE (node));
+
+ if (node_is_visited (node))
+ return;
+
+ node_set_visited (node, TRUE);
+
+ /* Keep track of netlist nr <---> Node. */
+ nan = g_new0 (NodeAndNumber, 1);
+ nan->node_nr = data->node_nr;
+ nan->node = node;
+ data->node_and_number_list = g_list_prepend (data->node_and_number_list,
+ nan);
+
+ /* Traverse the pins at this node. */
+ for (pins = node->pins; pins; pins = pins->next) {
+ Pin *pin = pins->data;
+
+ /* First see if the pin belongs to an "internal", special part. */
+ prop = part_get_property (pin->part, "internal");
+ if (prop) {
+ if (!g_strcasecmp (prop, "marker")) {
+ Marker *marker;
+ gchar *name, *value;
+
+ name = part_get_property (pin->part, "name");
+
+ if (!name) {
+ g_free (prop);
+ continue;
+ }
+
+ value = part_property_expand_macros (pin->part, name);
+ g_free (name);
+
+ if (!value)
+ continue;
+
+ marker = g_new0 (Marker, 1);
+ marker->node_nr = data->node_nr;
+ marker->name = value;
+ data->mark_list = g_slist_prepend (data->mark_list, marker);
+ } else if (!g_strcasecmp (prop, "ground")) {
+ data->gnd_list = g_slist_prepend (data->gnd_list, GINT_TO_POINTER (data->node_nr));
+ } else if (!g_strcasecmp (prop, "point")) {
+ data->clamp_list = g_slist_prepend (data->clamp_list, GINT_TO_POINTER (data->node_nr));
+ } else if (!g_strncasecmp (prop, "jumper", 5)) {
+ /* Either jumper2 or jumper4. */
+ Node *opposite_node;
+ Pin opposite_pin;
+ gint pin_nr, opposite_pin_nr;
+ SheetPos pos;
+ Pin *jumper_pins;
+ gint num_pins;
+
+ opposite_pin_nr = -1;
+ num_pins = part_get_num_pins (pin->part);
+ jumper_pins = part_get_pins (pin->part);
+ for (pin_nr = 0; pin_nr < num_pins; pin_nr++) {
+ if (&jumper_pins[pin_nr] == pin) {
+ opposite_pin_nr = pin_nr;
+ break;
+ }
+ }
+
+ switch (opposite_pin_nr) {
+ case 0:
+ opposite_pin_nr = 1;
+ break;
+ case 1:
+ opposite_pin_nr = 0;
+ break;
+ case 2:
+ opposite_pin_nr = 3;
+ break;
+ case 3:
+ opposite_pin_nr = 2;
+ break;
+ default:
+ g_assert (TRUE);
+ break;
+ }
+
+ opposite_pin = jumper_pins[opposite_pin_nr];
+
+ item_data_get_pos (ITEM_DATA (pin->part), &pos);
+ pos.x += opposite_pin.offset.x;
+ pos.y += opposite_pin.offset.y;
+
+ opposite_node = node_store_get_node (data->store, pos);
+
+#if 0
+ if (node_is_visited (opposite_node)) {
+ GList *list;
+
+ /* Set the node name on the current node to the same as the
+ already visited node. */
+ for (list = data->node_and_number_list; list; list = list->next) {
+ NodeAndNumber *opposite_nan = list->data;
+
+ if (opposite_nan->node == opposite_node)
+ nan->node_nr = opposite_nan->node_nr;
+ }
+ }
+#endif
+ netlist_helper_node_traverse (opposite_node, data);
+ }
+
+ if (g_strcasecmp (prop, "point")) {
+ g_free (prop);
+ continue;
+ }
+ g_free(prop);
+ }
+
+ /* Keep track of models to include. Needs to be freed when the
+ * hash table is no longer needed.
+ */
+ prop = part_get_property (pin->part, "model");
+ if (prop) {
+ if (!g_hash_table_lookup (data->models, prop))
+ g_hash_table_insert (data->models, prop, NULL);
+ }
+
+ g_hash_table_insert (data->pins, pin, GINT_TO_POINTER (data->node_nr));
+ }
+
+ /* Traverse the wires at this node. */
+ for (wires = node->wires; wires; wires = wires->next) {
+ Wire *wire = wires->data;
+ netlist_helper_wire_traverse (wire, data);
+ }
+}
+
+void
+netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data)
+{
+ Node *node = value;
+
+ /* Only visit nodes that are not already visited. */
+ if (node_is_visited (node))
+ return;
+
+ netlist_helper_node_traverse (node, data);
+
+ data->node_nr++;
+}
+
+gint
+compare_marker (gconstpointer a, gconstpointer b)
+{
+ const Marker *ma;
+ gint node_nr;
+
+ ma = a;
+ node_nr = GPOINTER_TO_INT (b);
+
+ if (ma->node_nr == node_nr)
+ return 0;
+ else
+ return 1;
+}
+
+gboolean
+foreach_model_save (gpointer key, gpointer model, gpointer user_data)
+{
+ GList **l = (GList **)user_data;
+
+ (*l) = g_list_prepend (*l, g_strdup ((gchar *)key));
+
+ return TRUE;
+}
+
+gboolean
+foreach_model_free (gpointer key, gpointer model, gpointer user_data)
+{
+ g_free (key);
+
+ return FALSE;
+}
+
+char *
+linebreak (char *str)
+{
+ char **split, *tmp;
+ GString *out;
+ int i;
+
+ split = g_strsplit (str, "\\", 0);
+
+ out = g_string_new ("");
+
+ i = 0;
+ while (split[i] != NULL) {
+ if (split[i][0] == 'n') {
+ if (strlen (split[i]) > 1) {
+ out = g_string_append_c (out, '\n');
+ out = g_string_append (out, split[i] + 1);
+ }
+ } else {
+ out = g_string_append (out, split[i]);
+ }
+
+ i++;
+ }
+
+ g_strfreev (split);
+ tmp = out->str;
+ g_string_free (out, FALSE); /* Don't free the string data. */
+ return tmp;
+}
+
+void
+netlist_helper_create (Schematic *sm, Netlist *out, GError **error)
+{
+ NetlistData data;
+ GList *parts, *wires, *list;
+ Part *part;
+ gint pin_nr, num_pins, num_nodes, num_gnd_nodes, i, j, num_clamps;
+ Pin *pins;
+ gchar *template, **template_split;
+ NodeStore *store;
+ gchar **node2real;
+
+ schematic_log_clear (sm);
+
+ out->models = NULL;
+ out->title = schematic_get_filename (sm);
+ out->settings = schematic_get_sim_settings (sm);
+ store = schematic_get_store (sm);
+ out->store = store;
+ node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL);
+ for (wires = store->wires; wires; wires = wires->next) {
+ Wire *wire = wires->data;
+ wire_set_visited (wire, FALSE);
+ }
+
+ netlist_helper_init_data (&data);
+ data.store = store;
+
+ node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_traverse, &data);
+
+ num_gnd_nodes = g_slist_length (data.gnd_list);
+ num_clamps = g_slist_length (data.clamp_list);
+
+ /* Check if there is a Ground node */
+ if (num_gnd_nodes == 0) {
+ schematic_log_append (sm, _("No ground node. Aborting.\n"));
+ schematic_log_show (sm);
+ g_set_error (error,
+ OREGANO_ERROR,
+ OREGANO_SIMULATE_ERROR_NO_GND,
+ _("Possibly due to a faulty circuit schematic. Please check that\n"
+ "you have a ground node and try again.")
+ );
+
+ /* FIXME!!! */
+ goto bail_out;
+ }
+
+ if (num_clamps == 0) {
+ schematic_log_append (sm, _("No test clamps found. Aborting.\n"));
+ schematic_log_show (sm);
+ g_set_error (error,
+ OREGANO_ERROR,
+ OREGANO_SIMULATE_ERROR_NO_CLAMP,
+ _("Possibly due to a faulty circuit schematic. Please check that\n"
+ "you have one o more test clamps and try again.")
+ );
+
+ goto bail_out;
+ }
+
+ num_nodes = data.node_nr - 1;
+
+ /*
+ * Build an array for going from node nr to "real node nr",
+ * where gnd nodes are nr 0 and the rest of the nodes are
+ * 1, 2, 3, ...
+ */
+ node2real = g_new0 (gchar*, num_nodes + 1);
+
+ for (i = 1, j = 1; i <= num_nodes; i++) {
+ GSList *mlist;
+
+ if (g_slist_find (data.gnd_list, GINT_TO_POINTER (i)))
+ node2real[i] = g_strdup ("0");
+ else if ((mlist = g_slist_find_custom (data.mark_list,
+ GINT_TO_POINTER (i),
+ compare_marker))) {
+ Marker *marker = mlist->data;
+ node2real[i] = g_strdup (marker->name);
+ }
+ else node2real[i] = g_strdup_printf ("%d", j++);
+ }
+
+
+ /*
+ * Fill in the netlist node names for all the used nodes.
+ */
+ for (list = data.node_and_number_list; list; list = list->next) {
+ NodeAndNumber *nan = list->data;
+ if (nan->node->netlist_node_name != NULL)
+ g_free (nan->node->netlist_node_name);
+ if (nan->node_nr != 0)
+ nan->node->netlist_node_name = g_strdup (node2real[nan->node_nr]);
+ }
+
+ /* Initialize out->template */
+ out->template = g_string_new("");
+ for (parts = store->parts; parts; parts = parts->next) {
+ gchar *tmp, *internal;
+ GString *str;
+
+ part = parts->data;
+ internal = part_get_property (part, "internal");
+ if (internal != NULL) {
+ gint node_nr;
+ Pin *pins;
+ if (g_strcasecmp (internal, "point") != 0) {
+ g_free (internal);
+ continue;
+ }
+
+ /* Got a clamp!, set node number */
+ pins = part_get_pins (part);
+ node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[0]));
+ if (!node_nr) {
+ g_warning ("Couln't find part, pin_nr %d.", 0);
+ } else {
+ gchar *tmp;
+ tmp = node2real[node_nr];
+
+ /* need to substrac 1, netlist starts in 0, and node_nr in 1 */
+ pins[0].node_nr = atoi(node2real[node_nr]);
+ }
+ g_free (internal);
+ continue;
+ }
+
+ tmp = part_get_property (part, "template");
+ if (!tmp) {
+ continue;
+ }
+
+ template = part_property_expand_macros (part, tmp);
+ //g_print ("Template: '%s'\n" "macro : '%s'\n", tmp, template);
+ if (tmp != NULL) g_free (tmp);
+
+ tmp = linebreak (template);
+
+ if (template != NULL) g_free (template);
+ template = tmp;
+
+ num_pins = part_get_num_pins (part);
+ pins = part_get_pins (part);
+
+ template_split = g_strsplit (template, " ", 0);
+ if (template != NULL) g_free (template);
+ template = NULL;
+
+ str = g_string_new ("");
+
+ i = 0;
+ while (template_split[i] != NULL && template_split[i][0] != '%') {
+ g_string_append (str, template_split[i++]);
+ g_string_append_c (str , ' ');
+ //g_print ("str: %s\n", str->str);
+ }
+
+ //g_print ("Reading %d pins.\n)", num_pins);
+
+ for (pin_nr = 0; pin_nr < num_pins; pin_nr++) {
+ gint node_nr;
+ node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[pin_nr]));
+ if (!node_nr) {
+ g_warning ("Couldn't find part, pin_nr %d.", pin_nr);
+ } else {
+ gchar *tmp;
+ tmp = node2real[node_nr];
+
+ /* need to substrac 1, netlist starts in 0, and node_nr in 1 */
+ pins[pin_nr].node_nr = atoi(node2real[node_nr]);
+ g_string_append (str, tmp);
+ g_string_append_c (str, ' ');
+ /*g_print ("str: %s\n", str->str);*/
+ i++;
+ }
+
+ while (template_split[i] != NULL) {
+ if (template_split[i][0] == '%')
+ break;
+
+ g_string_append (str, template_split[i]);
+ g_string_append_c (str, ' ');
+ //g_print ("str: %s\n", str->str);
+ i++;
+ }
+ }
+
+ //g_print ("Done with pins, i = %d\n", i);
+
+ while (template_split[i] != NULL) {
+ g_string_append (str, template_split[i]);
+ g_string_append_c (str, ' ');
+ //g_print ("str: %s\n", str->str);
+ i++;
+ }
+
+ g_strfreev (template_split);
+ //g_print ("str: %s\n", str->str);
+ out->template = g_string_append(out->template, str->str);
+ out->template = g_string_append_c(out->template, '\n');
+ g_string_free (str, TRUE);
+ }
+
+ for (i = 0; i < num_nodes + 1; i++) {
+ g_free (node2real[i]);
+ }
+ g_free (node2real);
+
+ g_hash_table_foreach (data.models, (GHFunc)foreach_model_save, &out->models);
+
+ return;
+
+bail_out:
+ g_hash_table_foreach (data.models, (GHFunc)foreach_model_free, NULL);
+ g_hash_table_destroy (data.models);
+ g_hash_table_destroy (data.pins);
+ for (list = data.node_and_number_list; list; list = list->next) {
+ NodeAndNumber *nan = list->data;
+ if (nan != NULL) g_free (nan);
+ }
+ g_list_free (data.node_and_number_list);
+}
+
+char *
+netlist_helper_create_analisys_string (NodeStore *store, gboolean do_ac)
+{
+ GList *parts;
+ GList *p;
+ gchar *prop, *type, *ac;
+ parts = node_store_get_parts (store);
+ GString *out;
+ gchar *ret;
+
+ out = g_string_new ("");
+
+ for(p=parts; p; p = p->next) {
+ prop = part_get_property (p->data, "internal");
+ if (prop) {
+ if (!g_strcasecmp (prop, "point")) {
+ Pin *pins = part_get_pins (p->data);
+ g_free (prop);
+
+ prop = part_get_property (p->data, "type");
+
+ if (!g_strcasecmp (prop, "v")) {
+ if (!do_ac) {
+ g_string_append_printf (out, " %s(%d)", prop, pins[0].node_nr);
+ } else {
+ gchar *ac_type, *ac_db;
+ ac_type = part_get_property (p->data, "ac_type");
+ ac_db = part_get_property (p->data, "ac_db");
+
+ if (!g_strcasecmp (ac_db, "true"))
+ g_string_append_printf (out, " %s%sdb(%d)", prop, ac_type, pins[0].node_nr);
+ else
+ g_string_append_printf (out, " %s%s(%d)", prop, ac_type, pins[0].node_nr);
+ }
+ } else {
+ Node *node;
+ SheetPos lookup_key;
+ SheetPos part_pos;
+
+ item_data_get_pos (ITEM_DATA (p->data), &part_pos);
+
+ lookup_key.x = part_pos.x + pins[0].offset.x;
+ lookup_key.y = part_pos.y + pins[0].offset.y;
+
+ node = node_store_get_or_create_node (store, lookup_key);
+
+ if (node) {
+ GSList *lst, *it;
+ it = lst = netlist_helper_get_clamp_parts (store, node);
+ while (it) {
+ g_string_append_printf (out, " i(%s)", (char *)it->data);
+ it = it->next;
+ }
+ g_slist_free (lst);
+ }
+ }
+ }
+ }
+ }
+
+ ret = out->str;
+ g_string_free (out, FALSE);
+ return ret;
+}
+
diff --git a/src/engines/netlist.h b/src/engines/netlist.h
new file mode 100644
index 0000000..9d9ca0f
--- /dev/null
+++ b/src/engines/netlist.h
@@ -0,0 +1,71 @@
+/*
+ * netlist.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 __NETLIST_H
+#define __NETLIST_H
+
+#include <glib.h>
+#include "schematic.h"
+#include "sim-settings.h"
+
+typedef struct {
+ gint node_nr; ///< Node number
+ GHashTable *pins;
+ GHashTable *models;
+ GSList *gnd_list; ///< Ground parts on the schematic
+ GSList *clamp_list; ///< Test clamps on the schematic
+ GSList *mark_list;
+ GList *node_and_number_list;
+ NodeStore *store;
+} NetlistData;
+
+typedef struct {
+ gchar *cmd;
+ gchar *title;
+ GString *template;
+ SimSettings *settings;
+ NodeStore *store;
+ GList *models;
+} Netlist;
+
+typedef struct {
+ gint node_nr;
+ gchar *name;
+} Marker;
+
+typedef struct {
+ gint node_nr;
+ Node *node;
+} NodeAndNumber;
+
+void netlist_helper_init_data (NetlistData *data);
+void netlist_helper_create (Schematic *sm, Netlist *out, GError **error);
+char *netlist_helper_create_analisys_string (NodeStore *store, gboolean do_ac);
+
+#endif
diff --git a/src/engines/ngspice.c b/src/engines/ngspice.c
new file mode 100644
index 0000000..98c2372
--- /dev/null
+++ b/src/engines/ngspice.c
@@ -0,0 +1,633 @@
+/*
+ * ngspice.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.
+ */
+
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include "ngspice.h"
+#include "netlist.h"
+
+// TODO Move analisys data and result to another file
+#include "simulation.h"
+
+/* Parser STATUS */
+struct _OreganoNgSpicePriv {
+ GPid child_pid;
+ Schematic *schematic;
+
+ gboolean aborted;
+
+ GList *analysis;
+ gint num_analysis;
+ SimulationData *current;
+ double progress;
+ gboolean char_last_newline;
+ guint status;
+ guint buf_count;
+ gchar buf[256]; // FIXME later
+};
+
+static void ngspice_instance_init (GTypeInstance *instance, gpointer g_class);
+static void ngspice_interface_init (gpointer g_iface, gpointer iface_data);
+static gboolean ngspice_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoNgSpice *ngspice);
+static void ngspice_parse (FILE *, OreganoNgSpice *ngspice);
+
+GType
+oregano_ngspice_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (OreganoNgSpiceClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (OreganoNgSpice),
+ 0, /* n_preallocs */
+ ngspice_instance_init /* instance_init */
+ };
+
+ static const GInterfaceInfo ngspice_info = {
+ (GInterfaceInitFunc) ngspice_interface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT, "OreganoNgSpice", &info, 0);
+ g_type_add_interface_static (type, OREGANO_TYPE_ENGINE, &ngspice_info);
+ }
+ return type;
+}
+
+static gboolean
+ngspice_has_warnings (OreganoEngine *self)
+{
+ return FALSE;
+}
+
+static gboolean
+ngspice_is_available (OreganoEngine *self)
+{
+ gchar *exe;
+ exe = g_find_program_in_path ("ngspice");
+
+ if (!exe) return FALSE; // ngspice not found
+
+ g_free (exe);
+ return TRUE;
+}
+
+static void
+ngspice_generate_netlist (OreganoEngine *engine, const gchar *filename, GError **error)
+{
+ OreganoNgSpice *ngspice;
+ Netlist output;
+ GList *list;
+ SimOption *so;
+ GError *local_error = NULL;
+ FILE *file;
+
+ ngspice = OREGANO_NGSPICE (engine);
+
+ netlist_helper_create (ngspice->priv->schematic, &output, &local_error);
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ return;
+ }
+
+ file = fopen (filename, "w");
+ if (!file) {
+ g_print ("Cannot create file %s\n", filename);
+ return;
+ }
+
+ /* Prints title */
+ fputs ("* ",file);
+ fputs (output.title ? output.title : "Title: <unset>", file);
+ fputs ("\n"
+ "*----------------------------------------------"
+ "\n"
+ "*\tSPICE 3 - NETLIST"
+ "\n", file);
+
+ /* Prints Options */
+ fputs (".options\n", file);
+
+ list = sim_settings_get_options (output.settings);
+ while (list) {
+ so = list->data;
+ if (so->value)
+ if (strlen(so->value) > 0)
+ fprintf (file,"+ %s=%s\n", so->name, so->value);
+ list = list->next;
+ }
+ fputc ('\n',file);
+
+ /* Include of subckt models */
+ fputs ("*------------- Models -------------------------\n",file);
+ list = output.models;
+ while (list) {
+ gchar *model;
+ model = (gchar *)list->data;
+ fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model);
+ list = list->next;
+ }
+
+ /* Prints template parts */
+ fputs ("\n*----------------------------------------------\n\n",file);
+ fputs (output.template->str, file);
+ fputs ("\n*----------------------------------------------\n\n",file);
+
+ /* Prints Transient Analisis */
+ if (sim_settings_get_trans (output.settings)) {
+ gdouble st = 0;
+ if (sim_settings_get_trans_step_enable (output.settings))
+ st = sim_settings_get_trans_step (output.settings);
+ else
+ st = (sim_settings_get_trans_stop (output.settings) -
+ sim_settings_get_trans_start (output.settings)) / 50;
+
+ fprintf (file, ".tran %g %g %g", st,
+ sim_settings_get_trans_stop (output.settings),
+ sim_settings_get_trans_start (output.settings));
+ if (sim_settings_get_trans_init_cond (output.settings)) {
+ fputs(" UIC\n", file);
+ } else {
+ fputs("\n", file);
+ }
+ }
+
+ /* Print dc Analysis */
+ if (sim_settings_get_dc (output.settings)) {
+ fputs(".dc ",file);
+ if (sim_settings_get_dc_vsrc (output.settings, 0)) {
+ fprintf (file, "%s %g %g %g",
+ sim_settings_get_dc_vsrc (output.settings, 0),
+ sim_settings_get_dc_start (output.settings, 0),
+ sim_settings_get_dc_stop (output.settings, 0),
+ sim_settings_get_dc_step (output.settings, 0));
+ }
+ if (sim_settings_get_dc_vsrc (output.settings, 1)) {
+ fprintf (file, "%s %g %g %g",
+ sim_settings_get_dc_vsrc (output.settings, 1),
+ sim_settings_get_dc_start (output.settings, 1),
+ sim_settings_get_dc_stop (output.settings, 1),
+ sim_settings_get_dc_step (output.settings, 1));
+ }
+ }
+
+ /* Prints ac Analysis*/
+ if (sim_settings_get_ac (output.settings)) {
+ fprintf (file, ".ac %s %d %g %g\n",
+ sim_settings_get_ac_type (output.settings),
+ sim_settings_get_ac_npoints (output.settings),
+ sim_settings_get_ac_start (output.settings),
+ sim_settings_get_ac_stop (output.settings));
+ }
+
+ /* Debug op analysis. */
+ fputs (".op\n", file);
+ fputs ("\n.END\n", file);
+ fclose (file);
+}
+
+static void
+ngspice_progress (OreganoEngine *self, double *d)
+{
+ (*d) = OREGANO_NGSPICE (self)->priv->progress;
+}
+
+static void
+ngspice_stop (OreganoEngine *self)
+{
+ OreganoNgSpice *ngspice = OREGANO_NGSPICE (self);
+ g_spawn_close_pid (ngspice->priv->child_pid);
+}
+
+static void
+ngspice_watch_cb (GPid pid, gint status, OreganoNgSpice *ngspice)
+{
+ /* check for status, see man waitpid(2) */
+ if (WIFEXITED (status)) {
+ FILE *fp;
+
+ g_spawn_close_pid (ngspice->priv->child_pid);
+
+ /* Parse data */
+ if ((fp = fopen ("/tmp/netlist.raw", "r")) != NULL) {
+ g_print ("File Open\n");
+ while (!feof (fp))
+ ngspice_parse (fp, ngspice);
+ }
+
+ ngspice->priv->current = NULL;
+
+ if (ngspice->priv->num_analysis == 0) {
+ schematic_log_append_error (ngspice->priv->schematic, _("### Too few or none analysis found ###\n"));
+ ngspice->priv->aborted = TRUE;
+ g_signal_emit_by_name (G_OBJECT (ngspice), "aborted");
+ } else
+ g_signal_emit_by_name (G_OBJECT (ngspice), "done");
+ }
+}
+
+static void
+ngspice_start (OreganoEngine *self)
+{
+ OreganoNgSpice *ngspice;
+ GError *error = NULL;
+ char *argv[] = {"ngspice", "-r", "/tmp/netlist.raw", "-b", "/tmp/netlist.tmp", NULL};
+
+ ngspice = OREGANO_NGSPICE (self);
+ oregano_engine_generate_netlist (self, "/tmp/netlist.tmp", &error);
+ if (error != NULL) {
+ ngspice->priv->aborted = TRUE;
+ schematic_log_append_error (ngspice->priv->schematic, error->message);
+ g_signal_emit_by_name (G_OBJECT (ngspice), "aborted");
+ g_error_free (error);
+ return;
+ }
+
+ error = NULL;
+ if (g_spawn_async_with_pipes (
+ NULL, /* Working directory */
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &ngspice->priv->child_pid,
+ NULL, /* STDIN */
+ NULL, /* STDOUT */
+ NULL, /* STDERR*/
+ &error
+ )) {
+ /* Add a watch for process status */
+ g_child_watch_add (ngspice->priv->child_pid, (GChildWatchFunc)ngspice_watch_cb, ngspice);
+ } else {
+ ngspice->priv->aborted = TRUE;
+ schematic_log_append_error (ngspice->priv->schematic, _("Unable to execute NgSpice."));
+ g_signal_emit_by_name (G_OBJECT (ngspice), "aborted");
+ }
+}
+
+static GList*
+ngspice_get_results (OreganoEngine *self)
+{
+ return OREGANO_NGSPICE (self)->priv->analysis;
+}
+
+const gchar*
+ngspice_get_operation (OreganoEngine *self)
+{
+ OreganoNgSpicePriv *priv = OREGANO_NGSPICE (self)->priv;
+
+ if (priv->current == NULL)
+ return _("Waiting NgSpice backend");
+
+ return oregano_engine_get_analysis_name (priv->current);
+}
+
+static void
+ngspice_interface_init (gpointer g_iface, gpointer iface_data)
+{
+ OreganoEngineClass *klass = (OreganoEngineClass *)g_iface;
+ klass->start = ngspice_start;
+ klass->stop = ngspice_stop;
+ klass->progress = ngspice_progress;
+ klass->get_netlist = ngspice_generate_netlist;
+ klass->has_warnings = ngspice_has_warnings;
+ klass->get_results = ngspice_get_results;
+ klass->get_operation = ngspice_get_operation;
+ klass->is_available = ngspice_is_available;
+}
+
+static void
+ngspice_instance_init (GTypeInstance *instance, gpointer g_class)
+{
+ OreganoNgSpice *self = OREGANO_NGSPICE (instance);
+
+ self->priv = g_new0 (OreganoNgSpicePriv, 1);
+}
+
+OreganoEngine*
+oregano_ngspice_new (Schematic *sc)
+{
+ OreganoNgSpice *ngspice;
+
+ ngspice = OREGANO_NGSPICE (g_object_new (OREGANO_TYPE_NGSPICE, NULL));
+ ngspice->priv->schematic = sc;
+
+ return OREGANO_ENGINE (ngspice);
+}
+
+/* Parser stuff */
+
+/* By now, the parser read the STDOUt just like in GnuCap. In a near future I'll
+ * to use the raw outout feature of NgSpice wich will improve the parser
+ */
+
+typedef enum {
+ STATE_IDLE,
+ IN_VARIABLES,
+ IN_VALUES,
+ STATE_ABORT
+} ParseDataState;
+
+#define SP_TITLE "Title"
+#define SP_DATE "Date:"
+#define SP_PLOT_NAME "Plotname:"
+#define SP_FLAGS "Flags:"
+#define SP_N_VAR "No. Variables:"
+#define SP_N_POINTS "No. Points:"
+#define SP_COMMAND "Command:"
+#define SP_VARIABLES "Variables:"
+#define SP_BINARY "Binary:"
+#define SP_VALUES "Values:"
+#define IS_THIS_ITEM(str,item) (!strncmp(str,item,strlen(item)))
+
+static gchar *analysis_names[] = {
+ N_("Operating Point") ,
+ N_("Transient Analysis") ,
+ N_("DC transfer characteristic") ,
+ N_("AC Analysis") ,
+ N_("Transfer Function") ,
+ N_("Distortion Analysis") ,
+ N_("Noise Analysis") ,
+ N_("Pole-Zero Analysis") ,
+ N_("Sensitivity Analysis") ,
+ N_("Unknown Analysis") ,
+ NULL
+};
+
+#define NG_DEBUG(s) if (0) g_print ("%s\n", s)
+
+static void
+ngspice_parse (FILE *fp, OreganoNgSpice *ngspice)
+{
+ static SimulationData *sdata = NULL;
+ static char buf[1024];
+ static int len;
+ static is_complex = FALSE;
+ SimSettings *sim_settings;
+ gint status, iter;
+ gdouble val, np1, np2;
+ gchar **tmp = NULL;
+ gchar *send;
+ int i, c;
+
+ sim_settings = (SimSettings *)schematic_get_sim_settings (ngspice->priv->schematic);
+
+ if (!sdata || sdata->state != IN_VALUES) {
+ /* Read a line */
+ len = fscanf (fp, "%[^\n]", buf);
+ fgetc (fp);
+ if (len == 0) return;
+ NG_DEBUG (g_strdup_printf ("(%s)", buf));
+ } else {
+ buf[0] = '\0';
+ len = 0;
+ }
+
+ /* We are getting the simulation title */
+ if (IS_THIS_ITEM (buf, SP_TITLE)) {
+ sdata = SIM_DATA (g_new0 (Analysis, 1));
+ ngspice->priv->analysis = g_list_append (ngspice->priv->analysis, sdata);
+ ngspice->priv->num_analysis++;
+ NG_DEBUG ("Nuevo Analisis");
+ } else if (IS_THIS_ITEM (buf, SP_DATE)) {
+ sdata->state = STATE_IDLE;
+ } else if (IS_THIS_ITEM (buf,SP_PLOT_NAME)) {
+ gint i;
+ gchar *analysis = buf+strlen(SP_PLOT_NAME)+1;
+
+ NG_DEBUG ("Analisis Type");
+ sdata->state = STATE_IDLE;
+ sdata->type = ANALYSIS_UNKNOWN;
+ for (i = 0; analysis_names[i]; i++)
+ if (IS_THIS_ITEM (analysis, analysis_names[i])) {
+ sdata->type = i;
+ break;
+ }
+
+ switch ( sdata->type ) {
+ case TRANSIENT:
+ ANALYSIS(sdata)->transient.sim_length =
+ sim_settings_get_trans_stop (sim_settings) -
+ sim_settings_get_trans_start (sim_settings);
+ ANALYSIS(sdata)->transient.step_size =
+ sim_settings_get_trans_step (sim_settings);
+ break;
+ case AC:
+ ANALYSIS(sdata)->ac.start = sim_settings_get_ac_start (sim_settings);
+ ANALYSIS(sdata)->ac.stop = sim_settings_get_ac_stop (sim_settings);
+ ANALYSIS(sdata)->ac.sim_length = sim_settings_get_ac_npoints (sim_settings);
+ break;
+ case OP_POINT:
+ case DC_TRANSFER:
+ np1 = np2 = 1.;
+ ANALYSIS(sdata)->dc.start1 = sim_settings_get_dc_start (sim_settings, 0);
+ ANALYSIS(sdata)->dc.stop1 = sim_settings_get_dc_stop (sim_settings, 0);
+ ANALYSIS(sdata)->dc.step1 = sim_settings_get_dc_step (sim_settings, 0);
+ ANALYSIS(sdata)->dc.start2 = sim_settings_get_dc_start (sim_settings, 1);
+ ANALYSIS(sdata)->dc.stop2 = sim_settings_get_dc_stop (sim_settings, 1);
+ ANALYSIS(sdata)->dc.step2 = sim_settings_get_dc_step (sim_settings, 1);
+
+ np1 = (ANALYSIS(sdata)->dc.stop1-ANALYSIS(sdata)->dc.start1) / ANALYSIS(sdata)->dc.step1;
+ if (ANALYSIS(sdata)->dc.step2 != 0.) {
+ np2 = (ANALYSIS(sdata)->dc.stop2-ANALYSIS(sdata)->dc.start2) / ANALYSIS(sdata)->dc.step2;
+ }
+ ANALYSIS(sdata)->dc.sim_length = np1 * np2;
+ break;
+ case TRANSFER:
+ case DISTORTION:
+ case NOISE:
+ case POLE_ZERO:
+ case SENSITIVITY:
+ break;
+ case ANALYSIS_UNKNOWN:
+ g_error ("Unknown analysis: %s", analysis);
+ break;
+ }
+ ngspice->priv->current = sdata;
+ } else if (IS_THIS_ITEM(buf, SP_FLAGS) ) {
+ char *f = buf + strlen (SP_FLAGS) + 1;
+ if (strncmp (f, "complex", 7) == 0)
+ is_complex = TRUE;
+ else
+ is_complex = FALSE;
+ } else if (IS_THIS_ITEM (buf, SP_COMMAND)) {
+ /* pass */
+ } else if (IS_THIS_ITEM (buf, SP_N_VAR)) {
+ gint i, n = atoi (buf + strlen (SP_N_VAR));
+
+ NG_DEBUG (g_strdup_printf ("NVAR %d", n));
+ sdata->state = STATE_IDLE;
+ sdata->n_variables = n;
+ sdata->got_points = 0;
+ sdata->got_var = 0;
+ sdata->var_names = (char**) g_new0 (gpointer, n);
+ sdata->var_units = (char**) g_new0 (gpointer, n);
+ sdata->data = (GArray**) g_new0 (gpointer, n);
+ for (i = 0; i < n; i++)
+ sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double));
+ sdata->min_data = g_new (double, n);
+ sdata->max_data = g_new (double, n);
+ for (i = 0; i < n; i++) {
+ sdata->min_data[i] = G_MAXDOUBLE;
+ sdata->max_data[i] = -G_MAXDOUBLE;
+ }
+ } else if (IS_THIS_ITEM (buf, SP_N_POINTS)) {
+ sdata->state = STATE_IDLE;
+ sdata->n_points = atoi (buf + strlen (SP_N_POINTS));
+ NG_DEBUG (g_strdup_printf ("NPOINTS %d", sdata->n_points));
+ } else if (IS_THIS_ITEM (buf, SP_VARIABLES)) {
+ NG_DEBUG ("In variables");
+ sdata->state = IN_VARIABLES;
+ } else if (IS_THIS_ITEM (buf, SP_BINARY)) {
+ NG_DEBUG ("Data Bynari");
+ sdata->state = IN_VALUES;
+ sdata->binary = TRUE;
+ sdata->got_var = 0;
+ sdata->got_points = 0;
+ } else if (IS_THIS_ITEM (buf, SP_VALUES)) {
+ sdata->state = IN_VALUES;
+ sdata->binary = FALSE;
+ sdata->got_var = 0;
+ sdata->got_points = 0;
+ } else {
+ if (ngspice->priv->analysis == NULL) {
+ if (len > 1)
+ schematic_log_append (ngspice->priv->schematic, buf);
+ return;
+ }
+
+ switch (sdata->state) {
+ case IN_VARIABLES:
+ tmp = g_strsplit (buf, "\t", 0);
+ sdata->var_names[sdata->got_var] = g_strdup (tmp[2]);
+ sdata->var_units[sdata->got_var] = g_strdup (tmp[3]);
+ send = strchr (sdata->var_units[sdata->got_var], '\n');
+ if (send)
+ *send = 0;
+ g_strfreev (tmp);
+
+ if ((sdata->got_var + 1) < sdata->n_variables)
+ sdata->got_var++;
+ break;
+ case IN_VALUES:
+ if (sdata->binary) {
+ int i, j;
+ double d, dimg;
+ NG_DEBUG ("Reading Binary");
+ for (i=0; i<sdata->n_points; i++) {
+ for (j=0; j<sdata->n_variables; j++) {
+ /* TODO : This show always the real part only. We need to detect
+ * the probe settings for this node, and show the correct
+ * value : real, imaginary, module or phase, of the complex number.
+ */
+ fread (&d, sizeof (double), 1, fp);
+ if (is_complex)
+ fread (&dimg, sizeof (double), 1, fp);
+ if (j == 0) {
+ switch (sdata->type) {
+ case TRANSIENT:
+ ngspice->priv->progress = d / ANALYSIS(sdata)->transient.sim_length;
+ break;
+ case AC:
+ ngspice->priv->progress = (d - ANALYSIS(sdata)->ac.start) / ANALYSIS(sdata)->ac.sim_length;
+ break;
+ case DC_TRANSFER:
+ ngspice->priv->progress = ((gdouble) iter) / ANALYSIS(sdata)->ac.sim_length;
+ }
+ if (ngspice->priv->progress > 1.0)
+ ngspice->priv->progress = 1.0;
+ if (ngspice->priv->progress < 0.0)
+ ngspice->priv->progress = 0.0;
+ g_main_context_iteration (NULL, FALSE);
+ }
+ sdata->data[j] = g_array_append_val (sdata->data[j], d);
+
+ /* Update the minimal and maximal values so far. */
+ if (d < sdata->min_data[j])
+ sdata->min_data[j] = d;
+ if (d > sdata->max_data[j])
+ sdata->max_data[j] = d;
+ }
+ }
+ sdata->state = STATE_IDLE;
+ NG_DEBUG ("Reading Binary Done");
+ } else {
+ if (sdata->got_var)
+ sscanf(buf, "\t%lf", &val);
+ else
+ sscanf(buf, "%d\t\t%lf", &iter, &val);
+ if (sdata->got_var == 0) {
+ switch (sdata->type) {
+ case TRANSIENT:
+ ngspice->priv->progress = val / ANALYSIS(sdata)->transient.sim_length;
+ break;
+ case AC:
+ ngspice->priv->progress = (val - ANALYSIS(sdata)->ac.start) / ANALYSIS(sdata)->ac.sim_length;
+ break;
+ case DC_TRANSFER:
+ ngspice->priv->progress = ((gdouble) iter) / ANALYSIS(sdata)->ac.sim_length;
+ }
+ if (ngspice->priv->progress > 1.0)
+ ngspice->priv->progress = 1.0;
+ }
+
+ sdata->data[sdata->got_var] = g_array_append_val (sdata->data[sdata->got_var], val);
+
+ /* Update the minimal and maximal values so far. */
+ if (val < sdata->min_data[sdata->got_var])
+ sdata->min_data[sdata->got_var] = val;
+ if (val > sdata->max_data[sdata->got_var])
+ sdata->max_data[sdata->got_var] = val;
+
+ /* Check for the end of the point. */
+ if (sdata->got_var + 1 == sdata->n_variables) {
+ sdata->got_var = 0;
+ sdata->got_points++;
+ } else
+ sdata->got_var++;
+ }
+ break;
+ default:
+ if (len > 1) {
+ if (strstr (buf,"abort"))
+ sdata->state = STATE_ABORT;
+ schematic_log_append (ngspice->priv->schematic, buf);
+ }
+ break;
+ }
+ }
+}
+
diff --git a/src/engines/ngspice.h b/src/engines/ngspice.h
new file mode 100644
index 0000000..b422c15
--- /dev/null
+++ b/src/engines/ngspice.h
@@ -0,0 +1,59 @@
+/*
+ * ngspice.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 __NGSPICE_ENGINE
+#define __NGSPICE_ENGINE
+
+#include <gtk/gtk.h>
+#include "engine.h"
+
+#define OREGANO_TYPE_NGSPICE (oregano_ngspice_get_type ())
+#define OREGANO_NGSPICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_NGSPICE, OreganoNgSpice))
+#define OREGANO_NGSPICE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), OREGANO_TYPE_NGSPICE, OreganoNgSpiceClass))
+#define OREGANO_IS_NGSPICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_NGSPICE))
+#define OREGANO_IS_NGSPICE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), OREGANO_TYPE_NGSPICE))
+#define OREGANO_NGSPICE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), OREGANO_TYPE_NGSPICE, OreganoNgSpiceClass))
+
+typedef struct _OreganoNgSpice OreganoNgSpice;
+typedef struct _OreganoNgSpicePriv OreganoNgSpicePriv;
+typedef struct _OreganoNgSpiceClass OreganoNgSpiceClass;
+
+struct _OreganoNgSpice {
+ GObject parent;
+
+ OreganoNgSpicePriv *priv;
+};
+
+struct _OreganoNgSpiceClass {
+ GObjectClass parent;
+};
+
+GType oregano_ngspice_get_type (void);
+OreganoEngine *oregano_ngspice_new (Schematic *sm);
+
+#endif
+
diff --git a/src/errors.c b/src/errors.c
new file mode 100644
index 0000000..df2d40b
--- /dev/null
+++ b/src/errors.c
@@ -0,0 +1,37 @@
+/*
+ * errors.c
+ *
+ *
+ * Author:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 2003-2008 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 "errors.h"
+
+GQuark
+oregano_error_quark (void)
+{
+ static GQuark err = 0;
+ if (!err) {
+ err = g_quark_from_static_string ("oregano-error");
+ }
+ return err;
+}
+
diff --git a/src/errors.h b/src/errors.h
new file mode 100644
index 0000000..ffff6bc
--- /dev/null
+++ b/src/errors.h
@@ -0,0 +1,44 @@
+/*
+ * errors.h
+ *
+ *
+ * Author:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 2003-2008 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 __ERRORS_H
+#define __ERRORS_H
+
+#include <glib.h>
+
+#define OREGANO_ERROR (oregano_error_quark())
+
+GQuark oregano_error_quark (void);
+
+typedef enum {
+ OREGANO_SIMULATE_ERROR_NO_GND,
+ OREGANO_SIMULATE_ERROR_NO_CLAMP,
+ OREGANO_SIMULATE_ERROR_IO_ERROR,
+ OREGANO_SCHEMATIC_BAD_FILE_FORMAT,
+ OREGANO_SCHEMATIC_FILE_NOT_FOUND,
+} OREGANO_ERRORS;
+
+#endif
+
diff --git a/src/file-manager.c b/src/file-manager.c
new file mode 100644
index 0000000..6a46698
--- /dev/null
+++ b/src/file-manager.c
@@ -0,0 +1,64 @@
+/*
+ * file-manager.c
+ *
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * 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 "file-manager.h"
+
+FileType file_types[] = {
+ FILE_TYPE("oregano", "Oregano Schematic File", schematic_parse_xml_file, schematic_write_xml)
+};
+
+#define FILE_TYPES_COUNT (sizeof(file_types)/sizeof(FileType))
+
+
+FileType *file_manager_get_handler (const gchar *fname)
+{
+ int i;
+ gchar *ext, *ptr;
+ FileType *ft = NULL;
+
+ g_return_val_if_fail (fname != NULL, NULL);
+
+ ptr = ext = (gchar *)fname;
+
+ /* Search for file extension */
+ while (*ptr != '\0') {
+ if (*ptr == '.') {
+ ext = ptr + 1;
+ }
+ ptr++;
+ }
+
+ for (i=0; i<FILE_TYPES_COUNT; i++)
+ if (!strcmp (file_types[i].extension, ext)) {
+ ft = &file_types[i];
+ break;
+ }
+
+ return ft;
+}
+
diff --git a/src/file-manager.h b/src/file-manager.h
new file mode 100644
index 0000000..d5cb087
--- /dev/null
+++ b/src/file-manager.h
@@ -0,0 +1,51 @@
+/*
+ * file-manager.h
+ *
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * 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 _FILE_MANAGER_H_
+#define _FILE_MANAGER_H_ 1
+
+#include <gtk/gtk.h>
+#include "schematic.h"
+
+/* Oregano Files */
+#include "load-schematic.h"
+#include "save-schematic.h"
+
+#define FILE_TYPE(a,b,c,d) {a, b, c, d}
+
+typedef struct _file_manager_ext_ {
+ gchar *extension;
+ gchar *description;
+ int (*load_func)(Schematic *schematic, const gchar *filename, GError **error);
+ int (*save_func)(Schematic *schematic, GError **error);
+} FileType;
+
+FileType *file_manager_get_handler (const gchar *fname);
+
+#endif
+
diff --git a/src/file.c b/src/file.c
new file mode 100644
index 0000000..89dd6fd
--- /dev/null
+++ b/src/file.c
@@ -0,0 +1,415 @@
+/*
+ * file.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 <string.h>
+#include "file.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "dialogs.h"
+#include "save-schematic.h"
+
+#if (GTK_MINOR_VERSION >= 4)
+
+char *
+dialog_open_file (SchematicView *sv)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *allfilter, *orefilter;
+ char *name;
+
+ allfilter = gtk_file_filter_new ();
+ orefilter = gtk_file_filter_new ();
+
+ gtk_file_filter_set_name (orefilter, _("Oregano Files"));
+ gtk_file_filter_add_pattern (orefilter, "*.oregano");
+ gtk_file_filter_set_name (allfilter, _("All Files"));
+ gtk_file_filter_add_pattern (allfilter, "*");
+
+ dialog = gtk_file_chooser_dialog_new (_("Open File"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), orefilter);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), allfilter);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ name = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog));
+ if (name[strlen (name)-1] == '/')
+ name = NULL;
+ else
+ name = g_strdup (name);
+ } else
+ name = NULL;
+
+ gtk_widget_destroy (dialog);
+ return name;
+}
+
+void
+dialog_save_as (SchematicView *sv)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *orefilter, *allfilter;
+ char *name;
+ Schematic *sm;
+ GError *error = NULL;
+
+ orefilter = gtk_file_filter_new ();
+ allfilter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (orefilter, _("Oregano Files"));
+ gtk_file_filter_add_pattern (orefilter, "*.oregano");
+ gtk_file_filter_set_name (allfilter, _("All Files"));
+ gtk_file_filter_add_pattern (allfilter, "*");
+
+ dialog = gtk_file_chooser_dialog_new (_("Save File"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), orefilter);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), allfilter);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+
+ name = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog));
+ if (name [strlen (name) - 1] != '/') {
+ gchar *tmp;
+ const gchar *base = g_path_get_basename (name);
+
+ if (strchr (base, '.') == NULL){
+ tmp = g_strconcat (name, ".oregano", NULL);
+ } else {
+ tmp = g_strdup (name);
+ }
+
+ sm = schematic_view_get_schematic (sv);
+
+ schematic_set_filename (sm, tmp);
+ // schematic_set_title (sm, (gchar *) g_basename (tmp));
+
+ if (!schematic_save_file (sm, &error)){
+ char *msg = g_strdup_printf (
+ "Could not save Schematic file %s\n",
+ tmp);
+ oregano_error (msg);
+ g_free (msg);
+ g_free (name);
+ }
+ g_free (tmp);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+char *
+dialog_netlist_file (SchematicView *sv)
+{
+ GtkWidget *dialog;
+ char *name = NULL;
+
+ dialog = gtk_file_chooser_dialog_new (_("Netlist File"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ name = gtk_file_chooser_get_filename (
+ GTK_FILE_CHOOSER (dialog));
+
+ if (name[strlen (name) - 1] == '/') {
+ g_free (name);
+ name = NULL;
+ } else
+ name = g_strdup (name);
+ } else
+ name = NULL;
+
+ gtk_widget_destroy (dialog);
+ return name;
+}
+
+char *
+dialog_file_open (const gchar *title)
+{
+ GtkWidget *dialog;
+ char *name = NULL;
+
+ dialog = gtk_file_chooser_dialog_new (title,
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ if (name[strlen (name) - 1] == '/') {
+ g_free (name);
+ name = NULL;
+ } else
+ name = g_strdup (name);
+ } else
+ name = NULL;
+
+ gtk_widget_destroy (dialog);
+ return name;
+}
+
+#else
+static void
+set_ok (GtkWidget *widget, gboolean *dialog_result)
+{
+ *dialog_result = TRUE;
+ gtk_main_quit ();
+}
+
+static guint
+file_dialog_delete_event (GtkWidget *widget, GdkEventAny *event)
+{
+ gtk_main_quit ();
+ return TRUE;
+}
+
+char *
+dialog_open_file (SchematicView *sv)
+{
+ GtkFileSelection *fsel;
+ gboolean accepted = FALSE;
+ char *result;
+
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ fsel = GTK_FILE_SELECTION (gtk_file_selection_new (_("Open file")));
+ gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
+
+ gtk_window_set_transient_for (GTK_WINDOW (fsel),
+ GTK_WINDOW (sv->toplevel));
+
+ /* Connect the signals for Ok and Cancel. */
+ g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
+ GTK_SIGNAL_FUNC (set_ok), &accepted);
+ g_signal_connect (G_OBJECT (fsel->cancel_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
+
+ /* Make sure that we quit the main loop if the window is destroyed. */
+ g_signal_connect (G_OBJECT (fsel), "delete_event",
+ GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
+
+ gtk_widget_show (GTK_WIDGET (fsel));
+ gtk_grab_add (GTK_WIDGET (fsel));
+ gtk_main ();
+
+ if (accepted){
+ const gchar *name = gtk_file_selection_get_filename (fsel);
+
+ if (name[strlen (name)-1] == '/')
+ result = NULL;
+ else
+ result = g_strdup (name);
+ } else
+ result = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (fsel));
+
+ return result;
+}
+
+void
+dialog_save_as (SchematicView *sv)
+{
+ Schematic *sm;
+ GtkFileSelection *fsel;
+ gboolean accepted = FALSE;
+ char *filename;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sm = schematic_view_get_schematic (sv);
+
+ fsel = GTK_FILE_SELECTION (
+ gtk_file_selection_new (_("Save schematic as")));
+
+ gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
+ filename = schematic_get_filename (sm);
+ if (filename)
+ gtk_file_selection_set_filename (fsel, filename);
+
+ /* Connect the signals for Ok and Cancel. */
+ g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
+ GTK_SIGNAL_FUNC (set_ok), &accepted);
+ g_signal_connect (G_OBJECT (fsel->cancel_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
+
+ /* Make sure that we quit the main loop if the window is destroyed. */
+ g_signal_connect (G_OBJECT (fsel), "delete_event",
+ GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
+
+ gtk_widget_show (GTK_WIDGET (fsel));
+ gtk_grab_add (GTK_WIDGET (fsel));
+ gtk_main ();
+
+ if (accepted){
+ const gchar *name = gtk_file_selection_get_filename (fsel);
+
+ if (name [strlen (name) - 1] != '/') {
+ gchar *tmp;
+ const gchar *base = g_basename (name);
+
+ if (strchr (base, '.') == NULL){
+ tmp = g_strconcat (name, ".oregano", NULL);
+ } else
+ tmp = g_strdup (name);
+
+ schematic_set_filename (sm, tmp);
+ // schematic_set_title (sm, (gchar *) g_basename (tmp));
+
+ if (!schematic_save_file (sm)){
+ char *msg = g_strdup_printf (
+ "Could not save Schematic file %s\n",
+ tmp);
+
+ oregano_error (msg);
+ g_free (msg);
+ } else {
+ schematic_set_dirty (schematic_view_get_schematic (sv), FALSE);
+ }
+ g_free (tmp);
+ }
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (fsel));
+}
+
+char *
+dialog_netlist_file (SchematicView *sv)
+{
+ GtkFileSelection *fsel;
+ gboolean accepted = FALSE;
+ char *result = NULL;
+
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ fsel = GTK_FILE_SELECTION (
+ gtk_file_selection_new (_("Netlist filename")));
+ gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
+
+ gtk_window_set_transient_for (GTK_WINDOW (fsel),
+ GTK_WINDOW (sv->toplevel));
+
+ /* Connect the signals for Ok and Cancel. */
+ g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
+ GTK_SIGNAL_FUNC (set_ok), &accepted);
+ g_signal_connect (G_OBJECT (fsel->cancel_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
+
+ /* Make sure that we quit the main loop if the window is destroyed. */
+ g_signal_connect (G_OBJECT (fsel), "delete_event",
+ GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
+
+ gtk_widget_show (GTK_WIDGET (fsel));
+ gtk_grab_add (GTK_WIDGET (fsel));
+ gtk_main ();
+
+ if (accepted) {
+ const gchar *name = gtk_file_selection_get_filename (fsel);
+
+ if (name[strlen (name) - 1] == '/')
+ result = NULL;
+ else
+ result = g_strdup (name);
+ } else
+ result = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (fsel));
+
+ return result;
+}
+
+char *
+dialog_file_open (const gchar *title)
+{
+ GtkFileSelection *fsel;
+ gboolean accepted = FALSE;
+ char *result = NULL;
+
+ fsel = GTK_FILE_SELECTION (
+ gtk_file_selection_new (title));
+ gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
+
+ gtk_window_set_transient_for (GTK_WINDOW (fsel),
+ GTK_WINDOW (sv->toplevel));
+
+ /* Connect the signals for Ok and Cancel. */
+ g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
+ GTK_SIGNAL_FUNC (set_ok), &accepted);
+ g_signal_connect (G_OBJECT (fsel->cancel_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
+
+ /* Make sure that we quit the main loop if the window is destroyed. */
+ g_signal_connect (G_OBJECT (fsel), "delete_event",
+ GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
+
+ gtk_widget_show (GTK_WIDGET (fsel));
+ gtk_grab_add (GTK_WIDGET (fsel));
+ gtk_main ();
+
+ if (accepted) {
+ const gchar *name = gtk_file_selection_get_filename (fsel);
+
+ if (name[strlen (name) - 1] == '/')
+ result = NULL;
+ else
+ result = g_strdup (name);
+ } else
+ result = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (fsel));
+
+ return result;
+}
+#endif
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000..e47aea5
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,42 @@
+/*
+ * file.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 __FILE_H
+#define __FILE_H
+
+#include <libgnome/gnome-i18n.h>
+#include "schematic-view.h"
+
+/* FIXME : unificar API!! */
+char *dialog_open_file (SchematicView *sv);
+void dialog_save_as (SchematicView *sv);
+char *dialog_netlist_file (SchematicView *sv);
+char *dialog_file_open (const gchar *file);
+
+#endif
diff --git a/src/gplot/Makefile.am b/src/gplot/Makefile.am
new file mode 100644
index 0000000..85665ee
--- /dev/null
+++ b/src/gplot/Makefile.am
@@ -0,0 +1,14 @@
+oreganodir = $(datadir)/oregano
+INCLUDES = \
+ $(OREGANO_CFLAGS)
+
+noinst_LIBRARIES = libgplot.a
+libgplot_a_SOURCES = \
+ gplot.c \
+ gplotfunction.c \
+ gplotfunction.h \
+ gplot.h \
+ gplotlines.c \
+ gplotlines.h
+
+libgplot_a_LIBADD = libgplot.a
diff --git a/src/gplot/gplot.c b/src/gplot/gplot.c
new file mode 100644
index 0000000..b41408d
--- /dev/null
+++ b/src/gplot/gplot.c
@@ -0,0 +1,839 @@
+/*
+ * gplot.c
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include "gplot.h"
+
+#define BORDER_SIZE 50
+
+static void g_plot_class_init (GPlotClass* class);
+static void g_plot_init (GPlot* plot);
+static gint g_plot_expose (GtkWidget* widget, GdkEventExpose* event);
+static cairo_t* g_plot_create_cairo (GPlot *);
+static gboolean g_plot_motion_cb (GtkWidget *, GdkEventMotion *, GPlot *);
+static gboolean g_plot_button_press_cb (GtkWidget *, GdkEventButton *, GPlot *);
+static gboolean g_plot_button_release_cb (GtkWidget *, GdkEventButton *, GPlot *);
+static void g_plot_update_bbox (GPlot *);
+static void g_plot_finalize (GObject *object);
+static void g_plot_dispose (GObject *object);
+
+static void get_order_of_magnitude (gdouble val, gdouble *man, gdouble *pw);
+
+enum {
+ ACTION_NONE,
+ ACTION_STARTING_PAN,
+ ACTION_PAN,
+ ACTION_STARTING_REGION,
+ ACTION_REGION
+};
+
+static GtkLayoutClass* parent_class = NULL;
+
+struct _GPlotPriv {
+ GList *functions;
+
+ gchar *xlabel;
+ gchar *xlabel_unit;
+ gchar *ylabel;
+ gchar *ylabel_unit;
+
+ gdouble left_border;
+ gdouble right_border;
+ gdouble top_border;
+ gdouble bottom_border;
+
+ guint zoom_mode;
+ guint action;
+ gdouble zoom;
+ gdouble offset_x;
+ gdouble offset_y;
+ gdouble last_x;
+ gdouble last_y;
+
+ /* Window->Viewport * Transformation Matrix */
+ cairo_matrix_t matrix;
+
+ gboolean window_valid;
+ GPlotFunctionBBox window_bbox;
+ GPlotFunctionBBox viewport_bbox;
+
+ GPlotFunctionBBox rubberband;
+};
+
+GType
+g_plot_get_type ()
+{
+ static GType g_plot_type = 0;
+
+ if (!g_plot_type) {
+ static const GTypeInfo g_plot_info = {
+ sizeof (GPlotClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) g_plot_class_init,
+ NULL,
+ NULL,
+ sizeof (GPlot),
+ 0,
+ (GInstanceInitFunc) g_plot_init,
+ NULL
+ };
+ g_plot_type = g_type_register_static (GTK_TYPE_LAYOUT, "GPlot", &g_plot_info, 0);
+ }
+ return g_plot_type;
+}
+
+static void
+g_plot_class_init (GPlotClass* class)
+{
+ GObjectClass* object_class;
+ GtkWidgetClass* widget_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
+ widget_class->expose_event = g_plot_expose;
+
+ object_class->dispose = g_plot_dispose;
+ object_class->finalize = g_plot_finalize;
+}
+
+static cairo_t*
+g_plot_create_cairo (GPlot *p)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (GTK_LAYOUT (p)->bin_window);
+
+ return cr;
+}
+
+static void
+g_plot_finalize (GObject *object)
+{
+ GPlot *p = GPLOT (object);
+
+ if (p->priv->xlabel)
+ g_free (p->priv->xlabel);
+
+ if (p->priv->ylabel)
+ g_free (p->priv->ylabel);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+g_plot_dispose (GObject *object)
+{
+ GList *lst;
+ GPlot *plot;
+
+ plot = GPLOT (object);
+
+ lst = plot->priv->functions;
+ while (lst) {
+ GPlotFunction *f = (GPlotFunction *)lst->data;
+ g_object_unref (G_OBJECT (f));
+ lst = lst->next;
+ }
+ g_list_free (plot->priv->functions);
+ plot->priv->functions = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static int
+get_best_exponent (int div)
+{
+ /* http://en.wikipedia.org/wiki/Micro */
+ switch (div) {
+ case -24:
+ case -21:
+ case -18:
+ case -15:
+ case -12:
+ case -9:
+ case -6:
+ case -3:
+ case 0:
+ case 3:
+ case 6:
+ case 9:
+ case 12:
+ case 15:
+ case 18:
+ case 21:
+ case 24:
+ return div;
+ }
+ if (div == -1) return -3;
+
+ if ((div - 1) % 3 == 0)
+ return div - 1;
+ return div + 1;
+}
+
+void
+draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, gboolean vertical, gint *div)
+{
+ gchar *label;
+ cairo_text_extents_t extents;
+ gdouble i, j;
+ gdouble x1, y1, x2, y2, x3, y3;
+ gdouble step = (max - min) / 10.0;
+ gdouble s;
+ gdouble divisor;
+ gdouble man1, pw1, man2, pw2;
+
+ get_order_of_magnitude (min, &man1, &pw1);
+ get_order_of_magnitude (max, &man2, &pw2);
+ (*div) = get_best_exponent ((pw2+pw1) / 2.0 + 0.5);
+ if ((*div) == 0)
+ divisor = 1;
+ else
+ divisor = pow (10, *div);
+
+ if (vertical)
+ s = (bbox->ymax - bbox->ymin) / 10.0;
+ else
+ s = (bbox->xmax - bbox->xmin) / 10.0;
+
+ for (i = (vertical?bbox->ymin:bbox->xmin), j = max; i <= (vertical?bbox->ymax:bbox->xmax) + 0.5; i += s, j -= step) {
+ label = g_strdup_printf ("%.2f", j / divisor);
+ cairo_text_extents (cr, label, &extents);
+
+ if (vertical) {
+ x1 = bbox->xmin - 4;
+ y1 = i;
+ x2 = bbox->xmin + 4;
+ y2 = i;
+ x3 = bbox->xmin - extents.width * 1.5;
+ y3 = i + extents.height / 2.0;
+ } else {
+ x1 = i;
+ y1 = bbox->ymax - 4;
+ x2 = i;
+ y2 = bbox->ymax + 4;
+ x3 = i;
+ y3 = bbox->ymax + extents.height * 1.5;
+ }
+
+ cairo_move_to (cr, x1, y1);
+ cairo_line_to (cr, x2, y2);
+ cairo_move_to (cr, x3, y3);
+ cairo_show_text (cr, label);
+ g_free (label);
+ }
+ cairo_stroke (cr);
+}
+
+static gchar*
+get_unit_text (int div)
+{
+ /* http://en.wikipedia.org/wiki/Micro */
+ switch (div) {
+ case -24: return g_strdup ("y");
+ case -21: return g_strdup ("z");
+ case -18: return g_strdup ("a");
+ case -15: return g_strdup ("f");
+ case -12: return g_strdup ("p");
+ case -9: return g_strdup ("n");
+ case -6: return g_strdup ("\302\265");
+ case -3: return g_strdup ("m");
+ case 0: return g_strdup ("");
+ case 3: return g_strdup ("k");
+ case 6: return g_strdup ("M");
+ case 9: return g_strdup ("G");
+ case 12: return g_strdup ("T");
+ case 15: return g_strdup ("P");
+ case 18: return g_strdup ("E");
+ case 21: return g_strdup ("Z");
+ case 24: return g_strdup ("Y");
+ }
+
+ return g_strdup_printf ("10e%02d", div);
+}
+
+static gint
+g_plot_expose (GtkWidget* widget, GdkEventExpose* event)
+{
+ static double dashes[] = {3, /* ink */
+ 3, /* skip */
+ 3, /* ink */
+ 3 /* skip*/ };
+ static int ndash = sizeof (dashes)/sizeof(dashes[0]);
+ static double offset = -0.2;
+
+ GPlot *plot;
+ GPlotPriv *priv;
+ cairo_t* cr;
+ guint width;
+ guint height;
+ guint graph_width;
+ guint graph_height;
+ GPlotFunction *f;
+ GList *lst;
+ GPlotFunctionBBox bbox;
+ gdouble aX, bX, aY, bY;
+ gint div;
+ int i = 0;
+ cairo_text_extents_t extents;
+
+ plot = GPLOT (widget);
+ priv = plot->priv;
+
+ if (!priv->window_valid) {
+ g_plot_update_bbox (plot);
+ }
+
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+
+ graph_width = width - priv->left_border - priv->right_border;
+ graph_height = height - priv->top_border - priv->bottom_border;
+
+ cr = g_plot_create_cairo (plot);
+
+ /* Set a clip region for the expose event */
+ /* TODO :This is useful if we use gtk_widget_queue_draw_area */
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_clip (cr);
+
+ /* Paint background */
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+ cairo_restore (cr);
+
+ /* Plot Border */
+ cairo_save (cr);
+ cairo_set_line_width (cr, 0.5);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr, priv->left_border, priv->right_border, graph_width, graph_height);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ /* TODO : Move this to SizeAllocation functions */
+ priv->viewport_bbox.xmax = widget->allocation.width - priv->right_border;
+ priv->viewport_bbox.xmin = priv->left_border;
+ priv->viewport_bbox.ymax = widget->allocation.height - priv->bottom_border;
+ priv->viewport_bbox.ymin = priv->top_border;
+
+ /* Save real bbox */
+ bbox = priv->window_bbox;
+
+ /* Calculating Window to Viewport matrix */
+ aX = (priv->viewport_bbox.xmax - priv->viewport_bbox.xmin)
+ / (priv->window_bbox.xmax - priv->window_bbox.xmin);
+ bX = -aX * priv->window_bbox.xmin + priv->viewport_bbox.xmin;
+ aY = (priv->viewport_bbox.ymax - priv->viewport_bbox.ymin)
+ / (priv->window_bbox.ymin - priv->window_bbox.ymax);
+ bY = -aY * priv->window_bbox.ymax + priv->viewport_bbox.ymin;
+
+ cairo_matrix_init (&priv->matrix, aX, 0, 0, aY, bX, bY);
+
+ cairo_save (cr);
+ cairo_rectangle (cr, priv->left_border, priv->right_border, graph_width, graph_height);
+ cairo_clip (cr);
+
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_matrix (cr, &priv->matrix);
+ lst = plot->priv->functions;
+ while (lst) {
+ f = (GPlotFunction *)lst->data;
+ g_plot_function_draw (f, cr, &priv->window_bbox);
+ lst = lst->next;
+ }
+ cairo_restore (cr);
+
+ cairo_save (cr);
+ cairo_rectangle (cr, priv->left_border, priv->right_border, graph_width, graph_height);
+ cairo_clip (cr);
+ cairo_save (cr);
+ cairo_set_matrix (cr, &priv->matrix);
+ cairo_move_to (cr, priv->window_bbox.xmin, 0.0);
+ cairo_line_to (cr, priv->window_bbox.xmax, 0.0);
+ cairo_restore (cr);
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+ cairo_set_line_width (cr, 2);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ cairo_save (cr);
+ cairo_set_matrix (cr, &priv->matrix);
+ cairo_move_to (cr, 0.0, priv->window_bbox.ymin);
+ cairo_line_to (cr, 0.0, priv->window_bbox.ymax);
+ cairo_restore (cr);
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+ cairo_set_line_width (cr, 2);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ cairo_restore (cr);
+
+ cairo_save (cr);
+ i = 0;
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_line_width (cr, 1);
+
+ draw_axis (cr, &priv->viewport_bbox, priv->window_bbox.ymin, priv->window_bbox.ymax, TRUE, &div);
+ if (priv->ylabel_unit) {
+ g_free (priv->ylabel_unit);
+ priv->ylabel_unit = NULL;
+ }
+ if (div != 1)
+ priv->ylabel_unit = get_unit_text (div);
+ draw_axis (cr, &priv->viewport_bbox, priv->window_bbox.xmax, priv->window_bbox.xmin, FALSE, &div);
+ if (priv->xlabel_unit) {
+ g_free (priv->xlabel_unit);
+ priv->xlabel_unit = NULL;
+ }
+ if (div != 1)
+ priv->xlabel_unit = get_unit_text (div);
+ cairo_restore (cr);
+
+ /* Axis Labels */
+ if (priv->xlabel) {
+ char *txt;
+ if (priv->xlabel_unit == NULL)
+ txt = g_strdup (priv->xlabel);
+ else
+ txt = g_strdup_printf ("%s %s", priv->xlabel_unit, priv->xlabel);
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_font_size (cr, 14);
+ cairo_text_extents (cr, txt, &extents);
+ cairo_move_to (cr, width/2.0 - extents.width/2.0, height-extents.height/2.0);
+ cairo_show_text (cr, txt);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ g_free (txt);
+ }
+
+ if (priv->ylabel) {
+ char *txt;
+ if (priv->ylabel_unit == NULL)
+ txt = g_strdup (priv->ylabel);
+ else
+ txt = g_strdup_printf ("%s %s", priv->ylabel_unit, priv->ylabel);
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_font_size (cr, 14);
+ cairo_text_extents (cr, txt, &extents);
+ cairo_move_to (cr, extents.height, height/2.0 + extents.width/2.0);
+ cairo_rotate (cr, 4.7124);
+ cairo_show_text (cr, txt);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+
+ if (priv->action == ACTION_REGION) {
+ gdouble x, y, w, h;
+ x = priv->rubberband.xmin;
+ y = priv->rubberband.ymin;
+ w = priv->rubberband.xmax;
+ h = priv->rubberband.ymax;
+ cairo_save (cr);
+ cairo_identity_matrix (cr);
+ cairo_set_dash (cr, dashes, ndash, offset);
+
+ cairo_set_line_width (cr, 2);
+ cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+ cairo_rectangle (cr, x, y, w-x, h-y);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+g_plot_init (GPlot* plot)
+{
+ plot->priv = g_new0 (GPlotPriv, 1);
+
+ plot->priv->zoom_mode = GPLOT_ZOOM_REGION;
+ plot->priv->functions = NULL;
+ plot->priv->action = ACTION_NONE;
+ plot->priv->zoom = 1.0;
+ plot->priv->offset_x = 0.0;
+ plot->priv->offset_y = 0.0;
+ plot->priv->left_border = BORDER_SIZE;
+ plot->priv->right_border = BORDER_SIZE;
+ plot->priv->top_border = BORDER_SIZE;
+ plot->priv->bottom_border = BORDER_SIZE;
+ plot->priv->xlabel = NULL;
+ plot->priv->xlabel_unit = NULL;
+ plot->priv->ylabel = NULL;
+ plot->priv->ylabel_unit = NULL;
+}
+
+GtkWidget*
+g_plot_new ()
+{
+ GPlot *plot;
+
+ plot = GPLOT (g_object_new (TYPE_GPLOT, NULL));
+
+ gtk_widget_add_events (GTK_WIDGET (plot),
+ GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
+ GDK_BUTTON_RELEASE_MASK|GDK_BUTTON_MOTION_MASK|
+ GDK_BUTTON1_MOTION_MASK|GDK_BUTTON2_MOTION_MASK|
+ GDK_BUTTON3_MOTION_MASK);
+
+ g_signal_connect (G_OBJECT (plot), "motion-notify-event", G_CALLBACK(g_plot_motion_cb), plot);
+ g_signal_connect (G_OBJECT (plot), "button-press-event", G_CALLBACK(g_plot_button_press_cb), plot);
+ g_signal_connect (G_OBJECT (plot), "button-release-event", G_CALLBACK(g_plot_button_release_cb), plot);
+
+ return GTK_WIDGET (plot);
+}
+
+int
+g_plot_add_function (GPlot *plot, GPlotFunction *func)
+{
+ g_return_val_if_fail (IS_GPLOT (plot), -1);
+
+ plot->priv->functions = g_list_append (plot->priv->functions, func);
+ plot->priv->window_valid = FALSE;
+ return 0;
+}
+
+static gboolean
+g_plot_motion_cb (GtkWidget *w, GdkEventMotion *e, GPlot *p)
+{
+ switch (p->priv->zoom_mode) {
+ case GPLOT_ZOOM_INOUT:
+ if ((p->priv->action == ACTION_STARTING_PAN) || (p->priv->action == ACTION_PAN)) {
+ gdouble dx, dy;
+ cairo_matrix_t t = p->priv->matrix;
+ GdkCursor *cursor = gdk_cursor_new (GDK_FLEUR);
+ gdk_window_set_cursor (w->window, cursor);
+ gdk_cursor_destroy (cursor);
+ gdk_flush ();
+
+ dx = p->priv->last_x - e->x;
+ dy = p->priv->last_y - e->y;
+
+ cairo_matrix_invert (&t);
+ cairo_matrix_transform_distance (&t, &dx, &dy);
+
+ p->priv->window_bbox.xmin -= dx;
+ p->priv->window_bbox.xmax -= dx;
+ p->priv->window_bbox.ymin -= dy;
+ p->priv->window_bbox.ymax -= dy;
+
+ p->priv->last_x = e->x;
+ p->priv->last_y = e->y;
+ p->priv->action = ACTION_PAN;
+ gtk_widget_queue_draw (w);
+ }
+ break;
+ case GPLOT_ZOOM_REGION:
+ if ((p->priv->action == ACTION_STARTING_REGION) || (p->priv->action == ACTION_REGION)) {
+ gdouble dx, dy;
+ GdkCursor *cursor = gdk_cursor_new (GDK_CROSS);
+ gdk_window_set_cursor (w->window, cursor);
+ gdk_cursor_destroy (cursor);
+ gdk_flush ();
+
+ /* dx < 0 == moving to the left */
+ dx = e->x - p->priv->last_x;
+ dy = e->y - p->priv->last_y;
+
+ if (dx < 0) {
+ p->priv->rubberband.xmin = e->x;
+ } else {
+ p->priv->rubberband.xmax = e->x;
+ }
+ if (dy < 0) {
+ p->priv->rubberband.ymin = e->y;
+ } else {
+ p->priv->rubberband.ymax = e->y;
+ }
+
+ p->priv->action = ACTION_REGION;
+ gtk_widget_queue_draw (w);
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+g_plot_button_press_cb (GtkWidget *w, GdkEventButton *e, GPlot *p)
+{
+ g_return_val_if_fail (IS_GPLOT (p), TRUE);
+
+ if (e->type == GDK_2BUTTON_PRESS) {
+ /* TODO : Chekck function below cursor and open a property dialog :) */
+ } else {
+ switch (p->priv->zoom_mode) {
+ case GPLOT_ZOOM_INOUT:
+ if (e->button == 1) {
+ p->priv->action = ACTION_STARTING_PAN;
+ p->priv->last_x = e->x;
+ p->priv->last_y = e->y;
+ }
+ break;
+ case GPLOT_ZOOM_REGION:
+ if ((e->button == 1)) {
+ p->priv->action = ACTION_STARTING_REGION;
+ p->priv->rubberband.xmin = e->x;
+ p->priv->rubberband.ymin = e->y;
+ p->priv->rubberband.xmax = e->x;
+ p->priv->rubberband.ymax = e->y;
+ p->priv->last_x = e->x;
+ p->priv->last_y = e->y;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+g_plot_button_release_cb (GtkWidget *w, GdkEventButton *e, GPlot *p)
+{
+ gdouble zoom;
+
+ g_return_val_if_fail (IS_GPLOT (p), TRUE);
+
+ switch (p->priv->zoom_mode) {
+ case GPLOT_ZOOM_INOUT:
+ if (p->priv->action != ACTION_PAN) {
+ switch (e->button) {
+ case 1:
+ p->priv->zoom += 0.1;
+ zoom = 1.1;
+ break;
+ case 3:
+ p->priv->zoom -= 0.1;
+ zoom = 0.9;
+ }
+ p->priv->window_bbox.xmin /= zoom;
+ p->priv->window_bbox.xmax /= zoom;
+ p->priv->window_bbox.ymin /= zoom;
+ p->priv->window_bbox.ymax /= zoom;
+ gtk_widget_queue_draw (w);
+ } else {
+ gdk_window_set_cursor (w->window, NULL);
+ gdk_flush ();
+ }
+ break;
+ case GPLOT_ZOOM_REGION:
+ if ((e->button == 1) && (p->priv->action == ACTION_REGION)) {
+ gdk_window_set_cursor (w->window, NULL);
+ gdk_flush ();
+ {
+ gdouble x1, y1, x2, y2;
+ cairo_matrix_t t = p->priv->matrix;
+ cairo_matrix_invert (&t);
+
+ x1 = p->priv->rubberband.xmin;
+ y1 = p->priv->rubberband.ymin;
+ x2 = p->priv->rubberband.xmax;
+ y2 = p->priv->rubberband.ymax;
+
+ cairo_matrix_transform_point (&t, &x1, &y1);
+ cairo_matrix_transform_point (&t, &x2, &y2);
+
+ p->priv->window_bbox.xmin = x1;
+ p->priv->window_bbox.xmax = x2;
+ p->priv->window_bbox.ymin = y2;
+ p->priv->window_bbox.ymax = y1;
+ }
+ } else if (e->button == 3) {
+ gdk_window_set_cursor (w->window, NULL);
+ gdk_flush ();
+ }
+ gtk_widget_queue_draw (w);
+ }
+
+ p->priv->action = ACTION_NONE;
+ return TRUE;
+}
+
+void
+g_plot_set_zoom_mode (GPlot *p, guint mode)
+{
+ g_return_if_fail (IS_GPLOT (p));
+
+ p->priv->zoom_mode = mode;
+}
+
+guint
+g_plot_get_zoom_mode (GPlot *p)
+{
+ g_return_val_if_fail (IS_GPLOT (p), 0);
+
+ return p->priv->zoom_mode;
+}
+
+static void
+g_plot_update_bbox (GPlot *p)
+{
+ GPlotFunction *f;
+ GPlotFunctionBBox bbox;
+ GPlotPriv *priv;
+ GList *lst;
+
+ priv = p->priv;
+
+ /* Get functions bbox */
+ priv->window_bbox.xmax = -9999999;
+ priv->window_bbox.xmin = 9999999;
+ priv->window_bbox.ymax = -9999999;
+ priv->window_bbox.ymin = 9999999;
+ lst = priv->functions;
+ while (lst) {
+ f = (GPlotFunction *)lst->data;
+ g_plot_function_get_bbox (f, &bbox);
+ priv->window_bbox.xmax = MAX (priv->window_bbox.xmax, bbox.xmax);
+ priv->window_bbox.xmin = MIN (priv->window_bbox.xmin, bbox.xmin);
+ priv->window_bbox.ymax = MAX (priv->window_bbox.ymax, bbox.ymax);
+ priv->window_bbox.ymin = MIN (priv->window_bbox.ymin, bbox.ymin);
+ lst = lst->next;
+ }
+
+ if (priv->window_bbox.xmin == priv->window_bbox.xmax)
+ priv->window_bbox.xmax += 1;
+
+ if (priv->window_bbox.ymin == priv->window_bbox.ymax)
+ priv->window_bbox.ymax += 1;
+
+ priv->window_bbox.ymin *= 1.1;
+ priv->window_bbox.ymax *= 1.1;
+
+ priv->window_valid = TRUE;
+}
+
+void
+g_plot_reset_zoom (GPlot *p)
+{
+ g_return_if_fail (IS_GPLOT (p));
+
+ p->priv->window_valid = FALSE;
+ p->priv->zoom = 1.0;
+ gtk_widget_queue_draw (GTK_WIDGET (p));
+}
+
+void
+g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y)
+{
+ cairo_text_extents_t extents;
+
+ g_return_if_fail (IS_GPLOT (p));
+
+ if (x) {
+ cairo_t *cr = g_plot_create_cairo (p);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_font_size (cr, 14);
+ cairo_text_extents (cr, x, &extents);
+ cairo_destroy (cr);
+
+ p->priv->xlabel = g_strdup (x);
+ p->priv->bottom_border = BORDER_SIZE + extents.height;
+ } else {
+ p->priv->bottom_border = BORDER_SIZE;
+ if (p->priv->xlabel) {
+ g_free (p->priv->xlabel);
+ p->priv->xlabel = NULL;
+ }
+ }
+
+ if (y) {
+ cairo_t *cr = g_plot_create_cairo (p);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_set_font_size (cr, 14);
+ cairo_text_extents (cr, y, &extents);
+ cairo_destroy (cr);
+
+ p->priv->xlabel = g_strdup (x);
+ p->priv->left_border = BORDER_SIZE + extents.height;
+
+ p->priv->ylabel = g_strdup (y);
+ } else {
+ p->priv->left_border = BORDER_SIZE;
+ if (p->priv->ylabel) {
+ g_free (p->priv->ylabel);
+ p->priv->ylabel = NULL;
+ }
+ }
+}
+
+void
+g_plot_clear (GPlot *plot)
+{
+ GList *lst;
+
+ g_return_if_fail (plot != NULL);
+ g_return_if_fail (IS_GPLOT (plot));
+
+ lst = plot->priv->functions;
+
+ while (lst) {
+ g_object_unref (G_OBJECT (lst->data));
+ lst = lst->next;
+ }
+ g_list_free (plot->priv->functions);
+ plot->priv->functions = NULL;
+ plot->priv->window_valid = FALSE;
+}
+
+void
+g_plot_window_to_device (GPlot *plot, double *x, double *y)
+{
+ cairo_t *cr;
+
+ g_return_if_fail (plot != NULL);
+ g_return_if_fail (IS_GPLOT (plot));
+
+ cr = g_plot_create_cairo (plot);
+ cairo_set_matrix (cr, &plot->priv->matrix);
+ cairo_device_to_user (cr, x, y);
+ cairo_destroy (cr);
+}
+
+static void
+get_order_of_magnitude (gdouble val, gdouble *man, gdouble *pw)
+{
+ gdouble b;
+ gdouble sx;
+
+ b = (val != 0.0 ? log10 (fabs (val)) / 3.0 : 0.0);
+ b = 3.0 * rint (b);
+ sx = (b != 0.0 ? pow (10.0, b) : 1.0);
+ *man = val / sx;
+ *pw = b;
+}
+
+
diff --git a/src/gplot/gplot.h b/src/gplot/gplot.h
new file mode 100644
index 0000000..041a597
--- /dev/null
+++ b/src/gplot/gplot.h
@@ -0,0 +1,70 @@
+/*
+ * gplot.h
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GPLOT_H_
+#define _GPLOT_H_
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "gplotfunction.h"
+
+#define TYPE_GPLOT (g_plot_get_type())
+#define GPLOT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, TYPE_GPLOT, GPlot)
+#define GPLOT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, TYPE_GPLOT, GPlotClass)
+#define IS_GPLOT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, TYPE_GPLOT)
+
+typedef struct _GPlot GPlot;
+typedef struct _GPlotClass GPlotClass;
+typedef struct _GPlotPriv GPlotPriv;
+
+enum {
+ GPLOT_ZOOM_INOUT,
+ GPLOT_ZOOM_REGION
+};
+
+struct _GPlot {
+ GtkLayout parent;
+
+ GPlotPriv *priv;
+};
+
+struct _GPlotClass {
+ GtkLayoutClass parent_class;
+};
+
+GType g_plot_get_type ();
+GtkWidget* g_plot_new ();
+void g_plot_clear (GPlot *);
+int g_plot_add_function (GPlot *, GPlotFunction *);
+void g_plot_set_zoom_mode (GPlot *, guint);
+guint g_plot_get_zoom_mode (GPlot *);
+void g_plot_reset_zoom (GPlot *);
+void g_plot_set_axis_labels (GPlot *, gchar *, gchar *);
+void g_plot_window_to_device (GPlot *, double *x, double *y);
+
+#endif
+
diff --git a/src/gplot/gplotfunction.c b/src/gplot/gplotfunction.c
new file mode 100644
index 0000000..576aa85
--- /dev/null
+++ b/src/gplot/gplotfunction.c
@@ -0,0 +1,80 @@
+/*
+ * gplotfunction.c
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gplotfunction.h"
+
+
+static void
+g_plot_function_base_init (gpointer g_class)
+{
+ static gboolean initialized = FALSE;
+ if (!initialized) {
+ /* create interface signals here. */
+ initialized = TRUE;
+
+ g_object_interface_install_property (g_class,
+ g_param_spec_boolean ("visible",
+ "gplot_function_visible",
+ "Get/Set if function is visible",
+ TRUE,
+ G_PARAM_READWRITE));
+ }
+}
+
+GType
+g_plot_function_get_type (void)
+{
+ static GType type = 0;
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (GPlotFunctionClass),
+ g_plot_function_base_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL, /* class_init */
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL /* instance_init */
+ };
+ type = g_type_register_static (G_TYPE_INTERFACE, "GPlotFunction", &info, 0);
+ }
+ return type;
+}
+
+void
+g_plot_function_draw (GPlotFunction *self, cairo_t * cr, GPlotFunctionBBox *bbox)
+{
+ if (GPLOT_FUNCTION_GET_CLASS (self)->draw)
+ GPLOT_FUNCTION_GET_CLASS (self)->draw (self, cr, bbox);
+}
+
+void
+g_plot_function_get_bbox (GPlotFunction *self, GPlotFunctionBBox *bbox)
+{
+ if (GPLOT_FUNCTION_GET_CLASS (self)->get_bbox)
+ GPLOT_FUNCTION_GET_CLASS (self)->get_bbox (self, bbox);
+}
+
diff --git a/src/gplot/gplotfunction.h b/src/gplot/gplotfunction.h
new file mode 100644
index 0000000..8cbc523
--- /dev/null
+++ b/src/gplot/gplotfunction.h
@@ -0,0 +1,69 @@
+/*
+ * gplotfunction.h
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GPLOT_FUNCTION_H_
+#define _GPLOT_FUNCTION_H_
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#define TYPE_GPLOT_FUNCTION (g_plot_function_get_type())
+#define GPLOT_FUNCTION(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, TYPE_GPLOT_FUNCTION, GPlotFunction)
+#define GPLOT_FUNCTION_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, TYPE_GPLOT_FUNCTION, GPlotFunctionClass)
+#define IS_GPLOT_FUNCTION (obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, TYPE_GPLOT_FUNCTION)
+#define GPLOT_FUNCTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TYPE_GPLOT_FUNCTION, GPlotFunctionClass))
+/*
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+*/
+
+typedef struct _GPlotFunctionBBox {
+ gdouble xmin;
+ gdouble ymin;
+ gdouble xmax;
+ gdouble ymax;
+} GPlotFunctionBBox;
+
+typedef struct _GPlotFunction {} GPlotFunction;
+typedef struct _GPlotFunctionClass GPlotFunctionClass;
+
+struct _GPlotFunctionClass {
+ GTypeInterface parent;
+
+ void (*draw)(GPlotFunction *, cairo_t *, GPlotFunctionBBox *);
+ void (*get_bbox)(GPlotFunction *, GPlotFunctionBBox *);
+};
+
+
+GType g_plot_function_get_type ();
+void g_plot_function_draw (GPlotFunction *, cairo_t *, GPlotFunctionBBox *);
+void g_plot_function_get_bbox (GPlotFunction *, GPlotFunctionBBox *);
+void g_plot_function_set_visible (GPlotFunction *, gboolean);
+gboolean g_plot_function_get_visible (GPlotFunction *);
+
+#endif
+
+
diff --git a/src/gplot/gplotlines.c b/src/gplot/gplotlines.c
new file mode 100644
index 0000000..5e9743e
--- /dev/null
+++ b/src/gplot/gplotlines.c
@@ -0,0 +1,294 @@
+/*
+ * gplotlines.c
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gplotlines.h"
+#include <string.h>
+
+static void g_plot_lines_class_init (GPlotLinesClass* class);
+static void g_plot_lines_init (GPlotLines* plot);
+static void g_plot_lines_draw (GPlotFunction *, cairo_t *, GPlotFunctionBBox *);
+static void g_plot_lines_get_bbox (GPlotFunction *, GPlotFunctionBBox *);
+static void g_plot_lines_function_init (GPlotFunctionClass *iface);
+static void g_plot_lines_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *spec);
+static void g_plot_lines_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *spec);
+
+static GObjectClass* parent_class = NULL;
+
+enum {
+ ARG_0,
+ ARG_WIDTH,
+ ARG_COLOR,
+ ARG_COLOR_GDKCOLOR,
+ ARG_VISIBLE,
+};
+
+struct _GPlotLinesPriv {
+ gboolean bbox_valid;
+ GPlotFunctionBBox bbox;
+ gdouble *x;
+ gdouble *y;
+ gdouble points;
+ gboolean visible;
+
+ /** Line width */
+ gdouble width;
+
+ /** Line Color */
+ gchar *color_string;
+ GdkColor color;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GPlotLines, g_plot_lines, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (TYPE_GPLOT_FUNCTION,
+ g_plot_lines_function_init));
+
+static void
+g_plot_lines_function_init (GPlotFunctionClass *iface)
+{
+ iface->draw = g_plot_lines_draw;
+ iface->get_bbox = g_plot_lines_get_bbox;
+}
+
+static void
+g_plot_lines_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static void
+g_plot_lines_finalize(GObject *object)
+{
+ GPlotLines *lines;
+
+ lines = GPLOT_LINES (object);
+
+ if (lines->priv) {
+ g_free (lines->priv->x);
+ g_free (lines->priv->y);
+ g_free (lines->priv->color_string);
+ g_free (lines->priv);
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+g_plot_lines_class_init (GPlotLinesClass* class)
+{
+ GObjectClass* object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->set_property = g_plot_lines_set_property;
+ object_class->get_property = g_plot_lines_get_property;
+ object_class->dispose = g_plot_lines_dispose;
+ object_class->finalize = g_plot_lines_finalize;
+
+ g_object_class_override_property (object_class, ARG_VISIBLE, "visible");
+
+ g_object_class_install_property(
+ object_class,
+ ARG_WIDTH,
+ g_param_spec_double ("width", "GPlotLines::width",
+ "the line width", 0.1, 5.0, 1.0, G_PARAM_READWRITE));
+
+ g_object_class_install_property(
+ object_class,
+ ARG_COLOR,
+ g_param_spec_string ("color", "GPlotLines::color",
+ "the string color line", "white", G_PARAM_READWRITE));
+
+ g_object_class_install_property(
+ object_class,
+ ARG_COLOR_GDKCOLOR,
+ g_param_spec_pointer ("color-rgb", "GPlotLines::color-rgb",
+ "the GdkColor of the line", G_PARAM_READWRITE));
+}
+
+static void
+g_plot_lines_init (GPlotLines* plot)
+{
+ GPlotLinesPriv* priv = g_new0 (GPlotLinesPriv, 1);
+
+ priv->bbox_valid = FALSE;
+
+ plot->priv = priv;
+
+ priv->width = 1.0;
+ priv->visible = TRUE;
+ priv->color_string = g_strdup ("white");
+ memset (&priv->color, 0xFF, sizeof (GdkColor));
+}
+
+static void
+g_plot_lines_set_property(GObject *object, guint prop_id, const GValue *value,
+ GParamSpec *spec)
+{
+ GPlotLines *plot = GPLOT_LINES (object);
+
+ switch (prop_id) {
+ case ARG_VISIBLE:
+ plot->priv->visible = g_value_get_boolean (value);
+ break;
+ case ARG_WIDTH:
+ plot->priv->width = g_value_get_double (value);
+ break;
+ case ARG_COLOR:
+ g_free (plot->priv->color_string);
+ plot->priv->color_string = g_strdup (g_value_get_string (value));
+ gdk_color_parse (plot->priv->color_string, &plot->priv->color);
+ break;
+ case ARG_COLOR_GDKCOLOR:
+ //s = g_value_get_string (value)
+ //gdk_color_parse (s, &plot->priv->color);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+g_plot_lines_get_property(GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec)
+{
+ GPlotLines *plot = GPLOT_LINES (object);
+
+ switch (prop_id) {
+ case ARG_WIDTH:
+ g_value_set_double (value, plot->priv->width);
+ break;
+ case ARG_VISIBLE:
+ g_value_set_boolean (value, plot->priv->visible);
+ break;
+ case ARG_COLOR:
+ g_value_set_string (value, plot->priv->color_string);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (plot, prop_id, spec);
+ }
+}
+
+GPlotFunction*
+g_plot_lines_new (gdouble *x, gdouble *y, guint points)
+{
+ GPlotLines *plot;
+
+ plot = GPLOT_LINES (g_object_new (TYPE_GPLOT_LINES, NULL));
+ plot->priv->x = x;
+ plot->priv->y = y;
+ plot->priv->points = points;
+
+ return GPLOT_FUNCTION (plot);
+}
+
+static void
+g_plot_lines_get_bbox (GPlotFunction *f, GPlotFunctionBBox *bbox)
+{
+ GPlotLines *plot;
+
+ g_return_if_fail (IS_GPLOT_LINES (f));
+
+ plot = GPLOT_LINES (f);
+ if (!plot->priv->bbox_valid) {
+ /* Update bbox */
+ guint point;
+ gdouble *x;
+ gdouble *y;
+ guint points;
+
+ points = plot->priv->points;
+ x = plot->priv->x;
+ y = plot->priv->y;
+ plot->priv->bbox.xmin = 99999999;
+ plot->priv->bbox.xmax = -99999999;
+ plot->priv->bbox.ymin = 99999999;
+ plot->priv->bbox.ymax = -99999999;
+ for (point = 0; point < points; point++) {
+ plot->priv->bbox.xmin = MIN(plot->priv->bbox.xmin, x[point]);
+ plot->priv->bbox.ymin = MIN(plot->priv->bbox.ymin, y[point]);
+ plot->priv->bbox.xmax = MAX(plot->priv->bbox.xmax, x[point]);
+ plot->priv->bbox.ymax = MAX(plot->priv->bbox.ymax, y[point]);
+ }
+ plot->priv->bbox_valid = TRUE;
+ }
+
+ (*bbox) = plot->priv->bbox;
+}
+
+static void
+g_plot_lines_draw (GPlotFunction *f, cairo_t *cr, GPlotFunctionBBox *bbox)
+{
+ gboolean first_point = TRUE;
+ guint point;
+ gdouble *x;
+ gdouble *y;
+ guint points;
+ GPlotLines *plot;
+
+ g_return_if_fail (IS_GPLOT_LINES (f));
+
+ plot = GPLOT_LINES (f);
+
+ if (!plot->priv->visible) return;
+
+ points = plot->priv->points;
+ x = plot->priv->x;
+ y = plot->priv->y;
+
+ for (point = 1; point < points; point++) {
+ if ((x[point] >= bbox->xmin) && (x[point] <= bbox->xmax) &&
+ (y[point] >= bbox->ymin) && (y[point] <= bbox->ymax)) {
+
+ if (first_point) {
+ cairo_move_to (cr, x[point-1], y[point-1]);
+ first_point = FALSE;
+ }
+
+ cairo_line_to (cr, x[point], y[point]);
+ } else {
+ if (!first_point) {
+ cairo_line_to (cr, x[point], y[point]);
+ first_point = TRUE;
+ }
+ }
+ }
+
+ if (point < points) {
+ cairo_line_to (cr, x[point], y[point]);
+ }
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr,
+ plot->priv->color.red / 65535.0,
+ plot->priv->color.green / 65535.0,
+ plot->priv->color.blue / 65535.0);
+ cairo_identity_matrix (cr);
+ cairo_set_line_width (cr, plot->priv->width);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+}
+
diff --git a/src/gplot/gplotlines.h b/src/gplot/gplotlines.h
new file mode 100644
index 0000000..c3dafb2
--- /dev/null
+++ b/src/gplot/gplotlines.h
@@ -0,0 +1,55 @@
+/*
+ * gplotlines.h
+ *
+ * Authors:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GPLOT_LINES_H_
+#define _GPLOT_LINES_H_
+
+#include "gplotfunction.h"
+
+#define TYPE_GPLOT_LINES (g_plot_lines_get_type())
+#define GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, TYPE_GPLOT_LINES, GPlotLines)
+#define GPLOT_LINES_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, TYPE_GPLOT_LINES, GPlotLinesClass)
+#define IS_GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, TYPE_GPLOT_LINES)
+
+typedef struct _GPlotLines GPlotLines;
+typedef struct _GPlotLinesPriv GPlotLinesPriv;
+typedef struct _GPlotLinesClass GPlotLinesClass;
+
+struct _GPlotLines {
+ GObject parent;
+
+ GPlotLinesPriv *priv;
+};
+
+struct _GPlotLinesClass {
+ GObjectClass parent;
+};
+
+GType g_plot_lines_get_type ();
+GPlotFunction* g_plot_lines_new (gdouble *x, gdouble *y, guint points);
+
+#endif
+
+
diff --git a/src/load-common.h b/src/load-common.h
new file mode 100644
index 0000000..265bc1c
--- /dev/null
+++ b/src/load-common.h
@@ -0,0 +1,75 @@
+/*
+ * load-common.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 __LOAD_COMMON_H
+#define __LOAD_COMMON_H
+
+#include <glib.h>
+#include <libgnome/gnome-i18n.h>
+#include "sheet-pos.h"
+
+/* Note: this must be synced with Pin in part.h for now. */
+typedef struct {
+ SheetPos pos;
+} Connection;
+
+typedef struct {
+ gchar *name;
+ gchar *value;
+} Property;
+
+typedef struct {
+ gchar *name;
+ gchar *author;
+ gchar *version;
+
+ GSList *parts_list;
+
+ GHashTable *part_hash;
+ GHashTable *symbol_hash;
+} Library;
+
+typedef struct {
+ gchar *name;
+
+ gchar *description;
+
+ Library* library;
+
+ gchar *symbol_name;
+ int symbol_rotation;
+
+ gchar *refdes;
+ gchar *template;
+ gchar *model;
+ GSList *labels;
+ GSList *properties;
+} LibraryPart;
+
+#endif
diff --git a/src/load-library.c b/src/load-library.c
new file mode 100644
index 0000000..c489f6c
--- /dev/null
+++ b/src/load-library.c
@@ -0,0 +1,655 @@
+/*
+ * load-library.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 <string.h>
+#include "xml-compat.h"
+#include "main.h"
+#include "xml-helper.h"
+#include "load-common.h"
+#include "load-library.h"
+#include "sheet-private.h"
+#include "part-label.h"
+
+typedef enum {
+ PARSE_START,
+ PARSE_LIBRARY,
+ PARSE_NAME,
+ PARSE_AUTHOR,
+ PARSE_SYMBOLS,
+ PARSE_SYMBOL,
+ PARSE_SYMBOL_NAME,
+ PARSE_SYMBOL_OBJECTS,
+ PARSE_SYMBOL_LINE,
+ PARSE_SYMBOL_ARC,
+ PARSE_SYMBOL_TEXT,
+ PARSE_SYMBOL_CONNECTIONS,
+ PARSE_SYMBOL_CONNECTION,
+ PARSE_PARTS,
+ PARSE_PART,
+ PARSE_PART_NAME,
+ PARSE_PART_DESCRIPTION,
+ PARSE_PART_USESYMBOL,
+ PARSE_PART_LABELS,
+ PARSE_PART_LABEL,
+ PARSE_PART_LABEL_NAME,
+ PARSE_PART_LABEL_TEXT,
+ PARSE_PART_LABEL_POS,
+ PARSE_PART_PROPERTIES,
+ PARSE_PART_PROPERTY,
+ PARSE_PART_PROPERTY_NAME,
+ PARSE_PART_PROPERTY_VALUE,
+ PARSE_FINISH,
+ PARSE_UNKNOWN,
+ PARSE_ERROR
+} State;
+
+typedef struct {
+ Library *library;
+
+ State state;
+ State prev_state;
+ gint unknown_depth;
+ GString *content;
+
+ /* Temporary placeholder for part */
+ LibraryPart *part;
+ PartLabel *label;
+ Property *property;
+
+ /* Temporary placeholder for symbol */
+ LibrarySymbol *symbol;
+ Connection *connection;
+ SymbolObject *object;
+} ParseState;
+
+static xmlEntityPtr get_entity (void *user_data, const xmlChar *name);
+static void start_document (ParseState *state);
+static void end_document (ParseState *state);
+static void start_element (ParseState *state, const xmlChar *name,
+ const xmlChar **attrs);
+static void end_element (ParseState *state, const xmlChar *name);
+
+static void my_characters (ParseState *state, const xmlChar *chars, int len);
+static void my_warning (void *user_data, const char *msg, ...);
+static void my_error (void *user_data, const char *msg, ...);
+static void my_fatal_error (void *user_data, const char *msg, ...);
+
+static xmlSAXHandler oreganoSAXParser = {
+ 0, /* internalSubset */
+ 0, /* isStandalone */
+ 0, /* hasInternalSubset */
+ 0, /* hasExternalSubset */
+ 0, /* resolveEntity */
+ (getEntitySAXFunc) get_entity, /* getEntity */
+ 0, /* entityDecl */
+ 0, /* notationDecl */
+ 0, /* attributeDecl */
+ 0, /* elementDecl */
+ 0, /* unparsedEntityDecl */
+ 0, /* setDocumentLocator */
+ (startDocumentSAXFunc) start_document, /* startDocument */
+ (endDocumentSAXFunc) end_document, /* endDocument */
+ (startElementSAXFunc)start_element, /* startElement */
+ (endElementSAXFunc)end_element, /* endElement */
+ 0, /* reference */
+ (charactersSAXFunc) my_characters, /* characters */
+ 0, /* ignorableWhitespace */
+ 0, /* processingInstruction */
+ 0, /*(commentSAXFunc)0, comment */
+ (warningSAXFunc)my_warning, /* warning */
+ (errorSAXFunc)my_error, /* error */
+ (fatalErrorSAXFunc)my_fatal_error, /* fatalError */
+};
+
+LibrarySymbol *
+library_get_symbol (const gchar *symbol_name)
+{
+ LibrarySymbol *symbol;
+ Library *library;
+ GList *libraries;
+
+ g_return_val_if_fail (symbol_name != NULL, NULL);
+
+ symbol = NULL;
+ for (libraries = oregano.libraries; libraries; libraries = libraries->next) {
+ library = libraries->data;
+ symbol = g_hash_table_lookup (library->symbol_hash, symbol_name);
+ if (symbol)
+ break;
+ }
+
+ if (symbol == NULL) {
+ g_message (_("Could not find the requested symbol: %s\n"), symbol_name);
+ }
+
+ return symbol;
+}
+
+LibraryPart *
+library_get_part (Library *library, const gchar *part_name)
+{
+ LibraryPart *part;
+
+ g_return_val_if_fail (library != NULL, NULL);
+ g_return_val_if_fail (part_name != NULL, NULL);
+
+ part = g_hash_table_lookup (library->part_hash, part_name);
+ if (part == NULL){
+ g_message (_("Could not find the requested part: %s\n"), part_name);
+ }
+ return part;
+}
+
+Library *
+library_parse_xml_file (const gchar *filename)
+{
+ Library *library;
+ ParseState state;
+
+ if (oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename) < 0) {
+ g_warning ("Library '%s' not well formed!", filename);
+ }
+
+ if (state.state == PARSE_ERROR) {
+ library = NULL;
+ } else {
+ library = state.library;
+ }
+
+ return library;
+}
+
+static void
+start_document (ParseState *state)
+{
+ state->state = PARSE_START;
+ state->unknown_depth = 0;
+ state->prev_state = PARSE_UNKNOWN;
+
+ state->content = g_string_sized_new (128);
+ state->part = NULL;
+ state->symbol = NULL;
+ state->connection = NULL;
+ state->label = NULL;
+ state->property = NULL;
+
+ state->library = g_new0 (Library, 1);
+ state->library->name = NULL;
+ state->library->author = NULL;
+ state->library->version = NULL;
+
+ state->library->part_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ state->library->symbol_hash = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+static void
+end_document (ParseState *state)
+{
+ if (state->unknown_depth != 0)
+ g_warning ("unknown_depth != 0 (%d)", state->unknown_depth);
+}
+
+static void
+start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs)
+{
+ const char *name = (const char *)xml_name;
+
+ switch (state->state) {
+ case PARSE_START:
+ if (strcmp (name, "ogo:library")) {
+ g_warning ("Expecting 'ogo:library'. Got '%s'", name);
+ state->state = PARSE_ERROR;
+ } else
+ state->state = PARSE_LIBRARY;
+ break;
+
+ case PARSE_LIBRARY:
+ if (!strcmp (name, "ogo:author")) {
+ state->state = PARSE_AUTHOR;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:name")) {
+ state->state = PARSE_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:symbols")) {
+ state->state = PARSE_SYMBOLS;
+ } else if (!strcmp (name, "ogo:parts")) {
+ state->state = PARSE_PARTS;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_SYMBOLS:
+ if (!strcmp (name, "ogo:symbol")) {
+ state->state = PARSE_SYMBOL;
+ state->symbol = g_new0 (LibrarySymbol, 1);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_SYMBOL:
+ if (!strcmp (name, "ogo:name")) {
+ state->state = PARSE_SYMBOL_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:objects")) {
+ state->state = PARSE_SYMBOL_OBJECTS;
+ } else if (!strcmp (name, "ogo:connections")) {
+ state->state = PARSE_SYMBOL_CONNECTIONS;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_SYMBOL_OBJECTS:
+ if (!strcmp (name, "ogo:line")) {
+ state->object = g_new0 (SymbolObject, 1);
+ state->object->type = SYMBOL_OBJECT_LINE;
+ state->state = PARSE_SYMBOL_LINE;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:arc")) {
+ state->object = g_new0 (SymbolObject, 1);
+ state->object->type = SYMBOL_OBJECT_ARC;
+ state->state = PARSE_SYMBOL_ARC;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:text")) {
+ state->object = g_new0 (SymbolObject, 1);
+ state->object->type = SYMBOL_OBJECT_TEXT;
+ state->state = PARSE_SYMBOL_TEXT;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_SYMBOL_CONNECTIONS:
+ if (!strcmp (name, "ogo:connection")) {
+ state->state = PARSE_SYMBOL_CONNECTION;
+ state->connection = g_new0 (Connection, 1);
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+
+ case PARSE_PARTS:
+ if (!strcmp (name, "ogo:part")) {
+ state->state = PARSE_PART;
+ state->part = g_new0 (LibraryPart, 1);
+ state->part->library = state->library;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART:
+ if (!strcmp (name, "ogo:name")) {
+ state->state = PARSE_PART_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:description")) {
+ state->state = PARSE_PART_DESCRIPTION;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:symbol")) {
+ state->state = PARSE_PART_USESYMBOL;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:labels")) {
+ state->state = PARSE_PART_LABELS;
+ } else if (!strcmp (name, "ogo:properties")) {
+ state->state = PARSE_PART_PROPERTIES;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_LABELS:
+ if (!strcmp (name, "ogo:label")) {
+ state->state = PARSE_PART_LABEL;
+ state->label = g_new0 (PartLabel, 1);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_LABEL:
+ if (!strcmp (name, "ogo:name")) {
+ state->state = PARSE_PART_LABEL_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:text")) {
+ state->state = PARSE_PART_LABEL_TEXT;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:position")) {
+ state->state = PARSE_PART_LABEL_POS;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_PART_PROPERTIES:
+ if (!strcmp (name, "ogo:property")) {
+ state->state = PARSE_PART_PROPERTY;
+ state->property = g_new0 (Property, 1);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_PROPERTY:
+ if (!strcmp (name, "ogo:name")) {
+ state->state = PARSE_PART_PROPERTY_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!strcmp (name, "ogo:value")) {
+ state->state = PARSE_PART_PROPERTY_VALUE;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_SYMBOL_NAME:
+ case PARSE_SYMBOL_LINE:
+ case PARSE_SYMBOL_ARC:
+ case PARSE_SYMBOL_TEXT:
+ case PARSE_SYMBOL_CONNECTION:
+ case PARSE_PART_NAME:
+ case PARSE_PART_DESCRIPTION:
+ case PARSE_PART_USESYMBOL:
+ case PARSE_PART_LABEL_NAME:
+ case PARSE_PART_LABEL_TEXT:
+ case PARSE_PART_LABEL_POS:
+ case PARSE_PART_PROPERTY_NAME:
+ case PARSE_PART_PROPERTY_VALUE:
+ case PARSE_NAME:
+ case PARSE_AUTHOR:
+ /* there should be no tags inside these types of tags */
+ g_message ("*** '%s' tag found", name);
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ break;
+
+ case PARSE_ERROR:
+ break;
+ case PARSE_UNKNOWN:
+ state->unknown_depth++;
+ break;
+ case PARSE_FINISH:
+ /* should not start new elements in this state */
+ g_assert_not_reached ();
+ break;
+ }
+ /*g_message("Start element %s (state %s)", name, states[state->state]);*/
+}
+
+static void
+end_element (ParseState *state, const xmlChar *name)
+{
+ switch (state->state) {
+ case PARSE_UNKNOWN:
+ state->unknown_depth--;
+ if (state->unknown_depth == 0)
+ state->state = state->prev_state;
+ break;
+ case PARSE_AUTHOR:
+ state->library->author = g_strdup (state->content->str);
+ state->state = PARSE_LIBRARY;
+ break;
+ case PARSE_NAME:
+ state->library->name = g_strdup (state->content->str);
+ state->state = PARSE_LIBRARY;
+ break;
+ case PARSE_SYMBOLS:
+ state->state = PARSE_LIBRARY;
+ break;
+ case PARSE_SYMBOL:
+ g_hash_table_insert (state->library->symbol_hash, state->symbol->name,
+ state->symbol);
+ state->state = PARSE_SYMBOLS;
+ break;
+ case PARSE_SYMBOL_NAME:
+ state->symbol->name = g_strdup (state->content->str);
+ state->state = PARSE_SYMBOL;
+ break;
+ case PARSE_SYMBOL_OBJECTS:
+ state->state = PARSE_SYMBOL;
+ break;
+ case PARSE_SYMBOL_LINE:
+ {
+ int i, j;
+ gchar *ptr;
+ gchar **points;
+
+ i= sscanf (state->content->str,"%d %n",
+ &state->object->u.uline.spline,&j);
+ if ( i )
+ ptr = state->content->str+j;
+ else {
+ state->object->u.uline.spline = FALSE;
+ ptr = state->content->str;
+ }
+
+ points = g_strsplit (ptr, "(", 0);
+
+ i = 0;
+ /* Count the points. */
+ while (points[i] != NULL) {
+ i++;
+ }
+
+ /* Do not count the first string, which simply is a (. */
+ i--;
+
+ /* Construct gnome canvas points. */
+ state->object->u.uline.line = gnome_canvas_points_new (i);
+ for (j = 0; j < i; j++) {
+ double x, y;
+
+ sscanf (points[j+1], "%lf %lf)", &x, &y);
+
+ state->object->u.uline.line->coords[2 * j] = x;
+ state->object->u.uline.line->coords[2 * j + 1] = y;
+ }
+
+ g_strfreev (points);
+ state->symbol->symbol_objects = g_slist_prepend (state->symbol->symbol_objects, state->object);
+ state->state = PARSE_SYMBOL_OBJECTS;
+ }
+ break;
+ case PARSE_SYMBOL_ARC:
+ sscanf (state->content->str, "(%lf %lf)(%lf %lf)",
+ &state->object->u.arc.x1, &state->object->u.arc.y1,
+ &state->object->u.arc.x2, &state->object->u.arc.y2);
+ state->symbol->symbol_objects = g_slist_prepend (state->symbol->symbol_objects, state->object);
+ state->state = PARSE_SYMBOL_OBJECTS;
+ break;
+
+ case PARSE_SYMBOL_TEXT:
+ sscanf ( state->content->str,"(%d %d)%s",
+ &state->object->u.text.x,
+ &state->object->u.text.y,
+ state->object->u.text.str
+ );
+ state->symbol->symbol_objects = g_slist_prepend (state->symbol->symbol_objects, state->object);
+ state->state = PARSE_SYMBOL_OBJECTS;
+ break;
+
+ case PARSE_SYMBOL_CONNECTIONS:
+ state->state = PARSE_SYMBOL;
+ state->symbol->connections = g_slist_reverse (state->symbol->connections);
+ break;
+ case PARSE_SYMBOL_CONNECTION:
+ sscanf (state->content->str, "(%lf %lf)", &state->connection->pos.x,
+ &state->connection->pos.y);
+ state->symbol->connections = g_slist_prepend (state->symbol->connections, state->connection);
+ state->state = PARSE_SYMBOL_CONNECTIONS;
+ break;
+
+ case PARSE_PARTS:
+ state->state = PARSE_LIBRARY;
+ break;
+ case PARSE_PART:
+ g_hash_table_insert (state->library->part_hash, state->part->name,
+ state->part);
+ state->state = PARSE_PARTS;
+ break;
+ case PARSE_PART_NAME:
+ state->part->name = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_DESCRIPTION:
+ state->part->description = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_USESYMBOL:
+ state->part->symbol_name = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_LABELS:
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_LABEL:
+ state->state = PARSE_PART_LABELS;
+ state->part->labels = g_slist_prepend (state->part->labels,
+ state->label);
+ break;
+ case PARSE_PART_LABEL_NAME:
+ state->label->name = g_strdup (state->content->str);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_LABEL_TEXT:
+ state->label->text = g_strdup (state->content->str);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_LABEL_POS:
+ sscanf (state->content->str, "(%lf %lf)", &state->label->pos.x,
+ &state->label->pos.y);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_PROPERTIES:
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_PROPERTY:
+ state->state = PARSE_PART_PROPERTIES;
+ state->part->properties = g_slist_prepend (state->part->properties,
+ state->property);
+ break;
+ case PARSE_PART_PROPERTY_NAME:
+ state->property->name = g_strdup (state->content->str);
+ state->state = PARSE_PART_PROPERTY;
+ break;
+ case PARSE_PART_PROPERTY_VALUE:
+ state->property->value = g_strdup (state->content->str);
+ state->state = PARSE_PART_PROPERTY;
+ break;
+
+ case PARSE_LIBRARY:
+ /* The end of the file. */
+ state->state = PARSE_FINISH;
+ break;
+ case PARSE_ERROR:
+ break;
+ case PARSE_START:
+ case PARSE_FINISH:
+ /* There should not be a closing tag in this state. */
+ g_assert_not_reached ();
+ break;
+ }
+ /*g_message("End element %s (state %s)", name, states[state->state]);*/
+}
+
+static void
+my_characters (ParseState *state, const xmlChar *chars, int len)
+{
+ int i;
+
+ if (state->state == PARSE_FINISH ||
+ state->state == PARSE_START ||
+ state->state == PARSE_PARTS ||
+ state->state == PARSE_PART)
+ return;
+
+ for (i = 0; i < len; i++)
+ g_string_append_c (state->content, chars[i]);
+}
+
+static xmlEntityPtr
+get_entity (void *user_data, const xmlChar *name)
+{
+ return xmlGetPredefinedEntity (name);
+}
+
+static void
+my_warning (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_WARNING, msg, args);
+ va_end (args);
+}
+
+static void
+my_error (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
+ va_end (args);
+}
+
+static void
+my_fatal_error (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_ERROR, msg, args);
+ va_end (args);
+}
+
diff --git a/src/load-library.h b/src/load-library.h
new file mode 100644
index 0000000..d08316c
--- /dev/null
+++ b/src/load-library.h
@@ -0,0 +1,78 @@
+/*
+ * load-library.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 __LOAD_LIBRARY_H
+#define __LOAD_LIBRARY_H
+
+#include <gnome.h>
+#include "sheet.h"
+
+typedef struct _SymbolObject SymbolObject;
+typedef struct _LibrarySymbol LibrarySymbol;
+
+#include "load-common.h"
+
+struct _LibrarySymbol {
+ char *name;
+ GSList *connections;
+ GSList *symbol_objects;
+};
+
+typedef enum {
+ SYMBOL_OBJECT_LINE,
+ SYMBOL_OBJECT_ARC,
+ SYMBOL_OBJECT_TEXT
+} SymbolObjectType;
+
+struct _SymbolObject {
+ SymbolObjectType type;
+
+ union {
+ struct {
+ gboolean spline;
+ GnomeCanvasPoints *line;
+ } uline;
+ struct {
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+ } arc;
+ struct {
+ gint x,y;
+ gchar str[256];
+ } text;
+ } u;
+};
+
+Library *library_parse_xml_file (const gchar *filename);
+LibrarySymbol *library_get_symbol (const gchar *symbol_name);
+LibraryPart *library_get_part (Library *library, const gchar *part_name);
+
+#endif
diff --git a/src/load-schematic.c b/src/load-schematic.c
new file mode 100644
index 0000000..654bc1a
--- /dev/null
+++ b/src/load-schematic.c
@@ -0,0 +1,1024 @@
+/*
+ * load-schematic.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 <string.h>
+#include "xml-compat.h"
+#include "main.h"
+#include "xml-helper.h"
+#include "create-wire.h"
+#include "load-common.h"
+#include "load-schematic.h"
+#include "sheet-pos.h"
+#include "part-label.h"
+#include "sim-settings.h"
+#include "textbox.h"
+#include "errors.h"
+
+typedef enum {
+ PARSE_START,
+ PARSE_SCHEMATIC,
+ PARSE_TITLE,
+ PARSE_AUTHOR,
+ PARSE_COMMENTS,
+
+ PARSE_SIMULATION_SETTINGS,
+ PARSE_TRANSIENT_SETTINGS,
+ PARSE_TRANSIENT_ENABLED,
+ PARSE_TRANSIENT_START,
+ PARSE_TRANSIENT_STOP,
+ PARSE_TRANSIENT_STEP,
+ PARSE_TRANSIENT_STEP_ENABLE,
+ PARSE_TRANSIENT_INIT_COND,
+
+ PARSE_AC_SETTINGS,
+ PARSE_AC_ENABLED,
+ PARSE_AC_NPOINTS,
+ PARSE_AC_START,
+ PARSE_AC_STOP,
+
+ PARSE_DC_SETTINGS,
+ PARSE_DC_ENABLED,
+ PARSE_DC_VSRC1,
+ PARSE_DC_START1,
+ PARSE_DC_STOP1,
+ PARSE_DC_STEP1,
+ PARSE_DC_VSRC2,
+ PARSE_DC_START2,
+ PARSE_DC_STOP2,
+ PARSE_DC_STEP2,
+
+
+ PARSE_OPTION_LIST,
+ PARSE_OPTION,
+ PARSE_OPTION_NAME,
+ PARSE_OPTION_VALUE,
+
+
+ PARSE_ZOOM,
+
+ PARSE_PARTS,
+ PARSE_PART,
+ PARSE_PART_NAME,
+ PARSE_PART_LIBNAME,
+ PARSE_PART_REFDES,
+ PARSE_PART_POSITION,
+ PARSE_PART_ROTATION,
+ PARSE_PART_FLIP,
+ PARSE_PART_SYMNAME,
+ PARSE_PART_TEMPLATE,
+ PARSE_PART_MODEL,
+ PARSE_PART_LABELS,
+ PARSE_PART_LABEL,
+ PARSE_PART_LABEL_NAME,
+ PARSE_PART_LABEL_TEXT,
+ PARSE_PART_LABEL_POS,
+ PARSE_PART_PROPERTIES,
+ PARSE_PART_PROPERTY,
+ PARSE_PART_PROPERTY_NAME,
+ PARSE_PART_PROPERTY_VALUE,
+ PARSE_WIRES,
+ PARSE_WIRE,
+ PARSE_WIRE_POINTS,
+/* PARSE_WIRE_LABEL,*/
+ PARSE_TEXTBOXES,
+ PARSE_TEXTBOX,
+ PARSE_TEXTBOX_TEXT,
+ PARSE_TEXTBOX_POSITION,
+ PARSE_FINISH,
+ PARSE_UNKNOWN,
+ PARSE_ERROR
+} State;
+
+typedef struct {
+ State state;
+ State prev_state;
+ int unknown_depth;
+ GString *content;
+ Schematic *schematic;
+ SimSettings *sim_settings;
+
+ char *title;
+ char *author;
+ char *comments;
+
+ char *textbox_text;
+
+ /* Temporary place holder for a wire */
+ SheetPos wire_start;
+ SheetPos wire_end;
+
+ /* Temporary place holder for a part */
+ LibraryPart *part;
+ PartLabel *label;
+ Property *property;
+ SheetPos pos;
+ int rotation;
+ IDFlip flip;
+
+ /* Temporary place holder for an option */
+ SimOption *option;
+} ParseState;
+
+static xmlEntityPtr get_entity (void *user_data, const xmlChar *name);
+static void start_document (ParseState *state);
+static void end_document (ParseState *state);
+static void start_element (ParseState *state, const xmlChar *name,
+ const xmlChar **attrs);
+static void end_element (ParseState *state, const xmlChar *name);
+
+static void my_characters (ParseState *state, const xmlChar *chars, int len);
+static void my_warning (void *user_data, const char *msg, ...);
+static void my_error (void *user_data, const char *msg, ...);
+static void my_fatal_error (void *user_data, const char *msg, ...);
+
+static void create_wire (ParseState *state);
+static void create_part (ParseState *state);
+
+static xmlSAXHandler oreganoSAXParser = {
+ 0, /* internalSubset */
+ 0, /* isStandalone */
+ 0, /* hasInternalSubset */
+ 0, /* hasExternalSubset */
+ 0, /* resolveEntity */
+ (getEntitySAXFunc) get_entity, /* getEntity */
+ 0, /* entityDecl */
+ 0, /* notationDecl */
+ 0, /* attributeDecl */
+ 0, /* elementDecl */
+ 0, /* unparsedEntityDecl */
+ 0, /* setDocumentLocator */
+ (startDocumentSAXFunc) start_document, /* startDocument */
+ (endDocumentSAXFunc) end_document, /* endDocument */
+ (startElementSAXFunc)start_element, /* startElement */
+ (endElementSAXFunc)end_element, /* endElement */
+ 0, /* reference */
+ (charactersSAXFunc) my_characters, /* characters */
+ 0, /* ignorableWhitespace */
+ 0, /* processingInstruction */
+ 0, /*(commentSAXFunc)0, comment */
+ (warningSAXFunc)my_warning, /* warning */
+ (errorSAXFunc)my_error, /* error */
+ (fatalErrorSAXFunc)my_fatal_error, /* fatalError */
+};
+
+static void
+create_textbox (ParseState *state)
+{
+ Textbox *textbox;
+
+ textbox = textbox_new (NULL);
+ item_data_set_pos (ITEM_DATA (textbox), &state->pos);
+ textbox_set_text (textbox, state->textbox_text);
+
+ schematic_add_item (state->schematic, ITEM_DATA (textbox));
+}
+
+static void
+create_wire (ParseState *state)
+{
+ SheetPos start_pos, length;
+ Wire *wire;
+
+ start_pos.x = state->wire_start.x;
+ start_pos.y = state->wire_start.y;
+ length.x = state->wire_end.x - start_pos.x;
+ length.y = state->wire_end.y - start_pos.y;
+
+ wire = wire_new ();
+ item_data_set_pos (ITEM_DATA (wire), &state->wire_start);
+ wire_set_length (wire, &length);
+
+ schematic_add_item (state->schematic, ITEM_DATA (wire));
+}
+
+static void
+create_part (ParseState *state)
+{
+ Part *part;
+ LibraryPart *library_part = state->part;
+
+ part = part_new_from_library_part (library_part);
+ if (!part)
+ return;
+
+ item_data_set_pos (ITEM_DATA (part), &state->pos);
+ item_data_rotate (ITEM_DATA (part), state->rotation, NULL);
+
+ if (state->flip & ID_FLIP_HORIZ)
+ item_data_flip (ITEM_DATA (part), TRUE, NULL);
+ if (state->flip & ID_FLIP_VERT)
+ item_data_flip (ITEM_DATA (part), FALSE, NULL);
+
+ schematic_add_item (state->schematic, ITEM_DATA (part));
+}
+
+int
+schematic_parse_xml_file (Schematic *sm, const char *filename, GError **error)
+{
+ ParseState state;
+ int retval = 0;
+
+ state.schematic = sm;
+ state.sim_settings = schematic_get_sim_settings (sm);
+ state.title = NULL;
+ state.author = NULL;
+ state.comments = NULL;
+
+ if (oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename) < 0) {
+ g_warning ("Document not well formed!");
+ if (error != NULL) {
+ g_set_error (error,
+ OREGANO_ERROR,
+ OREGANO_SCHEMATIC_BAD_FILE_FORMAT,
+ _("Bad file format."));
+ }
+ retval = -1;
+ }
+
+ if (state.state == PARSE_ERROR) {
+ if (error != NULL) {
+ g_set_error (error,
+ OREGANO_ERROR,
+ OREGANO_SCHEMATIC_BAD_FILE_FORMAT,
+ _("Unknown parser error."));
+ }
+ retval = -2;
+ }
+
+ schematic_set_author (sm, state.author);
+ schematic_set_title (sm, state.title);
+ schematic_set_comments (sm, state.comments);
+
+ return retval;
+}
+
+static void
+start_document (ParseState *state)
+{
+ state->state = PARSE_START;
+ state->unknown_depth = 0;
+ state->prev_state = PARSE_UNKNOWN;
+
+ state->content = g_string_sized_new (128);
+ state->part = NULL;
+}
+
+static void
+end_document (ParseState *state)
+{
+
+ if (state->unknown_depth != 0)
+ g_warning ("unknown_depth != 0 (%d)", state->unknown_depth);
+}
+
+static void
+start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs)
+{
+ switch (state->state) {
+ case PARSE_START:
+ if (xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:schematic")) {
+ g_warning ("Expecting 'ogo:schematic'. Got '%s'", name);
+ state->state = PARSE_ERROR;
+ } else
+ state->state = PARSE_SCHEMATIC;
+ break;
+
+ case PARSE_SCHEMATIC:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:author")) {
+ state->state = PARSE_AUTHOR;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:title")) {
+ state->state = PARSE_TITLE;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:comments")) {
+ state->state = PARSE_COMMENTS;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:zoom")) {
+ state->state = PARSE_ZOOM;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:simulation-settings")) {
+ state->state = PARSE_SIMULATION_SETTINGS;
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:parts")) {
+ state->state = PARSE_PARTS;
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wires")) {
+ state->state = PARSE_WIRES;
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textboxes")) {
+ state->state = PARSE_TEXTBOXES;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_SIMULATION_SETTINGS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:transient")) {
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ } else if ( !xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:ac")) {
+ state->state = PARSE_AC_SETTINGS;
+ } else if ( !xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:dc-sweep")) {
+ state->state = PARSE_DC_SETTINGS;
+ }
+ else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:options")) {
+ state->state = PARSE_OPTION_LIST;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_TRANSIENT_SETTINGS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) {
+ state->state = PARSE_TRANSIENT_ENABLED;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:start")) {
+ state->state = PARSE_TRANSIENT_START;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:stop")) {
+ state->state = PARSE_TRANSIENT_STOP;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:step")) {
+ state->state = PARSE_TRANSIENT_STEP;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:step-enabled")) {
+ state->state = PARSE_TRANSIENT_STEP_ENABLE;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:init-conditions")) {
+ state->state = PARSE_TRANSIENT_INIT_COND;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_AC_SETTINGS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) {
+ state->state = PARSE_AC_ENABLED;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:npoints")) {
+ state->state = PARSE_AC_NPOINTS;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:start")) {
+ state->state = PARSE_AC_START;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:stop")) {
+ state->state = PARSE_AC_STOP;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_DC_SETTINGS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) {
+ state->state = PARSE_DC_ENABLED;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:vsrc1")) {
+ state->state = PARSE_DC_VSRC1;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:start1")) {
+ state->state = PARSE_DC_START1;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:stop1")) {
+ state->state = PARSE_DC_STOP1;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:step1")) {
+ state->state = PARSE_DC_STEP1;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:vsrc2")) {
+ state->state = PARSE_DC_VSRC2;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:start2")) {
+ state->state = PARSE_DC_START2;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:stop2")) {
+ state->state = PARSE_DC_STOP2;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:step2")) {
+ state->state = PARSE_DC_STEP2;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_OPTION_LIST:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:option")) {
+ state->state = PARSE_OPTION;
+ state->option= g_new0(SimOption,1);
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_OPTION:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:name")) {
+ state->state = PARSE_OPTION_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:value")) {
+ state->state = PARSE_OPTION_VALUE;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+
+ break;
+ case PARSE_PARTS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:part")) {
+ state->state = PARSE_PART;
+ state->part = g_new0 (LibraryPart, 1);
+ state->rotation = 0;
+ state->flip = ID_FLIP_NONE;
+ state->label = NULL;
+ state->property = NULL;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:name")) {
+ state->state = PARSE_PART_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:library")) {
+ state->state = PARSE_PART_LIBNAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:refdes")) {
+ state->state = PARSE_PART_REFDES;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:position")) {
+ state->state = PARSE_PART_POSITION;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:rotation")) {
+ state->state = PARSE_PART_ROTATION;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:flip")) {
+ state->state = PARSE_PART_FLIP;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:template")) {
+ state->state = PARSE_PART_TEMPLATE;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:model")) {
+ state->state = PARSE_PART_MODEL;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:symbol")) {
+ state->state = PARSE_PART_SYMNAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:labels")) {
+ state->state = PARSE_PART_LABELS;
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:properties")) {
+ state->state = PARSE_PART_PROPERTIES;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_LABELS:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:label")) {
+ state->state = PARSE_PART_LABEL;
+ state->label = g_new0 (PartLabel, 1);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_LABEL:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:name")) {
+ state->state = PARSE_PART_LABEL_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:text")) {
+ state->state = PARSE_PART_LABEL_TEXT;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:position")) {
+ state->state = PARSE_PART_LABEL_POS;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_PART_PROPERTIES:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:property")) {
+ state->state = PARSE_PART_PROPERTY;
+ state->property = g_new0 (Property, 1);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_PART_PROPERTY:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:name")) {
+ state->state = PARSE_PART_PROPERTY_NAME;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:value")) {
+ state->state = PARSE_PART_PROPERTY_VALUE;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+
+ case PARSE_WIRES:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:wire")) {
+ state->state = PARSE_WIRE;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_WIRE:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:points")) {
+ state->state = PARSE_WIRE_POINTS;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_TEXTBOXES:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:textbox")) {
+ state->state = PARSE_TEXTBOX;
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSE_TEXTBOX:
+ if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:position")) {
+ state->state = PARSE_TEXTBOX_POSITION;
+ g_string_truncate (state->content, 0);
+ } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:text")) {
+ state->state = PARSE_TEXTBOX_TEXT;
+ g_string_truncate (state->content, 0);
+ } else {
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+
+ case PARSE_TRANSIENT_ENABLED:
+ case PARSE_TRANSIENT_START:
+ case PARSE_TRANSIENT_STOP:
+ case PARSE_TRANSIENT_STEP:
+ case PARSE_TRANSIENT_STEP_ENABLE:
+
+ case PARSE_AC_ENABLED:
+ case PARSE_AC_NPOINTS:
+ case PARSE_AC_START:
+ case PARSE_AC_STOP:
+
+ case PARSE_DC_ENABLED:
+ case PARSE_DC_VSRC1:
+ case PARSE_DC_START1:
+ case PARSE_DC_STOP1:
+ case PARSE_DC_STEP1:
+ case PARSE_DC_VSRC2:
+ case PARSE_DC_START2:
+ case PARSE_DC_STOP2:
+ case PARSE_DC_STEP2:
+
+ case PARSE_WIRE_POINTS:
+ case PARSE_OPTION_NAME:
+ case PARSE_OPTION_VALUE:
+ case PARSE_PART_NAME:
+ case PARSE_PART_LIBNAME:
+ case PARSE_PART_TEMPLATE:
+ case PARSE_PART_MODEL:
+ case PARSE_PART_REFDES:
+ case PARSE_PART_POSITION:
+ case PARSE_PART_ROTATION:
+ case PARSE_PART_FLIP:
+ case PARSE_PART_SYMNAME:
+ case PARSE_PART_LABEL_NAME:
+ case PARSE_PART_LABEL_TEXT:
+ case PARSE_PART_LABEL_POS:
+ case PARSE_PART_PROPERTY_NAME:
+ case PARSE_PART_PROPERTY_VALUE:
+ case PARSE_TEXTBOX_POSITION:
+ case PARSE_TEXTBOX_TEXT:
+ case PARSE_ZOOM:
+ case PARSE_TITLE:
+ case PARSE_AUTHOR:
+ case PARSE_COMMENTS:
+ /* there should be no tags inside these types of tags */
+ g_message ("*** '%s' tag found", name);
+ state->prev_state = state->state;
+ state->state = PARSE_UNKNOWN;
+ state->unknown_depth++;
+ break;
+
+ case PARSE_ERROR:
+ break;
+ case PARSE_UNKNOWN:
+ state->unknown_depth++;
+ break;
+ case PARSE_FINISH:
+ /* should not start new elements in this state */
+ g_assert_not_reached ();
+ break;
+ case PARSE_TRANSIENT_INIT_COND:
+ break;
+ }
+ /*g_message("Start element %s (state %s)", name, states[state->state]);*/
+}
+
+
+/*
+ *
+ */
+static void
+end_element (ParseState *state, const xmlChar *name)
+{
+ GList *libs;
+
+ switch (state->state) {
+ case PARSE_UNKNOWN:
+ state->unknown_depth--;
+ if (state->unknown_depth == 0)
+ state->state = state->prev_state;
+ break;
+ case PARSE_AUTHOR:
+ state->author = g_strdup (state->content->str);
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_TITLE:
+ state->title = g_strdup (state->content->str);
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_COMMENTS:
+ state->comments = g_strdup (state->content->str);
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_ZOOM:
+ {
+ double zoom;
+
+ zoom = g_strtod (state->content->str, NULL);
+ schematic_set_zoom (state->schematic, zoom);
+ state->state = PARSE_SCHEMATIC;
+
+ break;
+ }
+
+ case PARSE_SIMULATION_SETTINGS:
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_TRANSIENT_SETTINGS:
+ state->state = PARSE_SIMULATION_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_ENABLED:
+ sim_settings_set_trans (state->sim_settings,
+ !g_strcasecmp (state->content->str, "true"));
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_START:
+ sim_settings_set_trans_start (state->sim_settings,
+ state->content->str);
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_STOP:
+ sim_settings_set_trans_stop (state->sim_settings, state->content->str);
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_STEP:
+ sim_settings_set_trans_step (state->sim_settings, state->content->str);
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_STEP_ENABLE:
+ sim_settings_set_trans_step_enable (state->sim_settings,
+ !g_strcasecmp (state->content->str, "true"));
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+ case PARSE_TRANSIENT_INIT_COND:
+ sim_settings_set_trans_init_cond(state->sim_settings,
+ !g_strcasecmp (state->content->str, "true"));
+ state->state = PARSE_TRANSIENT_SETTINGS;
+ break;
+
+ case PARSE_AC_SETTINGS:
+ state->state = PARSE_SIMULATION_SETTINGS;
+ break;
+ case PARSE_AC_ENABLED:
+ sim_settings_set_ac (state->sim_settings,
+ !g_strcasecmp (state->content->str, "true"));
+ state->state = PARSE_AC_SETTINGS;
+ case PARSE_AC_NPOINTS:
+ sim_settings_set_ac_npoints (state->sim_settings, state->content->str);
+ state->state = PARSE_AC_SETTINGS;
+ case PARSE_AC_START:
+ sim_settings_set_ac_start (state->sim_settings, state->content->str);
+ state->state = PARSE_AC_SETTINGS;
+ break;
+ case PARSE_AC_STOP:
+ sim_settings_set_ac_stop (state->sim_settings, state->content->str);
+ state->state = PARSE_AC_SETTINGS;
+ break;
+
+ case PARSE_DC_SETTINGS:
+ state->state = PARSE_SIMULATION_SETTINGS;
+ break;
+
+ case PARSE_DC_ENABLED:
+ sim_settings_set_dc (state->sim_settings,
+ !g_strcasecmp (state->content->str, "true"));
+ state->state = PARSE_DC_SETTINGS;
+ case PARSE_DC_VSRC1:
+ sim_settings_set_dc_vsrc (state->sim_settings, 0, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ case PARSE_DC_START1:
+ sim_settings_set_dc_start (state->sim_settings, 0, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+ case PARSE_DC_STOP1:
+ sim_settings_set_dc_stop (state->sim_settings, 0, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+ case PARSE_DC_STEP1:
+ sim_settings_set_dc_step (state->sim_settings, 0, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+ case PARSE_DC_VSRC2:
+ sim_settings_set_dc_vsrc (state->sim_settings, 1, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ case PARSE_DC_START2:
+ sim_settings_set_dc_start (state->sim_settings, 1, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+ case PARSE_DC_STOP2:
+ sim_settings_set_dc_stop (state->sim_settings, 1, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+ case PARSE_DC_STEP2:
+ sim_settings_set_dc_step (state->sim_settings, 1, state->content->str);
+ state->state = PARSE_DC_SETTINGS;
+ break;
+
+
+ case PARSE_OPTION_LIST:
+ state->state = PARSE_SIMULATION_SETTINGS;
+ break;
+ case PARSE_OPTION:
+ state->state = PARSE_OPTION_LIST;
+ sim_settings_add_option (state->sim_settings,state->option);
+ state->option = g_new0(SimOption,1);
+ break;
+ case PARSE_OPTION_NAME:
+ state->option->name = g_strdup (state->content->str);
+ state->state=PARSE_OPTION;
+ break;
+
+ case PARSE_OPTION_VALUE:
+ state->option->value = g_strdup (state->content->str);
+ state->state=PARSE_OPTION;
+ break;
+
+ case PARSE_PARTS:
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_PART:
+ create_part (state);
+ state->state = PARSE_PARTS;
+ break;
+ case PARSE_PART_NAME:
+ state->part->name = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_LIBNAME:
+
+ /* FIXME: if the library name has changed, so it will
+ not be found, oregano will probably crash (this problem
+ also happen storing the library name in the part).
+ Why not search for the part name in all libraries ? */
+
+ state->state = PARSE_PART;
+ state->part->library = NULL;
+ libs = oregano.libraries;
+ while (libs) {
+ Library *lib = (Library *) libs->data;
+ if (g_strcasecmp (state->content->str, lib->name) == 0) {
+ state->part->library = lib;
+ break;
+ }
+ libs = libs->next;
+ }
+ break;
+ case PARSE_PART_POSITION:
+ sscanf (state->content->str, "(%lf %lf)", &state->pos.x, &state->pos.y);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_ROTATION:
+ sscanf (state->content->str, "%d", &state->rotation);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_FLIP:
+ if (g_strcasecmp ( state->content->str, "horizontal") == 0)
+ state->flip = state->flip | ID_FLIP_HORIZ;
+ else if (g_strcasecmp ( state->content->str, "vertical") == 0)
+ state->flip = state->flip | ID_FLIP_VERT;
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_REFDES:
+ state->part->refdes = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_TEMPLATE:
+ state->part->template = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_MODEL:
+ state->part->model = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_SYMNAME:
+ state->part->symbol_name = g_strdup (state->content->str);
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_LABELS:
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_LABEL:
+ state->state = PARSE_PART_LABELS;
+ state->part->labels = g_slist_prepend (state->part->labels,
+ state->label);
+ break;
+ case PARSE_PART_LABEL_NAME:
+ state->label->name = g_strdup (state->content->str);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_LABEL_TEXT:
+ state->label->text = g_strdup (state->content->str);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_LABEL_POS:
+ sscanf (state->content->str, "(%lf %lf)", &state->label->pos.x,
+ &state->label->pos.y);
+ state->state = PARSE_PART_LABEL;
+ break;
+ case PARSE_PART_PROPERTIES:
+ state->state = PARSE_PART;
+ break;
+ case PARSE_PART_PROPERTY:
+ state->state = PARSE_PART_PROPERTIES;
+ state->part->properties = g_slist_prepend (state->part->properties,
+ state->property);
+ break;
+ case PARSE_PART_PROPERTY_NAME:
+ state->property->name = g_strdup (state->content->str);
+ state->state = PARSE_PART_PROPERTY;
+ break;
+ case PARSE_PART_PROPERTY_VALUE:
+ state->property->value = g_strdup (state->content->str);
+ state->state = PARSE_PART_PROPERTY;
+ break;
+
+ case PARSE_WIRES:
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_WIRE:
+ create_wire (state);
+ state->state = PARSE_WIRES;
+ break;
+ case PARSE_WIRE_POINTS:
+ sscanf (state->content->str, "(%lf %lf)(%lf %lf)",
+ &state->wire_start.x, &state->wire_start.y,
+ &state->wire_end.x, &state->wire_end.y);
+ state->state = PARSE_WIRE;
+ break;
+
+ case PARSE_TEXTBOXES:
+ state->state = PARSE_SCHEMATIC;
+ break;
+ case PARSE_TEXTBOX:
+ create_textbox (state);
+ state->state = PARSE_TEXTBOXES;
+ break;
+ case PARSE_TEXTBOX_POSITION:
+ sscanf (state->content->str, "(%lf %lf)",
+ &state->pos.x, &state->pos.y);
+ state->state = PARSE_TEXTBOX;
+ break;
+ case PARSE_TEXTBOX_TEXT:
+ state->textbox_text = g_strdup (state->content->str);
+ state->state = PARSE_TEXTBOX;
+ break;
+
+
+ case PARSE_SCHEMATIC:
+ /* The end of the file. */
+ state->state = PARSE_FINISH;
+ break;
+
+ case PARSE_ERROR:
+ break;
+ case PARSE_START:
+ case PARSE_FINISH:
+ /* There should not be a closing tag in this state. */
+ g_assert_not_reached ();
+ break;
+ }
+ /*g_message("End element %s (state %s)", name, states[state->state]);*/
+}
+
+static void
+my_characters (ParseState *state, const xmlChar *chars, int len)
+{
+ int i;
+
+ if (state->state == PARSE_FINISH ||
+ state->state == PARSE_START ||
+ state->state == PARSE_PARTS ||
+ state->state == PARSE_PART)
+ return;
+
+ for (i = 0; i < len; i++)
+ g_string_append_c (state->content, chars[i]);
+}
+
+static xmlEntityPtr
+get_entity (void *user_data, const xmlChar *name)
+{
+ return xmlGetPredefinedEntity (name);
+}
+
+static void
+my_warning (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_WARNING, msg, args);
+ va_end (args);
+}
+
+static void
+my_error (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_CRITICAL, msg, args);
+ va_end (args);
+}
+
+static void
+my_fatal_error (void *user_data, const char *msg, ...)
+{
+ va_list args;
+
+ va_start (args, msg);
+ g_logv ("XML", G_LOG_LEVEL_ERROR, msg, args);
+ va_end (args);
+}
diff --git a/src/load-schematic.h b/src/load-schematic.h
new file mode 100644
index 0000000..fd7ec66
--- /dev/null
+++ b/src/load-schematic.h
@@ -0,0 +1,39 @@
+/*
+ * load-schematic.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 __LOAD_SCHEMATIC_H
+#define __LOAD_SCHEMATIC_H
+
+#include <glib.h>
+#include "schematic.h"
+
+gint schematic_parse_xml_file (Schematic *sm, const gchar *filename, GError **);
+
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b2cb3a1
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,252 @@
+/**
+ * @file main.c
+ * @author Richard Hult <rhult@hem.passagen.se>
+ * @author Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * @author Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * @date 1999-2001,2003-2006
+ *
+ * @brief Oregano, a tool for schematic capture and simulation of electronic
+ * circuits.
+ *
+ * @link 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.
+ */
+
+#define ENABLE_NLS 1
+#define HAVE_BIND_TEXTDOMAIN_CODESET 1
+#define HAVE_GETTEXT 1
+#define HAVE_LC_MESSAGES 1
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glade/glade.h>
+#include <libbonobo-2.0/libbonobo.h>
+#include <locale.h>
+#include <signal.h>
+
+
+#include "dialogs.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "cursors.h"
+#include "load-library.h"
+#include "load-schematic.h"
+#include "load-common.h"
+#include "oregano-config.h"
+#include "stock.h"
+#include "main.h"
+#include "splash.h"
+
+#include <libintl.h>
+OreganoApp oregano;
+static char **startup_files = NULL;
+int oregano_debugging;
+
+static void
+quit (void)
+{
+ printf("! GTK_MAIN_QUIT \n");
+ gtk_main_quit ();
+}
+
+/* I have no idea if this is right. */
+static gboolean
+quit_hook (GSignalInvocationHint *ihint,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer data)
+{
+ bonobo_main_quit ();
+ return FALSE;
+}
+
+static void
+session_die (void)
+{
+ /* FIXME: The session is ending. Save unsaved work etc. */
+ quit ();
+}
+
+static gchar* convert_all = NULL;
+static gint convert_width = 300;
+static gint convert_height = 300;
+
+static GOptionEntry options[] = {
+ {"convert", 'c', 0, G_OPTION_ARG_STRING, &convert_all, "Convert schematic to [PNG|SVG|PDF|PS] format", "[PNG|SVG|PDF|PS]"},
+ {"width", 'w', 0, G_OPTION_ARG_INT, &convert_width, "Set output width to W for converted schematic. Default 300", "W"},
+ {"height", 'h', 0, G_OPTION_ARG_INT, &convert_height, "Set output height to H for converted schematic. Default 300", "h"},
+ {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &startup_files, "Special option that collects any remaining arguments for us"},
+ {NULL}
+};
+
+int
+main (int argc, char **argv)
+{
+ GnomeProgram *OreProgram = NULL;
+ GnomeClient *client = NULL;
+ GOptionContext *context;
+
+ Schematic *schematic = NULL ;
+ SchematicView *schematic_view = NULL;
+
+ gchar *msg;
+ gint i;
+ Splash *splash=NULL;
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ setlocale (LC_ALL, "");
+
+ context = g_option_context_new (_("[FILES]"));
+ g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+
+ OreProgram = gnome_program_init (PACKAGE, VERSION, LIBGNOMEUI_MODULE,
+ argc, argv, GNOME_PARAM_GOPTION_CONTEXT, context,
+ GNOME_PARAM_APP_DATADIR, DATADIR, GNOME_PARAM_NONE);
+
+ cursors_init ();
+ stock_init ();
+
+ oregano_config_load ();
+
+ if (!g_file_test (OREGANO_GLADEDIR "/sim-settings.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("You seem to be running Oregano without\n"
+ "having it installed properly on your system.\n\n"
+ "Please install Oregano and try again."));
+
+ oregano_error (msg);
+ g_free (msg);
+ return 1;
+ }
+
+ setlocale (LC_NUMERIC, "C");
+
+ /* Connect to session manager. */
+ client = gnome_master_client ();
+ g_signal_connect (G_OBJECT (client), "die", G_CALLBACK(session_die), NULL);
+
+ if (oregano.show_splash){
+ splash = oregano_splash_new ();
+ }
+ /* splash == NULL if showing splash is disabled */
+ oregano_lookup_libraries(splash);
+
+ if (oregano.libraries == NULL) {
+ oregano_error (
+ _("Could not find a parts library.\n\n"
+ "This is probably due to a faulty installation\n"
+ "of Oregano. Please check your installation."));
+ return 1;
+ }
+
+ oregano.clipboard = NULL;
+
+ schematic = NULL;
+
+ if (startup_files) {
+ GError *error = NULL;
+ gint numfiles;
+
+ numfiles = g_strv_length (startup_files);
+ for (i = 0; i < numfiles; i++) {
+ Schematic *new_schematic;
+ char *fname = startup_files[i];
+
+ new_schematic = schematic_read (fname, &error);
+ if (new_schematic) {
+ if (!convert_all) {
+ schematic_view = schematic_view_new (new_schematic);
+
+ gtk_widget_show_all (schematic_view->toplevel);
+ schematic_set_filename (new_schematic, fname);
+ schematic_set_title (new_schematic, g_path_get_basename(fname));
+
+ /* Show something */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ schematic = new_schematic;
+ } else {
+ gchar *filename, *ext, *tmp;
+ int format = -1;
+
+ if (!g_ascii_strcasecmp (convert_all, "PDF")) {
+ format = 1;
+ ext = "pdf";
+ } else if (!g_ascii_strcasecmp (convert_all, "PS")) {
+ format = 2;
+ ext = "ps";
+ } else if (!g_ascii_strcasecmp (convert_all, "SVG")) {
+ format = 0;
+ ext = "svg";
+ } else if (!g_ascii_strcasecmp (convert_all, "PNG")) {
+ format = 3;
+ ext = "png";
+ } else {
+ g_print (_("Format '%s' not supported."), convert_all);
+ exit (1);
+ }
+ tmp = g_filename_display_basename (startup_files[i]);
+ filename = g_strdup_printf ("%s.%s", tmp, ext);
+ g_print ("Converting %s to %s ...\n", startup_files[i], filename);
+ schematic_export (new_schematic, filename, convert_width, convert_height, 0, 1, format);
+ g_object_unref (G_OBJECT (new_schematic));
+ g_free (filename);
+ g_free (tmp);
+ }
+ }
+ }
+ g_strfreev (startup_files);
+ startup_files = NULL;
+ }
+
+ g_option_context_free (context);
+
+ if (convert_all != NULL) {
+ g_print (_("Done.\n"));
+ return 0;
+ }
+
+ if (schematic == NULL){
+ schematic = schematic_new ();
+ schematic_view = schematic_view_new (schematic);
+ gtk_widget_show_all (schematic_view->toplevel);
+ }
+
+ g_signal_add_emission_hook (
+ g_signal_lookup("last_schematic_destroyed", TYPE_SCHEMATIC),
+ 0,
+ quit_hook,
+ NULL,
+ NULL);
+
+ if (oregano.show_splash)
+ oregano_splash_done (splash, _("Welcome to Oregano"));
+
+ bonobo_main ();
+ cursors_shutdown ();
+ gnome_config_drop_all ();
+ return 0;
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..a204aa5
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,65 @@
+/*
+ * main.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 __MAIN_H
+#define __MAIN_H
+
+#undef GTK_ENABLE_BROKEN
+
+#include <libgnome/libgnome.h>
+
+typedef struct {
+ GList *libraries;
+ GSList *clipboard;
+
+ /**
+ * list for library paths
+ */
+ GList *lib_path;
+
+ /**
+ * list for model paths
+ */
+ GList *model_path;
+
+ /**
+ * hash table with model names as keys and paths as data
+ */
+ GHashTable *models;
+
+ gint engine;
+ gboolean compress_files;
+ gboolean show_log;
+ gboolean show_splash;
+} OreganoApp;
+
+extern OreganoApp oregano;
+extern int oregano_debugging;
+
+#endif
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
diff --git a/src/netlist-editor.c b/src/netlist-editor.c
new file mode 100644
index 0000000..35365d4
--- /dev/null
+++ b/src/netlist-editor.c
@@ -0,0 +1,429 @@
+/*
+ * netlist-editor.c
+ *
+ *
+ * Author:
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 2004-2008 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 "netlist-editor.h"
+#include "netlist.h"
+#include "simulation.h"
+#include "file.h"
+#include <glade/glade.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <gnome.h>
+#include <gtksourceview/gtksourcelanguagemanager.h>
+
+static void netlist_editor_finalize (GObject *object);
+static void netlist_editor_dispose (GObject *object);
+static void netlist_editor_class_init (NetlistEditorClass *klass);
+static void netlist_editor_finalize (GObject *object);
+void netlist_editor_init (NetlistEditor *nle);
+
+static GObjectClass *parent_class = NULL;
+
+struct _NetlistEditorPriv {
+ SchematicView *sv;
+ gchar * font;
+ GdkColor bgcolor, selectcolor, textcolor;
+ GtkSourceLanguageManager *lm;
+ GtkTextView *view;
+ GtkSourceBuffer *buffer;
+ GtkWindow *toplevel;
+ GtkButton *sim, *save, *close;
+};
+
+static void
+netlist_editor_class_init (NetlistEditorClass *klass) {
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class->finalize = netlist_editor_finalize;
+ object_class->dispose = netlist_editor_dispose;
+}
+
+static void
+netlist_editor_finalize (GObject *object)
+{
+ NetlistEditor *nle = NETLIST_EDITOR (object);
+
+ if (nle->priv) {
+ /* kill the priv struct */
+ if (nle->priv->toplevel) {
+ gtk_widget_destroy (GTK_WIDGET (nle->priv->toplevel));
+ nle->priv->toplevel = NULL;
+ }
+
+ g_free (nle->priv);
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+netlist_editor_dispose (GObject *object)
+{
+ NetlistEditor *nle = NETLIST_EDITOR(object);
+
+ if (nle->priv) {
+ }
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+GType
+netlist_editor_get_type (void)
+{
+ static GType netlist_editor_type = 0;
+
+ if (!netlist_editor_type) {
+ static const GTypeInfo netlist_editor_info = {
+ sizeof(NetlistEditorClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)netlist_editor_class_init,
+ NULL,
+ NULL,
+ sizeof(NetlistEditor),
+ 0,
+ (GInstanceInitFunc)netlist_editor_init,
+ NULL
+ };
+ netlist_editor_type = g_type_register_static(G_TYPE_OBJECT,
+ "NetlistEditor",
+ &netlist_editor_info, 0);
+ }
+ return netlist_editor_type;
+}
+
+void
+netlist_editor_get_config (NetlistEditor * nle)
+{
+ GConfClient *gconf_client;
+
+ gconf_client = gconf_client_get_default ();
+
+ if (gconf_client) {
+ g_object_unref (gconf_client);
+ }
+}
+
+void
+netlist_editor_set_config (NetlistEditor * nle)
+{
+ GConfClient *gconf_client;
+
+ gconf_client = gconf_client_get_default ();
+
+ if (gconf_client) {
+ g_object_unref (gconf_client);
+ }
+}
+
+void
+netlist_editor_print (GtkWidget * widget, NetlistEditor * nle)
+{
+
+}
+
+void
+netlist_editor_simulate (GtkWidget * widget, NetlistEditor * nle)
+{
+ Schematic *sm;
+ gchar *name;
+ GtkTextView *tview;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ FILE *fp;
+ GError *error = 0;
+ OreganoEngine *engine;
+
+ // FIXME: OreganoEngine now override the netlist when start!
+ return;
+
+ tview = nle->priv->view;
+ buffer = gtk_text_view_get_buffer (tview);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ sm = schematic_view_get_schematic (nle->priv->sv);
+ //name = nl_generate (sm, NULL, &error);
+
+ if (error != NULL) {
+ if (g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) {
+ oregano_error_with_title (_("Could not create a netlist"), error->message);
+ g_clear_error (&error);
+ } else {
+ oregano_error (
+ _("An unexpected error has occurred"));
+ }
+ return;
+ }
+
+ /* Save TextBuffer to 'name' */
+ fp = fopen (name, "wt");
+ if (!fp) {
+ gchar *msg;
+ msg = g_strdup_printf (_("The file %s could not be saved"), name);
+ oregano_error_with_title (_("Could not save temporary netlist file"), msg);
+ g_free (msg);
+ return;
+ }
+
+ fputs (gtk_text_buffer_get_text (buffer, &start, &end, FALSE), fp);
+ fclose (fp);
+
+ schematic_set_netlist_filename (sm, name);
+ simulation_show (NULL, nle->priv->sv);
+ g_free (name);
+}
+
+void
+netlist_editor_save (GtkWidget * widget, NetlistEditor * nle)
+{
+ char *name;
+
+ name = dialog_netlist_file ((SchematicView *)NULL);
+
+ if (name != NULL) {
+ GtkTextView *tview;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ FILE *fp;
+
+ tview = nle->priv->view;
+ buffer = gtk_text_view_get_buffer (tview);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ fp = fopen (name, "wt");
+ if (!fp) {
+ gchar *msg;
+ msg = g_strdup_printf (_("The file %s could not be saved"), name);
+ oregano_error_with_title (_("Could not save temporary netlist file"), msg);
+ g_free (msg);
+ return;
+ }
+
+ fputs (gtk_text_buffer_get_text (buffer, &start, &end, FALSE), fp);
+ fclose (fp);
+
+ g_free (name);
+ }
+}
+
+/* This method append OREGANO_LANGDIR directory where the netlist.lang file
+ * is located to the search path of GtkSourceLanguageManager.
+ */
+void
+setup_language_manager_path(GtkSourceLanguageManager *lm)
+{
+ gchar **lang_files;
+ int i, lang_files_count;
+ char **new_langs;
+
+ lang_files = g_strdupv (gtk_source_language_manager_get_search_path (lm));
+
+ lang_files_count = g_strv_length (lang_files);
+ new_langs = g_new (char*, lang_files_count + 2);
+
+ for (i = 0; lang_files[i]; i++)
+ new_langs[i] = lang_files[i];
+
+ new_langs[lang_files_count] = g_strdup (OREGANO_LANGDIR);
+ new_langs[lang_files_count+1] = NULL;
+
+ g_free (lang_files);
+
+ gtk_source_language_manager_set_search_path (lm, new_langs);
+}
+
+NetlistEditor *
+netlist_editor_new (GtkSourceBuffer * textbuffer) {
+ gchar** lang_files;
+ NetlistEditor * nle;
+ GladeXML * gui;
+ GtkWidget * toplevel;
+ GtkScrolledWindow * scroll;
+ GtkSourceView * source_view;
+ GtkSourceLanguageManager * lm;
+ GtkButton * save, * sim, * close, * print;
+ GtkSourceLanguage *lang=NULL;
+ const GSList *list;
+
+ if (!textbuffer)
+ return NULL;
+
+ nle = NETLIST_EDITOR (g_object_new (netlist_editor_get_type (), NULL));
+
+ netlist_editor_get_config (nle);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/view-netlist.glade", G_FILE_TEST_EXISTS)) {
+ gchar *msg;
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/view-netlist.glade");
+ oregano_error_with_title (_("Could not create the netlist dialog"), msg);
+ g_free (msg);
+ return NULL;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/view-netlist.glade", NULL, NULL);
+
+ toplevel = glade_xml_get_widget (gui, "toplevel");
+ gtk_window_set_default_size (GTK_WINDOW (toplevel), 800, 600);
+ gtk_window_set_title (GTK_WINDOW (toplevel), "Net List Editor\n");
+
+ scroll = GTK_SCROLLED_WINDOW (glade_xml_get_widget (gui, "netlist-scrolled-window"));
+
+ source_view = GTK_SOURCE_VIEW (gtk_source_view_new ());
+ lm = GTK_SOURCE_LANGUAGE_MANAGER (gtk_source_language_manager_new ());
+
+ setup_language_manager_path (lm);
+
+ g_object_set_data_full (G_OBJECT (source_view), "language-manager",
+ lm, (GDestroyNotify) g_object_unref);
+
+ lang = gtk_source_language_manager_get_language (lm, "netlist");
+
+ if (lang) {
+ gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (textbuffer), lang);
+ gtk_source_buffer_set_highlight_syntax (GTK_SOURCE_BUFFER (textbuffer), TRUE);
+ gtk_source_buffer_set_highlight_matching_brackets (GTK_SOURCE_BUFFER (textbuffer), TRUE);
+ } else {
+ g_warning ("Can't load netlist.lang in %s", OREGANO_LANGDIR "/netlist.lang");
+ }
+
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (source_view), TRUE);
+ gtk_text_view_set_buffer (GTK_TEXT_VIEW (source_view), GTK_TEXT_BUFFER (textbuffer));
+
+ gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (source_view));
+
+ close = GTK_BUTTON (glade_xml_get_widget (gui, "btn_close"));
+ g_signal_connect_swapped (G_OBJECT (close), "clicked", G_CALLBACK (g_object_unref), G_OBJECT (nle));
+ save = GTK_BUTTON (glade_xml_get_widget (gui, "btn_save"));
+ g_signal_connect (G_OBJECT (save), "clicked", G_CALLBACK (netlist_editor_save), nle);
+ sim = GTK_BUTTON (glade_xml_get_widget (gui, "btn_sim"));
+ g_signal_connect (G_OBJECT (sim), "clicked", G_CALLBACK (netlist_editor_simulate), nle);
+ print = GTK_BUTTON (glade_xml_get_widget (gui, "btn_print"));
+ g_signal_connect (G_OBJECT (print), "clicked", G_CALLBACK (netlist_editor_print), nle);
+ /*
+ * Set tab, fonts, wrap mode, colors, etc. according
+ * to preferences
+ */
+
+ nle->priv->lm = lm;
+ nle->priv->view = GTK_TEXT_VIEW (source_view);
+ nle->priv->toplevel = GTK_WINDOW (toplevel);
+ nle->priv->sim = sim;
+ nle->priv->save = save;
+ nle->priv->close = close;
+ nle->priv->buffer = textbuffer;
+
+ gtk_widget_show_all (GTK_WIDGET (toplevel));
+
+ return nle;
+}
+
+NetlistEditor *
+netlist_editor_new_from_file (gchar * filename)
+{
+ GtkSourceBuffer *buffer;
+ gchar *content;
+ gsize length;
+ GError *error = NULL;
+ NetlistEditor *editor;
+
+ if (!filename)
+ return NULL;
+ if (!(g_file_test (filename, G_FILE_TEST_EXISTS))) {
+ gchar *msg;
+ /* gettext support */
+ msg = g_strdup_printf (_("The file %s could not be found."), filename);
+ oregano_error_with_title (_("Could not find the required file"), msg);
+ g_free (msg);
+ return NULL;
+ }
+
+ buffer = gtk_source_buffer_new (NULL);
+ g_file_get_contents (filename, &content, &length, &error);
+ gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), content, -1);
+
+ editor = netlist_editor_new (buffer);
+
+ return editor;
+}
+
+void
+netlist_editor_init (NetlistEditor * nle) {
+ nle->priv = g_new0 (NetlistEditorPriv, 1);
+
+ nle->priv->toplevel = NULL;
+ nle->priv->sv = NULL;
+ /*nle->priv->source_view = NULL;
+ nle->priv->bgcolor = 1;
+ nle->priv->selectcolor = 0;
+ nle->priv->textcolor = 0;*/
+}
+
+NetlistEditor *
+netlist_editor_new_from_schematic_view (SchematicView *sv)
+{
+ NetlistEditor *editor;
+ gchar *name = "/tmp/oregano.netlist"; // FIXME
+ GError *error = 0;
+ Schematic *sm;
+ OreganoEngine *engine;
+
+ sm = schematic_view_get_schematic (sv);
+
+ engine = oregano_engine_factory_create_engine (OREGANO_ENGINE_GNUCAP, sm);
+ oregano_engine_generate_netlist (engine, name, &error);
+ g_object_unref (engine);
+
+ if (error != NULL) {
+ if (g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) {
+ oregano_error_with_title (_("Could not create a netlist"), error->message);
+ g_clear_error (&error);
+ } else {
+ oregano_error (_("An unexpected error has occurred"));
+ }
+ return NULL;
+ }
+
+ editor = netlist_editor_new_from_file (name);
+ if (editor) {
+ editor->priv->sv = sv;
+ }
+
+ return editor;
+}
+
diff --git a/src/netlist-editor.h b/src/netlist-editor.h
new file mode 100644
index 0000000..36ed5ea
--- /dev/null
+++ b/src/netlist-editor.h
@@ -0,0 +1,66 @@
+/*
+ * netlist-editor.h
+ *
+ *
+ * Author:
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 2004-2008 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 __NETLIST_EDIT_H
+#define __NETLIST_EDIT_H
+
+#include <gconf/gconf-client.h>
+#include <gtk/gtk.h>
+#include <gtksourceview/gtksourceview.h>
+#include "schematic-view.h"
+#include "errors.h"
+#include "engine.h"
+
+#define TYPE_NETLIST_EDITOR (netlist_editor_get_type ())
+#define NETLIST_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NETLIST_EDITOR, NetlistEditor))
+#define NETLIST_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_NETLIST_EDITOR, NetlistEditorClass))
+#define IS_NETLIST_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NETLIST_EDITOR))
+#define IS_NETLIST_EDITOR_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_NETLIST_EDITOR, NetlistEditorClass))
+
+
+typedef struct _NetlistEditor NetlistEditor;
+typedef struct _NetlistEditorClass NetlistEditorClass;
+typedef struct _NetlistEditorPriv NetlistEditorPriv;
+
+struct _NetlistEditor {
+ GObject parent;
+
+ NetlistEditorPriv *priv;
+};
+
+struct _NetlistEditorClass {
+ GObjectClass parent_class;
+
+ /* Signals go here */
+};
+
+GType netlist_editor_get_type (void);
+NetlistEditor *netlist_editor_new_from_file (gchar * filename);
+NetlistEditor *netlist_editor_new_from_schematic_view (SchematicView *sv);
+NetlistEditor *netlist_editor_new (GtkSourceBuffer * textbuffer);
+
+#endif
diff --git a/src/oregano-config.c b/src/oregano-config.c
new file mode 100644
index 0000000..251d6e4
--- /dev/null
+++ b/src/oregano-config.c
@@ -0,0 +1,173 @@
+/*
+ * oregano-config.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 <sys/types.h>
+#include <dirent.h>
+#include <gconf/gconf-client.h>
+#include <string.h>
+
+#include "main.h"
+#include "oregano-config.h"
+#include "load-library.h"
+#include "dialogs.h"
+#include "engine.h"
+
+#define OREGLIB_EXT "oreglib"
+
+static gboolean is_oregano_library_name (gchar* name);
+static void load_library_error (gchar *name);
+
+void
+oregano_config_load (void)
+{
+ GConfClient * gconf;
+
+ gconf = gconf_client_get_default ();
+
+ oregano.engine = gconf_client_get_int (gconf, "/apps/oregano/engine",
+ NULL);
+ oregano.compress_files = gconf_client_get_bool (gconf, "/apps/oregano/compress_files",
+ NULL);
+ oregano.show_log = gconf_client_get_bool (gconf, "/apps/oregano/show_log",
+ NULL);
+ oregano.show_splash = gconf_client_get_bool (gconf, "/apps/oregano/show_splash",
+ NULL);
+
+ g_object_unref (gconf);
+
+ /* Let's deal with first use -I don't like this- */
+
+ if ((oregano.engine < 0) || (oregano.engine >= OREGANO_ENGINE_COUNT))
+ oregano.engine = 0;
+}
+
+void
+oregano_config_save (void)
+{
+ GConfClient * gconf;
+
+ gconf = gconf_client_get_default ();
+
+ gconf_client_set_int (gconf, "/apps/oregano/engine", oregano.engine,
+ NULL);
+ gconf_client_set_bool (gconf, "/apps/oregano/compress_files", oregano.compress_files,
+ NULL);
+ gconf_client_set_bool (gconf, "/apps/oregano/show_log", oregano.show_log,
+ NULL);
+ gconf_client_set_bool (gconf, "/apps/oregano/show_splash", oregano.show_splash,
+ NULL);
+
+ g_object_unref (gconf);
+}
+
+void
+oregano_lookup_libraries (Splash *sp)
+{
+ gchar *fname;
+ DIR *libdir;
+ struct dirent *libentry;
+ Library *library;
+
+ oregano.libraries = NULL;
+ libdir = opendir (OREGANO_LIBRARYDIR);
+
+ if (libdir == NULL) return;
+
+ if (oregano.libraries != NULL) {
+ closedir (libdir);
+ return;
+ }
+
+ fname = g_build_filename (OREGANO_LIBRARYDIR, "default.oreglib", NULL);
+
+ if (g_file_test (fname, G_FILE_TEST_EXISTS)) {
+ library = library_parse_xml_file (fname);
+ oregano.libraries = g_list_append (oregano.libraries, library);
+ }
+ g_free (fname);
+
+ while ((libentry=readdir (libdir)) != NULL) {
+ if (is_oregano_library_name (libentry->d_name) &&
+ strcmp (libentry->d_name,"default.oreglib")) {
+ fname = g_build_filename (OREGANO_LIBRARYDIR, libentry->d_name, NULL);
+
+ /* do the following only if splash is enabled */
+ if (sp) {
+ char txt[50];
+ sprintf (txt, _("Loading %s ..."), libentry->d_name);
+
+ oregano_splash_step (sp, txt);
+ }
+
+ library = library_parse_xml_file (fname);
+
+ if (library)
+ oregano.libraries = g_list_append ( oregano.libraries, library);
+ else
+ load_library_error (fname);
+
+ g_free(fname);
+ }
+ }
+ closedir (libdir);
+}
+
+/*
+ * Helpers
+ */
+
+static gboolean
+is_oregano_library_name (gchar *name)
+{
+ gchar *dot;
+ dot = strchr (name, '.');
+ if (dot == NULL || dot[1] == '\0')
+ return FALSE;
+
+ dot++; /* Points to the extension. */
+
+ if (strcmp (dot, OREGLIB_EXT) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+load_library_error (gchar *name)
+{
+ gchar *title, *desc;
+ title = g_strdup_printf (_("Could not read the parts library: %s "), name);
+ desc = g_strdup_printf (_(
+ "The file is probably corrupt. Please reinstall the parts\n"
+ "library or Oregano and try again."));
+ oregano_error_with_title (title, desc);
+ g_free (title);
+ g_free (desc);
+}
diff --git a/src/oregano-config.h b/src/oregano-config.h
new file mode 100644
index 0000000..52ce7f0
--- /dev/null
+++ b/src/oregano-config.h
@@ -0,0 +1,45 @@
+/*
+ * oregano-config.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 __OREGANO_CONFIG_H
+#define __OREGANO_CONFIG_H
+
+#include "splash.h"
+
+void oregano_config_load (void);
+void oregano_config_save (void);
+
+/*
+ * Feb 2000, Elker Cavina <e.cavina@libero.it>
+ * perhaps make the oregano global variable pass through these functions ?
+ */
+
+void oregano_lookup_libraries (Splash *sp);
+
+#endif
diff --git a/src/oregano-convert b/src/oregano-convert
new file mode 100755
index 0000000..a6251f3
--- /dev/null
+++ b/src/oregano-convert
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+# Fix the namespace.
+while (<>) {
+ if (/<schematic/) {
+ s/</<ogo:/;
+ s/>/ xmlns:ogo="http:\/\/www.dtek.chalmers.se\/~d4hult\/oregano\/v1">/;
+ print;
+ last;
+ }
+
+ if (/<schematic/) {
+ print;
+ last;
+ }
+
+ print;
+}
+
+# Add namespace to tags.
+while (<>) {
+
+ if (/<[a-zA-Z]/) {
+ s/</<ogo:/;
+ }
+
+ s/<\//<\/ogo:/;
+ print;
+}
+
+
diff --git a/src/oregano-impl.c b/src/oregano-impl.c
new file mode 100644
index 0000000..942454c
--- /dev/null
+++ b/src/oregano-impl.c
@@ -0,0 +1,140 @@
+/*
+ * oregano-impl.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 "oregano.h"
+
+/*** App-specific servant structures ***/
+typedef struct
+{
+ POA_GNOME_Oregano_Sheet servant;
+ PortableServer_POA poa;
+} impl_POA_GNOME_Oregano_Sheet;
+
+/*** Implementation stub prototypes ***/
+static void impl_GNOME_Oregano_Sheet__destroy(impl_POA_GNOME_Oregano_Sheet *
+ servant, CORBA_Environment * ev);
+
+static void impl_GNOME_Oregano_Sheet_select_all(impl_POA_GNOME_Oregano_Sheet *
+ servant, CORBA_Environment * ev);
+
+static CORBA_boolean
+impl_GNOME_Oregano_Sheet_is_all_selected(impl_POA_GNOME_Oregano_Sheet *
+ servant, CORBA_Environment * ev);
+
+static GNOME_Oregano_Sheet
+impl_GNOME_Oregano_Sheet_sheet_load(impl_POA_GNOME_Oregano_Sheet * servant,
+ CORBA_char * name, CORBA_Environment * ev);
+
+/*** epv structures ***/
+static PortableServer_ServantBase__epv impl_GNOME_Oregano_Sheet_base_epv = {
+ NULL, /* _private data */
+ NULL, /* finalize routine */
+ NULL, /* default_POA routine */
+};
+
+static POA_GNOME_Oregano_Sheet__epv impl_GNOME_Oregano_Sheet_epv = {
+ NULL, /* _private */
+ (gpointer) & impl_GNOME_Oregano_Sheet_select_all,
+
+ (gpointer) & impl_GNOME_Oregano_Sheet_is_all_selected,
+
+ (gpointer) & impl_GNOME_Oregano_Sheet_sheet_load,
+
+};
+
+/*** vepv structures ***/
+static POA_GNOME_Oregano_Sheet__vepv impl_GNOME_Oregano_Sheet_vepv = {
+ &impl_GNOME_Oregano_Sheet_base_epv,
+ &impl_GNOME_Oregano_Sheet_epv,
+};
+
+/*** Stub implementations ***/
+GNOME_Oregano_Sheet
+impl_GNOME_Oregano_Sheet__create(PortableServer_POA poa, CORBA_Environment * ev)
+{
+ GNOME_Oregano_Sheet retval;
+ impl_POA_GNOME_Oregano_Sheet *newservant;
+ PortableServer_ObjectId *objid;
+
+ newservant = g_new0(impl_POA_GNOME_Oregano_Sheet, 1);
+ newservant->servant.vepv = &impl_GNOME_Oregano_Sheet_vepv;
+ newservant->poa = poa;
+ POA_GNOME_Oregano_Sheet__init((PortableServer_Servant) newservant, ev);
+ objid = PortableServer_POA_activate_object(poa, newservant, ev);
+ CORBA_free(objid);
+ retval = PortableServer_POA_servant_to_reference(poa, newservant, ev);
+
+ return retval;
+}
+
+static void
+impl_GNOME_Oregano_Sheet__destroy(impl_POA_GNOME_Oregano_Sheet * servant,
+ CORBA_Environment * ev)
+{
+ PortableServer_ObjectId *objid;
+
+ objid = PortableServer_POA_servant_to_id(servant->poa, servant, ev);
+ PortableServer_POA_deactivate_object(servant->poa, objid, ev);
+ CORBA_free(objid);
+
+ POA_GNOME_Oregano_Sheet__fini((PortableServer_Servant) servant, ev);
+ g_free(servant);
+}
+
+static void
+impl_GNOME_Oregano_Sheet_select_all(impl_POA_GNOME_Oregano_Sheet * servant,
+ CORBA_Environment * ev)
+{
+ g_print ("select all\n");
+}
+
+static CORBA_boolean
+impl_GNOME_Oregano_Sheet_is_all_selected(impl_POA_GNOME_Oregano_Sheet *
+ servant, CORBA_Environment * ev)
+{
+ CORBA_boolean retval;
+
+ g_print ("is all selected\n");
+ retval = FALSE;
+
+ return retval;
+}
+
+static GNOME_Oregano_Sheet
+impl_GNOME_Oregano_Sheet_sheet_load(impl_POA_GNOME_Oregano_Sheet * servant,
+ CORBA_char * name, CORBA_Environment * ev)
+{
+ GNOME_Oregano_Sheet retval;
+
+ g_print ("sheet load\n");
+ retval = TRUE;
+
+ return retval;
+}
+
diff --git a/src/oregano-utils.c b/src/oregano-utils.c
new file mode 100644
index 0000000..0b172cf
--- /dev/null
+++ b/src/oregano-utils.c
@@ -0,0 +1,80 @@
+/*
+ * oregano-utils.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 <gtk/gtk.h>
+#include "oregano-utils.h"
+
+double
+oregano_strtod (const gchar *str, const gchar unit)
+{
+ double ret;
+ gchar *endptr, *c;
+
+ if (!str)
+ return 0.0;
+
+ ret = g_strtod (str, &endptr);
+ for (c = endptr; *c; c++) {
+ switch (*c) {
+ case 'T':
+ ret *= 1e12;
+ return ret;
+ case 'G':
+ ret *= 1e9;
+ return ret;
+ case 'M':
+ ret *= 1e6;
+ return ret;
+ case 'k':
+ ret *= 1e3;
+ return ret;
+ case 'm':
+ ret *= 1e-3;
+ return ret;
+ case 'u':
+ ret *= 1e-6;
+ return ret;
+ case 'n':
+ ret *= 1e-9;
+ return ret;
+ case 'p':
+ ret *= 1e-12;
+ return ret;
+ case 'f':
+ ret *= 1e-15;
+ return ret;
+ default:
+ if (*c == unit)
+ return ret;
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/src/oregano-utils.h b/src/oregano-utils.h
new file mode 100644
index 0000000..3dfba88
--- /dev/null
+++ b/src/oregano-utils.h
@@ -0,0 +1,36 @@
+/*
+ * oregano-utils.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 __OREGANO_UTILS_H
+#define __OREGANO_UTILS_H
+
+
+double oregano_strtod (const gchar *str, const gchar unit);
+
+#endif
diff --git a/src/part-browser.c b/src/part-browser.c
new file mode 100644
index 0000000..f2fa032
--- /dev/null
+++ b/src/part-browser.c
@@ -0,0 +1,715 @@
+/*
+ * part-browser.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 <gtk/gtktreeselection.h>
+#include <gtk/gtktreemodel.h>
+#include <glade/glade.h>
+#include <math.h>
+#include <string.h>
+#include "main.h"
+#include "load-library.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "part-browser.h"
+#include "part-item.h"
+#include "dialogs.h"
+#include "sheet-pos.h"
+
+typedef struct _Browser Browser;
+struct _Browser {
+ SchematicView *schematic_view;
+ GtkWidget *viewport;
+ GtkWidget *list;
+ GtkWidget *canvas;
+ GnomeCanvasText *description;
+ GnomeCanvasGroup *preview;
+ Library *library;
+ /**
+ * The currently selected option menu item.
+ */
+ GtkWidget *library_menu_item;
+
+ gboolean hidden;
+
+ /**
+ * Models for the TreeView
+ */
+ GtkTreeModel *real_model;
+ GtkTreeModel *sort_model;
+ GtkTreeModel *filter_model;
+ GtkEntry *filter_entry;
+ gint filter_len;
+};
+
+typedef struct {
+ Browser *br;
+ SchematicView *schematic_view;
+ char *library_name;
+ char *part_name;
+} DndData;
+
+#define PREVIEW_WIDTH 100
+#define PREVIEW_HEIGHT 100
+#define PREVIEW_TEXT_HEIGHT 25
+
+static void update_preview (Browser *br);
+static void add_part (gpointer key, LibraryPart *part, Browser *br);
+static int part_selected (GtkTreeView *list, GtkTreePath *arg1,
+ GtkTreeViewColumn *col, Browser *br);
+static void part_browser_setup_libs (Browser *br, GladeXML *gui);
+static void library_switch_cb (GtkWidget *item, Browser *br);
+static void preview_realized (GtkWidget *widget, Browser *br);
+static void wrap_string(char* str, int width);
+static void place_cmd (GtkWidget *widget, Browser *br);
+
+static gboolean
+part_list_filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+ char *part_name;
+ const char *s;
+ char *comp1, *comp2; /* auxilires para guardar el nombre en upcase */
+ Browser *br = (Browser *)data;
+
+ s = gtk_entry_get_text (GTK_ENTRY (br->filter_entry));
+ /* Si no hay filtro, la componente se muestra */
+ if (s == NULL) return TRUE;
+ if (br->filter_len == 0) return TRUE;
+
+ gtk_tree_model_get (model, iter, 0, &part_name, -1);
+
+ if (part_name) {
+ comp1 = g_utf8_strup (s, -1);
+ comp2 = g_utf8_strup (part_name, -1);
+
+ if (g_strrstr(comp2, comp1)) {
+ g_free (comp1);
+ g_free (comp2);
+ return TRUE;
+ }
+
+ g_free (comp1);
+ g_free (comp2);
+ }
+ return FALSE;
+}
+
+static int
+part_search_change (GtkWidget *widget, Browser *br)
+{
+ const char *s = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ if (s) {
+ /* Keep record of the filter text length for each item. */
+ br->filter_len = strlen (s);
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (br->filter_model));
+ }
+
+ return TRUE;
+}
+
+static void
+part_search_activate (GtkWidget *widget, Browser *br)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ path = gtk_tree_path_new_from_string ("0");
+ if (path) {
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list));
+
+ gtk_tree_selection_select_path (selection, path);
+
+ gtk_tree_path_free (path);
+ place_cmd (widget, br);
+ }
+}
+
+static void
+place_cmd (GtkWidget *widget, Browser *br)
+{
+ LibraryPart *library_part;
+ char *part_name;
+ SheetPos pos;
+ Sheet *sheet;
+ Part *part;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ schematic_view_reset_tool (br->schematic_view);
+ sheet = schematic_view_get_sheet (br->schematic_view);
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (br->list));
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ return;
+ }
+
+ gtk_tree_model_get(model, &iter, 0, &part_name, -1);
+
+ library_part = library_get_part (br->library, part_name);
+ part = part_new_from_library_part (library_part);
+ if (!part) {
+ oregano_error(_("Unable to load required part"));
+ return;
+ }
+
+ pos.x = pos.y = 0;
+ item_data_set_pos (ITEM_DATA (part), &pos);
+
+ schematic_view_select_all(br->schematic_view, FALSE);
+ schematic_view_clear_ghosts(br->schematic_view);
+ schematic_view_add_ghost_item(br->schematic_view, ITEM_DATA(part));
+
+ part_item_signal_connect_floating_group (sheet, br->schematic_view);
+}
+
+static int
+part_selected (GtkTreeView *list, GtkTreePath *arg1, GtkTreeViewColumn *col,
+ Browser *br)
+{
+ /* if double-click over an item, place it on work area */
+ place_cmd (NULL, br);
+
+ return FALSE;
+}
+
+static void
+update_preview (Browser *br)
+{
+ LibraryPart *library_part;
+ gdouble new_x, new_y, x1, y1, x2, y2;
+ gdouble scale;
+ double affine[6];
+ double transf[6];
+ gdouble width, height;
+ gchar *part_name;
+ char *description;
+ gdouble text_width;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(br->list));
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(br->list));
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+
+ gtk_tree_model_get(model, &iter, 0, &part_name, -1);
+
+ library_part = library_get_part (br->library, part_name);
+
+ /*
+ * If there already is a preview part, destroy its group and create a
+ * new one.
+ */
+ if (br->preview != NULL)
+ /* FIXME: Check if gnomecanvas are killed by this way */
+ gtk_object_destroy (GTK_OBJECT (br->preview));
+
+ br->preview = GNOME_CANVAS_GROUP (gnome_canvas_item_new (
+ gnome_canvas_root (GNOME_CANVAS (br->canvas)),
+ gnome_canvas_group_get_type(),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ if (!library_part)
+ return;
+
+ part_item_create_canvas_items_for_preview (br->preview, library_part);
+
+ width = br->canvas->allocation.width;
+ height = br->canvas->allocation.height - PREVIEW_TEXT_HEIGHT;
+
+ /* Get the coordonates */
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (br->preview),
+ &x1, &y1, &x2, &y2);
+
+ /* Translate in such a way that the canvas centre remains in (0, 0) */
+ art_affine_translate(transf, -(x2 + x1) / 2.0f, -(y2 + y1) / 2.0f);
+
+ /* Compute the scale of the widget */
+ if((x2 - x1 != 0) || (y2 - y1 != 0)) {
+ if ((x2 - x1) < (y2 - y1))
+ scale = 0.60f * PREVIEW_HEIGHT / (y2 - y1);
+ else
+ scale = 0.60f * PREVIEW_WIDTH / (x2 - x1);
+
+ } else
+ scale = 5;
+
+ art_affine_scale (affine, scale, scale);
+ art_affine_multiply (transf, transf, affine);
+
+ /* Apply the transformation */
+ gnome_canvas_item_affine_absolute (GNOME_CANVAS_ITEM (br->preview), transf);
+
+ /* Get the new coordonates after transformation */
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (br->preview),
+ &x1, &y1, &x2, &y2);
+
+ /* Compute the motion to centre the Preview widget */
+ new_x = (PREVIEW_WIDTH - x1 - x2) / 2;
+ new_y = (PREVIEW_HEIGHT - y1 - y2) / 2;
+
+ art_affine_translate (affine, new_x, new_y);
+ art_affine_multiply (transf, transf, affine);
+
+ /* Apply the transformation */
+ gnome_canvas_item_affine_absolute (GNOME_CANVAS_ITEM (br->preview),
+ transf);
+
+ description = g_strdup (library_part->description);
+ wrap_string (description, 20);
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (br->description), "text",
+ description, NULL);
+ g_free (description);
+
+ g_object_get (G_OBJECT (br->description),
+ "text_width", &text_width, NULL);
+
+ g_object_set (G_OBJECT(br->description), "x",
+ PREVIEW_WIDTH / 2 - text_width / 2, NULL);
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (br->description));
+ g_free(part_name);
+}
+
+static gboolean
+select_row (GtkTreeView *list, Browser *br)
+{
+ update_preview (br);
+ return FALSE;
+}
+
+static void
+add_part (gpointer key, LibraryPart *part, Browser *br)
+{
+ GtkTreeIter iter;
+ //GtkTreeModel *sort; /* The sortable interface */
+ GtkListStore *model;
+
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (part->name != NULL);
+ g_return_if_fail (br != NULL);
+ g_return_if_fail (br->list != NULL);
+
+ model = GTK_LIST_STORE(br->real_model);
+ gtk_list_store_append(model, &iter);
+ gtk_list_store_set(model, &iter, 0, part->name, -1);
+}
+
+/*
+ * Read the available parts from the library and put them in the browser clist.
+ */
+static void
+update_list (Browser *br)
+{
+ GtkListStore *model;
+
+ model = GTK_LIST_STORE (br->real_model);
+ gtk_list_store_clear (model);
+ g_hash_table_foreach (br->library->part_hash, (GHFunc) add_part, br);
+}
+
+
+/**
+ * Show a part browser. If one already exists, just bring it up, otherwise
+ * create it. We can afford to keep it in memory all the time, and we don't
+ * have to delete it and build it every time it is needed. If already shown,
+ * hide it.
+ */
+void
+part_browser_toggle_show (SchematicView *schematic_view)
+{
+ Browser *browser = schematic_view_get_browser (schematic_view);
+
+ if (browser == NULL){
+ part_browser_create (schematic_view);
+ } else {
+ browser->hidden = !(browser->hidden);
+ if (browser->hidden){
+ gtk_widget_hide_all (browser->viewport);
+ } else {
+ gtk_widget_show_all (browser->viewport);
+ }
+ }
+}
+
+void
+part_browser_dnd (GtkSelectionData *selection_data, int x, int y)
+{
+ LibraryPart *library_part;
+ SheetPos pos;
+ DndData *data;
+ Sheet *sheet;
+ Part *part;
+
+ data = (DndData *) (selection_data->data);
+
+ g_return_if_fail (data != NULL);
+
+ pos.x = x;
+ pos.y = y;
+
+ sheet = schematic_view_get_sheet (data->schematic_view);
+
+ snap_to_grid (sheet->grid, &pos.x, &pos.y);
+
+ library_part = library_get_part (data->br->library, data->part_name);
+ part = part_new_from_library_part (library_part);
+ if (!part) {
+ oregano_error(_("Unable to load required part"));
+ return;
+ }
+
+ item_data_set_pos (ITEM_DATA (part), &pos);
+
+ schematic_view_select_all (data->schematic_view, FALSE);
+ schematic_view_clear_ghosts (data->schematic_view);
+ schematic_view_add_ghost_item (data->schematic_view, ITEM_DATA (part));
+
+ part_item_signal_connect_floating_group (sheet, data->schematic_view);
+}
+
+static void
+drag_data_get (GtkWidget *widget, GdkDragContext *context,
+ GtkSelectionData *selection_data, guint info, guint time, Browser *br)
+{
+ DndData *data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *part_name;
+
+ schematic_view_reset_tool (br->schematic_view);
+
+ data = g_new0 (DndData, 1);
+
+ data->schematic_view = br->schematic_view;
+ data->br = br;
+
+ data->library_name = br->library->name;
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (br->list));
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) {
+ /* No selection, Action canceled */
+ return;
+ }
+
+ gtk_tree_model_get(model, &iter, 0, &part_name, -1);
+
+ data->part_name = part_name;
+
+ gtk_selection_data_set (selection_data,
+ selection_data->target,
+ 8,
+ (gpointer) data,
+ sizeof (DndData));
+
+ /*
+ * gtk_selection_data_set copies the information so we can free it now.
+ */
+ g_free (data);
+}
+
+/*
+ * part_browser_create
+ *
+ * Creates a new part browser. This is only called once per schematic window.
+ */
+GtkWidget *
+part_browser_create (SchematicView *schematic_view)
+{
+ Browser *br;
+ GladeXML *gui;
+ char *msg;
+ GtkWidget *w;
+ GtkCellRenderer *cell_text;
+ GtkTreeViewColumn *cell_column;
+ GtkStyle *style;
+ GdkColormap *colormap;
+ GnomeCanvasPoints *points;
+ static GtkTargetEntry dnd_types[] =
+ { { "x-application/oregano-part", 0, DRAG_PART_INFO } };
+
+ static int dnd_num_types = sizeof (dnd_types) / sizeof (dnd_types[0]);
+ GtkTreePath *path;
+
+ br = g_new0 (Browser, 1);
+ br->preview = NULL;
+ br->schematic_view = schematic_view;
+ br->hidden = FALSE;
+
+ schematic_view_set_browser(schematic_view, br);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/part-browser.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/part-browser.glade");
+
+ oregano_error_with_title (_("Could not create part browser"), msg);
+ g_free (msg);
+ return NULL;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/part-browser.glade",
+ "part_browser_vbox", GETTEXT_PACKAGE);
+ if (!gui) {
+ oregano_error (_("Could not create part browser"));
+ return NULL;
+ }
+
+ w = glade_xml_get_widget (gui, "preview_canvas");
+ br->canvas = w;
+
+ g_signal_connect(w, "realize", (GCallback) preview_realized, br);
+
+ style = gtk_style_new ();
+ colormap = gtk_widget_get_colormap (GTK_WIDGET (w));
+ style->bg[GTK_STATE_NORMAL].red = 65535;
+ style->bg[GTK_STATE_NORMAL].blue = 65535;
+ style->bg[GTK_STATE_NORMAL].green = 65535;
+ gdk_colormap_alloc_color (colormap, &style->bg[GTK_STATE_NORMAL],
+ TRUE, TRUE);
+ gtk_widget_set_style (GTK_WIDGET (w), style);
+ gtk_widget_set_usize (w, PREVIEW_WIDTH,
+ PREVIEW_HEIGHT + PREVIEW_TEXT_HEIGHT);
+ gnome_canvas_set_scroll_region (GNOME_CANVAS (w), 0, 0, PREVIEW_WIDTH,
+ PREVIEW_HEIGHT + PREVIEW_TEXT_HEIGHT);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = - 10;
+ points->coords[1] = PREVIEW_HEIGHT - 10;
+ points->coords[2] = PREVIEW_WIDTH + 10;
+ points->coords[3] = PREVIEW_HEIGHT - 10;
+
+ gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS(br->canvas)),
+ gnome_canvas_line_get_type (),
+ "fill_color", "gray",
+ "line_style", GDK_LINE_ON_OFF_DASH,
+ "points", points,
+ NULL);
+
+ gnome_canvas_points_unref (points);
+
+ br->description = GNOME_CANVAS_TEXT (
+ gnome_canvas_item_new (
+ gnome_canvas_root (GNOME_CANVAS (br->canvas)),
+ gnome_canvas_text_get_type (),
+ "x", 0.0,
+ "y", PREVIEW_HEIGHT - 9.0,
+ "anchor", GTK_ANCHOR_NORTH_WEST,
+ "justification", GTK_JUSTIFY_CENTER,
+ "font", "sans 10",
+ "text", "",
+ NULL));
+
+ /*
+ * Set up dnd.
+ */
+ g_signal_connect (G_OBJECT (br->canvas), "drag_data_get",
+ GTK_SIGNAL_FUNC (drag_data_get), br);
+
+ gtk_drag_source_set (br->canvas, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+ dnd_types, dnd_num_types, GDK_ACTION_MOVE);
+
+ br->filter_entry = GTK_ENTRY (glade_xml_get_widget(gui, "part_search"));
+
+ g_signal_connect (G_OBJECT (br->filter_entry), "changed",
+ G_CALLBACK (part_search_change), br);
+ g_signal_connect (G_OBJECT (br->filter_entry), "activate",
+ G_CALLBACK (part_search_activate), br);
+
+ /* Buttons. */
+ w = glade_xml_get_widget (gui, "place_button");
+ g_signal_connect (G_OBJECT (w), "clicked",
+ GTK_SIGNAL_FUNC (place_cmd), br);
+
+ /* Update the libraries option menu */
+ br->library = g_list_nth_data (oregano.libraries, 0);
+ part_browser_setup_libs (br, gui);
+
+ /* Parts list. */
+ w = glade_xml_get_widget (gui, "parts_list");
+ br->list = w;
+
+ /* Create de ListModel for TreeView, this is a Real model */
+ br->real_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
+ cell_text = gtk_cell_renderer_text_new ();
+ cell_column = gtk_tree_view_column_new_with_attributes (
+ " ", cell_text, "text", 0, NULL);
+
+ /* Create the sort model for the items, this sort the real model */
+ br->sort_model = gtk_tree_model_sort_new_with_model (
+ GTK_TREE_MODEL(br->real_model));
+
+ gtk_tree_sortable_set_sort_column_id (
+ GTK_TREE_SORTABLE(br->sort_model),
+ 0, GTK_SORT_ASCENDING);
+
+ /* Create the filter sorted model. This filter items based on user
+ request for fast item search */
+
+ br->filter_model = gtk_tree_model_filter_new (br->sort_model, NULL);
+ gtk_tree_model_filter_set_visible_func (
+ GTK_TREE_MODEL_FILTER (br->filter_model),
+ part_list_filter_func, br, NULL);
+
+ /* If we have TreeFilter use it, if not, just use sorting model only */
+ if (br->filter_model)
+ gtk_tree_view_set_model (GTK_TREE_VIEW(w), br->filter_model);
+ else
+ gtk_tree_view_set_model (GTK_TREE_VIEW(w), br->sort_model);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW(w), cell_column);
+ update_list (br);
+
+ /*
+ * Set up TreeView dnd.
+ */
+ g_signal_connect (G_OBJECT (w), "drag_data_get",
+ GTK_SIGNAL_FUNC (drag_data_get), br);
+
+ gtk_drag_source_set (w, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+ dnd_types, dnd_num_types, GDK_ACTION_MOVE);
+
+ g_signal_connect (G_OBJECT (w),
+ "cursor_changed", G_CALLBACK(select_row), br);
+ g_signal_connect (G_OBJECT (w),
+ "row_activated", G_CALLBACK(part_selected), br);
+
+ br->viewport = glade_xml_get_widget (gui, "part_browser_vbox");
+
+ path = gtk_tree_path_new_first ();
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (w),path, NULL, FALSE);
+ gtk_tree_path_free (path);
+
+ return br->viewport;
+}
+
+static void
+part_browser_setup_libs(Browser *br, GladeXML *gui) {
+
+ GtkWidget *combo_box, *w;
+
+ GList *libs;
+ gboolean first = FALSE;
+
+ w = glade_xml_get_widget (gui, "library_optionmenu");
+ gtk_widget_destroy (w);
+
+ w = glade_xml_get_widget (gui, "table1");
+ combo_box = gtk_combo_box_new_text ();
+ gtk_table_attach_defaults (GTK_TABLE(w),combo_box,1,2,0,1);
+
+ libs = oregano.libraries;
+
+ while (libs) {
+ gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box),
+ ((Library *)libs->data)->name);
+ libs = libs->next;
+ if (!first) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX(combo_box),0);
+ first = TRUE;
+ }
+ }
+
+ g_signal_connect (G_OBJECT (combo_box),
+ "changed", G_CALLBACK(library_switch_cb), br);
+}
+
+static void
+library_switch_cb (GtkWidget *combo_box, Browser *br)
+{
+ GList *libs = oregano.libraries;
+
+ br->library = (Library *) g_list_nth_data (libs,
+ gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)));
+
+ update_list (br);
+}
+
+static void
+wrap_string (char* str, int width)
+{
+ const int minl = width/2;
+ char *lnbeg, *sppos, *ptr;
+ int te = 0;
+
+ g_return_if_fail (str != NULL);
+
+ lnbeg= sppos = ptr = str;
+
+ while (*ptr) {
+ if (*ptr == '\t')
+ te += 7;
+
+ if (*ptr == ' ')
+ sppos = ptr;
+
+ if(ptr - lnbeg > width - te && sppos >= lnbeg + minl) {
+ *sppos = '\n';
+ lnbeg = ptr;
+ te = 0;
+ }
+
+ if(*ptr=='\n') {
+ lnbeg = ptr;
+ te = 0;
+ }
+ ptr++;
+ }
+}
+
+static void
+preview_realized (GtkWidget *widget, Browser *br)
+{
+ update_preview (br);
+}
+
+void
+part_browser_reparent (gpointer *br, GtkWidget *new_parent)
+{
+ Browser *b;
+ g_return_if_fail (br != NULL);
+ b = (Browser *)br;
+ gtk_widget_reparent (GTK_WIDGET (b->viewport), new_parent);
+}
diff --git a/src/part-browser.h b/src/part-browser.h
new file mode 100644
index 0000000..5dcfa73
--- /dev/null
+++ b/src/part-browser.h
@@ -0,0 +1,43 @@
+/*
+ * part-browser.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 __PART_BROWSER_H
+#define __PART_BROWSER_H
+
+#include <gnome.h>
+#include <gtk/gtk.h>
+#include "schematic.h"
+
+void part_browser_toggle_show (SchematicView *schematic_view);
+GtkWidget *part_browser_create (SchematicView *schematic_view);
+void part_browser_dnd (GtkSelectionData *selection_data, gint x, gint y);
+void part_browser_place_selected_part (Schematic *sm);
+void part_browser_reparent (gpointer *br, GtkWidget *new_parent);
+
+#endif
diff --git a/src/pixmaps/Makefile.am b/src/pixmaps/Makefile.am
new file mode 100644
index 0000000..1e94d03
--- /dev/null
+++ b/src/pixmaps/Makefile.am
@@ -0,0 +1,13 @@
+EXTRA_DIST = \
+ README \
+ log.xpm \
+ menu_zoom.xcf \
+ menu_zoom.xpm \
+ mini_icon.xpm \
+ mini_icon_plot.xcf \
+ mini_icon_plot.xpm \
+ plot.xpm \
+ tool_arrow.xpm \
+ tool_text.xpm \
+ tool_wire.xpm \
+ logo.xpm
diff --git a/src/pixmaps/README b/src/pixmaps/README
new file mode 100644
index 0000000..4df941e
--- /dev/null
+++ b/src/pixmaps/README
@@ -0,0 +1,2 @@
+The pixmaps here should eventually be moved to ../stock/
+
diff --git a/src/pixmaps/log.xpm b/src/pixmaps/log.xpm
new file mode 100644
index 0000000..459f4ba
--- /dev/null
+++ b/src/pixmaps/log.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static char * log_xpm[] = {
+"16 16 2 2",
+" c None",
+". c #000000",
+" ",
+" . . . . . . . . . . . ",
+" ",
+" . . . . . . . . . . . ",
+" ",
+" . . . . . . . . . . . ",
+" ",
+" . . . . . . . . . ",
+" ",
+" . . . . . . . . . . . . . ",
+" ",
+" . . . . . . . . . . . . ",
+" ",
+" . . . . . . . . . ",
+" ",
+" "};
diff --git a/src/pixmaps/logo.png b/src/pixmaps/logo.png
new file mode 100644
index 0000000..3ce93f6
--- /dev/null
+++ b/src/pixmaps/logo.png
Binary files differ
diff --git a/src/pixmaps/logo.xpm b/src/pixmaps/logo.xpm
new file mode 100644
index 0000000..730f2bb
--- /dev/null
+++ b/src/pixmaps/logo.xpm
@@ -0,0 +1,2896 @@
+/* XPM */
+static char *logo_xpm[] = {
+"285 80 2813 2",
+" c None",
+". c #BBBDF5",
+"+ c #A0A2EB",
+"@ c #ABACE9",
+"# c #E8E9F4",
+"$ c #EAEBF6",
+"% c #E3E5EF",
+"& c #EAEBF5",
+"* c #E5E6F0",
+"= c #DADBE4",
+"- c #E6E8F1",
+"; c #EAEBF4",
+"> c #EAEBF3",
+", c #ECEDF5",
+"' c #EDEEF6",
+") c #E5E7EE",
+"! c #DBDDE4",
+"~ c #EEF0F7",
+"{ c #F4F5FC",
+"] c #DEDFE7",
+"^ c #EBECF4",
+"/ c #E2E3EB",
+"( c #E1E2EA",
+"_ c #E0E2EA",
+": c #D8DAE2",
+"< c #E9EBF3",
+"[ c #EEF0F8",
+"} c #E7E8F1",
+"| c #D1D2DB",
+"1 c #EFF0F9",
+"2 c #ECEDF6",
+"3 c #E2E4ED",
+"4 c #EEF0F9",
+"5 c #E9EAF4",
+"6 c #ECEEF8",
+"7 c #ECEDF7",
+"8 c #ECEDF8",
+"9 c #E6E8F2",
+"0 c #E6E7F2",
+"a c #EAECF7",
+"b c #DFE1ED",
+"c c #ECEEFA",
+"d c #EBEDFA",
+"e c #D6D8E5",
+"f c #E0E2F0",
+"g c #E3E5F2",
+"h c #E8EAF6",
+"i c #E4E6F1",
+"j c #E4E6F0",
+"k c #EDEFF8",
+"l c #DFE0E9",
+"m c #E4E5ED",
+"n c #E7E8F0",
+"o c #E9EAF1",
+"p c #EDEFF6",
+"q c #EAEAF2",
+"r c #E5E6ED",
+"s c #ECEDF4",
+"t c #E6E8F4",
+"u c #DEE0EE",
+"v c #E0E2F1",
+"w c #E7EAF8",
+"x c #E6E8F6",
+"y c #DEE1EF",
+"z c #DEE0EF",
+"A c #D9DCEA",
+"B c #DBDDEC",
+"C c #DDE0EE",
+"D c #DCDEEC",
+"E c #DBDDEB",
+"F c #DFE1EF",
+"G c #E2E4F3",
+"H c #E5E7F6",
+"I c #E4E6F4",
+"J c #D4D7E4",
+"K c #E0E1F0",
+"L c #E8EAFA",
+"M c #DFE2F0",
+"N c #E3E6F3",
+"O c #D5D8E8",
+"P c #D0D2E2",
+"Q c #D7DAEA",
+"R c #DEE0F1",
+"S c #DDDEEF",
+"T c #E0E3F4",
+"U c #E3E5F6",
+"V c #E2E4F5",
+"W c #DFE1F2",
+"X c #DCDFEF",
+"Y c #E0E2F3",
+"Z c #D7D8E9",
+"` c #CED1E1",
+" . c #E0E3F3",
+".. c #E0E1F2",
+"+. c #DEE1F1",
+"@. c #E5E7F8",
+"#. c #E4E7F7",
+"$. c #E4E6F6",
+"%. c #DDDFEF",
+"&. c #D6D8E8",
+"*. c #DFE2F3",
+"=. c #E2E3F2",
+"-. c #D9D9E2",
+";. c #DFE0E8",
+">. c #AFB0E0",
+",. c #8C8DE7",
+"'. c #D1D2F8",
+"). c #BCBDF8",
+"!. c #8E8EDC",
+"~. c #B1B1EF",
+"{. c #E2E3F4",
+"]. c #E0E1EB",
+"^. c #D7D8F4",
+"/. c #D0D2F8",
+"(. c #BFC0EB",
+"_. c #CFD1EF",
+":. c #DEE0ED",
+"<. c #E5E7F2",
+"[. c #E1E2ED",
+"}. c #E3E4EC",
+"|. c #AAC5C0",
+"1. c #BEDED5",
+"2. c #E9EEF3",
+"3. c #E8E9F1",
+"4. c #E2E4F0",
+"5. c #E9EBFA",
+"6. c #E2E5F3",
+"7. c #E4E5F4",
+"8. c #E4E7F5",
+"9. c #E6E9F8",
+"0. c #E3E5F3",
+"a. c #E1E4F2",
+"b. c #E1E2F1",
+"c. c #D9DBEE",
+"d. c #D7DAEC",
+"e. c #DCDFF1",
+"f. c #DFE2F5",
+"g. c #D6D9EC",
+"h. c #D4D7EA",
+"i. c #D8DBED",
+"j. c #D5D7EA",
+"k. c #D7DAED",
+"l. c #D6D9EB",
+"m. c #D6D8EB",
+"n. c #DADDF0",
+"o. c #DCDFF2",
+"p. c #D8DBEE",
+"q. c #E1E4F7",
+"r. c #DDE0F3",
+"s. c #E7EAFD",
+"t. c #E4E7FB",
+"u. c #DCDFF4",
+"v. c #CFD2E7",
+"w. c #D1D4E9",
+"x. c #D0D3E8",
+"y. c #CDCFE5",
+"z. c #D2D4EA",
+"A. c #DEE1F7",
+"B. c #E5E8FE",
+"C. c #D4D6EC",
+"D. c #D6D9EF",
+"E. c #DBDEF4",
+"F. c #D7DAEF",
+"G. c #D3D6EB",
+"H. c #DADDF2",
+"I. c #E1E4FA",
+"J. c #D7D9EF",
+"K. c #E3E6FB",
+"L. c #D7DAF0",
+"M. c #D8DBF1",
+"N. c #D4D7EC",
+"O. c #DCDFF5",
+"P. c #DCDEEF",
+"Q. c #DADDED",
+"R. c #B8BBEF",
+"S. c #C4C6E7",
+"T. c #DEE0F0",
+"U. c #CCCEE4",
+"V. c #BCBEEE",
+"W. c #D1D4EE",
+"X. c #D0D2EF",
+"Y. c #D8DAFA",
+"Z. c #C7C9F0",
+"`. c #BCBDF0",
+" + c #C4C5F6",
+".+ c #E4E4F7",
+"++ c #E1E3F5",
+"@+ c #B0B2F5",
+"#+ c #D7D8EA",
+"$+ c #DFE0EA",
+"%+ c #CFD1DA",
+"&+ c #D5D7E2",
+"*+ c #E5E7F3",
+"=+ c #DFE0ED",
+"-+ c #DADBE7",
+";+ c #D7D9E4",
+">+ c #D8D9E2",
+",+ c #E8EAF2",
+"'+ c #F0F1F8",
+")+ c #EBECF3",
+"!+ c #E6E7EF",
+"~+ c #E1E3EF",
+"{+ c #EBEDFB",
+"]+ c #E3E6F4",
+"^+ c #C4D1DA",
+"/+ c #CDDBE4",
+"(+ c #D7D9E7",
+"_+ c #E0E3F1",
+":+ c #E2E5F8",
+"<+ c #DEE1F4",
+"[+ c #E3E6F9",
+"}+ c #E0E3F6",
+"|+ c #DDDFF3",
+"1+ c #D2D5E7",
+"2+ c #D4D7E9",
+"3+ c #DDDFED",
+"4+ c #E2E4F4",
+"5+ c #DDDDED",
+"6+ c #D2D4E2",
+"7+ c #D1D3E1",
+"8+ c #DADCEB",
+"9+ c #DFE1F0",
+"0+ c #D9DBE9",
+"a+ c #DFE2EF",
+"b+ c #E3E4F3",
+"c+ c #E7E9F8",
+"d+ c #D8D9EA",
+"e+ c #DFE2F2",
+"f+ c #DDDFF0",
+"g+ c #D8DAEB",
+"h+ c #D8DAEA",
+"i+ c #E1E3F4",
+"j+ c #DADCED",
+"k+ c #D9DBEC",
+"l+ c #D9DBEB",
+"m+ c #DBDDED",
+"n+ c #DDE0F0",
+"o+ c #DCDEEE",
+"p+ c #E8EBFA",
+"q+ c #E2E3F5",
+"r+ c #E0E2F2",
+"s+ c #E1E3F3",
+"t+ c #D7DAEB",
+"u+ c #D3D5E6",
+"v+ c #CCCEDF",
+"w+ c #D5D7ED",
+"x+ c #D5D8ED",
+"y+ c #D2D5EA",
+"z+ c #D4D7EB",
+"A+ c #D4D6E9",
+"B+ c #E4E7F9",
+"C+ c #E4E7F8",
+"D+ c #D6D8EF",
+"E+ c #C1C4F0",
+"F+ c #CBCEE9",
+"G+ c #DADDEB",
+"H+ c #D5D8EF",
+"I+ c #C8CAF0",
+"J+ c #E3E5F4",
+"K+ c #E6E8F7",
+"L+ c #D8DBE9",
+"M+ c #EFF1F8",
+"N+ c #DCDEE5",
+"O+ c #EAECF5",
+"P+ c #E8E9F3",
+"Q+ c #E1E2EF",
+"R+ c #DBDCE9",
+"S+ c #E0E2ED",
+"T+ c #DEDFEB",
+"U+ c #EDEFF9",
+"V+ c #EEEFF8",
+"W+ c #E6E7F0",
+"X+ c #E2E4EC",
+"Y+ c #DEDFEE",
+"Z+ c #D6D8E7",
+"`+ c #D7D8E7",
+" @ c #D4D6E4",
+".@ c #DCDEED",
+"+@ c #D8DAEE",
+"@@ c #E9ECFC",
+"#@ c #D5D8EA",
+"$@ c #E1E3F2",
+"%@ c #E6E9F7",
+"&@ c #DFE0EF",
+"*@ c #DADCEA",
+"=@ c #E5E8F7",
+"-@ c #E7E9F5",
+";@ c #EEEFF6",
+">@ c #EEEFF7",
+",@ c #EDEEF5",
+"'@ c #DBDCE4",
+")@ c #D3D4DB",
+"!@ c #E3E4EB",
+"~@ c #F2F3FA",
+"{@ c #EFF0F8",
+"]@ c #E0E2E9",
+"^@ c #E1E3EA",
+"/@ c #E1E1EA",
+"(@ c #D3D4DC",
+"_@ c #EDEDF6",
+":@ c #E2E3EC",
+"<@ c #D6D8E0",
+"[@ c #E1E2EB",
+"}@ c #E9E9F2",
+"|@ c #F2F3FB",
+"1@ c #DCDDE6",
+"2@ c #E5E6EE",
+"3@ c #EDEFF7",
+"4@ c #E5E5EE",
+"5@ c #D5D7E6",
+"6@ c #CCCFEF",
+"7@ c #B6B8E7",
+"8@ c #D3D6EA",
+"9@ c #D8D9F2",
+"0@ c #C7CAEE",
+"a@ c #E4E6F5",
+"b@ c #DBDEF1",
+"c@ c #D9DCEF",
+"d@ c #D8DAE9",
+"e@ c #DEE1F2",
+"f@ c #E1E4F4",
+"g@ c #D9DBEA",
+"h@ c #E9EAF2",
+"i@ c #DADBE6",
+"j@ c #E9EAF7",
+"k@ c #E7E9F4",
+"l@ c #E0E1EA",
+"m@ c #D4D6DD",
+"n@ c #D8DAE7",
+"o@ c #DEDFF0",
+"p@ c #D8DBF0",
+"q@ c #D5D8EC",
+"r@ c #D1D4E6",
+"s@ c #DBDDF0",
+"t@ c #EAECFA",
+"u@ c #D4D9E6",
+"v@ c #C7D4DD",
+"w@ c #D6E0EA",
+"x@ c #E9EBF9",
+"y@ c #EAECFB",
+"z@ c #E1E4F1",
+"A@ c #E4E6F3",
+"B@ c #F1F2FA",
+"C@ c #F3F4FB",
+"D@ c #F4F5FD",
+"E@ c #F0F1F9",
+"F@ c #F1F2F9",
+"G@ c #F0F2F9",
+"H@ c #E7E8EF",
+"I@ c #E4E6ED",
+"J@ c #D7D8DF",
+"K@ c #D6D7DF",
+"L@ c #EFF0F4",
+"M@ c #E0E0E3",
+"N@ c #F5F5F7",
+"O@ c #F8F8FA",
+"P@ c #ECEDEF",
+"Q@ c #F4F4F6",
+"R@ c #F8F9FB",
+"S@ c #F6F7F9",
+"T@ c #E4E5E7",
+"U@ c #F3F3F6",
+"V@ c #EDEDF0",
+"W@ c #D7D7DA",
+"X@ c #F7F7FA",
+"Y@ c #E8E9EB",
+"Z@ c #F0F1F3",
+"`@ c #DCDDDF",
+" # c #F7F8FA",
+".# c #FBFBFE",
+"+# c #EEEEF1",
+"@# c #F8F8FB",
+"## c #D6D6D9",
+"$# c #E5E6E9",
+"%# c #F6F6F9",
+"&# c #E2E3E6",
+"*# c #EFF0F3",
+"=# c #F9F9FC",
+"-# c #E1E2E5",
+";# c #F4F4F7",
+"># c #EAEAED",
+",# c #ECECEF",
+"'# c #F2F2F5",
+")# c #E4E5E8",
+"!# c #DEDEE1",
+"~# c #F0F1F4",
+"{# c #F6F7FA",
+"]# c #DEDFE2",
+"^# c #E7E7EA",
+"/# c #F5F5F8",
+"(# c #EFEFF2",
+"_# c #F8F9FC",
+":# c #F4F4F9",
+"<# c #EBECF5",
+"[# c #CDCEF4",
+"}# c #9293E2",
+"|# c #B6B7E7",
+"1# c #E3E4F1",
+"2# c #E0E0ED",
+"3# c #D4D5F0",
+"4# c #B4B5F6",
+"5# c #A9AAEA",
+"6# c #E9EBF4",
+"7# c #D4D6ED",
+"8# c #CED0ED",
+"9# c #CFD1F2",
+"0# c #D3D6F2",
+"a# c #DDDFF1",
+"b# c #DEE0F2",
+"c# c #D3D5F5",
+"d# c #CDCFF2",
+"e# c #D3D6E8",
+"f# c #D5D8EB",
+"g# c #DADDF3",
+"h# c #D9DCF1",
+"i# c #D4D7ED",
+"j# c #DCDFEE",
+"k# c #DBDEEB",
+"l# c #E0E1EE",
+"m# c #DDDEE6",
+"n# c #C9CBD3",
+"o# c #DDDEE8",
+"p# c #ECEEF6",
+"q# c #EAEBF2",
+"r# c #E2E4F2",
+"s# c #DADBEA",
+"t# c #DADCF2",
+"u# c #DFE2F8",
+"v# c #DDE0F6",
+"w# c #D8DAE8",
+"x# c #DCDFED",
+"y# c #DDDEED",
+"z# c #E1E3F0",
+"A# c #C4C5CD",
+"B# c #7A7B82",
+"C# c #797C82",
+"D# c #C5C8CF",
+"E# c #DFE0E7",
+"F# c #F2F2F7",
+"G# c #F0F0F3",
+"H# c #E3E3E6",
+"I# c #EAEBED",
+"J# c #F5F6F8",
+"K# c #F9F9FB",
+"L# c #FAFBFD",
+"M# c #F6F6F8",
+"N# c #F1F2F3",
+"O# c #DBDBDB",
+"P# c #FBFBFB",
+"Q# c #FAFAFA",
+"R# c #E2E2E3",
+"S# c #D7D7D7",
+"T# c #ECECEC",
+"U# c #FCFCFC",
+"V# c #F2F2F2",
+"W# c #DADADB",
+"X# c #FDFDFD",
+"Y# c #EAEAEB",
+"Z# c #D5D5D5",
+"`# c #FEFEFE",
+" $ c #F4F4F4",
+".$ c #F3F3F3",
+"+$ c #F6F6F6",
+"@$ c #CFD0D0",
+"#$ c #D2D2D2",
+"$$ c #D4D5D5",
+"%$ c #F7F7F7",
+"&$ c #DFDFE0",
+"*$ c #DEDEDF",
+"=$ c #E4E4E5",
+"-$ c #F9F9F9",
+";$ c #F0F0F1",
+">$ c #F0F0F0",
+",$ c #E7E7E8",
+"'$ c #EBEBEC",
+")$ c #E9E9EB",
+"!$ c #E8E8EB",
+"~$ c #EBECEF",
+"{$ c #A5A5F7",
+"]$ c #9C9DF7",
+"^$ c #B1B1F3",
+"/$ c #A0A0E4",
+"($ c #9B9BEF",
+"_$ c #B0B0FC",
+":$ c #D2D2EF",
+"<$ c #EFEFF4",
+"[$ c #D3D4F7",
+"}$ c #A3A4E7",
+"|$ c #A5A5EC",
+"1$ c #CFD0FA",
+"2$ c #E0E2EC",
+"3$ c #D5D6DE",
+"4$ c #D1D2EB",
+"5$ c #B8B9F6",
+"6$ c #C8C9F8",
+"7$ c #F0F0F8",
+"8$ c #E4E6F2",
+"9$ c #DBDDEE",
+"0$ c #D5D7EB",
+"a$ c #DCDFEC",
+"b$ c #E5E8F6",
+"c$ c #E1E2F7",
+"d$ c #9EA0F3",
+"e$ c #B3B3EE",
+"f$ c #ECEDFA",
+"g$ c #E2E3EE",
+"h$ c #D4D4DC",
+"i$ c #D6D8EE",
+"j$ c #DDE0F1",
+"k$ c #E0E1E8",
+"l$ c #E2E4EB",
+"m$ c #E1E2E9",
+"n$ c #E2E3EA",
+"o$ c #7D7D80",
+"p$ c #222325",
+"q$ c #2D2E30",
+"r$ c #9D9EA0",
+"s$ c #DCDCDF",
+"t$ c #EBEBEE",
+"u$ c #E5E6E8",
+"v$ c #F3F3F5",
+"w$ c #E5E5E6",
+"x$ c #EAEAEA",
+"y$ c #D9D9D9",
+"z$ c #F1F1F2",
+"A$ c #E9E9EA",
+"B$ c #F3F3F4",
+"C$ c #EEEEEE",
+"D$ c #ECECED",
+"E$ c #EDEDED",
+"F$ c #FFFFFF",
+"G$ c #EFEFEF",
+"H$ c #E3E3E3",
+"I$ c #F5F5F5",
+"J$ c #E7E7E7",
+"K$ c #F8F8F8",
+"L$ c #F1F1F1",
+"M$ c #DFDFDF",
+"N$ c #D6D6D6",
+"O$ c #E6E6E6",
+"P$ c #CECECE",
+"Q$ c #DEDEDE",
+"R$ c #D7D7D8",
+"S$ c #D8D8D9",
+"T$ c #CAC9E5",
+"U$ c #B8B7F8",
+"V$ c #A3A3FD",
+"W$ c #4F4FEA",
+"X$ c #8685F7",
+"Y$ c #E2E2FC",
+"Z$ c #FCFCFD",
+"`$ c #C7C8FC",
+" % c #8586FD",
+".% c #7B7BF5",
+"+% c #B4B4F8",
+"@% c #E3E3FD",
+"#% c #E6E7ED",
+"$% c #CBCBF1",
+"%% c #8B8BEE",
+"&% c #BABBEB",
+"*% c #F4F5F9",
+"=% c #E4E5EC",
+"-% c #EDEEF7",
+";% c #EBEDF5",
+">% c #DADDEF",
+",% c #D9DAE9",
+"'% c #D0D2EE",
+")% c #BABCEB",
+"!% c #DEDFF3",
+"~% c #E8E9F8",
+"{% c #D0D1E0",
+"]% c #C0C1F4",
+"^% c #B1B3F1",
+"/% c #C8C9E1",
+"(% c #ABACEA",
+"_% c #DDDFEB",
+":% c #D4D6E2",
+"<% c #DDE0ED",
+"[% c #DBDEEC",
+"}% c #EFEFF8",
+"|% c #E4E6EE",
+"1% c #F3F3F8",
+"2% c #F1F2F4",
+"3% c #F4F5F7",
+"4% c #E6E6E9",
+"5% c #F9F9FA",
+"6% c #A6A6A7",
+"7% c #292934",
+"8% c #3B3B40",
+"9% c #C1C1C1",
+"0% c #EDEDEE",
+"a% c #E9E9E9",
+"b% c #E2E2E2",
+"c% c #EBEBEB",
+"d% c #E5E5E5",
+"e% c #E0E0E0",
+"f% c #DCDCDC",
+"g% c #E8E8E8",
+"h% c #C6C5C8",
+"i% c #D8D8E8",
+"j% c #DEDEFD",
+"k% c #6462D9",
+"l% c #8C8AF7",
+"m% c #EAEAF5",
+"n% c #CDCDCD",
+"o% c #B0B0DD",
+"p% c #9091F4",
+"q% c #8A8AF1",
+"r% c #9797F5",
+"s% c #9D9EFC",
+"t% c #BDBBF1",
+"u% c #F0EFF8",
+"v% c #DFDEFC",
+"w% c #8181F3",
+"x% c #C0C0E1",
+"y% c #E1E2E4",
+"z% c #E9EAEC",
+"A% c #F1F1F4",
+"B% c #EBECF1",
+"C% c #F1F3FA",
+"D% c #E3E5F1",
+"E% c #D6D8E6",
+"F% c #DDDFEE",
+"G% c #D5D7F4",
+"H% c #CBCDF1",
+"I% c #CDCFEA",
+"J% c #B8BAEB",
+"K% c #DADBF5",
+"L% c #E2E3FB",
+"M% c #BEBFF6",
+"N% c #B3B5E9",
+"O% c #DBDDE8",
+"P% c #E2E4F1",
+"Q% c #D3D5E3",
+"R% c #D8DAED",
+"S% c #E2E3F3",
+"T% c #E6E7F4",
+"U% c #D0D1D9",
+"V% c #E5E7EF",
+"W% c #E3E4E8",
+"X% c #D2D3D6",
+"Y% c #DBDBDE",
+"Z% c #FAFAFC",
+"`% c #6C6CAC",
+" & c #F4F4FA",
+".& c #E4E4E4",
+"+& c #D8D8D8",
+"@& c #E1E1E1",
+"#& c #D1D1D1",
+"$& c #DDDDDD",
+"%& c #C6C4E4",
+"&& c #8080FA",
+"*& c #DCDCE5",
+"=& c #E3E3FB",
+"-& c #CECEF1",
+";& c #C2C2EF",
+">& c #C6C6FD",
+",& c #C7C6DC",
+"'& c #C3C3F8",
+")& c #6F6FF5",
+"!& c #DCDCE9",
+"~& c #E6E6E7",
+"{& c #D9DADA",
+"]& c #E0E0E2",
+"^& c #FAFAFD",
+"/& c #D9D9DC",
+"(& c #E6E7EB",
+"_& c #EFF0F7",
+":& c #EBEDF9",
+"<& c #D7D8F8",
+"[& c #C2C4ED",
+"}& c #D7DAF1",
+"|& c #D1D4E8",
+"1& c #D0D2E6",
+"2& c #D6D8EC",
+"3& c #D2D5E8",
+"4& c #BDBFE6",
+"5& c #DEE0F9",
+"6& c #D7D8FC",
+"7& c #B8B9EE",
+"8& c #B4B5F9",
+"9& c #D6D7EF",
+"0& c #D8D9EC",
+"a& c #DBDCE7",
+"b& c #E1E2EC",
+"c& c #D8D9E6",
+"d& c #DADCEF",
+"e& c #D9DCEE",
+"f& c #E5E7FB",
+"g& c #E1E3F1",
+"h& c #E8E9F2",
+"i& c #DBDCE5",
+"j& c #DEDFE8",
+"k& c #E9E9EE",
+"l& c #ECEDF0",
+"m& c #DADBDE",
+"n& c #EDEDF5",
+"o& c #C7C7D6",
+"p& c #F5F5FD",
+"q& c #A0A0F7",
+"r& c #BBBBFE",
+"s& c #D0D0D0",
+"t& c #DADADA",
+"u& c #D3D3D3",
+"v& c #F7F7FB",
+"w& c #C4C4FC",
+"x& c #6666EC",
+"y& c #F2F3F4",
+"z& c #DADBDD",
+"A& c #DADADC",
+"B& c #D1D1D4",
+"C& c #EDEEF2",
+"D& c #E2E3F8",
+"E& c #9091E9",
+"F& c #CCCEF3",
+"G& c #C3C4EE",
+"H& c #CED0F4",
+"I& c #DBDDF3",
+"J& c #D2D5E9",
+"K& c #E6E9FB",
+"L& c #D5D7EF",
+"M& c #BEC1E9",
+"N& c #CACDEE",
+"O& c #E7E9F3",
+"P& c #E8EAF4",
+"Q& c #E0E3F0",
+"R& c #E8E9F0",
+"S& c #F4F5FA",
+"T& c #F9FAFD",
+"U& c #E6E7EA",
+"V& c #EEEEF0",
+"W& c #DBDBDC",
+"X& c #E8E8E9",
+"Y& c #EBEAF0",
+"Z& c #E4E4FF",
+"`& c #BCBDFC",
+" * c #8485FB",
+".* c #8B8CFE",
+"+* c #C6C5FB",
+"@* c #F2F1F9",
+"#* c #DEDEE6",
+"$* c #DCDCF4",
+"%* c #7878DF",
+"&* c #A3A3F7",
+"** c #CFCFCF",
+"=* c #F8F8FF",
+"-* c #CBCBFD",
+";* c #9090F4",
+">* c #E0E0E1",
+",* c #ECEDEE",
+"'* c #ACABCB",
+")* c #8C8CF2",
+"!* c #9898F9",
+"~* c #9797F2",
+"{* c #A6A7F5",
+"]* c #DFE0F9",
+"^* c #CCCFF0",
+"/* c #D1D3F2",
+"(* c #DFE1F3",
+"_* c #C6C8EF",
+":* c #D3D5E8",
+"<* c #F0F1FA",
+"[* c #E5E6F1",
+"}* c #E7E8F4",
+"|* c #E1E2EE",
+"1* c #E9EAF3",
+"2* c #E1E2F3",
+"3* c #E0E3EF",
+"4* c #EFEFF7",
+"5* c #E6E7EE",
+"6* c #D4D5D9",
+"7* c #E3E4E6",
+"8* c #BBBBEA",
+"9* c #A9A8FF",
+"0* c #9898F5",
+"a* c #9C9BE9",
+"b* c #8A89EB",
+"c* c #7372E8",
+"d* c #BABAF1",
+"e* c #FDFDFF",
+"f* c #C8C8F6",
+"g* c #9B9BFE",
+"h* c #C1C1FE",
+"i* c #E6E6F8",
+"j* c #CDCDF5",
+"k* c #8B8BEF",
+"l* c #BDBDFD",
+"m* c #CFD4DE",
+"n* c #AFBEEC",
+"o* c #A6B1E7",
+"p* c #CCCCCD",
+"q* c #B4B4E1",
+"r* c #E1E1F9",
+"s* c #F6F7FB",
+"t* c #D8D9E1",
+"u* c #CCCDE0",
+"v* c #A7A9F6",
+"w* c #9495F8",
+"x* c #9B9EF1",
+"y* c #B8BAF3",
+"z* c #D9DBF9",
+"A* c #B4B6ED",
+"B* c #D9DBF0",
+"C* c #D3D6E9",
+"D* c #D9DCED",
+"E* c #DCDDE5",
+"F* c #E0E1EC",
+"G* c #CDCED8",
+"H* c #D9DBF1",
+"I* c #ECEEF5",
+"J* c #D6D7DE",
+"K* c #F5F5F9",
+"L* c #D8D8DB",
+"M* c #E1E1E2",
+"N* c #DCDCDD",
+"O* c #EEEEEF",
+"P* c #7C7CF1",
+"Q* c #B6B6FF",
+"R* c #EAE9F9",
+"S* c #F7F7FD",
+"T* c #9796F4",
+"U* c #9E9EF7",
+"V* c #ECECFE",
+"W* c #CCCCEB",
+"X* c #A8A8FD",
+"Y* c #8585F6",
+"Z* c #C3C3F2",
+"`* c #A6A6CF",
+" = c #9E9EE5",
+".= c #E0E0FE",
+"+= c #CACACA",
+"@= c #DDEBE6",
+"#= c #ACD7D1",
+"$= c #B6D0D2",
+"%= c #E7E8EB",
+"&= c #BFC0E0",
+"*= c #B9BAE8",
+"== c #CACBF8",
+"-= c #DBDCFB",
+";= c #DDDFE8",
+">= c #8F91DD",
+",= c #A3A6EF",
+"'= c #D0D2F1",
+")= c #E8EAF8",
+"!= c #D7D9EC",
+"~= c #DCDEF4",
+"{= c #D6D9E9",
+"]= c #DADCEC",
+"^= c #E4E5F2",
+"/= c #E4E6F7",
+"(= c #D9DCF2",
+"_= c #DBDEF3",
+":= c #DADBE3",
+"<= c #ECECF0",
+"[= c #DEDEE0",
+"}= c #F9FAFB",
+"|= c #CCCCCC",
+"1= c #4143EA",
+"2= c #DBDBFD",
+"3= c #CAC9FD",
+"4= c #A0A0FE",
+"5= c #E8E8FE",
+"6= c #DADAF9",
+"7= c #8F8FFA",
+"8= c #CCCCF1",
+"9= c #9E9E9E",
+"0= c #2D2D38",
+"a= c #5A5A71",
+"b= c #DADAE4",
+"c= c #F3FCFA",
+"d= c #BCEFD6",
+"e= c #D8F0E3",
+"f= c #F9FAFC",
+"g= c #D6D6E8",
+"h= c #D1D2E5",
+"i= c #D9DAE7",
+"j= c #DFE1F1",
+"k= c #D6D9EE",
+"l= c #CCCFE4",
+"m= c #CDD0E5",
+"n= c #E7E8F3",
+"o= c #D4D6E3",
+"p= c #D8DAE4",
+"q= c #C6C7C9",
+"r= c #E8E9EC",
+"s= c #E3E3E4",
+"t= c #393AEF",
+"u= c #F7F6FE",
+"v= c #C9C9C9",
+"w= c #C2C2C3",
+"x= c #A4A3DE",
+"y= c #8C8EFD",
+"z= c #F5F5FF",
+"A= c #D9D9FB",
+"B= c #9191FE",
+"C= c #E7E7F8",
+"D= c #464646",
+"E= c #040404",
+"F= c #161616",
+"G= c #9999A7",
+"H= c #C9C9DB",
+"I= c #E3E3EB",
+"J= c #E1E1E7",
+"K= c #F7F7F8",
+"L= c #D9F0E4",
+"M= c #A2DFC0",
+"N= c #DEEBE4",
+"O= c #FCFCFE",
+"P= c #F2F2F6",
+"Q= c #DDDEE5",
+"R= c #DBDCE3",
+"S= c #DFE0F1",
+"T= c #D3D5E5",
+"U= c #CFD1E2",
+"V= c #DBDEEE",
+"W= c #ECEEF4",
+"X= c #F1F3F7",
+"Y= c #FBFBFC",
+"Z= c #E1E0F0",
+"`= c #9291FF",
+" - c #D6D6FF",
+".- c #F8F7FF",
+"+- c #DBDBE9",
+"@- c #6767CB",
+"#- c #9193FF",
+"$- c #D0D0D2",
+"%- c #B6B6FE",
+"&- c #6767E4",
+"*- c #818181",
+"=- c #060612",
+"-- c #3B3B42",
+";- c #6262E6",
+">- c #5757F4",
+",- c #8E8EFE",
+"'- c #A3A3FF",
+")- c #9292E7",
+"!- c #B4B4F3",
+"~- c #EEEEFD",
+"{- c #D3D3E6",
+"]- c #C3C3D5",
+"^- c #F1F1F9",
+"/- c #D4D4D4",
+"(- c #D0EDDE",
+"_- c #8CD0AC",
+":- c #D3D6D5",
+"<- c #EBECEE",
+"[- c #EEEEF3",
+"}- c #D8D8E0",
+"|- c #D9DCF0",
+"1- c #EAECF9",
+"2- c #EDEEF8",
+"3- c #D4D5E1",
+"4- c #D8DAE3",
+"5- c #DCDDEE",
+"6- c #E1E2F0",
+"7- c #FDFDFE",
+"8- c #B4B4B4",
+"9- c #969696",
+"0- c #767676",
+"a- c #5D5D5D",
+"b- c #515151",
+"c- c #434343",
+"d- c #494949",
+"e- c #555555",
+"f- c #696969",
+"g- c #8C8C8C",
+"h- c #ACACAC",
+"i- c #D4D3DC",
+"j- c #C4C4F4",
+"k- c #A9AAFD",
+"l- c #BFBEFA",
+"m- c #E8E5FD",
+"n- c #B5B4F5",
+"o- c #5656E1",
+"p- c #C7C7FC",
+"q- c #DEDEE7",
+"r- c #B8B8FA",
+"s- c #7979E4",
+"t- c #D1D1DB",
+"u- c #363673",
+"v- c #8C8CBC",
+"w- c #E2E2E5",
+"x- c #B9B9D9",
+"y- c #CBCBFF",
+"z- c #ABABFB",
+"A- c #5B5BCD",
+"B- c #5C5CE6",
+"C- c #8D8DFE",
+"D- c #6767FC",
+"E- c #4B4BE2",
+"F- c #9292FF",
+"G- c #9C9CFF",
+"H- c #B5B5FD",
+"I- c #DDDDFE",
+"J- c #C5C5D9",
+"K- c #CFCFE0",
+"L- c #E9E9F3",
+"M- c #E7E7EE",
+"N- c #FEFEFF",
+"O- c #DFF9EF",
+"P- c #A5E2C2",
+"Q- c #EAEAEC",
+"R- c #E0E0E5",
+"S- c #E5E8FB",
+"T- c #E8EBF9",
+"U- c #F0F2F8",
+"V- c #E0E3EB",
+"W- c #EAECF3",
+"X- c #DBDDE9",
+"Y- c #DBDCED",
+"Z- c #F5F6FA",
+"`- c #A2A3A3",
+" ; c #525252",
+".; c #424242",
+"+; c #4D4D4D",
+"@; c #9A9A9A",
+"#; c #C5C5C5",
+"$; c #B8B8F9",
+"%; c #4F50EA",
+"&; c #2E2FED",
+"*; c #8282FC",
+"=; c #BCBAF0",
+"-; c #E9E8F2",
+";; c #D3D3E7",
+">; c #D2D2F4",
+",; c #C4C4FE",
+"'; c #9999F2",
+"); c #D8D8F4",
+"!; c #8282E7",
+"~; c #B7B7FF",
+"{; c #E9E9F5",
+"]; c #B1B1D9",
+"^; c #AFAFEC",
+"/; c #8F8FEA",
+"(; c #8080F8",
+"_; c #7979FE",
+":; c #5D5DFC",
+"<; c #4141E9",
+"[; c #5757E6",
+"}; c #9797FD",
+"|; c #8E8EEE",
+"1; c #9D9DE4",
+"2; c #E6E6FF",
+"3; c #F9F9FE",
+"4; c #CCCCDF",
+"5; c #F3F3FF",
+"6; c #EEEEF6",
+"7; c #D6D6DC",
+"8; c #FBFBFD",
+"9; c #FBFCFB",
+"0; c #CED6D2",
+"a; c #D0F5E3",
+"b; c #A8DBC0",
+"c; c #EAEBF0",
+"d; c #E6E8F5",
+"e; c #DEE1EC",
+"f; c #E1E3ED",
+"g; c #EDEFF5",
+"h; c #E4E6EF",
+"i; c #E5E7F4",
+"j; c #E5E6F4",
+"k; c #ECEDF1",
+"l; c #C6C6C7",
+"m; c #585858",
+"n; c #5F5F5F",
+"o; c #F7F6F7",
+"p; c #CAC8FB",
+"q; c #ACAAFE",
+"r; c #DDDCFA",
+"s; c #9999E0",
+"t; c #5757F0",
+"u; c #4E4EFE",
+"v; c #3434ED",
+"w; c #9999EB",
+"x; c #FBFBFF",
+"y; c #C1C1EC",
+"z; c #7E7EE5",
+"A; c #C8C8DD",
+"B; c #B1B1F8",
+"C; c #6363E1",
+"D; c #7878FC",
+"E; c #5D5DFD",
+"F; c #5353F3",
+"G; c #7F7FFF",
+"H; c #A8A8FF",
+"I; c #A4A4FA",
+"J; c #9F9FDF",
+"K; c #E0E0FF",
+"L; c #D4D4E7",
+"M; c #EEEEFB",
+"N; c #ECECF5",
+"O; c #C7C7C7",
+"P; c #E3EDE8",
+"Q; c #DDEAE3",
+"R; c #EFF4F1",
+"S; c #E8ECEC",
+"T; c #D2F6E6",
+"U; c #B7E0CB",
+"V; c #DFE0E3",
+"W; c #E2E5F7",
+"X; c #E3E6FA",
+"Y; c #D4D7E5",
+"Z; c #E8EAF3",
+"`; c #D9DCEC",
+" > c #E4E5F6",
+".> c #E2E5FB",
+"+> c #D3D5EB",
+"@> c #F1F1F5",
+"#> c #F5F5F6",
+"$> c #5A5A5A",
+"%> c #6E6E6E",
+"&> c #8F8F8F",
+"*> c #A1A1A1",
+"=> c #A4A4A4",
+"-> c #A6A6A6",
+";> c #949494",
+">> c #6F6F6F",
+",> c #E5E4F5",
+"'> c #BFBEFF",
+")> c #E6E5FF",
+"!> c #FAFAFF",
+"~> c #D5D5F8",
+"{> c #DCDCF8",
+"]> c #B4B4EF",
+"^> c #9191F2",
+"/> c #CBCBF6",
+"(> c #DCDCDE",
+"_> c #BFBFDD",
+":> c #C1C1F5",
+"<> c #B3B3FC",
+"[> c #7E7EED",
+"}> c #5656DE",
+"|> c #6C6CFE",
+"1> c #6F6FFD",
+"2> c #5A5AF4",
+"3> c #7979F7",
+"4> c #9E9EFD",
+"5> c #AAAAF9",
+"6> c #CECEFF",
+"7> c #E1E1FE",
+"8> c #EAEAFD",
+"9> c #E3E3F4",
+"0> c #E9E9F7",
+"a> c #B7DBC9",
+"b> c #9FCEB5",
+"c> c #E1EFE8",
+"d> c #D3E3DB",
+"e> c #C3F5DB",
+"f> c #D4F3E3",
+"g> c #E5E5E8",
+"h> c #EAEBEF",
+"i> c #DFE1F7",
+"j> c #E7E9FA",
+"k> c #D7D9EA",
+"l> c #D3D5DE",
+"m> c #E4E5E9",
+"n> c #9B9B9B",
+"o> c #5B5B5B",
+"p> c #C8C8C8",
+"q> c #A9A9A9",
+"r> c #C3C3C3",
+"s> c #BDBDBD",
+"t> c #454545",
+"u> c #BCBCBC",
+"v> c #DDDDE8",
+"w> c #E0E0E4",
+"x> c #9F9FE9",
+"y> c #ADADF8",
+"z> c #EDEDFF",
+"A> c #EBEBF4",
+"B> c #C0C0E6",
+"C> c #8484DF",
+"D> c #7C7CEF",
+"E> c #7676FD",
+"F> c #4F4FFA",
+"G> c #4B4BF5",
+"H> c #6969F8",
+"I> c #7F7FF3",
+"J> c #7E7EE0",
+"K> c #9494DE",
+"L> c #C2C2EE",
+"M> c #F8F8FE",
+"N> c #E7E7F4",
+"O> c #D2D2DF",
+"P> c #F7F7FC",
+"Q> c #B7E2CC",
+"R> c #EAF4EF",
+"S> c #C8CFCC",
+"T> c #A1CBB5",
+"U> c #A7D5BD",
+"V> c #D7E6DE",
+"W> c #E0E1E1",
+"X> c #F4F4F8",
+"Y> c #E9EAF0",
+"Z> c #D5D6DF",
+"`> c #D5D6DD",
+" , c #D3D6EC",
+"., c #F1F1FA",
+"+, c #EDEDF2",
+"@, c #727272",
+"#, c #979797",
+"$, c #CBCBCB",
+"%, c #B8B8B8",
+"&, c #C0C0C0",
+"*, c #BABABA",
+"=, c #AEAEAE",
+"-, c #BFBFBF",
+";, c #A3A3A3",
+">, c #C2C2C2",
+",, c #7373CC",
+"', c #9595F3",
+"), c #F1F1FE",
+"!, c #EAEAFE",
+"~, c #D1D1FD",
+"{, c #B2B2F8",
+"], c #8181E6",
+"^, c #5D5DDD",
+"/, c #5B5BEC",
+"(, c #5959FE",
+"_, c #4A4AEC",
+":, c #5959E1",
+"<, c #9494FD",
+"[, c #A0A0F2",
+"}, c #E4E4FD",
+"|, c #F9F9FF",
+"1, c #E4E4EC",
+"2, c #93C7AC",
+"3, c #9BC9B0",
+"4, c #F9FBFA",
+"5, c #EAF8F1",
+"6, c #94CEB0",
+"7, c #D2F4E5",
+"8, c #E0ECE7",
+"9, c #D9DBDA",
+"0, c #F4F4F5",
+"a, c #E6E7F8",
+"b, c #E3E4ED",
+"c, c #E2E3EF",
+"d, c #D4D6E7",
+"e, c #E4E5EE",
+"f, c #EBEBED",
+"g, c #A5A5A5",
+"h, c #B3B3B3",
+"i, c #B9B9B9",
+"j, c #B1B1B1",
+"k, c #B0B0B0",
+"l, c #BEBEBE",
+"m, c #A7A7A7",
+"n, c #B5B5B5",
+"o, c #7A7A7A",
+"p, c #FAFBFC",
+"q, c #6878B5",
+"r, c #8B94E5",
+"s, c #CCCCE7",
+"t, c #B5B5EB",
+"u, c #ABABFD",
+"v, c #7A7AE7",
+"w, c #5858E1",
+"x, c #5151F5",
+"y, c #2F2FE2",
+"z, c #4747DF",
+"A, c #8181F9",
+"B, c #9595F7",
+"C, c #A2A2F5",
+"D, c #CACAFC",
+"E, c #ECECFD",
+"F, c #EDEDF9",
+"G, c #E6E6F5",
+"H, c #DFE6E2",
+"I, c #78AD92",
+"J, c #ACD5BF",
+"K, c #EFF0EF",
+"L, c #CDD3D0",
+"M, c #DDF5E9",
+"N, c #A2DFBF",
+"O, c #AAC4B6",
+"P, c #FCFEFD",
+"Q, c #F1F2F7",
+"R, c #E3E4F4",
+"S, c #E0E2EE",
+"T, c #D5D8E6",
+"U, c #E2E2E7",
+"V, c #838383",
+"W, c #AAAAAA",
+"X, c #C4C4C4",
+"Y, c #AFAFAF",
+"Z, c #ADADAD",
+"`, c #B7B7B7",
+" ' c #B6B6B6",
+".' c #B2B2B2",
+"+' c #636363",
+"@' c #ABABAB",
+"#' c #E6EEEB",
+"$' c #90BEAC",
+"%' c #C0E9DA",
+"&' c #CDCDF1",
+"*' c #BEBEFB",
+"=' c #8C8CE4",
+"-' c #6E6EE4",
+";' c #6464F5",
+">' c #3D3DEA",
+",' c #2A2AD6",
+"'' c #4545D7",
+")' c #6E6EE3",
+"!' c #9696EE",
+"~' c #BFBFFA",
+"{' c #D5D5FD",
+"]' c #E1E1FB",
+"^' c #F0F0FB",
+"/' c #E3F1EA",
+"(' c #A3D7BC",
+"_' c #CEF0DE",
+":' c #E8F0EC",
+"<' c #B5D6C6",
+"[' c #C8F1DD",
+"}' c #DFF1E7",
+"|' c #E4E4E7",
+"1' c #F5F6FB",
+"2' c #E6E7F6",
+"3' c #DEE1F6",
+"4' c #DFE1EE",
+"5' c #E1E3EB",
+"6' c #E4E9F3",
+"7' c #BCD3CF",
+"8' c #B6CCCE",
+"9' c #D1DCE5",
+"0' c #CFD2E4",
+"a' c #EDEDEF",
+"b' c #BBBBBB",
+"c' c #565656",
+"d' c #CCDCD4",
+"e' c #8FC0A6",
+"f' c #CCF0DD",
+"g' c #E8E8F9",
+"h' c #C5C5F2",
+"i' c #A0A0E8",
+"j' c #7D7DE0",
+"k' c #7A7AF3",
+"l' c #5D5DF3",
+"m' c #1C1CD1",
+"n' c #3E3EE1",
+"o' c #6D6DF3",
+"p' c #8181EF",
+"q' c #A4A4F6",
+"r' c #CECEFE",
+"s' c #DDDDFD",
+"t' c #E6E6FA",
+"u' c #F0F0FA",
+"v' c #CDE4D8",
+"w' c #8EC1A6",
+"x' c #C1DCCD",
+"y' c #DCEFE5",
+"z' c #AFD8C2",
+"A' c #BFDFD0",
+"B' c #F7FEFE",
+"C' c #F1F2F5",
+"D' c #F1F1F6",
+"E' c #DBDEF0",
+"F' c #E0E2EF",
+"G' c #E2E9F1",
+"H' c #C7D5DC",
+"I' c #ABB7C0",
+"J' c #A9B5BE",
+"K' c #D9DCE9",
+"L' c #EBEBF0",
+"M' c #DDDDE0",
+"N' c #4B4B4B",
+"O' c #A2A2A2",
+"P' c #898989",
+"Q' c #9C9C9C",
+"R' c #A8A8A8",
+"S' c #C6C6C6",
+"T' c #919191",
+"U' c #C7DDD1",
+"V' c #9CCDB3",
+"W' c #D2EDDF",
+"X' c #C1C1DA",
+"Y' c #B5B5EA",
+"Z' c #9090E0",
+"`' c #7979E5",
+" ) c #8383FA",
+".) c #5656F0",
+"+) c #2828DD",
+"@) c #5252ED",
+"#) c #7070EE",
+"$) c #9191F4",
+"%) c #B9C7E9",
+"&) c #D5E7E3",
+"*) c #EEF6F2",
+"=) c #F3F6F5",
+"-) c #E9EEEB",
+";) c #F3F5F4",
+">) c #C0DFCF",
+",) c #91C4A9",
+"') c #92C9AB",
+")) c #A2CEB6",
+"!) c #AFCABC",
+"~) c #D7E0DB",
+"{) c #E4E4E6",
+"]) c #EDEEF1",
+"^) c #E6E7EC",
+"/) c #DBDDEA",
+"() c #EBEEFC",
+"_) c #DEE0EA",
+":) c #C9DCE0",
+"<) c #B0BDC6",
+"[) c #CFD1E4",
+"}) c #D2D4E7",
+"|) c #BCCAD6",
+"1) c #CFDBE3",
+"2) c #E9EBF1",
+"3) c #F0F1F5",
+"4) c #909090",
+"5) c #8E8E8E",
+"6) c #9F9F9F",
+"7) c #484848",
+"8) c #C1DFD0",
+"9) c #B0E2C8",
+"0) c #E2F6EB",
+"a) c #E4E4EA",
+"b) c #D3D3F4",
+"c) c #A1A1DE",
+"d) c #8888E1",
+"e) c #8AA6CF",
+"f) c #72B19B",
+"g) c #4FA075",
+"h) c #56A87D",
+"i) c #87CCA8",
+"j) c #9FD8BA",
+"k) c #AEDDC4",
+"l) c #C9E9D8",
+"m) c #ADE2C5",
+"n) c #7ED8A9",
+"o) c #C5E8D5",
+"p) c #E1EBE6",
+"q) c #E2E6E4",
+"r) c #E8E9EA",
+"s) c #F5F5FA",
+"t) c #DCDEEB",
+"u) c #CEE0E6",
+"v) c #D1D3E6",
+"w) c #ADAEBD",
+"x) c #B6B8D3",
+"y) c #9EB6C4",
+"z) c #99CABC",
+"A) c #A0D2BD",
+"B) c #93C3AD",
+"C) c #A9D2BF",
+"D) c #C1E3D2",
+"E) c #C1D9CD",
+"F) c #CDDDD5",
+"G) c #EDF2EF",
+"H) c #989898",
+"I) c #929292",
+"J) c #FDFEFE",
+"K) c #F9FCFA",
+"L) c #F4F8F6",
+"M) c #EFF1F1",
+"N) c #F7FAF8",
+"O) c #FCFDFD",
+"P) c #F9FDFB",
+"Q) c #DEEEE6",
+"R) c #C6DFD2",
+"S) c #E4EEE9",
+"T) c #F3F4F3",
+"U) c #E9EBF8",
+"V) c #E8EAF1",
+"W) c #D8DAE6",
+"X) c #B0B6CE",
+"Y) c #ADB1CF",
+"Z) c #B5C5D8",
+"`) c #95C1BA",
+" ! c #CFEAE6",
+".! c #E8F9F9",
+"+! c #CAEDDD",
+"@! c #C8EADD",
+"#! c #BAE6D1",
+"$! c #99CEB3",
+"%! c #9DD5BA",
+"&! c #A1DFBF",
+"*! c #82C0A0",
+"=! c #89BDA2",
+"-! c #B5DFC9",
+";! c #B8D8C7",
+">! c #BAD0C5",
+",! c #5C5C5C",
+"'! c #8A8A8A",
+")! c #717171",
+"!! c #797D7B",
+"~! c #444544",
+"{! c #535454",
+"]! c #868686",
+"^! c #7E7E7E",
+"/! c #4E4E4E",
+"(! c #666666",
+"_! c #686868",
+":! c #505251",
+"<! c #69726D",
+"[! c #9EA7A2",
+"}! c #DADBDA",
+"|! c #9D9D9D",
+"1! c #7C7C7C",
+"2! c #5E5E5E",
+"3! c #D3DBE7",
+"4! c #A1B8C8",
+"5! c #85A8C6",
+"6! c #A6D1C2",
+"7! c #BCCCC7",
+"8! c #F5F6F6",
+"9! c #FDFEFD",
+"0! c #F2F8F5",
+"a! c #F8FEFD",
+"b! c #F2FFFA",
+"c! c #D4E8DE",
+"d! c #EAF9F7",
+"e! c #FCFEFE",
+"f! c #CFEEDE",
+"g! c #B1DAC5",
+"h! c #CFECDD",
+"i! c #ADD7C4",
+"j! c #97C0AB",
+"k! c #535459",
+"l! c #939393",
+"m! c #888888",
+"n! c #626262",
+"o! c #C9CAC9",
+"p! c #B2B9B5",
+"q! c #616161",
+"r! c #737373",
+"s! c #C5C5CA",
+"t! c #D1D4E7",
+"u! c #97AAD4",
+"v! c #95B4D6",
+"w! c #DDEAE4",
+"x! c #FAFDFC",
+"y! c #EBF3EF",
+"z! c #F4FEFC",
+"A! c #E0ECE6",
+"B! c #EDF0EF",
+"C! c #727296",
+"D! c #58588F",
+"E! c #54547E",
+"F! c #6B6B8A",
+"G! c #818190",
+"H! c #87878F",
+"I! c #959595",
+"J! c #848484",
+"K! c #4F4F4F",
+"L! c #6C6C6C",
+"M! c #9CA09E",
+"N! c #7CA992",
+"O! c #DBDBE0",
+"P! c #D7DAEE",
+"Q! c #E8E9F6",
+"R! c #DCDDF4",
+"S! c #A2A2F1",
+"T! c #AFAFFB",
+"U! c #7E7E9B",
+"V! c #69698B",
+"W! c #43437A",
+"X! c #484886",
+"Y! c #5A5A90",
+"Z! c #40408F",
+"`! c #49499D",
+" ~ c #6B6BB1",
+".~ c #6B6BAD",
+"+~ c #8484BB",
+"@~ c #B0B0D5",
+"#~ c #CACAE1",
+"$~ c #D2D2D9",
+"%~ c #8B8B8B",
+"&~ c #656565",
+"*~ c #6B6B6B",
+"=~ c #6D6D6D",
+"-~ c #7D9086",
+";~ c #6A6A6A",
+">~ c #7F7F7F",
+",~ c #606060",
+"'~ c #505050",
+")~ c #EFF0F1",
+"!~ c #E5E7F5",
+"~~ c #DDDEEB",
+"{~ c #C5C6E8",
+"]~ c #8585E5",
+"^~ c #AAAAF0",
+"/~ c #919195",
+"(~ c #838388",
+"_~ c #727284",
+":~ c #6E6E85",
+"<~ c #79798F",
+"[~ c #66668E",
+"}~ c #6E6E9D",
+"|~ c #9090B5",
+"1~ c #6969B2",
+"2~ c #5D5DB3",
+"3~ c #8888DD",
+"4~ c #7D7DE5",
+"5~ c #5050CF",
+"6~ c #6767DD",
+"7~ c #7979DD",
+"8~ c #9292E3",
+"9~ c #C2C2F9",
+"0~ c #D9D9FA",
+"a~ c #D3D3E8",
+"b~ c #F6F6F7",
+"c~ c #A0A0A0",
+"d~ c #484949",
+"e~ c #7D7D7D",
+"f~ c #F2F3F8",
+"g~ c #E7E9F7",
+"h~ c #EEF0FC",
+"i~ c #B7B7E8",
+"j~ c #8484EA",
+"k~ c #BEBEF2",
+"l~ c #A5A5A8",
+"m~ c #B4B4BB",
+"n~ c #ACACC1",
+"o~ c #A1A1BE",
+"p~ c #C5C5E2",
+"q~ c #BFBFE9",
+"r~ c #A8A8E7",
+"s~ c #AFAFF0",
+"t~ c #8888DE",
+"u~ c #7272D1",
+"v~ c #9E9EFC",
+"w~ c #9E9EF9",
+"x~ c #6161E1",
+"y~ c #7777FB",
+"z~ c #A0A0F4",
+"A~ c #9090DA",
+"B~ c #B8B8ED",
+"C~ c #D5D5E9",
+"D~ c #ECECEE",
+"E~ c #999999",
+"F~ c #787878",
+"G~ c #616462",
+"H~ c #EFEFF0",
+"I~ c #EBECED",
+"J~ c #EBECF6",
+"K~ c #ECEEFC",
+"L~ c #E8E8EC",
+"M~ c #F8F8F9",
+"N~ c #B0B0F0",
+"O~ c #9595F8",
+"P~ c #747474",
+"Q~ c #B6B6D3",
+"R~ c #D3D3F7",
+"S~ c #A6A6E3",
+"T~ c #B8B8F1",
+"U~ c #E3E3FC",
+"V~ c #B2B2F3",
+"W~ c #7979DA",
+"X~ c #8787EB",
+"Y~ c #9C9CEA",
+"Z~ c #EDEDF7",
+"`~ c #535353",
+" { c #616261",
+".{ c #777777",
+"+{ c #4C4C4C",
+"@{ c #9AB9A9",
+"#{ c #898F8C",
+"${ c #BFBFCA",
+"%{ c #C6C6E3",
+"&{ c #D7D7E4",
+"*{ c #C0C0C1",
+"={ c #808080",
+"-{ c #F2F3F7",
+";{ c #CED1E3",
+">{ c #E3E5F0",
+",{ c #F1F2F8",
+"'{ c #9797E4",
+"){ c #9696FB",
+"!{ c #F0F0FF",
+"~{ c #545454",
+"{{ c #878787",
+"]{ c #E5E5F3",
+"^{ c #C8C8E8",
+"/{ c #CACAEF",
+"({ c #C2C2E7",
+"_{ c #F7F7F9",
+":{ c #464647",
+"<{ c #474770",
+"[{ c #5D5D7E",
+"}{ c #78788E",
+"|{ c #898997",
+"1{ c #949496",
+"2{ c #898C8B",
+"3{ c #858585",
+"4{ c #BABBBA",
+"5{ c #95B0A2",
+"6{ c #BCC8C2",
+"7{ c #9C9CC3",
+"8{ c #6969E2",
+"9{ c #B0B0E8",
+"0{ c #E5E8F5",
+"a{ c #9393EC",
+"b{ c #9090FF",
+"c{ c #7B7B7B",
+"d{ c #434344",
+"e{ c #393968",
+"f{ c #414176",
+"g{ c #4D4D89",
+"h{ c #41418E",
+"i{ c #404A8A",
+"j{ c #759792",
+"k{ c #779B89",
+"l{ c #86A293",
+"m{ c #484948",
+"n{ c #8D8D8D",
+"o{ c #D6D6D7",
+"p{ c #8282BA",
+"q{ c #3232D8",
+"r{ c #8483E1",
+"s{ c #838392",
+"t{ c #8687A6",
+"u{ c #86868D",
+"v{ c #9696A4",
+"w{ c #9B9B9F",
+"x{ c #D2D3D4",
+"y{ c #D2D3D7",
+"z{ c #DADDEA",
+"A{ c #E9EBF2",
+"B{ c #DDDFEC",
+"C{ c #DBDCE2",
+"D{ c #D0D0D3",
+"E{ c #DADAEA",
+"F{ c #7C7CD9",
+"G{ c #8282E6",
+"H{ c #474747",
+"I{ c #4F4F50",
+"J{ c #636371",
+"K{ c #67677A",
+"L{ c #6C6C85",
+"M{ c #626285",
+"N{ c #717891",
+"O{ c #91A8A2",
+"P{ c #7B9B8A",
+"Q{ c #719683",
+"R{ c #FBFCFC",
+"S{ c #444444",
+"T{ c #C9C9D1",
+"U{ c #9191D0",
+"V{ c #5F5ED9",
+"W{ c #908FD9",
+"X{ c #8080A0",
+"Y{ c #5A5C9A",
+"Z{ c #61608E",
+"`{ c #99999A",
+" ] c #85859F",
+".] c #5657A0",
+"+] c #9090A1",
+"@] c #C6C6CA",
+"#] c #D4D5DD",
+"$] c #F2F5FE",
+"%] c #CACAE7",
+"&] c #7070D3",
+"*] c #8686DA",
+"=] c #A9ADAB",
+"-] c #A2A9A6",
+";] c #A1ADA8",
+">] c #828282",
+",] c #575757",
+"'] c #C1C1CF",
+")] c #9595D8",
+"!] c #8A89DE",
+"~] c #C2C1E0",
+"{] c #8C8D93",
+"]] c #61618A",
+"^] c #373786",
+"/] c #8A8A94",
+"(] c #585896",
+"_] c #2A2A90",
+":] c #5C5B8F",
+"<] c #ACACB2",
+"[] c #E0E2F6",
+"}] c #E7E9F6",
+"|] c #F1F2F6",
+"1] c #D8D8FB",
+"2] c #8E8EF0",
+"3] c #ADADEF",
+"4] c #777877",
+"5] c #A7A7BC",
+"6] c #7676C7",
+"7] c #8F8FD3",
+"8] c #C3C3D0",
+"9] c #84848B",
+"0] c #4F4F7C",
+"a] c #31317B",
+"b] c #80808F",
+"c] c #5E5D91",
+"d] c #4A498B",
+"e] c #45458E",
+"f] c #9898A3",
+"g] c #8989A3",
+"h] c #F1F1F7",
+"i] c #676767",
+"j] c #BEBEEF",
+"k] c #757575",
+"l] c #6E7A74",
+"m] c #7C7E7D",
+"n] c #777A78",
+"o] c #7A7D7C",
+"p] c #868786",
+"q] c #A3A3C0",
+"r] c #7777CC",
+"s] c #A3A3D1",
+"t] c #C8C7C8",
+"u] c #5C5C8C",
+"v] c #323378",
+"w] c #4A4B72",
+"x] c #727288",
+"y] c #646491",
+"z] c #747497",
+"A] c #6F6FA0",
+"B] c #8080AC",
+"C] c #8282AD",
+"D] c #D9DAE1",
+"E] c #D7D9DF",
+"F] c #F0F0F4",
+"G] c #A7A7EB",
+"H] c #6B6BDC",
+"I] c #D1D1EF",
+"J] c #295B41",
+"K] c #406B55",
+"L] c #375E4A",
+"M] c #436051",
+"N] c #7D807F",
+"O] c #707070",
+"P] c #ABABC3",
+"Q] c #7777D0",
+"R] c #BCBCD9",
+"S] c #52538A",
+"T] c #575674",
+"U] c #6D6D75",
+"V] c #646487",
+"W] c #55558B",
+"X] c #78788F",
+"Y] c #7D7DA0",
+"Z] c #6C6BB1",
+"`] c #9C9BB8",
+" ^ c #7C7CBA",
+".^ c #F1F1F3",
+"+^ c #E9EBF5",
+"@^ c #DEDFEC",
+"#^ c #DADBE8",
+"$^ c #B4B4FA",
+"%^ c #7979EE",
+"&^ c #F0F0FC",
+"*^ c #4F6A5D",
+"=^ c #7B807F",
+"-^ c #727875",
+";^ c #666F6B",
+">^ c #646464",
+",^ c #BBBBC0",
+"'^ c #A6A6C9",
+")^ c #D1D1D8",
+"!^ c #767678",
+"~^ c #5C5C8B",
+"{^ c #82828F",
+"]^ c #9A99A5",
+"^^ c #6160A8",
+"/^ c #7C7AB2",
+"(^ c #5F5EC0",
+"_^ c #CECFD1",
+":^ c #E9ECF9",
+"<^ c #E2E4F8",
+"[^ c #EBEBF3",
+"}^ c #DEE1F3",
+"|^ c #F1F2FC",
+"1^ c #A0A0F3",
+"2^ c #7676E7",
+"3^ c #75827C",
+"4^ c #6D827A",
+"5^ c #62628E",
+"6^ c #6A6A96",
+"7^ c #9A9A9F",
+"8^ c #A09FBC",
+"9^ c #7776C2",
+"0^ c #5050CB",
+"a^ c #E6E9FC",
+"b^ c #CACCD3",
+"c^ c #E7EAFC",
+"d^ c #E8EAF7",
+"e^ c #E5E7FA",
+"f^ c #A4A5F3",
+"g^ c #8E8EE9",
+"h^ c #787A79",
+"i^ c #62766B",
+"j^ c #546D60",
+"k^ c #7D7D7E",
+"l^ c #70708A",
+"m^ c #72728F",
+"n^ c #C1C1C2",
+"o^ c #7676CC",
+"p^ c #4243D2",
+"q^ c #595959",
+"r^ c #FBFCFD",
+"s^ c #D7D8E1",
+"t^ c #E8EBFC",
+"u^ c #E2E5F9",
+"v^ c #DBDDF4",
+"w^ c #A4A5EB",
+"x^ c #A6A6E8",
+"y^ c #797979",
+"z^ c #838484",
+"A^ c #576D62",
+"B^ c #5E7268",
+"C^ c #8C8E8D",
+"D^ c #B6B6B7",
+"E^ c #ADADBF",
+"F^ c #A9A9D7",
+"G^ c #E3E5ED",
+"H^ c #DADCE9",
+"I^ c #DCDEF1",
+"J^ c #DCE0F2",
+"K^ c #D5D7F1",
+"L^ c #B4B5E4",
+"M^ c #B9BAE7",
+"N^ c #F2F2F4",
+"O^ c #7E8683",
+"P^ c #6E867A",
+"Q^ c #87938E",
+"R^ c #5F5F61",
+"S^ c #F0F0F2",
+"T^ c #E5E6F3",
+"U^ c #EBEDF4",
+"V^ c #C1C3EB",
+"W^ c #CED0EC",
+"X^ c #E5E6EA",
+"Y^ c #858686",
+"Z^ c #748C80",
+"`^ c #859C91",
+" / c #AAAEAC",
+"./ c #D6D6E9",
+"+/ c #56565C",
+"@/ c #7B7B8E",
+"#/ c #838385",
+"$/ c #747486",
+"%/ c #858591",
+"&/ c #9C9CA1",
+"*/ c #DFDFE4",
+"=/ c #E1E4F8",
+"-/ c #E6E8EF",
+";/ c #587264",
+">/ c #6A8B7A",
+",/ c #A5AAA7",
+"'/ c #BFBFEF",
+")/ c #9999AA",
+"!/ c #6F6F7C",
+"~/ c #5F5F6A",
+"{/ c #79797A",
+"]/ c #79797F",
+"^/ c #3C3C7C",
+"// c #6D6D8E",
+"(/ c #7272A0",
+"_/ c #3D3DAC",
+":/ c #3C3CB4",
+"</ c #5C5DBA",
+"[/ c #8E8EBB",
+"}/ c #B7B7BF",
+"|/ c #E7E8F5",
+"1/ c #D7D9E5",
+"2/ c #D2D3DA",
+"3/ c #C5C8EB",
+"4/ c #878988",
+"5/ c #648071",
+"6/ c #6F8E7E",
+"7/ c #8585DA",
+"8/ c #C9C9E2",
+"9/ c #9E9EA7",
+"0/ c #7B7B7E",
+"a/ c #7B7B80",
+"b/ c #25257B",
+"c/ c #74748A",
+"d/ c #4C4C9C",
+"e/ c #5F5F94",
+"f/ c #7B7A9F",
+"g/ c #6969AE",
+"h/ c #4F4FAB",
+"i/ c #9495B5",
+"j/ c #C2C2C5",
+"k/ c #DFDFE1",
+"l/ c #F7F8FC",
+"m/ c #E4E7FA",
+"n/ c #C3C5F2",
+"o/ c #C5C7F1",
+"p/ c #D8DAE1",
+"q/ c #E3E4E9",
+"r/ c #818684",
+"s/ c #749181",
+"t/ c #859F91",
+"u/ c #CFCFD9",
+"v/ c #6262C5",
+"w/ c #7A7AE9",
+"x/ c #5E5E6C",
+"y/ c #252589",
+"z/ c #939398",
+"A/ c #5B5BA5",
+"B/ c #9494AD",
+"C/ c #B1B1B4",
+"D/ c #ACACBA",
+"E/ c #4545B0",
+"F/ c #7D7DB1",
+"G/ c #D3D3D4",
+"H/ c #E0E1E3",
+"I/ c #CBCCD0",
+"J/ c #EAECF8",
+"K/ c #BEBFF7",
+"L/ c #C8CAF6",
+"M/ c #DDDFF5",
+"N/ c #E5E6EB",
+"O/ c #6C7671",
+"P/ c #678474",
+"Q/ c #849A8E",
+"R/ c #D1D1E8",
+"S/ c #7C7CDB",
+"T/ c #9797E5",
+"U/ c #919196",
+"V/ c #BCBCD5",
+"W/ c #8E8EC4",
+"X/ c #B5B5C8",
+"Y/ c #D0D1F0",
+"Z/ c #D5D7E3",
+"`/ c #BCBDED",
+" ( c #697770",
+".( c #668274",
+"+( c #899991",
+"@( c #D0D0ED",
+"#( c #8E8EE1",
+"$( c #A0A0DB",
+"%( c #818185",
+"&( c #A7A7C0",
+"*( c #BBBBD4",
+"=( c #CBCBDE",
+"-( c #CBCBDC",
+";( c #ABABAC",
+">( c #D3D4D6",
+",( c #D9D9DE",
+"'( c #DEE0EC",
+")( c #D9DBE7",
+"!( c #D8DBEB",
+"~( c #EEEFF3",
+"{( c #F2F2F3",
+"]( c #7E8C85",
+"^( c #829A8E",
+"/( c #99A49E",
+"(( c #DFDFEE",
+"_( c #B6B6DC",
+":( c #B7B7CD",
+"<( c #959596",
+"[( c #B8B8B9",
+"}( c #C0C0C2",
+"|( c #C0C1C4",
+"1( c #D1D2D8",
+"2( c #D8D9E0",
+"3( c #D0D1DE",
+"4( c #DADCE8",
+"5( c #CED1E6",
+"6( c #DEE2E0",
+"7( c #E8EBE9",
+"8( c #AFB4B2",
+"9( c #9D9D9F",
+"0( c #A3A4A6",
+"a( c #B8B9BC",
+"b( c #C3C4CA",
+"c( c #BABBC1",
+"d( c #CACAD6",
+"e( c #D1D3E7",
+"f( c #D8DBE8",
+"g( c #D7D7DE",
+"h( c #E7E7EB",
+"i( c #4A4A4A",
+"j( c #A2A2A4",
+"k( c #A8A8AB",
+"l( c #ADAEB3",
+"m( c #BCBDC3",
+"n( c #BABBC6",
+"o( c #B8B9C6",
+"p( c #C4C6D3",
+"q( c #CED0E2",
+"r( c #DDDFEA",
+"s( c #D4D6EA",
+"t( c #D6D9ED",
+"u( c #E0E3F2",
+"v( c #DEE1F0",
+"w( c #D6D7DB",
+"x( c #D9DADB",
+"y( c #D9D9DA",
+"z( c #9E9E9F",
+"A( c #9A9A9C",
+"B( c #98999B",
+"C( c #939395",
+"D( c #999A9C",
+"E( c #A2A2A8",
+"F( c #A8A9AF",
+"G( c #ADAFB9",
+"H( c #B8BAC6",
+"I( c #C2C3D0",
+"J( c #C2C5D7",
+"K( c #C7CADD",
+"L( c #CFD2E5",
+"M( c #D4D7E6",
+"N( c #CECFDE",
+"O( c #D5D8EE",
+"P( c #E5E5EA",
+"Q( c #7F7F8C",
+"R( c #525286",
+"S( c #76769B",
+"T( c #9C9CA5",
+"U( c #7373A1",
+"V( c #62629F",
+"W( c #9D9E9E",
+"X( c #A3A3A5",
+"Y( c #A1A1A4",
+"Z( c #909196",
+"`( c #95969C",
+" _ c #A1A3AD",
+"._ c #A9ABB7",
+"+_ c #B1B3C0",
+"@_ c #B4B6C9",
+"#_ c #C1C3D6",
+"$_ c #C1C3D7",
+"%_ c #D1D3E3",
+"&_ c #E9EBF6",
+"*_ c #E7E8F8",
+"=_ c #D1D4EF",
+"-_ c #DEDFE6",
+";_ c #E3E4EA",
+">_ c #E6E6EB",
+",_ c #D5D5D7",
+"'_ c #73738B",
+")_ c #4D4D88",
+"!_ c #79799D",
+"~_ c #797983",
+"{_ c #494979",
+"]_ c #646497",
+"^_ c #AFAFB0",
+"/_ c #A8A8A9",
+"(_ c #AFAFB1",
+"__ c #A2A3A4",
+":_ c #9E9FA2",
+"<_ c #A1A2A7",
+"[_ c #A5A5AB",
+"}_ c #9E9FA7",
+"|_ c #9E9FA9",
+"1_ c #A2A4AE",
+"2_ c #ACAEBA",
+"3_ c #B4B6C8",
+"4_ c #B9BBCE",
+"5_ c #BABCCF",
+"6_ c #C1C3D2",
+"7_ c #C9CBDB",
+"8_ c #D3D4DF",
+"9_ c #9899F1",
+"0_ c #9596EC",
+"a_ c #D7D8E5",
+"b_ c #E0E1E9",
+"c_ c #D1D2D9",
+"d_ c #DADAE0",
+"e_ c #D8D9DC",
+"f_ c #C8C8CA",
+"g_ c #C8C8C9",
+"h_ c #707092",
+"i_ c #4F4F90",
+"j_ c #80809F",
+"k_ c #333333",
+"l_ c #0A0A0A",
+"m_ c #A9A9AA",
+"n_ c #B5B6B7",
+"o_ c #B8B8BA",
+"p_ c #B7B8BA",
+"q_ c #B3B3B7",
+"r_ c #B0B0B5",
+"s_ c #A9AAB0",
+"t_ c #AAABB0",
+"u_ c #A1A2AC",
+"v_ c #A2A3AD",
+"w_ c #A7A8B2",
+"x_ c #A4A6B4",
+"y_ c #A3A6B4",
+"z_ c #ACAEBF",
+"A_ c #B8BBCD",
+"B_ c #C3C5D4",
+"C_ c #C9CADA",
+"D_ c #CCCEDC",
+"E_ c #DBDCF1",
+"F_ c #D1D2E0",
+"G_ c #D6D8E3",
+"H_ c #CBCCD3",
+"I_ c #CACBD1",
+"J_ c #CACBCE",
+"K_ c #BDBEBF",
+"L_ c #BEBEC1",
+"M_ c #BBBBBC",
+"N_ c #7D7DA4",
+"O_ c #6262A2",
+"P_ c #9292A8",
+"Q_ c #1F1F1F",
+"R_ c #020202",
+"S_ c #A7A7A8",
+"T_ c #B1B2B4",
+"U_ c #A2A2A5",
+"V_ c #B6B7BA",
+"W_ c #B5B6BB",
+"X_ c #A7A8AE",
+"Y_ c #B3B3B9",
+"Z_ c #ADAEB7",
+"`_ c #A5A7B2",
+" : c #A6A7B2",
+".: c #A9AAB5",
+"+: c #A5A7B5",
+"@: c #A9ABB9",
+"#: c #ABADBC",
+"$: c #B5B7C3",
+"%: c #BBBCCA",
+"&: c #BCBFCD",
+"*: c #C9CAD7",
+"=: c #DADBE2",
+"-: c #CDCED9",
+";: c #E7E9F9",
+">: c #CED1E4",
+",: c #D4D7E3",
+"': c #D0D2DD",
+"): c #CDCED4",
+"!: c #C1C1C8",
+"~: c #C6C7CA",
+"{: c #B7B8B9",
+"]: c #B0B0B2",
+"^: c #BFBFC0",
+"/: c #B3B3B4",
+"(: c #A6A7A7",
+"_: c #B2B2B3",
+":: c #6B6BA6",
+"<: c #6666AE",
+"[: c #A5A5B1",
+"}: c #454A47",
+"|: c #131A16",
+"1: c #7F807F",
+"2: c #C5C5C6",
+"3: c #CDCDCE",
+"4: c #BEBEBF",
+"5: c #B7B7B9",
+"6: c #A8A8AA",
+"7: c #ABABAD",
+"8: c #B9BABD",
+"9: c #B5B6BA",
+"0: c #AEAFB5",
+"a: c #B6B7BD",
+"b: c #B3B4BE",
+"c: c #ACAEB9",
+"d: c #ADB0BA",
+"e: c #AAACBB",
+"f: c #AAADBB",
+"g: c #AEB0BF",
+"h: c #AFB1C0",
+"i: c #BABBC8",
+"j: c #C2C4CE",
+"k: c #C8C9D2",
+"l: c #CDCEDA",
+"m: c #E9EAF5",
+"n: c #CFD0E3",
+"o: c #D9DBED",
+"p: c #D0D2DF",
+"q: c #C9CBD8",
+"r: c #C6C8D4",
+"s: c #C9CAD5",
+"t: c #CFD0D6",
+"u: c #C7C7CD",
+"v: c #C0C1C7",
+"w: c #C6C7CC",
+"x: c #BEBEC2",
+"y: c #BBBCBD",
+"z: c #C7C7CA",
+"A: c #B6B6B8",
+"B: c #A7A7A9",
+"C: c #464692",
+"D: c #5E5EAE",
+"E: c #9DACA6",
+"F: c #49755E",
+"G: c #C8C9C9",
+"H: c #DCDDE0",
+"I: c #C1C2C5",
+"J: c #BABAC0",
+"K: c #AFB0B5",
+"L: c #B1B2B8",
+"M: c #B8B9BF",
+"N: c #AFB0BB",
+"O: c #ADAFBB",
+"P: c #B3B5C0",
+"Q: c #B6B8C4",
+"R: c #B4B7C6",
+"S: c #B0B3C2",
+"T: c #B1B3C2",
+"U: c #B4B6C6",
+"V: c #B6B7C3",
+"W: c #B9BBC6",
+"X: c #BEBFCC",
+"Y: c #C1C2CD",
+"Z: c #C6C7CE",
+"`: c #CACBD4",
+" < c #EAECF4",
+".< c #DBDCEB",
+"+< c #CDD0E1",
+"@< c #CDCFE1",
+"#< c #C5C8D9",
+"$< c #D3D3E2",
+"%< c #C6C9D5",
+"&< c #C1C3CF",
+"*< c #C5C7D1",
+"=< c #C7C8CE",
+"-< c #C9CAD0",
+";< c #C5C5CB",
+">< c #C2C3C6",
+",< c #C7C7C9",
+"'< c #C0C1C3",
+")< c #B1B1B3",
+"!< c #BCBCBD",
+"~< c #BCBCC8",
+"{< c #6868B9",
+"]< c #7C7CC3",
+"^< c #B5CBC2",
+"/< c #71A48A",
+"(< c #C9C9CA",
+"_< c #D7D8D8",
+":< c #E5E5E7",
+"<< c #E6E7E9",
+"[< c #E3E3E8",
+"}< c #C6C8D2",
+"|< c #BEC0CD",
+"1< c #BCBECA",
+"2< c #BEC0CC",
+"3< c #B8BBC6",
+"4< c #B5B7C7",
+"5< c #B6B9C9",
+"6< c #BABCCC",
+"7< c #BCBFCF",
+"8< c #BEC1CC",
+"9< c #BDBECB",
+"0< c #BDBFCC",
+"a< c #C0C2CD",
+"b< c #C7C8CF",
+"c< c #C7C8D0",
+"d< c #B8B9C3",
+"e< c #CBCEDF",
+"f< c #CACDDF",
+"g< c #C7C9DA",
+"h< c #CDCFDC",
+"i< c #C4C7D3",
+"j< c #C4C6D2",
+"k< c #C5C7D2",
+"l< c #CDCED5",
+"m< c #BDBEC4",
+"n< c #C6C6C9",
+"o< c #BDBDC0",
+"p< c #D1D1D2",
+"q< c #D0D0D1",
+"r< c #C2C2D8",
+"s< c #8A8AD5",
+"t< c #9F9FD8",
+"u< c #D7DFDA",
+"v< c #A4C9B6",
+"w< c #81AE97",
+"x< c #E8E8EA",
+"y< c #E9EAEE",
+"z< c #D7D8DE",
+"A< c #CECFD7",
+"B< c #D2D3DC",
+"C< c #D0D1E1",
+"D< c #CDCEDE",
+"E< c #D0D3DF",
+"F< c #C1C2D3",
+"G< c #BCBECF",
+"H< c #BFC2D1",
+"I< c #C0C3D3",
+"J< c #C7C9D6",
+"K< c #C8CAD7",
+"L< c #CBCDD9",
+"M< c #C8C9D1",
+"N< c #C2C3CD",
+"O< c #E3E4EF",
+"P< c #DCDEEA",
+"Q< c #DCDDEC",
+"R< c #CCCFDC",
+"S< c #C1C4D5",
+"T< c #C2C4D5",
+"U< c #C9CCDD",
+"V< c #C9CBDD",
+"W< c #C2C5D6",
+"X< c #C8C9D6",
+"Y< c #D3D4E2",
+"Z< c #D1D3DF",
+"`< c #D2D3D9",
+" [ c #D8D9DF",
+".[ c #C4C5CB",
+"+[ c #DDDDDF",
+"@[ c #CCCDD0",
+"#[ c #D6D7DA",
+"$[ c #E7E7E9",
+"%[ c #CBCBCC",
+"&[ c #AFAFD8",
+"*[ c #7676D0",
+"=[ c #A8A8DB",
+"-[ c #E2EBE7",
+";[ c #B4DCC7",
+">[ c #B4D8C5",
+",[ c #CFCFD0",
+"'[ c #D2D2D4",
+")[ c #E8E9ED",
+"![ c #CED1E5",
+"~[ c #C8CBDF",
+"{[ c #CBCEE0",
+"][ c #CED1E2",
+"^[ c #CBCDDB",
+"/[ c #CDD0DD",
+"([ c #CFD1DE",
+"_[ c #D3D5E0",
+":[ c #CBCCD2",
+"<[ c #CACCD4",
+"[[ c #CACCD6",
+"}[ c #D5D7E4",
+"|[ c #D4D5E0",
+"1[ c #CED0DE",
+"2[ c #D7D9E8",
+"3[ c #D8D9E9",
+"4[ c #C9CBDF",
+"5[ c #D5D8E9",
+"6[ c #D6D9EA",
+"7[ c #D0D2E0",
+"8[ c #C9CBD7",
+"9[ c #D2D3DB",
+"0[ c #E8E9EE",
+"a[ c #9999D2",
+"b[ c #6C6CCD",
+"c[ c #B8B8E2",
+"d[ c #DFEAE6",
+"e[ c #A4D1BB",
+"f[ c #BEDACB",
+"g[ c #EDEEF0",
+"h[ c #DBDCDF",
+"i[ c #DFDFE3",
+"j[ c #E0E0E8",
+"k[ c #CCCCD4",
+"l[ c #DBDEF2",
+"m[ c #D8D9E7",
+"n[ c #D9DBE8",
+"o[ c #D5D8E5",
+"p[ c #D9DBE6",
+"q[ c #E0E0E7",
+"r[ c #DCDDE4",
+"s[ c #D9DAE3",
+"t[ c #CFD1DD",
+"u[ c #CFD1DC",
+"v[ c #DFE0EB",
+"w[ c #D5D6E6",
+"x[ c #CDD0DC",
+"y[ c #CCCFE1",
+"z[ c #CFD1E3",
+"A[ c #D2D5E3",
+"B[ c #D4D6E5",
+"C[ c #D8D9D9",
+"D[ c #8B8BD4",
+"E[ c #7474D9",
+"F[ c #E3F0E9",
+"G[ c #88BEA2",
+"H[ c #AABCB2",
+"I[ c #DDDDDE",
+"J[ c #CFCFD2",
+"K[ c #D1D3DA",
+"L[ c #DADBE9",
+"M[ c #DEE0F4",
+"N[ c #D5D7E5",
+"O[ c #E0E1ED",
+"P[ c #DFDFE9",
+"Q[ c #D1D4EA",
+"R[ c #DCDFF0",
+"S[ c #F6F6FA",
+"T[ c #F6F7F8",
+"U[ c #7171C9",
+"V[ c #6A6AD3",
+"W[ c #E6E6F3",
+"X[ c #F6FAFA",
+"Y[ c #A8E5C5",
+"Z[ c #DCE5E0",
+"`[ c #EEEFF4",
+" } c #E6E8F0",
+".} c #D6D7E8",
+"+} c #D5D7E8",
+"@} c #CFD0E0",
+"#} c #D2D4E4",
+"$} c #D7D8E2",
+"%} c #E5E7F1",
+"&} c #DEE0E8",
+"*} c #DDDFE6",
+"=} c #D0D3E1",
+"-} c #F4F5F6",
+";} c #EAEAF0",
+">} c #616299",
+",} c #8384C4",
+"'} c #E0E1E0",
+")} c #DEEDE9",
+"!} c #ACEDCB",
+"~} c #E7E8E9",
+"{} c #EEEFF1",
+"]} c #E0E0E9",
+"^} c #D9DAE2",
+"/} c #CFD1E7",
+"(} c #DEDFEF",
+"_} c #E2E3ED",
+":} c #D7D9E3",
+"<} c #DFE1E7",
+"[} c #DDDFF2",
+"}} c #898A92",
+"|} c #5D5F6B",
+"1} c #7A7B8A",
+"2} c #E9E9EC",
+"3} c #E2E3E5",
+"4} c #E2E2E4",
+"5} c #E7E9E9",
+"6} c #D7E4DE",
+"7} c #9CCEB4",
+"8} c #89BFA4",
+"9} c #DFEAE5",
+"0} c #DFDFE2",
+"a} c #E2E3E7",
+"b} c #D5D6E7",
+"c} c #E6E8F3",
+"d} c #D4D6E6",
+"e} c #D4D5E4",
+"f} c #CFD1E0",
+"g} c #A6A9B9",
+"h} c #8F919F",
+"i} c #7C7D85",
+"j} c #CFD1D9",
+"k} c #FAFBFE",
+"l} c #F3F4F6",
+"m} c #D0D1D3",
+"n} c #FAFCFB",
+"o} c #F9FCFB",
+"p} c #E6F4F0",
+"q} c #B8DCCD",
+"r} c #9EC9B6",
+"s} c #A6E0C7",
+"t} c #DCF7F1",
+"u} c #DADADD",
+"v} c #E6E6EA",
+"w} c #DEDFE1",
+"x} c #D4D4D7",
+"y} c #D2D5EB",
+"z} c #D2D5E5",
+"A} c #EAECF6",
+"B} c #DDE0F2",
+"C} c #B7BBCA",
+"D} c #C7CADA",
+"E} c #D7D9E9",
+"F} c #D3D4E5",
+"G} c #D4D7E7",
+"H} c #E8E8ED",
+"I} c #C7C7C8",
+"J} c #979798",
+"K} c #A2A2A3",
+"L} c #B1B1B2",
+"M} c #BFBFC1",
+"N} c #C8C8CB",
+"O} c #D7D7D9",
+"P} c #E1E1E4",
+"Q} c #D9DAF3",
+"R} c #E2E2F2",
+"S} c #F1F5F7",
+"T} c #D3E3DE",
+"U} c #A7CBBB",
+"V} c #C0E1D5",
+"W} c #D1ECE7",
+"X} c #A2D1C1",
+"Y} c #A3C8BD",
+"Z} c #DCE9EC",
+"`} c #ECF1F9",
+" | c #DEE1EE",
+".| c #D4D6DF",
+"+| c #E7E9F0",
+"@| c #D2D4E9",
+"#| c #D5D7E7",
+"$| c #F3F4FC",
+"%| c #DEE0E7",
+"&| c #EEEFF2",
+"*| c #E1E1E3",
+"=| c #BDBEC0",
+"-| c #CCCCCE",
+";| c #B5B6B9",
+">| c #B1B2B3",
+",| c #9F9FA0",
+"'| c #A2A3A5",
+")| c #A7A8AA",
+"!| c #ADADAF",
+"~| c #A6A6A9",
+"{| c #AFB0B2",
+"]| c #BEBEC0",
+"^| c #9E9EA0",
+"/| c #B3B4B5",
+"(| c #C5C6C8",
+"_| c #BABABC",
+":| c #AAAAAD",
+"<| c #BCBCBE",
+"[| c #A9A9AB",
+"}| c #AFB0B1",
+"|| c #A6A6A8",
+"1| c #B4B4B6",
+"2| c #ACACAF",
+"3| c #BDBEC1",
+"4| c #BFBFC7",
+"5| c #898AC6",
+"6| c #A0A2DC",
+"7| c #C8C9E5",
+"8| c #D6D7E0",
+"9| c #D6D7F3",
+"0| c #A3A4E5",
+"a| c #D2D3EC",
+"b| c #CAE4DF",
+"c| c #A8CEC1",
+"d| c #BADED4",
+"e| c #E0E9F5",
+"f| c #DADBEB",
+"g| c #EDEEF9",
+"h| c #C7D3DD",
+"i| c #C1CFD9",
+"j| c #D4D9E8",
+"k| c #D3D6E6",
+"l| c #E6E7F7",
+"m| c #DEDFE5",
+"n| c #CACAD1",
+"o| c #C0C1C8",
+"p| c #B1B1B7",
+"q| c #A6A6AC",
+"r| c #9EA0A4",
+"s| c #A1A1A7",
+"t| c #A2A3A8",
+"u| c #A6A6AD",
+"v| c #A7A7AE",
+"w| c #B0B0B6",
+"x| c #B3B4BB",
+"y| c #B6B6BD",
+"z| c #B4B5BB",
+"A| c #B5B5BC",
+"B| c #ACADB2",
+"C| c #B3B4BA",
+"D| c #B0B1B6",
+"E| c #A5A7AC",
+"F| c #B0B1B7",
+"G| c #B7B8BE",
+"H| c #BCBCC3",
+"I| c #C4C5CC",
+"J| c #C3C5D1",
+"K| c #ACADCD",
+"L| c #ABADDA",
+"M| c #BDBFE4",
+"N| c #CBCDDF",
+"O| c #B9BBE8",
+"P| c #D6D8EA",
+"Q| c #DEE2F0",
+"R| c #CFDBE4",
+"S| c #D3E1EA",
+"T| c #D9E3F0",
+"U| c #C3DAD9",
+"V| c #AECBC6",
+"W| c #E6EAF1",
+"X| c #D0D1DA",
+"Y| c #E5E6EF",
+"Z| c #E1E3F7",
+"`| c #D1D3E0",
+" 1 c #C2C4D1",
+".1 c #BBBCC8",
+"+1 c #B3B5C1",
+"@1 c #B0B1BD",
+"#1 c #ACAEB8",
+"$1 c #9D9FA9",
+"%1 c #9D9EAA",
+"&1 c #9899A4",
+"*1 c #9D9FAB",
+"=1 c #A3A4B0",
+"-1 c #A5A6B3",
+";1 c #A8AAB6",
+">1 c #A8ABB7",
+",1 c #AAACB8",
+"'1 c #ACADB9",
+")1 c #ACADB8",
+"!1 c #ABADB7",
+"~1 c #ABADB8",
+"{1 c #AFB1BB",
+"]1 c #A6A8B2",
+"^1 c #A3A5AF",
+"/1 c #ADAEBA",
+"(1 c #B4B6C3",
+"_1 c #B8BAC7",
+":1 c #BFC1CF",
+"<1 c #C4C6D5",
+"[1 c #C1C4D7",
+"}1 c #C7CADE",
+"|1 c #D4D5EB",
+"11 c #DCDFF3",
+"21 c #E1E3F9",
+"31 c #DEE3F1",
+"41 c #E5E8F8",
+"51 c #E2E5F5",
+"61 c #DEDFEA",
+"71 c #E8EAF5",
+"81 c #D6D7E2",
+"91 c #DDDEE7",
+"01 c #D0D3E5",
+"a1 c #CCCFE0",
+"b1 c #C3C6D7",
+"c1 c #BEC1D1",
+"d1 c #B8BACA",
+"e1 c #A7A9B8",
+"f1 c #9EA0AD",
+"g1 c #9EA0AE",
+"h1 c #9D9FAD",
+"i1 c #A2A5B3",
+"j1 c #A4A6B5",
+"k1 c #A1A2B1",
+"l1 c #A2A4B2",
+"m1 c #A7A9B7",
+"n1 c #A8A9B8",
+"o1 c #A6A7B6",
+"p1 c #ADAFBE",
+"q1 c #B1B4C2",
+"r1 c #A9ABBA",
+"s1 c #A3A5B4",
+"t1 c #A6A8B7",
+"u1 c #B2B4C5",
+"v1 c #BABCD0",
+"w1 c #BFC2D5",
+"x1 c #C5C8DC",
+"y1 c #C9CCE0",
+"z1 c #BFC1E6",
+"A1 c #DEDFF2",
+"B1 c #D1D3F1",
+"C1 c #C2C3EE",
+"D1 c #DBDDF7",
+"E1 c #D8DAF7",
+"F1 c #DFE1F4",
+"G1 c #DADFEC",
+"H1 c #E0E7F4",
+"I1 c #CEDCE5",
+"J1 c #CED9E3",
+"K1 c #C3DFDE",
+"L1 c #BAE7D7",
+"M1 c #E3E4F0",
+"N1 c #EAEAF4",
+"O1 c #EBEDF6",
+"P1 c #D9DBE5",
+"Q1 c #CDCFDD",
+"R1 c #C0C1CE",
+"S1 c #BABBC7",
+"T1 c #B8B9C5",
+"U1 c #AFAFBB",
+"V1 c #ADAEB9",
+"W1 c #AAACB7",
+"X1 c #ADAFBA",
+"Y1 c #ACAFBA",
+"Z1 c #AFB0BC",
+"`1 c #AEB0BB",
+" 2 c #AEAFBB",
+".2 c #BABCC8",
+"+2 c #B4B6C2",
+"@2 c #B5B6C2",
+"#2 c #B9BBC7",
+"$2 c #BDC0CD",
+"%2 c #C2C4D3",
+"&2 c #C8CAD9",
+"*2 c #CED1E0",
+"=2 c #CDCFE6",
+"-2 c #C0C2EC",
+";2 c #C0C1EF",
+">2 c #DFDFF1",
+",2 c #B3B4F1",
+"'2 c #8485EA",
+")2 c #A7A8F1",
+"!2 c #9E9FF5",
+"~2 c #999AF1",
+"{2 c #CFD1F5",
+"]2 c #E6EEF2",
+"^2 c #C4DED9",
+"/2 c #B5D3CC",
+"(2 c #CFE8E6",
+"_2 c #BBDBD5",
+":2 c #B6D3D0",
+"<2 c #DDE2ED",
+"[2 c #D2D4DF",
+"}2 c #D9DBE4",
+"|2 c #E7E9F1",
+"12 c #E5E7F0",
+"22 c #E3E5EC",
+"32 c #DFE1E8",
+"42 c #CECFD6",
+"52 c #CACCD2",
+"62 c #BCBEC4",
+"72 c #ABACB3",
+"82 c #B5B7BC",
+"92 c #BEBFC5",
+"02 c #AFAFB5",
+"a2 c #BDBEC5",
+"b2 c #B6B7BC",
+"c2 c #C6C7CF",
+"d2 c #BBBCC4",
+"e2 c #BCBDC4",
+"f2 c #C1C2C9",
+"g2 c #B9BBC2",
+"h2 c #BBBDC6",
+"i2 c #C6C7D1",
+"j2 c #C9CAD6",
+"k2 c #D3D4E0",
+"l2 c #CFD0DE",
+"m2 c #C3C5E2",
+"n2 c #C1C2EA",
+" . + @ # $ % & * = - ; ; > > , ' ' ' ) ! ~ { ' { , ] ^ ^ / ( _ : < [ } | 1 2 = 3 4 5 6 7 8 9 0 a b c d e e f ",
+" g h i j k & l m , n o p q r s t u v w x y y z A B C D E E F G v H I J K L M N O P Q R S T U V U W X X Y Z ` O .R ..+.@.U @.#.+.$.%.&.*.=.-.;.n >.,.'.).!.~.{.].^./.(._. ",
+" :.<.[.].2 }.|.1.2.3.4.5.6.D 7.v 8.9.0.a.v y u F b.a.c.d.e.f.g.h.i.g.j.k.i.l.m.n.o.p.n.q.o.r.s.t.u.v.w.v.x.y.z.A.B.A.C.D.E.F.v.G.H.A.I.H.J.t.I.K.L.M.G.v.N.O.P.&.Q.c.R.S.T.R U.V.W.z X.Y.Z.`. +.+, ++@+#+$+%+&+*+ ",
+" =+-+;+>+,+'+)+!+~+G F z {+]+^+/+D (+_+:+<+l.[+}+[+[+p.|+k.1+2+2+m.o.e.v 3+4+y 5+6+7+8+9+0+D F a+b+G f E u v w c+T.d+e+f+g+h+f+i+T.j+k+l+m+n+o+&.l+*.V p+q+r+s+m+k+t+u+v+` N.w+x+y+w.x+x.v.G.z+h.A+q.B+C+p.D+E+F+G+H+I+J+K+0+L+t M+N+} O+P+8 Q+R+ ",
+" S+T+U+V+W+X+V+Y+&.m+Z+`+y E @.@p.h.+@@@f.#@m.h.:+q.o.Y+B w $@b+%@K u 3+&@8+*@E M =@-@M+;@>@,@'@( )+n )@!@r !@' ~@'+r {@~ ]@^@/@(@m >@2 < V+{@_@, :@<@[@3.;.( ^ }@; |@} 1@2@3@, _@V+4@5@l+i+P.k+6@7@8@o+o+o+9@0@%@x a@l.d.b@p.p.n.n.c@c@j.A+$@G d@e@U R @.f@g@, h@'@= ; 5 i@ ",
+" j@k@5 l@m@]@,@n@Q.r++.j+X W o@p@x+q@r@l.s@m.A+#@D $@t@u@v@w@9+x@y@z@A@B@{@{@C@D@E@F@' G@H@I@!@J@K@s L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@Z@`@ #.#+#@#X@##$#%#&#*#=#+#-#;#=#>#U@@#,#'#=#)#!#~#{#]#^#/#(#;#_#:#^ ^ <#}.!+[#}#|#1#2#3#4#5#h@6#7#8#9#0#a#b#c#d#_+J+.@e#r@1+f#g#h#D.i#D.k+R r+%.j#k#F l#m#,@!@n#o#<.t ",
+" [.7 p#H@q#1#=.r#M s#3+F L+C.N.x+D.h#t#u#v#S .@w#E F x#3+y#z#p A#B#C#D#E#2@;@/ E#F#G#H#I#J#P@V@=#K#@#L#=#,###M#N#O#P#Q#R#S#P#Q#T#U#V#W#X#Q#Y#Z#`#X# $X#X#.$+$X#@$#$P#P#$$Q#%$&$Q#`#*$=$P#-$;$Q#X#>$+$P#,$'$-$)$!$/#'#~$/#R@{$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$'+7$8$K 9+u 8+k+f+X 9$h#t#u#v#w+0$r@A+F a$3+.@z b$x z#c$d$e$f$s+ ",
+" g$<#[ ~ h$z#H x#E 3+*@i.i.o.b@s@c@p.i$j$o+g+o@e@j+l+u !+k$l$m$!@)+!@n$F#o$p$q$r$s$t$J#u$M@v$Q#w$x$+$y$Z#z$V#A$B$P#C$D$X# $E$F$F$P#%$U#G$H$I$J$O#K$P#K$>$F$%$S#G$L$T#F$P#M$N$F$X#O$`#V#P$F$`#Q$N$P#K$U#`#L$O$X#F$F$F$F$+$R$%$V#S$ $.$T$U$V$W$X$Y$Z$`$ %.%+%@%#%H#$%%%&%;#*%E#=%'+'+;@-%;%-%b.%.9$*.o@Z 8+w#e#d.A+2+1+>%q.<+,%'%)%!%~%b {%]%^%/%(% ",
+" _%g$[@/ r :%z <%a+[%f n.k.p.c@c@c@a.r#L+$@_+g@+.G {@1@}%V+|%' E@1%2%3%I#,#%#4%t$5%6%7%8%9%Q$;$Q#V#0%%$`#K$-$F$C$L$I$a%T#K$Q#b%a%-$O$C$F$ $ $X#`#Q# $ $E$V#F$`#P#Q#F$-$V#F$I$c%F$P#H$#$F$+$d%F$%$e%F$`#I$c%P#.$I$U#H$f%X#>$E$Q#F$`#g%`#`#M$V#T#h%i%j%k%l%m%n%o%p%q%r%s%t%u%v%w%x%I$Z$y%z% #A%/#@#-#B%E@E@V+-%;%2 C%D%s#D y Y+E%F%c+c@e#b@i.<+<+D d@G%H%I%J%K%G@L%M%N% ",
+" O%* >@>@P%F%o@v x#Q%o.<+r.n.c@R%*@9+=.v S%T%, E#' !+U%V%>@W%X%;#/#Y%)#X@Z%`#X#V#K$X#-$P#X#F$`%/$ &.&K$P#c%.&+&@&O$E$%$c%Q#`#E$%$`#F$I$Q#T#S#G$F$M$#&G$F$F$F$-$g%L$`#c%$&.&>$I$Q#`# $>$X#F$V#x$F$+$C$F$L$O$F$ $f%b%U#P#U#X#+$C$U#O$n%a%F$P#T#X#U#`#F$`#L$+$Z$%&&&`#C$*&=&-&;&>&,&R$'&)&!&U#X#D$-$X#~&-$`#{&]&J#>#;#^&G#/&(&_&!@m ,@, !@s :&$@=@<&[&}&|&|&1&f#2&3&4&7#M 5&6&_.7&8&9&0& ",
+" a&b&,@m$c&[%*@[%a$d&b@e&d.g.f&{+J+$@G g&:.h&; i&j&B@k&s$;#l&m&=#X@0%,$%$n&o&,$L$V#F$K$O#a%U#T#d%Q#p&q&r&`#.$F$F$F$F$F$-$V#c%G$s&-$`##$g%X#>$ $`#-$t&+$F$O$u&.$F$H$%$U#Q$O$K$Q#.$C$E$d%O$.&S#Q$G$ $.$P#F$V#.$F$L$E$F$.$b%a%I$G$.$.$b%T#`#C$N$E$`#Q# $P#+$-$`#Q#V#F$U#%$X#X#`#Q#F$F$F$F$F$v&w&x&d%`#U##$F$`#f%.$`#P#X#X#S#-$X#X#;$y&Z@z&A&;#X@B&C&_&> {@D&E&F&9$D+G&H&I&J&h.c.k.b@K&L&M&N&0+R+;@' h&O& ",
+" <.P&<#[ 9+e@Q&K J+r.p.k.l.b@8+F%3+G+H A@R&,@M+_&,@S&T&!$U&^&V&,$%$Y#W& $I$X&Y&Z&`& *.*+*@*F$+$#*7$-$.&t& $$*%*&*Q#C$F$P#G$K$`#`#X#-$ $J$`#X#@& $`#e%S#`#G$#$X#`#T#f%U#F$e%I$`#G$H$S#K$F$F$F$%$U#`#d%C$.$C$t&c%L$**Q$-$-$X#F$-$%$Q#%$E$>$L$H$C$F$E$b%>$U#+$K$U#.$I$X#G$x$F$+$C$-$a%$&+$K$T# $X#X#=*-*;*T#F$K$$&X#Q#@&E$P#d%X#P#Z#a%P#%$P#X#Q#C$>*X#`#**,*+#V@{#>#'*)*!*~*{*]*S%^*/*(*q@_*q.o.l.f#:*(+f _+.@b <*}.[*}* ",
+" I |*%+1*F R .f@2*I&s@c@f.}+G C F a@3*;@4*)@5*q#6*##^#^#7*;$-$P#X#`#Q#P#F$F$F$`#U#@&8*9*0*a*b*c*d*e*+$f*g*h*i*V#K$j*k*l*.$E$F$L$#&f%U#-$`#F$`#U#`#`#P#`#X#I$G$X#C$b%`#U#O$**F$X#f%O$-$Q#`#F$x$@&X#F$-$`#F$Q$P#F$F$G$P#`#Z#Z#C$d%+$`#.&.&-$P#`#F$`#I$-$F$V#V#.$d%M$V#Q#a%.$F$G$>$F$V#V#F$L$E$U#G$y$d%+$e%m*n*o*T#`#K$%$`#Q#>$-$%$M$G$U#x$.& $@&f%`#P#T#g%F$P#b%U#K$-$X#L$p*!$=&q*r*s*t*u*v*w*x*y*z*W A*B*C*c@d.r.}+C +.D*%.f E*1@F*E% ",
+" R+G*i&c+s+j+o+T.F.M.H*t#g#a@f 9+5.-@E#' I*J*K*=#L*J#=#M*S#~&>*N*O*C$>$K$-$E$K$%$L$P#F$`#@&P*Q*R*5%S*T*U*V*d%W*X*Y*Z*Q#L$`* =.=K$%$F$C$Q$T#.$H$C$P# $I$U# $O$.$`#U#U#`#U#%$F$F$Q#T#F$-$H$H$T#O$Q#`#K$d%F$X#G$`#I$+=x$`#I$V#`#F$I$G$V#@&V#X#O#f%P#T#-$`#x$g%P#F$+$X#F$`#I$Q#Q#$&>$F$b%L$F$.&G$U#.$K$F$+$J$L$Q#>$@=#=$=E$Q#H$O$F$L$g%K$F$`#`#`#Q#+$-$E$.&K$L$+&O$F$+$f%.&H$-$X#%$O*X#P#O*`#K#U&%=F@&=*===-=;=>=,='=)=v F }+!=J.~=i$l+{=]=o+*@K@O&[* ",
+" ^=0 1*g&l+f+/=(=i$J.H._=P.i+W T.g n :={@;@<= #;#[=}=K$M*P#P#P#`#P#I$.$X#x$>$`#x$t&-$.$|=O$U#F$+&1=2=%$J$Q#3=4=5=S#@&6=7=8=Q#9=0=a=b=c%I$F$J$e%I$`# $+$Q#E$.$F$I$H$C$-$L$ $Q# $+$F$I$C$Q#`#X#K$ $E$$&g%G$>$x$C$V#P#F$U#E$ $U#a%Z#F$U#V#F$`#P#`#`#T#H$`#O$.$`#O#d%`#+$+&I$`#T#L$X#F$L$U#`#L$P#F$N$O$.$u&E$X#P#G$-$`#U#c=d=e=Q#-$t&@&Q#O$M$.$Q#U#`#Q#T#.$F$%$-$`#%$x$V#F$Q#V#O$b%-$K$.&Q$P#%$c%+$X#P#U#Q#w$M*f=R@+#(&g=h=H@,@i=9+3+j= .k=l=m=w.t#4+m+m+4.!@} n= ",
+" o=p=h@7.W +.X N.D.F.k=T.r+i+n+F } }.<#o 6*q=r=v$v$+$.$+$K$%$V#-$%$G$U#K$x$+$X#K$-$`#K$f%X#U#O#+$`#Q#s=t=u=C$v=w=x=y=z=Z#$&A=B=C=X#D=E=F=G=H=;#F$I=J=K=I$x$K$F$I$-$X#`#I$K$K$V#V#@&$&V#F$J$**g%-$C$I$F$X#K$P#%$ $+$a%e%E$X#%$L$`#X# $T#F$.$O#T#-$E$+$U#T#O#U#C$>$X#V#c%`# $N$c%U#y$y$`#V#Q#`#V#L$`#F$a%E$%$S#L$X#I$H$Q#%$e%L=M=N=U#F$`#`#`# $L$Q#T#C$P#%$e%>$F$b%E$F$I$T#%$F$X#`#P#K$X#%$Q$e%Q#c%y$T#U#L$L$U#T#X&`#B$0%O=v$>#2%P=Q=R=;@:@F%S=T=U=%.g#M.N.|&D k#3+~+q#h&}* ",
+" P&<#0+k+e+R p@w+x+k=V=j=9$F%W=X={#f=Y=U#`#`#`#P#P#%$+$I$d%M$I$L$L$%$x$e%K$G$y$.&-$x$.&`#V#+&.$I$f%X#`#P#Z=`= -.-Z$+-@-#-U#**$-%-&-f%`#*-=---;->-,-'-)-!-~-{-]-^-z=v&U#c%/-x$P#-$Q#`#U#>$-$F$V#y$G$`#M$.&Q#-$Q$H$P#X#`#%$a%d%V#J$J$U# $g%V#F$.$e%c%%$d%T#U#E$O$-$a%g%Q#c%x$F$-$G$`#F$-$E$X#V#+$Q#O#M$X#%$F$`#%$/-J$`#-$x$`#I$#$(-_-:-U#P#+$`#F$>$%$`#K$X#`#U#O$K$F$e%L$`#$&t&I$P#>$K$C$%$`#`#d%L$`#-$a%V#U#T#G$K$c%L$F$c%g%`#.$E$-$K#<->#^&[-3.V+X+}-0.T.]=k+|->%l.#@f F a.1-G@2- ",
+" 3-4-_ b.o+5-D.t#t#D.5-o+g+6- &/#7-`#E$$$8-9-0-a-b-c-d-e-f-g-h-P$g%I$H$O#G$M$#$J$.$Q$@&-$a%H$X#K$C$+$V#e%V#X#E$i-j-k-l-m-n-o-p-K$$&q-r-s-e%U#t-u-v-w-x-y-z-A-B-C-D-E-F-G-H-I-z=J-K-Y=L-M-X#N-@&Q#F$-$J$Q#F$T#L$U#K$f%#&@&g%-$F$-$X#F$U#Q#X#V#O#T#F$L$J$ $K$>$%$K$I$-$P#.$+$K$x$G$X#E$J$U#X#`#U#P#%$P#`#U#K$X#-$X#F$+$Z#H$K$L$%$`#+$:-O-P-@&`#>$/-F$X#y$E$`#@&T#`#a%n%Q#`#C$X#`#g%g%P#I$@&G$#&e%Q#`#**g%U#T#J$K$X#.$P#`#K$P#F$%$Q#F$+$C$%$%$W&=$`#Q-l&_#+#R-3., h@v j=0+ @#@p.}+S-T-0.l#U-W=V- ",
+" p#W-X-Y-g+5-N.N.L.4+{.D*3+;@Z-7-`#~&`- ;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;+;@;M$E$O##;s&x$+&O#G$O$T#P#P#U#`#U#X#`#`#x$*$L-$;%;&;*;=;-;I$;;>;,;';x$F$);!;~;%$J$F$I$u&@&`#{;];^;/;(;_;:;<;[;};|;1;2;3;4;5;N-6;7;8;F$d% $`#Q#`#F$+$Q$-$F$.&+$`#E$a%P#F$T#+$`#+$+$U#.$O$ $H$#$g%G$ $U#K$I$K$`#O$a%%$d%C$F$c%J$-$P#V# $`#X#X#X#9;K$L$L$J$I$U#g%0;a;b;@&`# $.&F$U#T#G$`#%$I$U#g%+=-$P#O#`#`#L$E$F$`#V#`#E$+&X#F$#&P#`#Z#O#Q#U#H$L$a%e%I$+$G$K$`#X#Q#U#P#Q$M$ $=$G$U#B$*$I#(#c;s }%!@g 5.x@y@f&}+r.]+d;e;f;g;h; ",
+" 3 q#i;a@z h#E.t#p@F m+j;, E*k;`#U#l;m;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;n;**H$.&g%e%e%g%N$S#d%f%@&L$c%E$P#F$V#.$X#U#o;p;q;r;-$G$s;t;u;v;w;x;y;z;h*Q#+$F$+$T#Q#U#.$T#>$g%-$X#>$A;8*B;%*C;D;E;F;G;H;I;J;K;Z%L;M;7-N;x;e*`#V#F$F$M$C$`#a%O;L$`#H$g%U#E$U#F$`#`#F$U#g%a%y$O$-$$&t&+$P#Q$>$`#P#U#F$C$J$K$I$b%>$ $.$P#Q#P;Q;R;-$ $Q#-$c%S;T;U;.&U#.&S#-$-$T#C$U#Q#`#`#K$C$U#Q#O$E$P#c%b%F$P#g%`#%$/-`#Q#Z#`#`#c%O$`#U#+$`#Q$O#Q#>$O#O$%$G$M$P#F$P#`#`#-$P#`#F$C$K$P#Y#;#@#V;r _&, 8$x 0.f#W;X;d.Y;F%i;h@& ",
+" Z;' 0+`; >.>+>i$Q.]=j=8$n H@@>#>`#@&$>.;.;.;.;.;.;.;.;%>&>*>=>->=>;>>>.;.;.;.;.;.;.;.;.;>>u&Q$b%O$x$E$L$V#x$x$L$N$#$.$-$T#-$F$L$@&,>'>)>F$>$O#G$!>~>{>e*]>^>/>Q$c%F$E$G$`#`#Q#-$X#P#F$F$F$Q#Q#%$f%Z#T#(>_>:><>[>}>|>1>2>3>4>5>6>7>8>9>0>m%4*@#N--$t&K$`#c%a%@&P$T#+$x$U#`#`#P#K$d%x$X#L$V#U#U#/-L$`#E$C$U# $O$ $U#`#X#-$ $P#X#a>b>c>X#`#F$x$s&d>e>f>%$F$g%d%X#.$.&C$.$H$C$Q#.$%$P#Q#P#Q#%$J$g%F$Q#.$F$U#L$`#V##$+$-$O$g%P#C$%$F$Q#%$`#Q#c%F$`#+$S#F$`#J$X#F$V#C$U#K$.$`#`#%$U#`#X&g>/#h>E#;@P%F T-$@y+x+i>j>k>,%t*l> ",
+" h&,@t j=P.p@t#E.f+R g@:=> s m>I#X#P#n>.;.;.;.;.;.;.;o>@;p>P$v=p>h-q>|=v=r>s>n>t>.;.;.;.;.;.;.;.;u>9%y$N$f%a%C$>$I$-$H$d%U#T#S#Q#F$c%/-%$v>w>P#G$/- $F$+$U#X#x>y>z>-$Q#F$Q$g%U#K$O#a% $V#P#F$`#F$F$X#G$C$-$g%x$P#P#H$$&Q#A>B>J;C>D>E>F>G>H>I>J>K>L>M>N>O>N-O=P>N-`#%$%$V#Z#%$P#@&c%`#-$M$Q#U#`#F$`#S#V#`#V#L$`#.$O#c%C$x$U#F$L$P#`#Q>b>R>U#`#F$T#S>T>U>V>Q#F$+$P#`#P# $-$I$O$L$c%M$L$-$E$+$`#c%+&c%U#P#%$K$F$F$F$K$>$U#K$T#L$K$$&$&`#Q# $F$Q#>$P#`#X#I$F$K$f%+$P#T#%$U#L$b%K$H$b%`#-$W>I$X#K#^#X>Y>s F@8+e@T.t#E.L.2* .9+> <# ",
+" Z>`>e G y |& ,N.T j>c+.,q +,%#O@M#`#Q#@,.;.;.;.;.;.;.;#,$,$,$,p>O;#;%,q>&,*,=,-,-,=,%>.;.;.;.;.;.;.;.;;,$,r>>,f%|=$,d%V#$&$&U#K$O;L$`#P#I$`#Q#T#`#>$P$P#U#t&P#X#,,',), $X#F$y$b%Q#F$E$+$V#@&I$F$Q$a%P#F$E$%$F$E$ $F$F$g%E$F$X#K$E$+&O$I$V#!,~,{,],^,/,(,_,:,<,[,~.},x;|,F$%#1,@#N-%$c%F$K$e%c%S#H$F$K$S#c%`#`#X#`#Q#G$K$E$@&U#.$Q$ $-$2,3,4,K$T#X#5,6,7,8,9,-$F$g%x$P#I$V#U#U#.$U#Q#x$+$F$O$.$F$a%#$T#Q#G$H$**Q$%$P#.$-$`#`#I$%$F$C$J$-$b%b%F$.$f%O$X# $G$+$L$E$F$X#Q#F$`#X#U#Q#O$a%`#L$v=H$X#>$W&0,3% #Z-I*} B T a,v#I&H*r+4+*+H@b, ",
+" c,n 1#&@E S-k.k=d,&.F e,/ <$z%f,-$X#`#U#%>.;.;.;.;.;.;D=g,h,h-i,9%j,k,*,l,s>l,h,m,n,u>=,h-o,.;.;.;.;.;.;.;.;->v=**S#S#u&.&a%y$@&Q#L$P$E$Q#x$c%F$`#U#X#.$Z#F$X#@&F$p,q,r,`#S#V#`#$&b%U#`#V#X#K$O#-$F$t&x$U#F$O$%$F$Z#M$-$F$O$I$F$F$F$F$L$+$F$+$T#%$F$P#Q#X#U#s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,!>`#X#O@D$>$F$ $t&d%-$.&a%F$K$c%-$P#Q#`#K$T#L$H,I,J,X#K,L,M,N,O,P,U#f%`#F$.&>$`#x$M$U#+$M$Q#L$Q$Q#F$+&I$F$Q#d%+$F$.$P#J$s&L$-$O#a%P#V#$&>$`#%$P#`#L$V#F$%$g%T#P#U#%$+$>$V#F$>$e%g%x$x$-$`#P#-$F$+$O$I$Q#d%N$G$0%z$Y=R@Q,/ , R,9$9$N.w+F.G a.4.^ 9 ",
+" V%S,T,3+b@f.<+$@l+ @h&' U,g>N@M*Z#-$I$`#V,.;.;.;.;.;.;.;W,X,Y,@;Z,`,g,9==,%,k, 'Y,*>.'l,u>*,#;+'.;.;.;.;.;.;.;.;@'s>$,u&+&b%O$e%x$K$G$b%K$+$@&O$+$c%+$P#O$#$`#K$J$`##'$'%'P#a%P#P#e%L$U#$&t&`#K$t&F$`#+&f%`#P#P#`#`#Q#E$U#F$H$Q#F$@&H$P# $V#P#I$$&I$F$-$L$Q#U#/-a%X#+$-$F$A>&'*'='-';'>',''')'!'~'{']'^'F$X@(#+$5%c%T#F$L$Q$C$x$O$F$Q#+$F$/'('_'`#:'<'['}'Z#>$G$S#X#P#x$-$`#%$C$F$`#V#-$O#s&`#F$$,E$`#-$g%X#F$.&P#U#>$P#F$e%L$X#>$@&+$+$d%%$F$ $Q#F$+$P#`#`#F$F$F$X#`#F$K$V#V#O$**d%X#x$c%U#+$>$Q#`#+$>$%$g%O$+$T#A$|'1'{@2 2'P.j+3's@k.u 9+4'5' ",
+" 6'7'8'9'0'1+c@G D g > ] S&X@a'=$X#V#+&X#`# '.;.;.;.;.;.;.;g-j,h,k,->Z,k,@'->Z,.' 'b'*,m,*,>,k,n,i,**.;.;.;.;.;.;.;.;c'=> 'v=9%P$b%+&M$I$.&.&Q#U#-$X#`#Q#P#U#c%H$.$O$T#P#d'e'f'%$C$`#V#t&I$X#P#G$`#Q#C$F$Q#t&S#L$.& $U#U#U#-$`#-$+&-$`#%$e%X# $Q$C$X#+$X#F$F$K$X#`#g%V# $V#P#X#X#U#X#e%J$Q#`#g'h'i'j'k'l'm'n'o'p'q'r's't'u'N--$.$-$-$%$F$L$a%-$v'w'x'y'z'A'B'U#X#U# $a%X#L$u&G$`#%$E$ $%$`#X#C$e%F$Q#t&T#U#.&$&F$U#+=T#`#C$C$I$.$U#`#-$L$X#V##&-$ $t&I$P#$&L$X#a%@&K$ $O$+$`#`#`#F$U#f%a%U#e%T#U#V#f%a%U#>$ $K$.&c%`#I$&$,$8;C'D''+{@J+T.3+n.E'>%9+F'{@D% ",
+" G'H'I'J'l.1+K'v A@)+_&L'M'N@X#;$M$F$ $f%F$ $c-.;.;.;.;.;.;N'n,O'*>;>P'Q'R'*>j,`,i,-,>,S'`,#;p>W,Y,s>&,9=.;.;.;.;.;.;.;.;T'k,s>&,+=t&f%d%V#T#H$E$C$b%E$X#`#`#`#I$K$X#P#Q#P#U'V'W'V#G$`#T#S#x$-$C$T#F$K$I$F$`#X#%$I$g%C$-$d%$&-$X#L$#$J$U#.$J$F$X#L$`#L$J$.$G$g%F$`#%$K$P# $O#%$U#Q$+$P#a%g%-$X#U#U#-$T#K$`#M$X'Y'Z'`' ).)+)@)#)$)%)&)*)X#`#=)-);)>),)')))!)~)+$V#c%L$ $%$`#x$y$>$I$a%x$c%J$-$Q#.&y$P#U#-$`#`#.$x$`#+$/-d%Q#x$a%C$O$L$P#.&J$X#I$**`#X#f%U#U#f%c%`#-$t&P#V#+=g%K$x$O$P#-$e%%$U#a%+$U#U# $-$K$e%c%G$f%c%K$U#+$K$`#w${)])^)> , /)(+D k.f#_+()*+_) ",
+" l@:)<)[)})C*|)1)>@2)3)f=V&=$V#`# $T#F$V#a%F$4).;.;.;.;.;.;.;T'j,q>=>9=5)@;6)5)h-%,k,m,v=-,n,#&s&n%+=O;-,N$.;.;.;.;.;.;.;.;7)Q'=,u>#;#$y$$&c%L$C$G$g%Z#Z#O$a%K$X#d%a%K$V#%$U#8)9)0)I$+$P#V#c%.$x$**H$X#O$e%-$P#X#`#U#G$O$%$+$C$Q#P#+$a%V#L$S#J$%$a%E$Q#-$ $-$T#S#`#-$M$M$X#%$+&+$-$H$a%K$C$J$-$Q#s& $-$c%P#X#U#K$K$C$b%-$`#a)b)c)d)e)f)g)h)i)j)k)l)m)n)o)p)q)c%C$O$a%Q$S#C$X#g%b%I$ $L$Q#K$>$%$`#O$$& $T#x$P#`#>$x$`#%$V#X#U#%$+$I$c%V#I$J$V#U#E$#$U#%$M$K$-$C$T#U#`#+$F$X#d%-$L$@&L$P#.$#&-$>$Z#+$-$d%C$K$-$ $I$C$g%-$`#X#%$U#F$.$-$`#r)2%s)R&)@t)a.E'n.c@u b , ",
+" m S,u)A+v)w)x)y)z)A)B)C)D)E)F)G)-$C$ $-$ $P#I$.;.;.;.;.;.;.;.;m,n>;>@;n>9=O'*>9-W,-,b'%,s&S'u>+&u&r>r>#&S'$,H).;.;.;.;.;.;.;.;I)->*>s>v=#;-,J$>$O$%$P#P#P#Q#Q#X#P#g%s&#&+=E$U#J)K)L)d%>$K$.$V#%$P#T#V#U#c%H$ $d%$& $Q#d%s&O$V#J$L$.$c%.$P#.$c% $+$H$Q$a%`#`#F$-$C$U#%$E$+$X#`#X#`#X#-$G$%$C$x$ $ $a%I$.$N$-$-$-$P#P#T#c%U#`#X#U#>$x$%$I$T#M)N)O)`#P)Q)R)S)T)I$I$ $.$+$V#g%.$Q#g%@& $a%O$-$+$ $U#`#c%C$+$y$O# $C$N$M$%$J$O#c%K$-$Q#L$E$I$K$%$-$P#+$L$X#I$a%Q#K$x$H$t&@&O$C$V#x$X#P#%$X#F$`#`#U#K$E$-$.$O;|=+$+$%$%$G$+&K$Q#y$S#d%>$ $Q#`#Y#%$Y=f=W%,@U)v a$c@:+j=D%] ",
+" V)W)[%l.J&X)Y)Z)`) !.!+!@!#!$!%!&!*!=!-!;!>!O)n,.;.;.;.;.;.;.;,!O'4)'!I)T'9-g,q>m,8-v=|=u&f%Z##&e%#&&,v=t&$,s>Q$.;.;.;.;.;.;.;.;f-@;*>h,s>8-8-H$g%S#O$%$ $P#`#-$*,)!%$%$I$+$F$a%!!~!{!]!$,G$c%N$O$I$>$%$P#U#%$U#X#`#`#F$`#+$I$K$a%G$g%#$H$%$G$a% $P#U#`#T#Z#d%K$I$K$X#F$P#y$@'^!a-/! ;(!V,Z,+&c%C$d%@&L$J$S#I$L$b%-$ $M$O#%$U#U#P#+$-$X#F$Q#u&@;_!d-c-:!<![!}!H$c%H$u&G$ $C$+$-$P#P#U# $C$Q#E$Z#V#X#@& $U#/-g%K$L$O$L$U#P#`#`#P#P#Q#H$+&a%c%O#d%I$L$ $Q#%$+$`#`#F$F$`#K$>$C$V#U#`#E$X,|!1!2! ;b-n;1!R's&a%M$G$a%u&V#c%P$g% $+$>$C$C$b%a%b%|=O$L$z$V&s)E@[@$@%.A.I&r+4';. ",
+" > b w#g.f#3!4!5!6!7!8!9!0!a!b!c!d!e!f!g!h!i!j!X#k!.;.;.;.;.;.;.;'!Q'4)&>l!]!4)g,j,Y,s>n%>,s&d%Z#S#a%g%g%H$b%s&&,E$D=.;.;.;.;.;.;.;.;#,;>=>`,&,>,$&.&d%G$P#F$V#W,c-.;.;x$x$+$`#-,.;.;.;.;m;M$T#x$$,v=a%J$Z#x$K$`#U#/-T'n;t>d-a-m!s>J$V#a%N$.&L$y$s&T#>$T#I$U#%$+$P#.$F$L$;>.;.;.;.;.;.;.;.;.;.;.;t>q>/-f%J$$&e%.$I$+$ $V#J$e%K$K$Q#L$E$`#+$O'.;.;.;.;.;.;.;.;.;.;n!|=o!p!a%Q$v=T#C$x$I$P#Q#Q#U#U#.&P#F$`#G$*,q!@&V#d%V#`#g%|!+'c-t>+';>$,d%E$b%n%O#T#f%.&I$c%V#X#>$E$-$X#X#U#`#P#-,m;.;.;.;.;.;.;.;.;.;.;.;.;r!/-b%u&J$x$Q$Z#S#%$-$P#Q#I$`#-$H$+$I$x$y$v$u$s!m r#Y p@t!.@A@>@ ",
+" X+~+9+f#f#z V=u!v!w!S#>*y$n%K$U#/-x$`#x!y!X#z!A!B!.;.;.;.;.;.;.;.;C!D!E!F!G!H!9=h-*,l,|=t&p>f%T#e%H$G$M$Q$g%a%/-s&c%'!.;.;.;.;.;.;.;.;I!J!&>m,u>e%L$-$F$-$Q$5).;.;.;.;.;+&J$`#l,.;.;.;.;.;q!f%O$O$J$Q$T#E$J$U#`#$,K!.;.;.;.;.;.;.;.;.;L!s&$&a%E$E$b%>$T#Q$E$+$Q#P#X#`#h,.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;d-X,M$$&$&f%s&u>n>b%$&H$T#L$K$U#`#s>t>.;.;.;.;.;.;.;.;.;.;.;.;.;M!N!e%S#-,O$e%X,+&C$E$L$P#`#`#E$u>+'.;.;.;>$V#P#-$g,.;.;.;.;.;.;.;.;t>n,e%e%b%a%M$T#L$@&V#P#$&**>$K$U#`#/-N'.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;o,r>u&M$d%T#d%S#@&K$+$+$U#L$.&F$F$Q#V#`#V#$-O!}..@U P!p.M Q!^ ",
+" [*n@0+k.h.(+D%R!S!T!`#F$F$ $$&P#%$#$**U#C$Q$Q#C$ $Z#.;.;.;.;.;.;.;.;U!V!W!X!Y!Z!`! ~.~+~@~#~$~x$V#>$ $%$N$f%C$e%s&s&H$l,.;.;.;.;.;.;.;.;'!V,%~|!`,+$|=g,f-.;.;.;.;.;.;.;.;a%F$Z#.;.;.;.;.;.;&~ 'n%$&y$Z#d%+$`#H$f-.;.;.;.;.;.;.;.;.;.;.;.;.;9-t&H$.&b%T#O$T#L$u&#&I$U#J!.;.;.;.;.;4)8-X,v=*>5).;.;.;.;.;.;.;.;.;.;.;.;.;*~y$s&y$u&N$F$E$=~.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;-~N$P$p>.&@&s&Q$-$H$Z#u>g-d-.;.;.;.;.;.;G$`#.$;~.;.;.;.;.;.;.;.;.;.;.;>~+=e%$&S#x$C$O$ $-$O#O#%$%$X#9=.;.;.;.;.;.;&>s>|=N$O;q>,~.;.;.;.;.;.;'~i,#;**e%J$$&b% $>$L$K$Q$/-L$-$>$%$F$`#I$)~k&/ !~j=m.#@y#~~# ",
+" E@F h.i.w#n@5*{~]~^~>$%$`#.$x$F$U#c%M$F$-$C$-$d%.$q>.;.;.;.;.;.;.;.;/~(~_~:~<~[~}~|~1~2~3~4~5~6~7~8~9~0~a~b~+$J$N$+&f%+&.;.;.;.;.;.;.;.;o,J!4)c~%,O$.;.;.;.;.;.;.;.;.;.;.;U#J$c-.;.;.;.;.;.;>>n,-,u&$,v=K$K$#,.;.;.;.;]!9%+&M$>,g,c-.;.;.;.;.;'!** '#$f%v=#$f%a%.$`#%~.;.;.;.;.;m,Q$t&N$u&S'$,h-.;.;.;.;.;.;.;.;.;.;.;.;1!y$y$@&a%C$G$.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;d~-,>,O;y$M$d%>$G$.;.;.;.;.;.;.;.;.;.;.;`#y$d-.;.;.;.;.;.;.;.;.;.;.;.;.;I)/-n%i,b%H$#$V#.$u&@&U#`##,.;.;.;.;.;c-=>N$|=X,|=S'9%v=e~.;.;.;.;.;.;+;k,-,+=#&u&N$f%g%I$-$.$>$-$+$Q$a%F$I$K$`#K#f~|@g~u g.d.f s ",
+" -%h~1-p.u X-)+s)i~j~k~f%J$Q#@&O$`#+$d%c%`#`#U#`#+$U#'!.;.;.;.;.;.;.;.;V,>~0-*-I)Q'l~m~n~o~p~q~r~s~t~u~v~w~x~y~$;z~A~B~C~D~.;.;.;.;.;.;.;.;q!r!%~E~=,t&.;.;.;.;.;.;.;.;.;.;.;c%+;.;.;.;.;.;.;.;F~Z,`,p>s&-$ $_!.;.;.;.;%~M$**|=+&O;-,**.;.;.;.;.;.;c~X,u&Z#$,n%N$C$U#P$.;.;.;.;.;>>$,s&>,-,p>r>*,e%a-.;.;.;.;.;.;.;.;.;.;.;m!v=|=S#t&c%c%.;.;K!q>O;Z#+&&,i,g,G~.;.;.;.;.;.;.;.;n>s>X,P$n%+&O$%,.;.;.;.;.;.;.;.;.;.;.;g,.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;c-u>9%`,y$+&n%T#T#f%g%`#p>.;.;.;.;.;.;n>v=P$>,s>u>->Z,X,y$'~.;.;.;.;.;.;>>u>|=v=Z#y$P$#$g%+$a%+$X#-$T#%$F$b%$&+$H~I~s)>@/)3+d.G+F'J~",
+" '+K~K&l.E !@L~M~N~O~K;T#+$F$T#.$`# $@&>$P#E$c%I$ $`#&~.;.;.;.;.;.;.;.;^!e~P~^!4)4)|!b'b'u>M$x$>$7$L;Q~I-R~S~T~U~V~W~X~Y~Z~.;.;.;.;.;.;.;.;`~)!m!l!T'I!%>m;c-.;.;.;.;.;.;.;.; {.;.;.;.;.;.;.;.;e~Q'@'n,g%%$_!.;.;.;.;N'n%+&>,#;**u>h,f%.{.;.;.;.;.;+{j,>,#&+&M$e%G$Q# ;.;.;.;.;.;*>-,S'j,R'u>Y,->p>b'.;.;.;.;.;.;P~j,Y,->W,n,h,`,+=8-n%x$.;.;6)f%t&O#Z#p>#;>,@{#{.;.;.;.;.;.;.;F~=,i,X,`,$,Q$**${%{&{*{.;.;.;.;.;.;.;.;.;e~q>b'9%b'q>={.;.;.;.;.;.;.;.;9==,@'+=u&+&O$@&f%%$%$t>.;.;.;.;.;N'R's>9%i,b'u>i,.'l,r>X,.;.;.;.;.;.;.;T'`,v=s&+&N$#&O#>$P$>$P#g%b%P#F$g%M$K$>$=$N@-{m$E ;{3&v > ",
+">{d;5.[+F%4.,{K# $'{){!{>$Q#`#g%C$U#K$$&I$`#L$.$K$J$`#~{.;.;.;.;.;.;.;.;>~={={{{9-6)=,>,S'+=$&$&@&Q#H$s&>$K$ $K=.#]{^{/{({_{.;.;.;.;.;.;.;.;:{<{[{}{|{1{`,u>l,+=.;.;.;.;.;.;.;.;.;2{;>;,m,h-/!.;V,=>@'l,F$n>.;.;.;.;.;3{|=**s>r>#;u>l,#&v=.;.;.;.;.;.;;>j,r>r>s&f%+$t&.;.;.;.;.;.;Y,k,u>n,j,`,8-.'u>H$.;.;.;.;.;.;~{->->q>=,8-j,i,+=#;X,**.;K!n%p>j,%,**l,Y,4{5{6{q!.;.;.;.;.;.;~{'!q>%,R'8-/-O#7{8{9{#>.;.;.;.;.;.;.;.;|!H)c~j,.'n,i,h-=,.;.;.;.;.;.;.;J!6)O'h,=,r>M$**v=`#j,.;.;.;.;.;.;J!m,=,n,W,h-n,Y,->b'i,g%c-.;.;.;.;.;.;n!@;@'n,**N$t&a%G$L$%$Q#g%Z#F$F$g%g%`#V#c%U#+#,@4.w#d.0{q#",
+"e,f r.}+C r k;}==#a{b{`#U#-$`#H$d%X#K$P$-$F$@&Q#U#b%`#t>.;.;.;.;.;.;.;.;>~o,c{m!#,m,`,p>N$b%T#C$ $P#V#H$x$d%O#C$`#T#M$E$L$`#.;.;.;.;.;.;.;.;d{e{f{g{h{i{j{k{l{8!.;.;.;.;.;.;.;m{|!9-n{4)9=|!6)d-E~*>h-@&c%.;.;.;.;.;.;@;j,8-@;q>=,@'h,9%a%.;.;.;.;.;.;e~j, 'W,&,s&Q#|!.;.;.;.;.;.;m,6)R'->q>k,W,=,i,I$.;.;.;.;.;.;c-H)9-%~#,@'h-j,>,|=#$$,O'h-e%N$s>n,p>b'8-8-9=->O;.;.;.;.;.;.;.;n{*>h-->*>>,o{p{q{r{Y=.;.;.;.;.;.;.;s{t{u{E~m,v{w{h-Y,P$=~.;.;.;.;.;.;=~I!->`,8-X,+&v=t&Q#d-.;.;.;.;.;.;9=m,R'R'9=;,k,W,q>*,i,t&V,.;.;.;.;.;.;.;g,->m,#;O;>,e%d%O#.&Q#%$g%Q#X# $Q#X#E$g%P#x{y{q#z{f#8.A{",
+"~ u k.J+B{C{D{X&E{F{G{P#+$K$`#P#%$F$X#d%F$U#v=%$P#$&U#H{.;.;.;.;.;.;.;.;*-F~r!m!l!I!Y, 'l,$&T#L$K$P#`#P#+$b%+&O$C$H$**d%V#`#.;.;.;.;.;.;.;.;I{J{K{L{M{N{O{P{Q{R{.;.;.;.;.;.;.;9-5)5)g-n{;>T'P'6)@;;>R'%$h-.;.;.;.;.;.;Y,s>%,g,->m,9-c~*> $.;.;.;.;.;.;$>W,=,;,-,r>U#(!.;.;.;.;.;.;8-R'=>H)c~@'#,|!.'P#.;.;.;.;.;.;S{n{;>'!T'O'O'=>i,X,.'=,9%|=/-+&Z#**+=>,-, '=>;,T#.;.;.;.;.;.;.;'!n>;,Z,*,v=T{U{V{W{`#.;.;.;.;.;.;.;X{Y{Z{`{ ].]+]W,h-u>@].;.;.;.;.;.;m;3{|!h-j,>,#&+=G$H$.;.;.;.;.;.;.;6)E~O'E~5)|!@'Z,h,u>-,|=u>.;.;.;.;.;.;.;9-g,W,b'-,l,/-Q$+=+=L$L$@&E$%$>$F$`#`#F$`#K$T@m w#o.J+#]",
+"p $@<+$]{ 3)~&Y#%]&]*]K$J$H$K$d%d%U#Q#.$F$-$e%P#K$O$U#~{.;.;.;.;.;.;.;.;m!o,P~g-n>;,i,r>@'S#T#O#g%Q#E$+$`#P#`#X#Q#C$M$>$ $`#.;.;.;.;.;.;.;.;2!F~e~3{'!E~=]-];]`#.;.;.;.;.;.;.;]!3{>]^!]!5)4);>6)9=|!8-`#n;.;.;.;.;.;.;Y,h,Y,@'q>W,->Y,k,U#.;.;.;.;.;.;t>6)g,E~h,s>X#d-.;.;.;.;.;.;Y,q>g,#,O'W,q>;,i,U#.;.;.;.;.;.;,]3{n{5)g-n>6)c~ 'v=#&P$**p>v=P$S'v=p>&,l,n,@'8-K$.;.;.;.;.;.;.;e~I)H)@;q>r>'])]!]~]N-.;.;.;.;.;.;.;{]]]^]/](]_]:]R'@'<]/@.;.;.;.;.;.;N'J!@;@;5)*,+=b'+$*,.;.;.;.;.;.;.;q>;,6)9-J!#,9=#,W,s>l,l,O#.;.;.;.;.;.;.;*-E~c~W,n,r>Z#b%t&$&.$.$x$J$N$+&C$E$G$X#X#X#%#n&K []v 5*",
+"'@B g.}]p |]}=`#1]2]3]`#.$T#>$u&N$K$G$H$`#>$f%.$K$.$P#*~.;.;.;.;.;.;.;.;%~F~)!{{9-g,s>$,#;+&J$s&g%P#@&c%`#c%.$`#`#@&O$%$I$P#.;.;.;.;.;.;.;.;)!%>o,>~>~@;Z,=>k,F$.;.;.;.;.;.;.;4]1!.{F~J!m!]!I)*>Q'c~r> $.;.;.;.;.;.;.;+$U#P#P#U#P#P#P#U#X#.;.;.;.;.;.;.;l!@;T'->b'K$t>.;.;.;.;.;.;l!O'|!J!4)R'k,j,p>P#.;.;.;.;.;.;)!={m!3{V,E~n>E~`,p>u&y$Q$S#/-#$-,+=#&/-M$d%L$F$K$.;.;.;.;.;.;.;V,n{T'J!l!i,5]6]7]8]F$.;.;.;.;.;.;.;9]0]a]b]c]d]e]f]E~g]h].;.;.;.;.;.;c->~;>Q'=>`,&,R'-$g-.;.;.;.;.;.;.;;,|!@;E~5)=>m,O'b'S'#;8-T#.;.;.;.;.;.;.;i]4)@;E~n>h-+=f%|=e%I$+$-$`#`#-$-$H$d%Q#>$M*(#B@a.}+]+,@",
+"> v })_+b E#Z@`#B~s-j]%$P#`#`#g%a%P#G$J$-$d%**a%>$O$%$5).;.;.;.;.;.;.;.;T'F~k]>~V,&>*,v=/-O$x$e%.$P#.$V#U#@&P$F$P#M$t&K$I$I$.;.;.;.;.;.;.;.;J!)!c{F~r!@;Z,E~*>F$.;.;.;.;.;.;.;l]m]n]o]p]%~{{#,->#,Q'p>d%.;.;.;.;.;.;.;d-N'N'N'N'N'N'N'N'd-.;.;.;.;.;.;.;I!H)n>q>.'c%&~.;.;.;.;.;.;;>|!9-3{]!I)|!n,N$L$.;.;.;.;.;.;&>3{'!g-4)Q'*>#,n,X,v=O#@&#$y$O$G$K$F$-$x$***>=~.;.;.;.;.;.;.;.;={m!I)P';>n,q]r]s]t]F$.;.;.;.;.;.;.;u]v]w]x]y]z]A]B]@'C]Y=.;.;.;.;.;.;.;@,g-I!|!j,n,q>U#i].;.;.;.;.;.;.;3{&>I!I!g-m,Y,R'S'P$#&`,+$.;.;.;.;.;.;.;'~4)9-@;@;R'S'S#9%b%G$e%L$P#X#F$F$V#I$X#F$E$2%E@I q.B D]",
+"V+7.1+n.z E]F]O=G]H]I]G$>$P#F$e%L$F$Q#X#F$+$d%L$L$x$L$h-.;.;.;.;.;.;.;.;n{o,={n{I)9=*,u>X,d%g%Q$c%K$G$C$F$V#Q$F$Q#O$t&%$G$O$.;.;.;.;.;.;.;.;T'P~e~J!^!c~Z,Z,Y,F$.;.;.;.;.;.;.;J]K]L]M]N]I)n>g,Z,k,j,#$O#.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.; ;9-l!E~->k,n%6).;.;.;.;.;.;5)H);>I!9=6)H)8-C$t&.;.;.;.;.;c-n{V,P'5)9-c~@'.'%,%,&,M$x$V#`#-$f%j,r!.;.;.;.;.;.;.;.;.;.;.;.;.;O]*-5)l!5)Y,P]Q]R]O#F$.;.;.;.;.;.;.;S]T]U]V]W]X]Y]Z]`] ^`#.;.;.;.;.;.;.;P~{{'!g-m,k,=,`#/!.;.;.;.;.;.;.;={'!T'T'5)R'h-E~%,u&u&9%Q#.;.;.;.;.;.;.;S{>~T'@;;,.'X,P$l,J$C$#$c%%$+=E$X#G$%$X#Q#.^-{_&x@q..@=%",
+"+^@^7+e&g&#^,@L#$^%^&^ $d%P#F$+&%$F$>$%$`#`#g%%$K$+$ $O#.;.;.;.;.;.;.;.;P~O]c{'!&>*>b'`,-,e%c%.&c%Q#x$g%F$J$y$-$I$f%O#K$P#S#.;.;.;.;.;.;.;.;{{%>1!>~>]*>=, 'X,F$.;.;.;.;.;.;.;*^=^-^;^>]H)Q'm, 's>r>f%y$.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.; ;&~o,5)n>I)'!;>n{I);,.'u>S#.;.;.;.;.;.;'~9-&>&>Q'=>g,P$F$T'.;.;.;.;.;P'^!e~m!*-'!*>W,%,+=s&Z#%$`#C$Q't>.;.;.;.;.;.;>^m!t&.;.;.;.;.;.;.;k]1!g-E~P'@;,^'^)^u&F$.;.;.;.;.;.;.;'!!^*-V!~^{^]^^^/^(^F$.;.;.;.;.;.;.;={%~P';>m,W,h,U#c-.;.;.;.;.;.;.;1!={g-T'n>R' 'l,#;y$Z#$,X#.;.;.;.;.;.;.;t>0-n{I!|!R'&,O;h-H$G$x$Q#P#C$%$`#s&G$Q#&$_^J@F':^<^Q&)+",
+" [^(+r.}^w#5*|^1^2^`#P#.&X#`#Q$`#F$.$C$X#U#O#Q#K$C$V#L$.;.;.;.;.;.;.;.;H{F~o,P'{{c~b'8-u>f%c%g%V#U#G$.$F$.&a%`#Q#T#c%%$U#h-.;.;.;.;.;.;.;m;r!P~c{P~c{6)g,;,-,F$.;.;.;.;.;.;.;3^4^>]]!n{Q'9==,s> 'X,M$t&.;.;.;.;.;.;.;#,g-{{m!'!%~I)@;l!H)W,=>Q'=>*>T'5)9-&>#,;,j, '#;%>.;.;.;.;.;.;T'Z,n{I!Y,N$U#M$.;.;.;.;.;f-Q']!J!'!^!%~g,m,8-**Z#Q#K$T'.;.;.;.;.;.;*-g,u>X,X,F$.;.;.;.;.;.;.;.{r!g-I!O'j,>,S'+&#&F$.;.;.;.;.;.;.;%~r!J!5^6^7^j,8^9^0^F$.;.;.;.;.;.;.;>]'!{{9-m,9=Z,Q#d-.;.;.;.;.;.;.;1!o,>~]!E~R' '#;#&t&P$$,`#.;.;.;.;.;.;.;b-V,'!l!9=q>s>p>O;f%@&a%%$P#X#X#-$O#V#`#Y=F#R&f a^:+G+b^",
+" 1*z#c+c^0.d^e^f^g^X#K$g%L$Q#H$I$P#E$-$F$X#x$`#Q#J$.$E$(!.;.;.;.;.;.;.;.;5)e~P'9-m,i,b'S'M$b%u&>$ $b%+$F$T#.$X#P#.$+$-$`#>^.;.;.;.;.;.;.;m!>>0-*-c{*-c~R'W,9%F$.;.;.;.;.;.;.;h^i^j^V,9-=>j,s>O;|=#&M$M$.;.;.;.;.;.;.;&>3{={]!g-I)n>;,W,h-Y,->I)g,;,]!g-n>&>6)*>h,h,h-S't>.;.;.;.;.;.;J!H$P#F$.$9%t>.;.;.;.;_!6)c~{{'!I!]!;>=,Y,s>Z#K$.$$>.;.;.;.;.;={u>O;P$v=X,X,F$.;.;.;.;.;.;.;k]L!m!l!n>h,u>*,#$f%F$.;.;.;.;.;.;.;g-={k^l^m^4)j,n^o^p^F$.;.;.;.;.;.;.;{{'!V,9-g,E~m, $q^.;.;.;.;.;.;.;l!3{J!]!E~g,;,*,/-$&$,+=`#.;.;.;.;.;.;.;(!c{>~>]l!@'`,#;/-e%t&$&+$G$d%P#K$x$>$r^Z-'+D%g&:+)=P%s^",
+" 5*r#t^u^J+v^w^x^Z$X#`#F$`#-$Q#I$S#O$`#%$.& $L$V# $C$9%.;.;.;.;.;.;.;.;>~y^]!g-9=n, '+=@&c%$&+$K$O$Q#F$H$f%O$$&#$b%P#V#.;.;.;.;.;.;.;.;T'k]o,V,J!4)g,R'Z,S'F$.;.;.;.;.;.;.;={z^A^B^C^|!Y,-,&,#;y$e%f,.;.;.;.;.;.;.;^!>>=~c{V,>]T'c~|!g,.'Y,=>Z,->I!c~O'Q'W,@'b'9%s>+= 't>.;.;.;.;.;.;.;.;c-.;.;.;.;.;.;g-O'=>c~%~l!@;J!9=n,j,S'a%X#f-.;.;.;.;.;>~+=.'k,r>-,h-j,F$.;.;.;.;.;.;.;.{)!]!n{;>.' 'k,p>t&F$.;.;.;.;.;.;.;n{m!m!5)4)T'*>D^E^F^F$.;.;.;.;.;.;.;g-%~'!H)=>R'n,c%F~.;.;.;.;.;.;.;;>{{{{>]l!h-j,O;S#@&O#M$U#.;.;.;.;.;.;.;e~*-V,e~5)R'h-@'p>.&y$$&+$L$g% $P#V#.^1'G^H^z I^q.!~'+ ",
+" j S,8.J^o.K^L^M^S&N^B$X#Q#Q#F$Q#c%T#X#c%f%C$d%d% $d%d%c-.;.;.;.;.;.;.;c-{{V,V,I)W,m,i,y$b%G$+$I$E$`#`#F$F$P#G$#&O$`#8-.;.;.;.;.;.;.;1!V,^!^!]!P'n>h- 'Z,O;F$.;.;.;.;.;.;.;%~P'O^P^Q^#,m,u>h,u>y$S#[-.;.;.;.;.;.;.;R^>~1!^!={c{{{6)E~;>m,h-R'j,m,9-=>@'W,h,n,&,-,l,**p>W,y^.;.;.;.;.;.;.;.;.;.;.;.;^!=,Y,c~m,Q'9-Q'c~@;=,u>=,O;I$u&.;.;.;.;.;.;*,$,.'h-b'*,@'h,F$.;.;.;.;.;.;.;c{c{{{l!O'.'l,l,n%Q$F$.;.;.;.;.;.;.;&>m!m!9-*>=,u>p>**O#F$.;.;.;.;.;.;.;]!m!>]9-O'@'`,e%->.;.;.;.;.;.;.;5)3{{{e~>]@'h,b'+&+&N$E$-$.;.;.;.;.;.;.;T'J!{{{{;>m,@'h,P$H$u&O#V#%$Q$T#K$S^D'{@v .@l.r.a@T^O& ",
+" U^h 0{C.G.V^W^>@X^*$&$**y$L$V#O$V#F$.$G$%$d%J$L$**#;=,.;.;.;.;.;.;.;.;n{3{4)|!@'k,i,P$$,S#G$Q$#$+$P#+$F$`#K$t&P#Q#N'.;.;.;.;.;.;.;5)y^P~*-P'&>c~j,-,u>s&F$.;.;.;.;.;.;.;P'3{Y^Z^`^ /l,|=$,#&M$$&./+/.;.;.;.;.;.;.;@/y^>~#/$/%/&/q>@'j,j,k,`,h-|!@'Z,q>i,l,>,v=#;P$#;f%`#|=.;.;.;.;.;.;'~f-]!|!m,i,h,H)E~m,4)g-*>->g,%,>,s>s&P#T'.;.;.;.;.;.;X,X, '-> '`,h,`,F$.;.;.;.;.;.;.;.{1!]!n{H)=,l,#;s&y$F$.;.;.;.;.;.;.;4)J!={T'6)W,u>9%9%u&F$.;.;.;.;.;.;.;*-*-F~3{O'O'=>p>#&.;.;.;.;.;.;.;n{3{{{m!m!=,u>s>+&**l,G$.$.;.;.;.;.;.;.;J!^!3{J!H)R'8-S'Z#H$S#g%%$P#w$*$N**/~ 8$F%e&=/8.A@3@ ",
+" -/}]2*(=E.g#a@B@P>^&a'H~%$E$u&E$F$O$>$U#b%.& $J$$&$&b-.;.;.;.;.;.;.;.;@;%~#,W,Y,b'S#e%J$>$g%M$.$L$**S#`#>$.&`#R'.;.;.;.;.;.;.;P'={P~L!3{'!>]I) 'r>r>u&F$.;.;.;.;.;.;.;5)*-F~;/>/,/*,P$n%9%e%x$'/)/.;.;.;.;.;.;.;!/~/{/]/^///(/_/:/</[/}/9%&,-,-,s>%,r>s&/-/-/-.&-$-$n,.;.;O]E~J!^!&>I!@;g,->R'k,.'Z,->->I!g-O'g,9-W,S'&,$,`#n;.;.;.;.;.;.;`,i,.'|!j,h-9-#$F$.;.;.;.;.;.;.;k]e~]!]!{{#,R'&,|=-,F$.;.;.;.;.;.;.;4)'!P';>Q'6)`,X,u>s&F$.;.;.;.;.;.;.;5)g-n{l!O'->c~j,J$.;.;.;.;.;.;.;>>V,]!g-9-Z,u>+=S#+&s&I$@&.;.;.;.;.;.;m;3{V,]!.{H)R'*>k,N$@&|=E$K$K$5%'#m>=%|/v s@p.v 1/|% ",
+" 2/5@3/k=J...9+2 ^-s)8;`#`#L$P#F$x$K$X#H$y$c%O#@&@&#;.;.;.;.;.;.;.;.;&~P'n{->*>j,P$S#d%G$L$ $-$P#V#C$U#%$`#O#.;.;.;.;.;.;.;r!l!m!e~.{P'5){{&>u>p>$,Z#F$.;.;.;.;.;.;.;4)%~4/5/6/R'Y,s&t&9%f%M$7/8/.;.;.;.;.;.;.;.;9/0/a/b/c/d/e/f/g/h/i/j/v=X,S'**#&#&S#$&s&V#`#H$q!.;.;.;6)H)m!^!^!.{3{Q'#,4)=>Z,W,R'W,=>O'=,n, '*,$,O;**U#D=.;.;.;.;.;.;Z,k,W,=>W,k,S#F$S#.;.;.;.;.;.;.;.{>]'!9-;,Z,`,+=#$%,F$.;.;.;.;.;.;.;T'J!3{9-=>Y,&,n%#$O#F$.;.;.;.;.;.;.;P''!%~I!=>k,`,&,$&={.;.;.;.;.;.;.;{{^!e~&>R'*,+=#$/-Q$U#u>.;.;.;.;.;.;4)P'{{{{V,E~R'm,8-N$N$u>>*V#k/l/~@3.h K+m/}+6.A@K@ ",
+" J+n/o/g#E.k+o@I p/q/_#z$G$X#F$K$`#`#P#I$K$p>#$b%v==>.;.;.;.;.;.;.;.;1!m,*>*>h,r>%,$,J$t&y$g%.$%$-$U#F$.&c-.;.;.;.;.;.;>^l!>~*-e~c{'!#,@;;,>,+=u&$&F$.;.;.;.;.;.;.;4)'!r/s/t/j,-,s&f%$&@&u/v/w/x/.;.;.;.;.;.;.;.;b'n>y/z/A/B/C/D/E/F/Y%K$E$Z,&,|=#$f%b%a%-$E~.;.;.;.;.;y$$&+&S#S##$N$t&+&u&t&Q$S#N$**`,g,j,i,9%v=#&S#e%Q# ;.;.;.;.;.;.;I)r>.'9%e%P#+$k,.;.;.;.;.;.;.;.;P~1!{{4)*>k,`,**t&M$F$.;.;.;.;.;.;.;&>={P~g-@;m,&,&,+=Q$F$.;.;.;.;.;.;.;J!={k]J!6)k,j,u>|=#$.;.;.;.;.;.;.;l!*-.{m!6)->s>v=l,e%`#_!.;.;.;.;.; ;>]P~c{3{%~|!h-b'+=y$f%G/k/H/I/H@g K'$@<+f.K+J/C% ",
+" K/L/ >I&M/E.W E ] 4*N/B&8;K$y$ $U# $K$-$V#a%g%y$u>6).;.;.;.;.;.;.;.;@,|=m,j,X,O;y$H$M$t&e%g%T#P#`#9%.;.;.;.;.;.;.;f-g,E~T'P'>~^!5)Q'I!@;.'i,+=@&`#.;.;.;.;.;.;.;4)V,O/P/Q/m,n,**y$Q$T#R/S/T/u&.;.;.;.;.;.;.;.;.;U/V/@]W/X/u&M'Y/Y=Q#/->^L!n%**$&H$O$U#h-.;.;.;.;.;.;c-'!;,q>q>g,R'q>R'=>R'W,R'Z, 'S'Q$t&h-@'9%>,r>/-V#r!.;.;.;.;.;.;.;m!u&M$u&;,d-.;.;.;.;.;.;.;.;.;1!e~e~c{5)@'R'%,u&Z#`#.;.;.;.;.;.;.;4)'!>]n{I)@;n,=,u>N$F$.;.;.;.;.;.;.;5)n{3{*-6)k,h-.'v=|=>].;.;.;.;.;.;7)Q'>~T'->8->,|=X,Q#@&.;.;.;.;.;.;#,5)>~.{y^={E~h-j,S'f%=$I#k;H@H@d;a.d.i.i.E%Z/1* ",
+" `/t@Y g+M.M.T.Y x } '@K*2%s=+$I$M$C$-$>$H$x$y$>,p>m,.;.;.;.;.;.;.;.;d-%,H$b%M$H$x$G$+$P#`#-$S#_!.;.;.;.;.;.;.;={m,n>I)4)5)%~n{E~R'q>b'+=#;#$g%U#.;.;.;.;.;.;.;I)]! (.(+(Q'=,$,v=#$a%@(#($(@&*>.;.;.;.;.;.;.;.;.;.;%(&(*(=(-(X/z/d-.;.;E~M$b%H$d%a%X#,].;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;/!j,r>v=#;*,p>#&@'.;.;.;.;.;.;.;.;.;.;.;.;.;~{e%.;.;.;.;.;.;.;{{J!P'5)n>Z,8-u>**$,`#.;.;.;.;.;.;.;4)n{&>E~;,k,9%+=#&S#F$.;.;.;.;.;.;.;V,5)n{={=>k,l,p>****>,q^.;.;.;.;.;.;P~.'H)O'Y,#;@&-$+$,!.;.;.;.;.;%~9-l!g-m!m!{{|!n,;(D^>(,(J@n$'(u _+>%p.$@y )(j& ",
+" }]p#D R h#N.h#W !(D >@,@~(@#{(Q$Y# $y$/-E$N$&,$,b';,&~.;.;.;.;.;.;.;.;c-J! '#&t&Z#n%`,J!c-.;.;.;.;.;.;.;`~6);,;,I)k]m!4)m!g-;,Y,b'$,O#@&x$-$I$.;.;.;.;.;.;.; 'c~](^(/(Q'j,P$u&y$J$((_(:(P$*,]!.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;S{Y,$,S#$&@&g%+$d-.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;>^s>#&s&+&**S#.;.;.;.;.;.;.;.;.;.;.;.;P~g,F$.;.;.;.;.;.;.;i,S#g,%~@;Y,u>+=S#.&X#.;.;.;.;.;.;.;4)]!'!#,Q'R'&,**t&e%F$.;.;.;.;.;.;.;V,5)P'e~Q'->Y,&,**#&O;&,e-.;.;.;.;.;.;a-#&.$K$F$-$**q^.;.;.;.;.;I)q>n>I!'!5)#,<(g,[(}(|(1(2(3( @g@c.o.<+K i;G@4( ",
+" P+H Y +.L.y+5(X Y+:%J*o L@H#D$P#D$J$V#b%Z#N$-,j,W,I!,].;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;`~I!*,.'O'g,n>]!I!H)g-g-=,j,->N$X# $V#a%n{.;.;.;.;.;.;.;k]Z#6(7(8(|!Y,p>O;&,+&c%G$T#d%S'n,H).;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.; ;;,v=n%**#&t&O$e%l!.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;%>`,&,u&Z#Z#c~.;.;.;.;.;.;.;.;.;.;P'@;=>F$.;.;.;.;.;.;.;.;c{%~P'I!h->,u&$&I$.$.;.;.;.;.;.;.;n,R'W,c~I!@;=,i,+=Q$F$.;.;.;.;.;.;.;h,=,Y,I!6)O'9=@'b' 'h,>,8-r!.;.;.;.;.;.;.;.;m;e-.;.;.;.;.;.;e-g,=>m,6)E~I!9-|!9(0(a(b(c(d(6+(+1+r@d.F 3+X-; ",
+" d;-%g&W X z.F.e(f(B z#!@g(h(X@,$0%L$H$@&Q$#$#;*,;,n>n>c{i(.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;N'={@'b'->`,;,I)|!|!E~Q'6)6)Q'%,n,g,t&C$.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;q>s>+=u&p>y$e%d%T#a%>,j,r>Z,={.;.;.;.;.;.;.;.;.;.;.;.;,~c~9%v=**#$#&**u&+&+=*,F~.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;6)=,v=+=+=S'&>.;.;.;.;.;.;.;b-E~&>@;c~F$.;.;.;.;.;.;.;.;.;.;&>n>&,%$g%g%e%{{.;.;.;.;.;.;.;V,+&@&`,=>k,&,#;s&$&F$.;.;.;.;.;.;.;'!Q$H$g,g,@'.'X,**O;r>$,&,q>H)+'.;.;.;.;.;.;.;.;.;.;.;.;o>@;u>Y,H)=>9=n>Q'9=j(k(l(m(n(o(p(q(n.c@z{A 4.' r( ",
+" $+>@g&T.%.s(8@t(u(v(:.> ;@w(<-(#x(y(M$N$|=-,@'m,g,Q'H)Q'9-3{0-,~K!H{c-i(~{>^c{&>=>=,*,O;S'9%b'8-->#,E~|!;>@;R'W,h-r>X,9%e%>,.;.;.;.;.;.;7)i(d-.;.;.;.;.;.;.;*> 'v=Z#y$H$O#u&E$c%O#`,+=u>=,;,4)]!=~ ;7).;D=2!c{9-9=h,l,h-n,S'9%P$S#O#Q$O#Z#P$=,m,>>.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;.;>]8-*,8-i,r>n,;,g-(!/!c-,]={l!g-&>#,9=O'.;.;.;.;.;.;.;.;.;.;5)T'O'6).;.;.;.;.;.;.;.;.;.;.;.;.;.;.;E~=,&,&,n%+&t&.;.;.;.;.;.;.;.;.;.;.;->W,%,$,**p>P$+&#&p>s>@'@;4)y^q!+;c-D= ;;~^!T'k,>,>,s>=,z(g,A(B(C(D(E(F(G(H(I(J(K(L(M(8+@^U%]. ",
+" b,N(P% .Q.H*O(H.9$j$0.} ^ )+P(H#&$S#N$O;k,Y,k,*>*>c~I)9-6)=>W,Y,j,Z,*,*,k,s>v=8-g,.'*,`,`,.'Z,->g,R'm,@'8-%,-,P$X,&,@&S##;Z#t&S#y$$,h,j,Y,c~l!I!Q(R(S(Q'l!Q'h- '|=Q$e%d%G$E$J$/-u&|=-,j,9-6)#,c{*-T'J!P'6)|!#,Y,`,i,s>X,l,9%p>N$$&M$M$c%U# $]!.;.;q^I)%~'!>~@,c{]!3{4)E~l!m!c-.;.;.;.;.;.;.;a-->k,R'Z,-,n,Y, 'W,c~@;3{F~P'P'J!T'E~#,O'T(U(V(@;4)J!>~n{n{J!H)R'O'8-S'n%+&y$#$p>&,.'E~6)E~3{#,9=H)W,s>i,p>**p>S#O##;i,`,W,@;@;E~;>Q'W,h-%,+=/-u>#;N$Z#P$#;*,Z,->n>H)@;9=g,W,j,m,->8-O'->n,m,W(X(Y(7^Z(`( _._+_@_#_$_%_Z+g@^ O+ ",
+" &_*_d#=_n+C.C.O(j=j+r+z#-_;_>_|'s$,_p>>,l,9=;,q>9-E~=>c~=>Z,h,m,`,l,n,l,r>b';,Z,Y,E~*>->=>;,=>q>W,n,&,>,|=+&y$e%c%x$d%x$b%u&N$#&n,R'->6)Q'H)'_)_!_;,@'i,s>-,#$f%#$#&>$d%P$s&$,O;#;s>k,q>c~n{'!#,n{n{6)E~&>h-.'.'j,>,S'%,q>|=N$$,V#`#+&/!.;.;=~|!T'^!={r!>^F~J!.{3{5)>~3{s>e-.;.;.;.;.;.;D=n>m,E~*>8-@'W,=,R'->O'#,g-&>'!{{I!H);>@;~_{_]_c~I)J!'!l!I!n>;,=,.' 'i,8-O;s&i,s>l,R'9-;,H)3{l!;,@'n,r>+=u&Z#$,s&f%i,l,s>m,I)c~6)5)=>=,Y,b'n%S#S##$$&t&Z#P$ '`,.'E~9=;,=>q>=,.'j, 'u>^_/_(___:_<_[_}_|_1_2_3_4_5_6_7_w#E*8_ ",
+" 9_0_(*P.l+D.H*g#u.v @a_b_c_d_e_q=f_g_9%*,n,m,->h-I!T'j,Y,I!%,u>q>u>l,-,->*,i,Q'j,8-Z,@'g,O'9- '&,j,&,S#$&.&C$c%c%G$E$O$M$N$X,8-=>n>9=6)h_i_j_R'k,s>#;P$O#@&f%$&>$c%t&u&v=u>r>h,->=,@'*>9=c~6)6);,O'Q'W,h,8-8-l,s>p>-,+&e%Q#K$;>.;.;.;i(E~#,5)P'3{V,)!>]m!^!P'T'F~1!O'+=.;.;.;.;.;.;7);>Q'|!R'j,Z,h-m,;>*>q>6)|!9=l!9-*>g,;,^!k_l_n!R'@;P'4)@;#,@;=>j,l,p>**/-+&N$$,#;l,O'H)W,n>J!4)R'q>q>n,9%N$Q$O#b%M$/-n%r>k,Q'W,=>P'9=W,9==,P$P$y$Q$O#Z#f%N$9%r>l,;,;,@'H)c~=,=,m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_D]b& ",
+" E_3 F%Y Y T v#:+l.E% @F_G_2(H_I_J_K_L_M_m_m_`,Z,Q' '.'@;u>i,I!->&,s>*>-,u>H)s>l,b'-,r>O;-,**#$&,+=Z##$>,Q$Z##$b%e%Q$e%y$**#; '=,h-R'N_O_P_Z,k,l,>,u>s&H$g%O$T#V#E$a%Q$Z#S#&,h,b'8-;,*>q>|!Q'R'=>O'm,h,*,l,>,9%S'v=y$>$-$n;.;.;.;.;n{I!H)'!]!'!J!r!g-5)5)9-H)I)'!|!>$.;.;.;.;.;.;,]{{l!P'*>=,8-*,s>m,8-j,I!=>R'9=@;R'W,O')!Q_R_+;|!->l!|!;,I!I)Q'O' '+=$,|=/-y$P$$,+=8-h-.'@'O';,=,Y,n,-,&,P$#$$,M$f%X,*,X,>,%,h,j,Z,Y,h,h-%,$,8-i,H$O#9%$&O#O;P$p>&,h, '=,S_8-T_U_V_W_X_Y_Z_`_ :.:+:@:#:$:%:&:*:=:-: ",
+" t)J~m#$@;:9+3&>:t!f#,:Q%e ':):!:b(~:{:]:^:/:(:&,_:9=u>9%i,Y,X,-,j,X,l,6)8-X,l, '$,**|=f%e%$&c%>$.$+$ $c%e%H$P$>,>, 'b'S'8-->j,k,::<:[:.'u>p>**$,+&d%d%f%M$Q$S#H$x$.&e%y$s&n%X,.'.'i,R'q>Y,m,*>n>@;=,&,>,O;#;p>t&%$*,.;.;.;.;.;W,O'9=5)3{&>{{e~;>9-l!|!O'c~I!=>U#.;.;.;.;.;.;P~*-J!e~5)6)O'%,&,W,9%l,n>Z, 'j,g, 'i,->O'}:|:1:n,h-I!@'h,8- 'i,m,*,P$p>>,/-|=9%|=+=.'%,s>n,n,i,s>9%v=s&/-y$N$#$M$b%u&r>O;O;u>*,b'b'&,#;|=u&O#Q$t&a%.&P$y$S#2:3:X,4:5:5:6:7:8:9:0:a:b:c:G(d:e:f:g:h:H(i:j:c_k:l: ",
+" m:{@z#0+*@Y;l.n:o:x#p:q:r:s:t:u:v:w:x:y:z:A:B:s>X,-,-,$,X,&,$,|=v=S's&+=9%/-Z#n%Q$s&N$G$T#E$K$-$K$%$ $T#E$O$y$**P$%,*>h-=>C:D:]:O'k,n%N$P$b%a%T#L$I$T#/-J$C$@&S#>,#&S#O;u>9%r>b'l,s>%,u>b'Y,s>n%O;+=s&t&H$`#@,.;.;.;.;.;R'O'c~T'%~l!5)n{6)*>c~=>=,=>9=`,U#.;.;.;.;.;.;5){{n{;>n>=>R'*,9%h-+=S'R'j,r>&, 'S'O;%,|=E:F:G:+=>,8--,9%i,r>p>q> '#$Z#f%@&u&s&S#|==>u>X,m,Y,9%&,&,P$y$M$d%d%b%a%x$d%O#N$P$r>S'u>%,r>#;O;/-Q$y$#$b%G$>$G$c%{)H:B&q=I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:`: ",
+" 8$ <6;~+B .<P%s@+<@<#<D_$<%<&<*<u:=<-<;<><,<'<)<^:s&!<D^3:/-s&#$O#f%Q$H$d%a%x$e%b%.$e%+=.&.$a% $V#a%T#c%O$@&O#N$#$**~<{<]<&,=,h-b'8-j,+&$&y$x$+$%$f%+$+$ $L$b%d%H$#&b'>,-,W,v=**%,#;s&#&/-S#N$$,>,/-d%`#H{.;.;.;.;.;W,=>O';>4)9-l!9-g,g,q>`,u>%,8-/-I$.;.;.;.;.;.;5)J!P'I)Q'=>Z,l,p>|=u&s&r>S'P$>,`,#&p>s>:-^</<u&N$s&S'|=|=n,#;#$9%b'f%**u&O$Z#+=e%M$k,S'#&%,l,P$>,n,s&$,S'O#M$s&J$H$/-e%b%Q$t&Z#u&**u&n%v=/-u&(<r>_<Y#:<<<$#[<( '@K@1(}<|<1<2<3<4<5<6<7<8<9<0<a<b<c<d< ",
+" >{1*_&h f 8+w#e<e<1+f<g<h<i<p(j<k<1(1(l<m<I/L*n<o<p<N$b'l,p>q<@&.&H$C$G$d%c%-$.$c% $Q# $+$T#N$t&+&S's&e%y$N$y$r<s<t<t&f%M$b%M$t&@&t&X,+=K$E$v=g%a%T#.$V#>$T#g%$&b%Q$|=O#t&k,O;O#>,S#@&$&S#**f%u&K$d-.;.;.;.;.;9==>=>6)9=*>=>q>h,q>g,u>p>%,*,G$t&.;.;.;.;.;i]^!V,J!J!#,O'R's>S'v=/-/-|=N$$&N$s&Q$+&#$u<v<w<O;p>b'%,P$+&|=S'$&O#t&O$J$H$c%J$O#a%.&P$S#f%-,>,f%f%$,b%d%M$O$.&S#G$x$$,y$N$#$f%O#>,#$f%O#e%J$A$x<T@!$y<=:z<A<B<*@C<D<E<D_F<G<H<I<I<J<p(K<L<t:M<N< ",
+" O<Z;q#P<w#E Q<R<S<T<U<V<W<X<Y<p:Z<s:`< [):.[m&H#+[Y%@[#[$[M$%[d% $@$#$c%.$G$+$V#C$%$+$x$C$V#Q$$&g%O#/-b%&[*[=[e%f%e%a%T#G$V#.$C$g%Q#C$+&x$H$$&M$S'n%M$Q$M$.&O$J$O$.&u&O#H$P$M$a%J$C$G$>$L$%$@,.;.;.;.;.;4)=>O';>Q'=>g,h,l,l,`,|=#& 'y$`#m!.;.;.;.;.;9-]!P'n{;>9=q>.'r>S'X,#$P$#;/-Q$M$O$O$M$b%-[;[>[T#O$u&+=t&H$s&`,u&M$b%b%.&T#L$>$C$G$x$M$a%O$+&O;H$@&$,M$b%a%.$ $>$+$I$C$c%&$,[$&$&8-p<H/'[_^H/)[r o H@!+b &@Q &.W ![~[{[][f<J<^[/[([_[:[<[[[ ",
+" }[|[} W+1[5@5@2[3[4[{[5[k>6[F_ @}[7[8[=:-_b_:@9[#]0[A%r=A%;#A$R#a%M*W&B$M*O# $%$a%V#%$L$>$V#C$E$E$a[b[c[J$s&/-+&|=$&L$ $+$Q#U#Q#%$U#U#Q#-$O$+&M$Z#u&t&s&u&a%J$y$M$x$E$T#L$G$a%C$.$G$a%n,.;.;.;.;.;L!m,6)g-I);>]!g,s>u>%,+&$&t&U#Q$.;.;.;.;.;>]J!*-g-%~#,;,m,`,v=S#M$d%H$N$e%$&Z#.&@&v=+&d[e[f[V#V#O#O#T#C$C$e%**y$J$Q$P$Z#b%Q$O$ $E$a%C$C$g%M$c%a%O#f%v=+='$0%w$L$x$c%+$N#g[,#r=h[i[j[3$k[`>!@Z/a.=.(+F o.v)t!l[3&m[F n[o[p[q[r[s[t[ ",
+" u[v[V%= 8+w[x[n[F%d.y[z[2+e#Q%A[B[&.`;k+ @}.W+m#; W=*%O@ #%#^&^&Q@w$-$.$C[D$M*M$J$M$b%L$ $D[E[@( $.&T#+$H$H$.$ $V#K$Q#T#L$%$ $Q#`#P#I$V#a%C$>$S#S#>$x$Z#Z#M$t&Q$.&b%Q$.&.$e%n%+&.;.;.;.;.;.;->q>c~=>R'R'%,X,s>8-M$E$U#E$+;.;.;.;.;_!;,3{3{I)={g-->;,Y,|=S#O#a%G$N$C$I$>$ $.$@&g%F[G[H[g%%$+&u&T#V#>$.$T#E$I$L$.&.&H$s&@&%$H$#$.&I$c%J$G$V#.$+$~&I[;$t$^#])Y%J[e_N/5'}.}.K@K[L[3[A 8+G+z M[r.A+k.!=N[B[F E%1/2@R&n [* ",
+" O[P[#]}.i;M N[w#w#C*f#2&i#Q[ ,O(R[&.l+Q o+e 9[,@_&o >@'+S[<-O@;#>#X@T[{(L$N*S'L$%$U[V[W[%$c%%$-$C$V#-$Q#+$U#X#t&a%I$.& $`#C$%$X#V#%$P#b%a%K$+$G$C$H$@&L$V#C$e%.&Q#%$c%@&R'.;.;.;.;.;.;|!#$l,h, '-,$,y$J$Q#`#s&d-.;.;.;.;_!Z,;,%~P'H)E~@;Z,j,*,u&#&r>G$ $O;V#.$E$-$Q#+$-$X[Y[Z[%$P#G$@&.$C$S#M$@&b%G$I$.$-$-$.&C$-$X&|=s=K$.$L$z$^#,#/#&#w-`[A{<#, ; }/ e,D .}l++}@}#}G.3&t!j.p.c@7.&@G %@4.)+>@b_$} ",
+" %}&}*},@)(=}2[$@P.!(P.D.t#x.N.G.O(x#f(0.%@_+f P%}.n$^ / k$_&s*-}S@3%!#t$;}>},}-$%$W&Q#V#s&>$%$E$E$P#`#T#K$X#J$Q#F$#$>$X#g%+$U#M$f%>$T#>$-$K$V#%$X#X#J$C$-$E$C$>$e%'!.;.;.;.;.;.;+;Z,e%.$P#F$P#>$P$f-.;.;.;.;.;J!n>=>g,I)g-n>E~I!@'8-X,+&e%d%V#.$b%I$T##&V#K$y$'})}!}y!X#X# $>$P#U#U#-$I$H~{(L$>*w$.$&$~}/#/#z&.^J#{}3%~(b_h@1 ]}^}l@`+&.5-j$h+h+%.9$5(z./}l=x.q@8+u 0+2[(}i;-/, <#_} ",
+" g$:}<}{@e,:@f l+Y-]=j+h+x+p.h.n.}+r.[}u v z H 8+w#4'm$!@5*;@s 9[}}|}1},#U@2} #;#3}/#U@4}I[Y#N**$`#X#A$X#`#s&+$`# $P#X#`#-$Q#b%@&Q#.$Z#+$%$U#.$%$G$P$+&x$J$Z#9=.;.;.;.;.;.;.;.;.;.;t>.;.;.;.;.;.;.;n!=,r>*,k,q>n>#,=>->q>n,8--,$&f%t& $>$g%K$Q# $X#U#G$5}6}7}8}9}+$o{o{>$I$-$p,=#f==#=#@#R@S@0}a}_&)+l<Q=Q=t*;@>@x V W X n+h+P z.x+L.m=v.G.G.+>o@b}h+%.]=P%{@;@> 9 c} ",
+" *+0 n 3.m#5@d}e}z w 9+2[o.d&i.e&c.l.s@b.v ]+L a@M f}g}h}i}j}m#[@2 h&G^V+, :#k}=#*#H:=#;#H#T&l}m}a'X#K$X#X#X#`#X#;$W>X#`#w$X#Q#-$P#X#X#+$G$>$a%s&#$`,^!c-.;.;.;.;.;.;.;.;.;.;.;.;c-^!=>b'X,-,k,Z,R'@;9=q>=,`,&,X,n%@&Q$M*+$c%R#B$%$%$U#X#n}o}p}q}r}s}t}$#u}v}w}x}m>s n h@q#q#_&_&)+V)t f .@b$p+_+]+H X x+y+k=N.i#y}v.9$]=z}h+/=l+%.f ^}!+;%A}$+ ",
+" F'O&3.r W-5*n[.@B w#k#C u >%<+o.B}S-d&#@>%P!C}D}d,T=d}E}d}F}G}Q<m <#>@3.:@3.3.3.E@R&J*H};#>#S@U@W@>#K#V@H/S@ #X@R@3%>#f,-$P#C$C$L$x$I}+&P$^_Z,c~n{)!,!i(c-7) ;&~e~#,m_#;s&l,8--,h,9-q>h-J}K}h-m_L}M}N}O}P}U&+#l}Q}R};#P@2}S}T}U}V}W}X}Y}Z}`} <W+> X+k$o :.E z g@5@3+ |8+[%r#<+o.B}S-d&#@>%P!G.]=j+m+]=d}l+F :=s[^ V+.|%}t ",
+" 2$6#U^+|5*=%~~9+I $@g&T-Y+*@x#@|z.v.5(v.y+F.~=M.G.Y-d}!(o+T=#|k+k>h+T.g&~+)+;@) ^ ~@'+_&$|o / A{m$D]r[%|R&m>&|3%M@[=g[*|=|,_-|;|]:>|6:,|'|0()|!|~|{|]|p_^|/|(|_|:|<|_|[|}|)<||{|1|2|3|4|5|6|7|8|W+9|0|a|B@D@{@~ b|c|d|e|Y d}P.i+g+.}h+5-G 0..@r@0'l.#@#@k.d&d&n.q.v g&b$Q<*@B f|9$u ;%, ^ } g|Q! ",
+" g a 4 ( {@~@;.R=b h|i|j|g+k|&.X r+f+v.x+Q[ ,g#m=x.t#F.y+q@b@E $@l|u M c+)=K+K+G K+v k#_+C D b r h@_&H@-_n m|H_n|o|0:L:p|q|r|s|t|q|u|v|w|x|y|z|A|M:B|E(z|C|[_D|t_E|F|G|H|I|J|K|L|M|N|G}7#O|P|K+;:g&F Q|R|S|T|.>D.C.O.y+x.h#t#s(e#p.b@9+C C F r#_+G J+F%S,;@>@n +|b_.|4(w# ",
+" c}U|V|W|^ X|^}h&Y|=.5-l+Q.P.#.S T.e@o+%.4+b@b@r.Z|[][+:+f.b@B}o.m/s@k.m.f#c.x#g@D r#.@E%E%Q%`|h< 1.1+1@1#1$1%1&1%1*1=1-1;1>1,1'1)1!1~1{1c:]1#1^1.:/1(1_1:1<1[1}1m=|1D.g.c@s@r.}+e.g.o.c@11h#2131r++.8+%.4151K Y+.@3+P%5*n$, B@!+p 1 6171 ",
+" T%|*811@, 2 91e, <W+2 !~z@g&H J+9+y@]+y#b.J+F%g~a.Z+0+g&p.b@}+n.c@e.f#k.1+01a1b1c1d1U:T:e1y_f1g1h1i1j1k1l1m1n1+:o1p1q1h:r1s1t1h:u13_v1w1x1y1z10@V=A1B1C1|+D1E1F1I I G1H1I1J1K1L1J@J*o 3.M1N1' O1j # ",
+" }]P1$+4 >@n ~@{@~ E@|@3.~@'+;@_&b L+k#.<v $@F%f 0+E%0+w#Q1i<j<R1S1T1U1V1W1X1Y1X1)1#1~1Z1`1 2V:.2+2Z1@1@2#2$2%2&2*2T==2-2;2m#>2,2'2)2!2~2{2]2^2/2(2_2:2<2 ",
+" ~+4.|*[2O&5 }21*|212 }2232k[`>42=<526272829202M:92a2b2m(m(y|o|c2d2e2f2g2h2i2k<j2k2l2m2n2 "};
diff --git a/src/pixmaps/menu_zoom.xcf b/src/pixmaps/menu_zoom.xcf
new file mode 100644
index 0000000..b47209b
--- /dev/null
+++ b/src/pixmaps/menu_zoom.xcf
Binary files differ
diff --git a/src/pixmaps/menu_zoom.xpm b/src/pixmaps/menu_zoom.xpm
new file mode 100644
index 0000000..9182122
--- /dev/null
+++ b/src/pixmaps/menu_zoom.xpm
@@ -0,0 +1,64 @@
+/* XPM */
+static char * menu_zoom_xpm[] = {
+"16 16 45 1",
+" c None",
+". c #303030",
+"+ c #363636",
+"@ c #2F2F2F",
+"# c #191919",
+"$ c #A29FA2",
+"% c #4A4A4A",
+"& c #EAE8EA",
+"* c #FFFFFF",
+"= c #D3D0D3",
+"- c #262626",
+"; c #989598",
+"> c #3E3E3E",
+", c #DCDCDC",
+"' c #D8D7D8",
+") c #272727",
+"! c #9A979A",
+"~ c #E5E2E5",
+"{ c #E1E0E1",
+"] c #202020",
+"^ c #171717",
+"/ c #CAC8CA",
+"( c #323232",
+"_ c #0D0D0D",
+": c #343434",
+"< c #9B989B",
+"[ c #050505",
+"} c #DFDBDF",
+"| c #E5E3E5",
+"1 c #989698",
+"2 c #030303",
+"3 c #252525",
+"4 c #9C999C",
+"5 c #969396",
+"6 c #000000",
+"7 c #DEDADE",
+"8 c #DEDBDE",
+"9 c #101010",
+"0 c #949194",
+"a c #010101",
+"b c #020202",
+"c c #959295",
+"d c #0B0B0B",
+"e c #040404",
+"f c #060606",
+" ",
+" .+@# ",
+" $%& *=-; ",
+" > ,' )! ",
+" #~ { ~] ",
+" ^ / (!",
+" _ :<",
+" [} |)1",
+" 2 345",
+" 6667**8915 ",
+" 666066ab00 ",
+" 66600 0000 ",
+" 6660c ",
+" a660c ",
+"dea00 ",
+"<f00 "};
diff --git a/src/pixmaps/mini_icon.xpm b/src/pixmaps/mini_icon.xpm
new file mode 100644
index 0000000..d14a7e7
--- /dev/null
+++ b/src/pixmaps/mini_icon.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * mini_icon_xpm[] = {
+"16 16 12 1",
+" c None",
+". c #000000",
+"+ c #D89C90",
+"@ c #D06458",
+"# c #843424",
+"$ c #505448",
+"% c #E0E4E0",
+"& c #9CACA4",
+"* c #D0D4C4",
+"= c #077C51",
+"- c #61A58A",
+"; c #909894",
+" ",
+" .............. ",
+" .+@@@@@@@#+@#. ",
+" .$$$$$$$$$$$$. ",
+" .%&%&%&%&%&%&. ",
+" .&*&*&=-*=-&;. ",
+" .%&*&*=-=-&*&. ",
+" .&*&*&==-&*&;. ",
+" .%=====-&*&*&. ",
+" .&*---==*&*&;. ",
+" .%&*&*=-=*&*&. ",
+" .&*&*&=--=*&;. ",
+" .%&*&*=-&-=*&. ",
+" .&;&;&;&;&;&;. ",
+" .............. ",
+" "};
diff --git a/src/pixmaps/mini_icon_plot.xcf b/src/pixmaps/mini_icon_plot.xcf
new file mode 100644
index 0000000..6e37d85
--- /dev/null
+++ b/src/pixmaps/mini_icon_plot.xcf
Binary files differ
diff --git a/src/pixmaps/mini_icon_plot.xpm b/src/pixmaps/mini_icon_plot.xpm
new file mode 100644
index 0000000..174729e
--- /dev/null
+++ b/src/pixmaps/mini_icon_plot.xpm
@@ -0,0 +1,33 @@
+/* XPM */
+static char * mini_icon_plot_xpm[] = {
+"16 16 14 1",
+" c None",
+". c #000000",
+"+ c #D89C90",
+"@ c #D06458",
+"# c #843424",
+"$ c #505448",
+"% c #E0E4E0",
+"& c #9CACA4",
+"* c #D0D4C4",
+"= c #686868",
+"- c #909894",
+"; c #D14B12",
+"> c #964537",
+", c #EF825B",
+" ",
+" .............. ",
+" .+@@@@@@@#+@#. ",
+" .$$$$$$$$$$$$. ",
+" .%&%&%&%&%&%&. ",
+" .&*&*=*&*&*&-. ",
+" .%&*;>,*&*&;&. ",
+" .&*;,>;,*&*;-. ",
+" .%;,&=&;&*;,&. ",
+" .&;&*=*;,&;,-. ",
+" .%======>>>=&. ",
+" .&*&*=*&;&;&-. ",
+" .%&*&=&*,;&*&. ",
+" .&-&-&-&-&-&-. ",
+" .............. ",
+" "};
diff --git a/src/pixmaps/plot.xpm b/src/pixmaps/plot.xpm
new file mode 100644
index 0000000..7aba933
--- /dev/null
+++ b/src/pixmaps/plot.xpm
@@ -0,0 +1,72 @@
+/* XPM */
+static char * plot_xpm[] = {
+"16 16 53 1",
+" c None",
+". c #1E1E1E",
+"+ c #272727",
+"@ c #2E2E2E",
+"# c #932F3D",
+"$ c #993242",
+"% c #A33142",
+"& c #962E3D",
+"* c #70232E",
+"= c #932F3F",
+"- c #A23243",
+"; c #292929",
+"> c #7F2836",
+", c #922F3E",
+"' c #9F3040",
+") c #963141",
+"! c #2C2C2C",
+"~ c #9D3243",
+"{ c #6D232F",
+"] c #933040",
+"^ c #8A2D3B",
+"/ c #952D3C",
+"( c #1E1D1D",
+"_ c #9B3444",
+": c #AF3547",
+"< c #491D23",
+"[ c #932C3B",
+"} c #2B2B2B",
+"| c #2D2D2D",
+"1 c #333333",
+"2 c #2A2A2A",
+"3 c #2C2B2B",
+"4 c #552930",
+"5 c #8A313E",
+"6 c #872A38",
+"7 c #9F3141",
+"8 c #813944",
+"9 c #AE384A",
+"0 c #571D26",
+"a c #9E3948",
+"b c #3E2D2F",
+"c c #94303F",
+"d c #7D2532",
+"e c #5D2A32",
+"f c #641E29",
+"g c #8E2B3A",
+"h c #AE3A4B",
+"i c #AC3445",
+"j c #6B2F38",
+"k c #A53445",
+"l c #A13141",
+"m c #63212B",
+"n c #6A212C",
+" . ",
+" + ",
+" @ ",
+" #$% . &",
+" *= - ; >,",
+" ' ) ! ~ ",
+"{] ^/ ( _ ",
+":< [ } ] ",
+"_|;1!2_}}1231451",
+"6 78. 90 ",
+" ab c ",
+" de fg ",
+" h i ",
+" jk_l ",
+" +mn ",
+" . "};
diff --git a/src/pixmaps/tool_arrow.xpm b/src/pixmaps/tool_arrow.xpm
new file mode 100644
index 0000000..e58a43a
--- /dev/null
+++ b/src/pixmaps/tool_arrow.xpm
@@ -0,0 +1,26 @@
+/* XPM */
+static char * tool_arrow_xpm[] = {
+"16 20 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+" ",
+" . ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ....... ",
+" ........ ",
+" ..... ",
+" .. .. ",
+" . .. ",
+" .. ",
+" .. ",
+" .. ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/src/pixmaps/tool_text.xpm b/src/pixmaps/tool_text.xpm
new file mode 100644
index 0000000..9f2aaa7
--- /dev/null
+++ b/src/pixmaps/tool_text.xpm
@@ -0,0 +1,27 @@
+/* XPM */
+static char *tool_text_xpm [] = {
+"20 20 3 1",
+". c None",
+"a c #000000",
+"e c #777777",
+ "....................",
+ "....................",
+ "....................",
+ "....aaaaaaaaaaa.....",
+ "....aaeeaaaeeaae....",
+ "....aee.aaae..ae....",
+ ".....e..aaae...e....",
+ ".......eaae.........",
+ ".......eaae.........",
+ ".......eaae.........",
+ ".......aaae.........",
+ ".......aae..........",
+ "......eaae..........",
+ "......eaae..........",
+ "......aaae..........",
+ ".....aaaaa..........",
+ "......eeeee.........",
+ "....................",
+ "....................",
+ "...................."
+};
diff --git a/src/pixmaps/tool_wire.xpm b/src/pixmaps/tool_wire.xpm
new file mode 100644
index 0000000..a843587
--- /dev/null
+++ b/src/pixmaps/tool_wire.xpm
@@ -0,0 +1,36 @@
+/* XPM */
+static char * tool_wire_xpm[] = {
+"24 21 12 1",
+" c None",
+". c #000000",
+"+ c #DCCB94",
+"@ c #8E7D45",
+"# c #0736F2",
+"$ c #D4C38D",
+"% c #AD8E30",
+"& c #756020",
+"* c #C5C5BF",
+"= c #A7A79A",
+"- c #060605",
+"; c #A3A395",
+" . ",
+" .+. ",
+" .+@. ",
+" .+@. ",
+" .+@. ",
+" ########## .+@. ",
+" # .+@. ",
+" # .+@. ",
+" # .+@. ",
+" # .$@. ",
+" #%&..*==== ",
+" #-.;;==== ",
+" # ",
+" # ",
+" # ",
+" # ",
+" # ",
+" # ",
+" # ",
+" # ",
+" ############# "};
diff --git a/src/plot-add-function.c b/src/plot-add-function.c
new file mode 100644
index 0000000..478595d
--- /dev/null
+++ b/src/plot-add-function.c
@@ -0,0 +1,126 @@
+/*
+ * plot-add-function.c
+ *
+ *
+ * Authors:
+ * 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 <glade/glade.h>
+
+#include "plot-add-function.h"
+
+int plot_add_function_show (OreganoEngine *engine, SimulationData *current)
+{
+ GladeXML *gui;
+ GtkDialog *dialog, * warning;
+ gchar *msg;
+ GtkComboBox *op1, *op2, *functiontype;
+ GtkListStore *model1, *model2;
+ GList *analysis = NULL;
+ int i;
+ gint result = 0;
+ GError *gerror = NULL;
+
+ SimulationFunction *func = g_new0 (SimulationFunction, 1);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/plot-add-function.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this"),
+ OREGANO_GLADEDIR "/plot-add-function.glade");
+ oregano_error_with_title (_("Could not create plot window"), msg);
+ g_free (msg);
+ return 0;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/plot-add-function.glade", NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create plot window"));
+ return 0;
+ }
+
+ dialog = GTK_DIALOG (glade_xml_get_widget (gui, "toplevel"));
+ op1 = GTK_COMBO_BOX (glade_xml_get_widget (gui, "op1"));
+ op2 = GTK_COMBO_BOX (glade_xml_get_widget (gui, "op2"));
+ functiontype = GTK_COMBO_BOX (glade_xml_get_widget (gui, "functiontype"));
+
+ model1 = GTK_LIST_STORE (gtk_combo_box_get_model (op1));
+ model2 = GTK_LIST_STORE (gtk_combo_box_get_model (op2));
+
+ gtk_list_store_clear (model1);
+ gtk_list_store_clear (model2);
+
+ for (i = 1; i < current->n_variables; i++) {
+ if (current->type != DC_TRANSFER) {
+ if (strchr(current->var_names[i], '#') == NULL) {
+ gtk_combo_box_append_text (op1, current->var_names[i]);
+ gtk_combo_box_append_text (op2, current->var_names[i]);
+ }
+ } else {
+ gtk_combo_box_append_text (op1, current->var_names[i]);
+ gtk_combo_box_append_text (op2, current->var_names[i]);
+ }
+ }
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if ((result == GTK_RESPONSE_OK) &&
+ ((gtk_combo_box_get_active (op1) == -1) ||
+ (gtk_combo_box_get_active (op2) == -1) ||
+ (gtk_combo_box_get_active (functiontype) == -1)))
+ {
+ warning = gtk_message_dialog_new_with_markup (
+ NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_OK,
+ ("<span weight=\"bold\" size=\"large\">Neither function, nor operators have been chosen</span>\n\n"
+ "Please, take care to choose a function and their associated operators"));
+
+ if (gtk_dialog_run (GTK_DIALOG (warning)) == GTK_RESPONSE_OK) {
+ gtk_widget_destroy (GTK_WIDGET (warning));
+ plot_add_function_show (engine, current);
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ return;
+ }
+ }
+ if ((result == GTK_RESPONSE_OK) &&
+ ((gtk_combo_box_get_active (op1) != -1) &&
+ (gtk_combo_box_get_active (op2) != -1) &&
+ (gtk_combo_box_get_active (functiontype) != -1))) {
+
+ for (i = 1; i < current->n_variables; i++) {
+ if (strcmp (current->var_names[i], gtk_combo_box_get_active_text (op1)) == 0)
+ func->first = i;
+ if (strcmp (current->var_names[i], gtk_combo_box_get_active_text (op2)) == 0)
+ func->second = i;
+ }
+ current->functions = g_list_append (current->functions, func);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
diff --git a/src/plot-add-function.h b/src/plot-add-function.h
new file mode 100644
index 0000000..72b5663
--- /dev/null
+++ b/src/plot-add-function.h
@@ -0,0 +1,38 @@
+/*
+ * plot-add-function.h
+ *
+ *
+ * Author:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * 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 __PLOT_ADD__H
+#define __PLOT_ADD_H
+
+#include "simulation.h"
+#include "engine.h"
+#include "dialogs.h"
+
+int plot_add_function_show (OreganoEngine *engine, SimulationData *current);
+
+#endif
+
diff --git a/src/plot.c b/src/plot.c
new file mode 100644
index 0000000..a57fe5c
--- /dev/null
+++ b/src/plot.c
@@ -0,0 +1,753 @@
+/*
+ * plot.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 <glade/glade.h>
+#include <math.h>
+#include <string.h>
+
+#include "file.h"
+#include "main.h"
+#include "schematic.h"
+#include "dialogs.h"
+#include "plot.h"
+#include "smallicon.h"
+
+#include "gplot.h"
+#include "gplotfunction.h"
+#include "gplotlines.h"
+#include "plot-add-function.h"
+
+#define PLOT_PADDING_X 50
+#define PLOT_PADDING_Y 40
+#define PLOT_WIDTH 400
+#define PLOT_HEIGHT 300
+#define DEBUG_PLOT 0
+
+#define GET_Y_POINT(y_val,y_min,factor) \
+ (PLOT_HEIGHT-((y_val)-(y_min))*(factor))
+#define GET_Y_VALUE(y_point,y_min,factor) \
+ ((y_min)+(PLOT_HEIGHT-(y_point))/(factor))
+#define GET_X_POINT(x_val,x_min,factor) (((x_val)-(x_min))*(factor))
+#define GET_X_VALUE(x_point,x_min,factor) ((x_min)+(x_point)/(factor))
+
+const gchar *TITLE_FONT = N_("Sans 10");
+const gchar *AXIS_FONT = N_("Sans 10");
+const gchar *UPSCRIPT_FONT = N_("Sans 8");
+
+#define PLOT_AXIS_COLOR "black"
+#define PLOT_CURVE_COLOR "medium sea green"
+
+static guint next_color = 0;
+static guint n_curve_colors = 7;
+static char *plot_curve_colors[] = {
+ "white",
+ "green",
+ "red",
+ "yellow",
+ "cyan",
+ "pink",
+ "orange1",
+ 0
+};
+
+/* Hack! */
+#ifndef G_FUNC
+#define G_FUNC(x) (x)
+#endif
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *canvas;
+ GtkWidget *coord; /* shows the coordinates of the mose */
+ GtkWidget *combobox;
+
+ GtkWidget *plot;
+
+ gboolean show_cursor;
+
+ OreganoEngine *sim;
+ SimulationData *current;
+
+ gboolean logx;
+ gboolean logy;
+
+ gchar *title;
+ gchar *xtitle;
+ gchar *ytitle;
+
+ gint width; /* width of the plot, excluding */
+ gint height; /* axes, titles and padding etc */
+
+ gdouble plot_min, plot_max;
+ gdouble x_min,x_max;
+
+ gdouble padding_x; /* padding around the plot. Note that */
+ gdouble padding_y; /* titles, axes etc live here */
+
+ gint selected; /* the currently selected plot in the clist */
+ gint prev_selected;
+} Plot;
+
+typedef enum {
+ X_AXIS,
+ Y_AXIS
+} AxisType;
+
+static GtkWidget *plot_window_create (Plot *plot);
+static void destroy_window (GtkWidget *widget, Plot *plot);
+static void zoom_100 (GtkWidget *widget, Plot *plot);
+static void zoom_region (GtkWidget *widget, Plot *plot);
+static void zoom_pan (GtkWidget *widget, Plot *plot);
+static gint delete_event_cb (GtkWidget *widget, GdkEvent *event, Plot *plot);
+static void plot_canvas_movement(GtkWidget *, GdkEventMotion *, Plot *);
+
+static void plot_toggle_cross (GtkWidget *widget, Plot *plot);
+static void plot_export (GtkWidget *widget, Plot *plot);
+static void add_function (GtkWidget *widget, Plot *plot);
+
+static const char *_plot_file_menu =
+"<ui>"
+" <popup name='plotMenu'>"
+" <menuitem action='Add-Function'/>"
+" <separator/>"
+" <menuitem action='Close'/>"
+" </popup>"
+"</ui>";
+
+static GnomeUIInfo plot_file_menu[] =
+{
+ GNOMEUIINFO_ITEM_NONE(N_("Add Function"),
+ N_("Add new function to the graph"), add_function),
+ GNOMEUIINFO_SEPARATOR,
+ GNOMEUIINFO_MENU_CLOSE_ITEM(destroy_window,NULL),
+ GNOMEUIINFO_END
+};
+
+static GnomeUIInfo plot_zoom_menu [] = {
+ { GNOME_APP_UI_ITEM, N_("100%"),
+ N_("Set the zoom factor to 100%"), NULL, NULL, NULL, 0,
+ 0, '2', 0 },
+ GNOMEUIINFO_END
+};
+
+static GnomeUIInfo plot_plot_menu [] = {
+ {GNOME_APP_UI_ITEM, N_("_Preferences..."), NULL, NULL, NULL, NULL,
+ GNOME_APP_PIXMAP_STOCK, GTK_STOCK_PREFERENCES, 0, 0 },
+ GNOMEUIINFO_TOGGLEITEM (N_("Show crosshairs"), "show or hide crosshairs", plot_toggle_cross, NULL),
+ GNOMEUIINFO_SUBTREE(N_("_Zoom"), plot_zoom_menu),
+ GNOMEUIINFO_END
+};
+
+static GnomeUIInfo plot_help_menu[] =
+{
+ GNOMEUIINFO_HELP(N_("Schematic Plot")),
+ GNOMEUIINFO_END
+};
+
+GnomeUIInfo plot_main_menu[] = {
+ GNOMEUIINFO_SUBTREE(N_("_File"), plot_file_menu),
+ GNOMEUIINFO_END
+};
+
+static gchar *
+get_variable_units (gchar *str)
+{
+gchar *tmp;
+
+ if (str == NULL)
+ return g_strdup ("##");
+
+ if (!strcmp (str, "voltage")) {
+ tmp = g_strdup ("Volt");
+ } else if (!strcmp (str, "db") ) {
+ tmp = g_strdup ("db");
+ } else if (!strcmp (str, "time") ) {
+ tmp = g_strdup ("s");
+ } else if (!strcmp (str, "frequency") ) {
+ tmp = g_strdup ("Hz");
+ } else if (!strcmp (str, "current") ) {
+ tmp = g_strdup ("A");
+ } else {
+ tmp = g_strdup ("##");
+ }
+ return tmp;
+}
+
+static gint
+delete_event_cb (GtkWidget *widget, GdkEvent *event, Plot *plot)
+{
+ plot->window = NULL;
+ g_object_unref (plot->sim);
+ if (plot->ytitle) g_free(plot->ytitle);
+ g_free(plot);
+ plot = NULL;
+ return FALSE; /* yes, please destroy me */
+}
+
+/* Call this to close the plot window */
+static void
+destroy_window(GtkWidget *widget, Plot *plot)
+{
+ /* TODO - chequear si no hay que destruir el combo box */
+ gtk_widget_destroy (plot->window);
+ g_object_unref (plot->sim);
+ plot->window = NULL;
+ if (plot->title) g_free (plot->title);
+ g_free (plot);
+ plot = NULL;
+}
+
+static void
+on_plot_selected (GtkCellRendererToggle *cell_renderer, gchar *path, Plot *plot)
+{
+ GPlotFunction *f;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeView *treeview;
+ gboolean activo;
+
+ treeview = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(plot->window), "clist"));
+
+ model = gtk_tree_view_get_model (treeview);
+ if (!gtk_tree_model_get_iter_from_string (model , &iter, path))
+ return;
+
+ gtk_tree_model_get (model, &iter, 0, &activo, 4, &f, -1);
+ activo = !activo;
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, activo, -1);
+
+ g_object_set (G_OBJECT (f), "visible", activo, NULL);
+
+ gtk_widget_queue_draw (plot->plot);
+}
+
+static GPlotFunction*
+create_plot_function_from_simulation_data (guint i, SimulationData *current)
+{
+ GPlotFunction *f;
+ double *X;
+ double *Y;
+ double data;
+ guint len, j;
+
+ len = current->data[i]->len;
+ X = g_new0 (double, len);
+ Y = g_new0 (double, len);
+
+ for (j = 0; j < len; j++) {
+ Y[j] = g_array_index (current->data[i], double, j);
+
+ data = g_array_index (current->data[0], double, j);
+
+ /*if (plot->logx)
+ data = log10 (data);
+ */
+ X[j] = data;
+ }
+
+ f = g_plot_lines_new (X, Y, len);
+ g_object_set (G_OBJECT (f), "color", plot_curve_colors[(next_color++)%n_curve_colors], NULL);
+
+ return f;
+}
+
+static void
+analysis_selected (GtkEditable *editable, Plot *plot)
+{
+ int i;
+ const gchar *ca;
+ GtkWidget *entry;
+ GtkTreeView *list;
+ GtkTreeModel *model;
+ GtkTreeIter parent_nodes, parent_functions;
+ GList *analysis;
+ SimulationData *sdat;
+
+ list = GTK_TREE_VIEW (g_object_get_data (G_OBJECT (plot->window), "clist"));
+ entry = GTK_COMBO (plot->combobox)->entry;
+
+ ca = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ plot->current = NULL;
+ analysis = oregano_engine_get_results (plot->sim);
+ for (; analysis; analysis = analysis->next) {
+ sdat = SIM_DATA (analysis->data);
+ if (!strcmp (ca, oregano_engine_get_analysis_name (sdat))) {
+ plot->current = sdat;
+ break;
+ }
+ }
+
+ if (plot->current == NULL) {
+ /* Simulation failed? */
+ g_warning (_("The simulation produced no data!!\n"));
+ return;
+ }
+
+ g_free (plot->xtitle);
+ plot->xtitle = get_variable_units (plot->current->var_units[0]);
+ g_free (plot->ytitle);
+ plot->ytitle = get_variable_units (plot->current->var_units[1]);
+
+ g_free (plot->title);
+ plot->title = g_strdup_printf (_("Plot - %s"),
+ oregano_engine_get_analysis_name (plot->current));
+
+ /* Set the variable names in the list */
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+
+ /* Create root nodes */
+ gtk_tree_store_append (GTK_TREE_STORE (model), &parent_nodes, NULL);
+ gtk_tree_store_set (GTK_TREE_STORE (model), &parent_nodes, 0, FALSE, 1, _("Nodes"), 2, FALSE, 3, "white", -1);
+
+ gtk_tree_store_append (GTK_TREE_STORE (model), &parent_functions, NULL);
+ gtk_tree_store_set (GTK_TREE_STORE (model), &parent_functions, 0, NULL, 1, _("Functions"), 2, FALSE, 3, "white", -1);
+
+ g_plot_set_axis_labels (GPLOT (plot->plot), plot->xtitle, plot->ytitle);
+ g_plot_clear (GPLOT (plot->plot));
+
+ for (i = 1; i < plot->current->n_variables; i++) {
+ GtkTreeIter iter;
+ GPlotFunction *f;
+
+ if (plot->current->type != DC_TRANSFER) {
+ if (strchr(plot->current->var_names[i], '#') == NULL) {
+ gchar *color;
+
+ f = create_plot_function_from_simulation_data (i, plot->current);
+ g_object_set (G_OBJECT (f), "visible", FALSE, NULL);
+ g_object_get (G_OBJECT (f), "color", &color, NULL);
+
+ g_plot_add_function (GPLOT (plot->plot), f);
+ gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_nodes);
+ gtk_tree_store_set (GTK_TREE_STORE (model),
+ &iter,
+ 0, FALSE,
+ 1, plot->current->var_names[i],
+ 2, TRUE,
+ 3, color,
+ 4, f,
+ -1);
+ }
+ } else {
+ gchar *color;
+
+ f = create_plot_function_from_simulation_data (i, plot->current);
+ g_object_set (G_OBJECT (f), "visible", FALSE, NULL);
+ g_object_get (G_OBJECT (f), "color", &color, NULL);
+
+ g_plot_add_function (GPLOT (plot->plot), f);
+ gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_nodes);
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+ 0, FALSE,
+ 1, plot->current->var_names[i],
+ 2, TRUE,
+ 3, color,
+ 4, f,
+ -1);
+ }
+ }
+
+ gtk_widget_queue_draw (plot->plot);
+}
+
+static void
+plot_canvas_movement (GtkWidget *w, GdkEventMotion *event, Plot *plot)
+{
+ gchar *coord;
+ gdouble x,y;
+
+ x = event->x;
+ y = event->y;
+
+ g_plot_window_to_device (GPLOT (plot->plot), &x, &y);
+
+ coord = g_strdup_printf ("(%g, %g)", x, y);
+
+ gtk_entry_set_text (GTK_ENTRY (plot->coord), coord);
+
+ g_free (coord);
+}
+
+static GtkWidget *
+plot_window_create (Plot *plot)
+{
+ GtkTreeView *list;
+ GtkTreeStore *tree_model;
+ GtkCellRenderer *cell;
+ GtkTreeViewColumn *column;
+
+ GtkWidget *outer_table, *window, *button, *plot_scrolled;
+ GladeXML *gui;
+ gchar *msg;
+
+ window = gnome_app_new("plot", _("Oregano - Plot"));
+
+ gnome_app_create_menus_with_data (GNOME_APP (window), plot_main_menu, plot);
+
+ g_signal_connect (G_OBJECT (window), "delete_event",
+ G_CALLBACK (delete_event_cb), plot);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/plot-window.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/plot-window.glade");
+ oregano_error_with_title (_("Could not create plot window."), msg);
+ g_free (msg);
+ return NULL;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/plot-window.glade", NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create plot window."));
+ return NULL;
+ }
+
+ outer_table = glade_xml_get_widget (gui, "plot_table");
+ plot_scrolled = plot->canvas = glade_xml_get_widget (gui, "plot_scrolled");
+ plot->coord = glade_xml_get_widget(gui, "pos_label");
+
+ plot->plot = g_plot_new ();
+
+ gtk_widget_set_size_request (plot->plot, 600, 400);
+ gtk_container_add (GTK_CONTAINER (plot_scrolled), plot->plot);
+
+ g_signal_connect(G_OBJECT (plot->plot),
+ "motion_notify_event",
+ G_CALLBACK(plot_canvas_movement), plot
+ );
+
+ g_object_ref(outer_table);
+ gtk_widget_unparent(outer_table);
+ gnome_app_set_contents(GNOME_APP(window), outer_table);
+
+ button = glade_xml_get_widget (gui, "close_button");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(destroy_window), plot);
+
+ button = glade_xml_get_widget (gui, "zoom_panning");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(zoom_pan), plot);
+
+ button = glade_xml_get_widget (gui, "zoom_region");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(zoom_region), plot);
+
+ button = glade_xml_get_widget (gui, "zoom_normal");
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(zoom_100), plot);
+
+ list = GTK_TREE_VIEW(glade_xml_get_widget (gui, "variable_list"));
+ tree_model = gtk_tree_store_new (5, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
+
+ /* One Column with 2 CellRenderer. First the Toggle and next a Text */
+ column = gtk_tree_view_column_new ();
+
+ cell = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (G_OBJECT (cell), "toggled", G_CALLBACK (on_plot_selected), plot);
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell, "active", 0, "visible", 2, NULL);
+
+ cell = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell, "text", 1, NULL);
+
+ cell = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_cell_renderer_set_fixed_size (cell, 20, 20);
+ gtk_tree_view_column_set_attributes (column, cell, "background", 3, NULL);
+
+ gtk_tree_view_append_column(list, column);
+ gtk_tree_view_set_model(list, GTK_TREE_MODEL(tree_model));
+
+ g_object_set_data (G_OBJECT(window), "clist", list);
+
+ gtk_widget_realize(GTK_WIDGET(window));
+
+ plot->combobox = glade_xml_get_widget (gui, "analysis_combo");
+ plot->window = window;
+ gtk_widget_show_all(window);
+
+ return window;
+}
+
+int
+plot_show (OreganoEngine *engine)
+{
+ GtkEntry *entry;
+ GtkTreeView *list;
+ GList *analysis = NULL;
+ GList *combo_items = NULL;
+ Plot *plot;
+ gchar *s_current = NULL;
+ SimulationData *first = NULL;
+
+ g_return_val_if_fail (engine != NULL, FALSE);
+
+ plot = g_new0(Plot, 1);
+
+ g_object_ref (engine);
+ plot->sim = engine;
+ plot->window = plot_window_create(plot);
+
+ plot->logx = FALSE;
+ plot->logy = FALSE;
+
+ plot->padding_x = PLOT_PADDING_X;
+ plot->padding_y = PLOT_PADDING_Y;
+
+ plot->show_cursor = TRUE;
+
+ next_color = 0;
+
+ /* Get the analysis we have */
+ analysis = oregano_engine_get_results (engine);
+ for (; analysis ; analysis = analysis->next) {
+ SimulationData *sdat = SIM_DATA (analysis->data);
+ gchar *str = oregano_engine_get_analysis_name (sdat);
+ if (sdat->type == OP_POINT) {
+ continue;
+ }
+ if (s_current == NULL) {
+ s_current = str;
+ first = sdat;
+ }
+ combo_items = g_list_append (combo_items, str);
+ }
+
+ gtk_combo_set_popdown_strings (GTK_COMBO (plot->combobox), combo_items);
+
+ entry = GTK_ENTRY (GTK_COMBO (plot->combobox)->entry);
+ g_signal_connect (G_OBJECT (entry), "changed",
+ G_CALLBACK (analysis_selected), plot);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), s_current ? s_current : "?");
+
+ list = GTK_TREE_VIEW (g_object_get_data (G_OBJECT (plot->window), "clist"));
+
+ plot->title = g_strdup_printf (_("Plot - %s"), s_current);
+ plot->xtitle = get_variable_units (first ? first->var_units[0] : "##");
+ plot->ytitle = get_variable_units (first ? first->var_units[1] : "!!");
+
+ g_free (s_current);
+
+ analysis_selected (GTK_EDITABLE (entry), plot);
+
+ return TRUE;
+}
+
+static void
+plot_toggle_cross (GtkWidget *widget, Plot *plot)
+{
+ plot->show_cursor = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
+}
+
+static GPlotFunction*
+create_plot_function_from_data (SimulationFunction *func, SimulationData *current)
+{
+ GPlotFunction *f;
+ double *X;
+ double *Y;
+ double data;
+ guint j, len;
+
+ len = current->data[func->first]->len;
+ X = g_new0 (double, len);
+ Y = g_new0 (double, len);
+
+ for (j = 0; j < len; j++) {
+ Y[j] = g_array_index (current->data[func->first], double, j);
+
+ data = g_array_index (current->data[func->second], double, j);
+ switch (func->type) {
+ case FUNCTION_MINUS:
+ Y[j] -= data;
+ break;
+ case FUNCTION_TRANSFER:
+ if (data < 0.000001f) {
+ if (Y[j] < 0)
+ Y[j] = -G_MAXDOUBLE;
+ else
+ Y[j] = G_MAXDOUBLE;
+ } else {
+ Y[j] /= data;
+ }
+ }
+
+ X[j] = g_array_index (current->data[0], double, j);
+ }
+
+ f = g_plot_lines_new (X, Y, len);
+ g_object_set (G_OBJECT (f), "color", plot_curve_colors[(next_color++)%n_curve_colors], NULL);
+
+ return f;
+}
+
+static void
+add_function (GtkWidget *widget, Plot *plot)
+{
+ GtkTreeView *tree;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GList *lst;
+ gchar *color;
+
+ plot_add_function_show (plot->sim, plot->current);
+
+ tree = GTK_TREE_VIEW(g_object_get_data(G_OBJECT (plot->window), "clist"));
+ model = gtk_tree_view_get_model (tree);
+
+ path = gtk_tree_path_new_from_string ("1");
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_path_free (path);
+
+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+
+ gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(model), &iter, 0, FALSE, 1, _("Functions"), 2, FALSE, 3, "white", -1);
+
+ lst = plot->current->functions;
+ while (lst) {
+ GPlotFunction *f;
+ GtkTreeIter child;
+ int i;
+ gchar *str, *name1, *name2;
+ SimulationFunction *func = (SimulationFunction *)lst->data;
+
+ for (i = 1; i < plot->current->n_variables; i++) {
+ if (func->first == i) {
+ name1 = g_strdup (plot->current->var_names[i]);
+ }
+ if (func->second == i) {
+ name2 = g_strdup (plot->current->var_names[i]);
+ }
+ }
+ switch (func->type) {
+ case FUNCTION_MINUS:
+ str = g_strdup_printf ("%s - %s", name1, name2);
+ break;
+ case FUNCTION_TRANSFER:
+ str = g_strdup_printf ("%s(%s, %s)", _("TRANSFER"), name1, name2);
+ }
+
+ f = create_plot_function_from_data (func, plot->current);
+ g_object_get (G_OBJECT (f), "color", &color, NULL);
+
+ g_plot_add_function (GPLOT (plot->plot), f);
+
+ gtk_tree_store_append(GTK_TREE_STORE(model), &child, &iter);
+ gtk_tree_store_set(GTK_TREE_STORE(model), &child, 0, TRUE, 1, str, 2, TRUE, 3, color, 4, f, -1);
+
+ lst = lst->next;
+ }
+
+
+ gtk_widget_queue_draw (plot->plot);
+}
+
+static void
+plot_export (GtkWidget *widget, Plot *plot)
+{
+ gchar *fn = NULL;
+ gint w, h, format;
+ GladeXML *gui;
+ gchar *msg;
+ GtkWidget *window;
+ GtkRadioButton *export_png, *export_ps;
+ GtkSpinButton *width, *height;
+ GError *gerror=NULL;
+ guint error;
+
+ if (!g_file_test (OREGANO_GLADEDIR "/plot-export.glade", G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/plot-export.glade");
+ oregano_error_with_title (_("Could not create plot export window."), msg);
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/plot-export.glade", NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create plot export window."));
+ return;
+ }
+
+ window = glade_xml_get_widget (gui, "plot_export");
+ export_png = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_png"));
+ export_ps = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_ps"));
+ width = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "export_width"));
+ height = GTK_SPIN_BUTTON (glade_xml_get_widget (gui, "export_height"));
+
+ if ((gtk_dialog_run (GTK_DIALOG (window)) != GTK_RESPONSE_ACCEPT)) {
+ gtk_widget_destroy (window);
+ return;
+ }
+
+ format = 0;
+ /*if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (export_png)))
+ format = GTK_CAIRO_PLOT_VIEW_SAVE_PNG;
+ else
+ format = GTK_CAIRO_PLOT_VIEW_SAVE_PS;*/
+
+ fn = dialog_file_open (_("Save PNG"));
+ if (fn == NULL) {
+ gtk_widget_destroy (window);
+ return;
+ }
+
+ w = gtk_spin_button_get_value_as_int (width);
+ h = gtk_spin_button_get_value_as_int (height);
+
+ gtk_widget_destroy (window);
+}
+
+static void
+zoom_100 (GtkWidget *widget, Plot *plot)
+{
+ g_plot_reset_zoom (GPLOT (plot->plot));
+}
+
+static void
+zoom_region (GtkWidget *widget, Plot *plot)
+{
+ g_plot_set_zoom_mode (GPLOT (plot->plot), GPLOT_ZOOM_REGION);
+}
+
+static void
+zoom_pan (GtkWidget *widget, Plot *plot)
+{
+ g_plot_set_zoom_mode (GPLOT (plot->plot), GPLOT_ZOOM_INOUT);
+}
+
diff --git a/src/plot.h b/src/plot.h
new file mode 100644
index 0000000..033e806
--- /dev/null
+++ b/src/plot.h
@@ -0,0 +1,37 @@
+/*
+ * plot.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 __PLOT_H
+#define __PLOT_H
+
+#include "engine.h"
+
+int plot_show (OreganoEngine *engine);
+
+#endif
diff --git a/src/save-schematic.c b/src/save-schematic.c
new file mode 100644
index 0000000..f380aa5
--- /dev/null
+++ b/src/save-schematic.c
@@ -0,0 +1,561 @@
+/*
+ * save-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 <gnome.h>
+#include <math.h>
+#include "xml-compat.h"
+#include "main.h"
+#include "schematic.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "load-library.h"
+#include "part.h"
+#include "textbox.h"
+#include "part-private.h"
+#include "part-label.h"
+#include "wire.h"
+#include "sim-settings.h"
+#include "node-store.h"
+#include "save-schematic.h"
+#include "xml-helper.h"
+
+typedef struct {
+ xmlDocPtr doc; /* Xml document. */
+ xmlNsPtr ns; /* Main namespace. */
+
+ xmlNodePtr node_symbols; /* For saving of symbols. */
+ xmlNodePtr node_parts; /* For saving of parts. */
+ xmlNodePtr node_props; /* For saving of properties. */
+ xmlNodePtr node_labels; /* For saving of labels. */
+ xmlNodePtr node_wires; /* For saving of wires. */
+ xmlNodePtr node_textboxes;
+} parseXmlContext;
+
+static void
+write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm)
+{
+ xmlNodePtr sim_settings_node, child,analysis,options;
+ gchar *str;
+ SimSettings *s;
+ SimOption *so;
+ GList *list;
+
+ s = schematic_get_sim_settings (sm);
+
+ sim_settings_node = xmlNewChild (cur, ctxt->ns,
+ BAD_CAST"simulation-settings", NULL);
+ if (!sim_settings_node){
+ g_warning ("Failed during save of simulation settings.\n");
+ return;
+ }
+
+ /* Transient analysis */
+ analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "transient",
+ NULL);
+ if (!analysis){
+ g_warning ("Failed during save of transient analysis settings.\n");
+ return;
+ }
+
+ if (sim_settings_get_trans (s))
+ str = "true";
+ else
+ str = "false";
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "enabled", BAD_CAST str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_trans_start (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_trans_stop (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "stop", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_trans_step (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step", BAD_CAST str);
+ g_free (str);
+
+ if (sim_settings_get_trans_step_enable (s))
+ str = "true";
+ else
+ str = "false";
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step-enabled", BAD_CAST str);
+
+ if (sim_settings_get_trans_init_cond(s))
+ str = "true";
+ else
+ str = "false";
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "init-conditions", BAD_CAST str);
+
+ /* AC analysis */
+ analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "ac", NULL);
+ if (!analysis){
+ g_warning ("Failed during save of AC analysis settings.\n");
+ return;
+ }
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "enabled",
+ BAD_CAST (sim_settings_get_ac (s) ? "true" : "false") );
+
+ str = g_strdup_printf ("%d", sim_settings_get_ac_npoints (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "npoints", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_ac_start (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_ac_stop (s));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "stop", BAD_CAST str);
+ g_free (str);
+
+ /* DC analysis */
+ analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "dc-sweep",
+ NULL);
+ if (!analysis){
+ g_warning ("Failed during save of DC sweep analysis settings.\n");
+ return;
+ }
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "enabled",
+ BAD_CAST (sim_settings_get_dc (s) ? "true" : "false") );
+
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "vsrc1",
+ BAD_CAST sim_settings_get_dc_vsrc(s,0));
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_start (s,0));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start1", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_stop (s,0));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "stop1", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_step (s,0));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step1", BAD_CAST str);
+ g_free (str);
+
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "vsrc2",
+ BAD_CAST sim_settings_get_dc_vsrc(s,1));
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_start (s,1));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start2", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_stop (s,1));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "stop2", BAD_CAST str);
+ g_free (str);
+
+ str = g_strdup_printf ("%g", sim_settings_get_dc_step (s,1));
+ child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step2", BAD_CAST str);
+ g_free (str);
+
+
+ /* Save the options */
+ list = sim_settings_get_options (s);
+ if ( list ) {
+ options = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "options",
+ NULL);
+ if (!options){
+ g_warning ("Failed during save of sim engine options.\n");
+ return;
+ }
+
+ while ( list ) {
+ so = list->data;
+ child=xmlNewChild (options, ctxt->ns, BAD_CAST "option", NULL);
+ xmlNewChild (child , ctxt->ns, BAD_CAST "name", BAD_CAST so->name);
+ xmlNewChild (child , ctxt->ns, BAD_CAST "value", BAD_CAST so->value);
+ list = list->next;
+ }
+ }
+}
+
+static void
+write_xml_property (Property *prop, parseXmlContext *ctxt)
+{
+ xmlNodePtr node_property;
+
+ /*
+ * Create a node for the property.
+ */
+ node_property = xmlNewChild (ctxt->node_props, ctxt->ns, BAD_CAST "property",
+ NULL);
+ if (!node_property){
+ g_warning ("Failed during save of property %s.\n", prop->name);
+ return;
+ }
+
+ /*
+ * Store the name.
+ */
+ xmlNewChild (node_property, ctxt->ns, BAD_CAST "name",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->name));
+
+ /*
+ * Store the value.
+ */
+ xmlNewChild (node_property, ctxt->ns, BAD_CAST "value",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->value));
+}
+
+static void
+write_xml_label (PartLabel *label, parseXmlContext *ctxt)
+{
+ xmlNodePtr node_label;
+ gchar *str;
+
+ /*
+ * Create a node for the property.
+ */
+ node_label = xmlNewChild (ctxt->node_labels, ctxt->ns, BAD_CAST "label", NULL);
+ if (!node_label){
+ g_warning ("Failed during save of label %s.\n", label->name);
+ return;
+ }
+
+ /*
+ * Store the name.
+ */
+ xmlNewChild (node_label, ctxt->ns, BAD_CAST "name",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST label->name));
+
+ /*
+ * Store the value.
+ */
+ xmlNewChild (node_label, ctxt->ns, BAD_CAST "text",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST label->text));
+
+ str = g_strdup_printf ("(%g %g)", label->pos.x, label->pos.y);
+ xmlNewChild (node_label, ctxt->ns, BAD_CAST "position", BAD_CAST str);
+ g_free (str);
+
+ /*
+ * Editable or not?
+ */
+/* xmlNewChild (node_property, ctxt->ns, "editable", prop->edit ? "true" : "false");*/
+}
+
+static void
+write_xml_part (Part *part, parseXmlContext *ctxt)
+{
+ PartPriv *priv;
+ xmlNodePtr node_part;
+ xmlNodePtr node_pos;
+ gchar *str;
+ SheetPos pos;
+
+ priv = part->priv;
+
+ /*
+ * Create a node for the part.
+ */
+ node_part = xmlNewChild (ctxt->node_parts, ctxt->ns, BAD_CAST "part", NULL);
+ if (!node_part){
+ g_warning ("Failed during save of part %s.\n", priv->name);
+ return;
+ }
+
+ str = g_strdup_printf ("%d", priv->rotation);
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "rotation",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str));
+ g_free (str);
+
+ if (priv->flip & ID_FLIP_HORIZ)
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "horizontal"));
+
+ if (priv->flip & ID_FLIP_VERT)
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "vertical"));
+
+ /*
+ * Store the name.
+ */
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "name",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->name));
+
+ /*
+ * Store the name of the library the part resides in.
+ */
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "library",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->library->name));
+
+ /*
+ * Which symbol to use.
+ */
+ xmlNewChild (node_part, ctxt->ns, BAD_CAST "symbol",
+ xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->symbol_name));
+
+ /*
+ * Position.
+ */
+ item_data_get_pos (ITEM_DATA (part), &pos);
+ str = g_strdup_printf ("(%g %g)", pos.x, pos.y);
+ node_pos = xmlNewChild (node_part, ctxt->ns, BAD_CAST "position", BAD_CAST str);
+ g_free (str);
+
+ /*
+ * Create a node for the properties.
+ */
+ ctxt->node_props = xmlNewChild (node_part, ctxt->ns, BAD_CAST "properties",
+ NULL);
+ if (!ctxt->node_props){
+ g_warning ("Failed during save of part %s.\n", priv->name);
+ return;
+ }
+ else{
+ g_slist_foreach (priv->properties, (GFunc) write_xml_property,
+ ctxt);
+ }
+
+ /*
+ * Create a node for the labels.
+ */
+ ctxt->node_labels = xmlNewChild (node_part, ctxt->ns, BAD_CAST "labels", NULL);
+ if (!ctxt->node_labels){
+ g_warning ("Failed during save of part %s.\n", priv->name);
+ return;
+ }
+ else{
+ g_slist_foreach (priv->labels, (GFunc) write_xml_label, ctxt);
+ }
+}
+
+static void
+write_xml_wire (Wire *wire, parseXmlContext *ctxt)
+{
+ xmlNodePtr node_wire;
+ gchar *str;
+ SheetPos start_pos, end_pos;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ /*
+ * Create a node for the wire.
+ */
+ node_wire = xmlNewChild (ctxt->node_wires, ctxt->ns, BAD_CAST "wire", NULL);
+ if (!node_wire){
+ g_warning ("Failed during save of wire.\n");
+ return;
+ }
+
+ wire_get_start_pos (wire, &start_pos);
+ wire_get_end_pos (wire, &end_pos);
+
+ str = g_strdup_printf ("(%g %g)(%g %g)",
+ start_pos.x, start_pos.y, end_pos.x, end_pos.y);
+ xmlNewChild (node_wire, ctxt->ns, BAD_CAST "points", BAD_CAST str);
+ g_free (str);
+}
+
+static void
+write_xml_textbox (Textbox *textbox, parseXmlContext *ctxt)
+{
+ xmlNodePtr node_textbox;
+ gchar *str;
+ SheetPos pos;
+
+ g_return_if_fail (textbox != NULL);
+
+ /*
+ * FIXME: just a quick hack to get this working.
+ */
+ if (!IS_TEXTBOX (textbox))
+ return;
+
+ g_return_if_fail (IS_TEXTBOX (textbox));
+
+ /*
+ * Create a node for the textbox.
+ */
+ node_textbox = xmlNewChild (ctxt->node_textboxes, ctxt->ns,
+ BAD_CAST "textbox", NULL);
+ if (!node_textbox){
+ g_warning ("Failed during save of text box.\n");
+ return;
+ }
+
+ item_data_get_pos (ITEM_DATA (textbox), &pos);
+
+ str = g_strdup_printf ("(%g %g)", pos.x, pos.y);
+ xmlNewChild (node_textbox, ctxt->ns, BAD_CAST "position", BAD_CAST str);
+ g_free (str);
+
+ str = textbox_get_text (textbox);
+ xmlNewChild (node_textbox, ctxt->ns, BAD_CAST "text", BAD_CAST str);
+}
+
+/*
+ * Create an XML subtree of doc equivalent to the given Schematic.
+ */
+static xmlNodePtr
+write_xml_schematic (parseXmlContext *ctxt, Schematic *sm, GError **error)
+{
+ xmlNodePtr cur;
+ xmlNodePtr grid;
+ xmlNsPtr ogo;
+ gchar *str;
+ /*
+ * Unused variables
+ double zoom;
+ */
+
+ cur = xmlNewDocNode (ctxt->doc, ctxt->ns, BAD_CAST "schematic", NULL);
+ if (cur == NULL) {
+ printf("%s:%d NULL que no debe ser NULL!\n", __FILE__,
+ __LINE__);
+ return NULL;
+ }
+
+ if (ctxt->ns == NULL) {
+ ogo = xmlNewNs (cur,
+ BAD_CAST "http://www.dtek.chalmers.se/~d4hult/oregano/v1",
+ BAD_CAST "ogo");
+ xmlSetNs (cur,ogo);
+ ctxt->ns = ogo;
+ }
+
+ /*
+ * General information about the Schematic.
+ */
+ str = g_strdup_printf ("%s", schematic_get_author (sm));
+ xmlNewChild (cur, ctxt->ns, BAD_CAST "author", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str));
+ g_free (str);
+
+ str = g_strdup_printf ("%s", schematic_get_title (sm));
+ xmlNewChild (cur, ctxt->ns, BAD_CAST "title", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str));
+ g_free (str);
+
+ str = g_strdup_printf ("%s", schematic_get_comments (sm));
+ xmlNewChild (cur, ctxt->ns, BAD_CAST "comments", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str));
+ g_free (str);
+
+ /*
+ * Zoom.
+ */
+// sheet_get_zoom (sm->sheet, &zoom);
+// str = g_strdup_printf ("%g", zoom);
+// xmlNewChild (cur, ctxt->ns, "zoom", str);
+// g_free (str);
+
+ /*
+ * Grid.
+ */
+ grid = xmlNewChild (cur, ctxt->ns, BAD_CAST "grid", NULL);
+ xmlNewChild (grid, ctxt->ns, BAD_CAST "visible", BAD_CAST "true");
+ xmlNewChild (grid, ctxt->ns, BAD_CAST "snap", BAD_CAST "true");
+ /*
+ * Simulation settings.
+ */
+ write_xml_sim_settings (cur, ctxt, sm);
+
+ /*
+ * Parts.
+ */
+ ctxt->node_parts = xmlNewChild (cur, ctxt->ns, BAD_CAST "parts", NULL);
+ schematic_parts_foreach (sm, (gpointer) write_xml_part, ctxt);
+
+ /*
+ * Wires.
+ */
+ ctxt->node_wires = xmlNewChild (cur, ctxt->ns, BAD_CAST "wires", NULL);
+ schematic_wires_foreach (sm, (gpointer) write_xml_wire, ctxt);
+
+ /*
+ * Text boxes.
+ */
+ ctxt->node_textboxes = xmlNewChild (cur, ctxt->ns, BAD_CAST "textboxes", NULL);
+ schematic_items_foreach (sm, (gpointer) write_xml_textbox, ctxt);
+
+ return cur;
+}
+
+/*
+ * schematic_write_xml
+ *
+ * Save a Sheet to an XML file.
+ */
+
+gint
+schematic_write_xml (Schematic *sm, GError **error)
+{
+ int ret = -1;
+ xmlDocPtr xml;
+ parseXmlContext ctxt;
+ GError *internal_error = NULL;
+
+ g_return_val_if_fail (sm != NULL, FALSE);
+
+ /*
+ * Create the tree.
+ */
+ xml = xmlNewDoc (BAD_CAST "1.0");
+ if (xml == NULL){
+ return FALSE;
+ }
+
+ ctxt.ns = NULL;
+ ctxt.doc = xml;
+
+ xmlDocSetRootElement(xml, write_xml_schematic(&ctxt, sm, &internal_error));
+
+ if (internal_error) {
+ g_propagate_error (error, internal_error);
+ return FALSE;
+ }
+
+ /*
+ * Dump the tree.
+ */
+ xmlSetDocCompressMode (xml, oregano.compress_files ? 9 : 0);
+
+ /* FIXME: check for != NULL. */
+ {
+ char *s =schematic_get_filename (sm);
+ if (s != NULL) {
+ xmlIndentTreeOutput;
+ ret = xmlSaveFormatFile (s, xml, 1);
+ } else {
+ g_warning("Schematic has no filename!!\n");
+ }
+ }
+
+ if (xml != NULL) {
+ xmlFreeDoc (xml);
+ } else {
+ g_warning("XML object is NULL\n");
+ }
+
+ if (ret < 0)
+ return FALSE;
+ return TRUE;
+}
+
+
diff --git a/src/save-schematic.h b/src/save-schematic.h
new file mode 100644
index 0000000..f6e36c1
--- /dev/null
+++ b/src/save-schematic.h
@@ -0,0 +1,38 @@
+/*
+ * save-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 _SAVE_SCHEMATIC_H
+#define _SAVE_SCHEMATIC_H
+
+#include <glib.h>
+#include "schematic.h"
+
+gint schematic_write_xml (Schematic *sm, GError **error);
+
+#endif
diff --git a/src/schematic-view-menu.h b/src/schematic-view-menu.h
new file mode 100644
index 0000000..af1c4bf
--- /dev/null
+++ b/src/schematic-view-menu.h
@@ -0,0 +1,183 @@
+/*
+ * schematic-view.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.
+ */
+
+#ifndef _SCHEMATIC_VIEW_MENU_
+#define _SCHEMATIC_VIEW_MENU_
+static GtkActionEntry entries[] = {
+ /* Name, ICON, Text, CTRL, DESC, CALLBACK */
+ {"MenuFile", NULL, N_("_File")},
+ {"MenuEdit", NULL, N_("_Edit")},
+ {"MenuTools", NULL, N_("_Tools")},
+ {"MenuView", NULL, N_("_View")},
+ {"MenuHelp", NULL, N_("_Help")},
+ {"MenuZoom", NULL, N_("_Zoom")},
+ {"New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("Create a new schematic"), G_CALLBACK (new_cmd)},
+ {"Open", GTK_STOCK_OPEN, N_("_Open"), "<control>O", N_("Open a schematic"), G_CALLBACK (open_cmd)},
+ {"DisplayRecentFiles", NULL, N_("_Recent Files"), NULL, NULL, NULL}, //G_CALLBACK (display_recent_files)},
+ {"Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save a schematic"), G_CALLBACK (save_cmd)},
+ {"SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), "<control><shift>S", N_("Save a schematic with other name"), G_CALLBACK (save_as_cmd)},
+ {"PageProperties", NULL, N_("Page Properties"), NULL, N_("Set print properties"), G_CALLBACK (page_properties_cmd)},
+ {"Print", GTK_STOCK_PRINT, N_("_Print"), NULL, N_("Print schematic"), G_CALLBACK (print_cmd)},
+ {"PrintPreview", GTK_STOCK_PRINT_PREVIEW, N_("Print Preview"), NULL, N_("Preview the schematic before printing"), G_CALLBACK (print_preview_cmd)},
+ {"SchematicProperties", NULL, N_("Schematic Pr_operties..."), NULL, N_("Modify the schematic's properties"), G_CALLBACK (properties_cmd)},
+ {"Export", NULL, N_("_Export..."), NULL, N_("Export schematic"), G_CALLBACK (export_cmd)},
+ {"Close", GTK_STOCK_CLOSE, N_("_Close"), "<control>W", N_("Close the current schematic"), G_CALLBACK (close_cmd)},
+ {"Quit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", N_("Close all schematics"), G_CALLBACK (quit_cmd)},
+ {"Cut", GTK_STOCK_CUT, N_("C_ut"), "<control>X", NULL, G_CALLBACK (cut_cmd)},
+ {"Copy", GTK_STOCK_COPY, N_("_Copy"), "<control>C", NULL, G_CALLBACK (copy_cmd)},
+ {"Paste", GTK_STOCK_PASTE, N_("_Paste"), "<control>V", NULL, G_CALLBACK (paste_cmd)},
+ {"Delete", GTK_STOCK_DELETE, N_("_Delete"), "<control>D", N_("Delete the selection"), G_CALLBACK (delete_cmd)},
+ {"Rotate", STOCK_PIXMAP_ROTATE, N_("_Rotate"), "<control>R", N_("Rotate the selection clockwise"), G_CALLBACK (rotate_cmd)},
+ {"FlipH", NULL, N_("Flip _horizontally"), "<control>F", N_("Flip the selection horizontally"), G_CALLBACK (flip_horizontal_cmd)},
+ {"FlipV", NULL, N_("Flip _vertically"), "<control><shift>F", N_("Flip the selection vertically"), G_CALLBACK (flip_vertical_cmd)},
+ {"SelectAll", NULL, N_("Select _all"), "<control>A", N_("Select all objects on the sheet"), G_CALLBACK (select_all_cmd)},
+ {"SelectNone", NULL, N_("Select _none"), "<control><shift>A", N_("Deselect the selected objects"), G_CALLBACK (deselect_all_cmd)},
+ {"ObjectProperties", GTK_STOCK_PROPERTIES, N_("_Object Properties..."), NULL, N_("Modify the object's properties"), G_CALLBACK (object_properties_cmd)},
+ {"SimulationSettings", GTK_STOCK_PROPERTIES, N_("Simulation S_ettings..."), NULL, N_("Edit the simulation settings"), G_CALLBACK (sim_settings_show)},
+ {"Settings", NULL, N_("_Preferences"), NULL, N_("Edit Oregano settings"), G_CALLBACK (settings_show)},
+ {"Simulate", GTK_STOCK_EXECUTE, N_("_Simulate"), "F5", N_("Run a simulation"), G_CALLBACK (simulate_cmd)},
+ {"Netlist", NULL, N_("_Generate netlist"), NULL, N_("Generate a netlist"), G_CALLBACK (netlist_cmd)},
+ {"Log", NULL, N_("_Log"), NULL, N_("View the latest simulation log"), G_CALLBACK (log_cmd)},
+ {"NetlistView", NULL, N_("N_etlist"), NULL, N_("View the circuit netlist"), G_CALLBACK (netlist_view_cmd)},
+ {"About", GTK_STOCK_HELP, N_("_About"), NULL, N_("About Oregano"), G_CALLBACK (about_cmd)},
+ {"ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), NULL, N_("Zoom in"), G_CALLBACK (zoom_in_cmd)},
+ {"ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), NULL, N_("Zoom out"), G_CALLBACK (zoom_out_cmd)},
+};
+
+static GtkToggleActionEntry toggle_entries[] = {
+ {"Labels", NULL, N_("_Node labels"), NULL, N_("Show or hide node labels"), G_CALLBACK (show_label_cmd), FALSE},
+ {"Parts", STOCK_PIXMAP_PART_BROWSER, N_("_Parts"), NULL, N_("Show or hide the part browser"), G_CALLBACK (part_browser_cmd), TRUE},
+ {"Grid", STOCK_PIXMAP_GRID, N_("_Grid"), NULL, N_("Show or hide the grid"), G_CALLBACK (grid_toggle_snap_cmd), TRUE},
+};
+
+static GtkRadioActionEntry zoom_entries[] = {
+ {"Zoom50", NULL, "50%", NULL, N_("Set the zoom factor to 50%"), 0},
+ {"Zoom75", NULL, "75%", NULL, N_("Set the zoom factor to 75%"), 1},
+ {"Zoom100", NULL, "100%", "1", N_("Set the zoom factor to 100%"), 2},
+ {"Zoom125", NULL, "125%", NULL, N_("Set the zoom factor to 125%"), 3},
+ {"Zoom150", NULL, "150%", NULL, N_("Set the zoom factor to 150%"), 4},
+};
+
+static GtkRadioActionEntry tools_entries[] = {
+ {"Arrow", STOCK_PIXMAP_ARROW, N_("Arrow"), NULL, N_("Select, move and modify objects"), 0},
+ {"Text", GTK_STOCK_BOLD, N_("Text"), NULL, N_("Put text on the schematic"), 1},
+ {"Wire", STOCK_PIXMAP_WIRE, N_("Wire"), "1", N_("Draw wires %"), 2},
+ {"VClamp", STOCK_PIXMAP_V_CLAMP, N_("Clamp"), NULL, N_("Add voltage clamp"), 3},
+};
+
+static const char *ui_description =
+"<ui>"
+" <menubar name='MainMenu'>"
+" <menu action='MenuFile'>"
+" <menuitem action='New'/>"
+" <menuitem action='Open'/>"
+" <menuitem action='DisplayRecentFiles'/>"
+" <menuitem action='Save'/>"
+" <menuitem action='SaveAs'/>"
+" <separator/>"
+" <menuitem action='PageProperties'/>"
+" <menuitem action='Print'/>"
+" <menuitem action='PrintPreview'/>"
+" <separator/>"
+" <menuitem action='SchematicProperties'/>"
+" <menuitem action='Export'/>"
+" <separator/>"
+" <menuitem action='Close'/>"
+" <menuitem action='Quit'/>"
+" </menu>"
+" <menu action='MenuEdit'>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <separator/>"
+" <menuitem action='Delete'/>"
+" <menuitem action='Rotate'/>"
+" <menuitem action='FlipH'/>"
+" <menuitem action='FlipV'/>"
+" <separator/>"
+" <menuitem action='SelectAll'/>"
+" <menuitem action='SelectNone'/>"
+" <separator/>"
+" <menuitem action='ObjectProperties'/>"
+" <menuitem action='SimulationSettings'/>"
+" <separator/>"
+" <menuitem action='Settings'/>"
+" </menu>"
+" <menu action='MenuTools'>"
+" <menuitem action='Simulate'/>"
+" <separator/>"
+" <menuitem action='Netlist'/>"
+" </menu>"
+" <menu action='MenuView'>"
+" <menu action='MenuZoom'>"
+" <menuitem action='Zoom50'/>"
+" <menuitem action='Zoom75'/>"
+" <menuitem action='Zoom100'/>"
+" <menuitem action='Zoom125'/>"
+" <menuitem action='Zoom150'/>"
+" </menu>"
+" <separator/>"
+" <menuitem action='Log'/>"
+" <menuitem action='Labels'/>"
+" <menuitem action='NetlistView'/>"
+" </menu>"
+" <menu action='MenuHelp'>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+" <toolbar name='StandartToolbar'>"
+" <toolitem action='New'/>"
+" <toolitem action='Open'/>"
+" <toolitem action='Save'/>"
+" <separator/>"
+" <toolitem action='Cut'/>"
+" <toolitem action='Copy'/>"
+" <toolitem action='Paste'/>"
+" <separator/>"
+" <toolitem action='Arrow'/>"
+" <toolitem action='Text'/>"
+" <toolitem action='Wire'/>"
+" <toolitem action='VClamp'/>"
+" <separator/>"
+" <toolitem action='Simulate'/>"
+" <toolitem action='SimulationSettings'/>"
+" <separator/>"
+" <toolitem action='ZoomIn'/>"
+" <toolitem action='ZoomOut'/>"
+" <separator/>"
+" <toolitem action='Grid'/>"
+" <toolitem action='Parts'/>"
+" </toolbar>"
+" <popup name='MainPopup'>"
+" <menuitem action='Paste'/>"
+" </popup>"
+"</ui>";
+
+#endif
diff --git a/src/schematic-view.c b/src/schematic-view.c
new file mode 100644
index 0000000..e4fc386
--- /dev/null
+++ b/src/schematic-view.c
@@ -0,0 +1,2698 @@
+/*
+ * schematic-view.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 <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+#include <libgnomeui/gnome-app.h>
+#include <libbonobo-2.0/libbonobo.h>
+#include <sys/time.h>
+#include <cairo/cairo-features.h>
+
+#include "sheet-private.h"
+#include "schematic-view.h"
+#include "schematic.h"
+#include "part-browser.h"
+#include "stock.h"
+#include "main.h"
+#include "load-schematic.h"
+#include "load-common.h"
+#include "netlist.h"
+#include "dialogs.h"
+#include "cursors.h"
+#include "save-schematic.h"
+#include "file.h"
+#include "settings.h"
+#include "sim-settings.h"
+#include "simulation.h"
+#include "smallicon.h"
+#include "plot.h"
+#include "sheet-item-factory.h"
+#include "part-item.h"
+#include "errors.h"
+#include "node-item.h"
+#include "engine.h"
+
+/* remove: */
+#include "create-wire.h"
+
+/*
+ * Pixmaps.
+ */
+#include "pixmaps/plot.xpm"
+#include "pixmaps/log.xpm"
+#include "pixmaps/menu_zoom.xpm"
+
+/*
+ * Mini-icon for IceWM, KWM, tasklist etc.
+ */
+#include "pixmaps/mini_icon.xpm"
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+typedef enum {
+ SCHEMATIC_TOOL_ARROW,
+ SCHEMATIC_TOOL_PART,
+ SCHEMATIC_TOOL_WIRE,
+ SCHEMATIC_TOOL_TEXT
+} SchematicTool;
+
+typedef enum {
+ RUBBER_NO = 0,
+ RUBBER_YES,
+ RUBBER_START
+} RubberState;
+
+typedef struct {
+ RubberState state;
+ int timeout_id;
+ int click_start_state;
+
+ GnomeCanvasItem *rectangle;
+ double start_x, start_y;
+} RubberbandInfo;
+
+struct _SchematicViewPriv {
+ Schematic *schematic;
+
+ Sheet *sheet;
+ GList *items;
+
+ GHashTable *dots;
+
+ RubberbandInfo *rubberband;
+ GtkPageSetup *page_setup;
+ GtkPrintSettings *print_settings;
+
+ GList *preserve_selection_items;
+
+ gboolean empty;
+
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+
+ GtkWidget *floating_part_browser;
+
+ guint grid : 1;
+ SchematicTool tool;
+ int cursor;
+
+ CreateWireContext *create_wire_context; /* FIXME: get rid of this somehow. */
+
+ gpointer browser;
+
+ /*
+ * FIXME: Move these to a special struct instead.
+ */
+ GtkWidget *log_window;
+ GtkTextView *log_text;
+ GladeXML *log_gui;
+
+ gboolean show_voltmeters;
+ GList *voltmeter_items; /* List of GnomeCanvasItem. */
+ GHashTable *voltmeter_nodes;
+};
+
+/*
+ * Class functions and members.
+ */
+static void schematic_view_init(SchematicView *sv);
+static void schematic_view_class_init(SchematicViewClass *klass);
+static void schematic_view_dispose(GObject *object);
+static void schematic_view_finalize(GObject *object);
+static GObjectClass *parent_class = NULL;
+static guint schematic_view_signals[LAST_SIGNAL] = { 0 };
+static void dot_removed_callback (Schematic *schematic, SheetPos *pos, SchematicView *sv);
+
+static GList *schematic_view_list = NULL;
+GdkBitmap *stipple;
+char stipple_pattern [] = { 0x02, 0x01 };
+extern GnomeCanvasClass *sheet_parent_class; /* FIXME: Remove later. */
+
+/*
+ * Signal callbacks.
+ */
+static void title_changed_callback (Schematic *schematic, char *new_title, SchematicView *sv);
+static void set_focus (GtkWindow *window, GtkWidget *focus, SchematicView *sv);
+static int delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv);
+static void item_destroy_callback (SheetItem *item, SchematicView *sv);
+static int sheet_event_callback (GtkWidget *widget, GdkEvent *event, SchematicView *sv);
+static void data_received (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *selection_data, guint info,
+ guint32 time, SchematicView *sv);
+
+static void item_data_added_callback (Schematic *schematic, ItemData *data, SchematicView *sv);
+static void item_selection_changed_callback (SheetItem *item, gboolean selected, SchematicView *sv);
+static void reset_tool_cb (Sheet *sheet, SchematicView *sv);
+
+/*
+ * Misc.
+ */
+static int can_close (SchematicView *sv);
+static void setup_dnd (SchematicView *sv);
+static int rubberband_timeout_cb (SchematicView *sv);
+static void stop_rubberband (SchematicView *sv, GdkEventButton *event);
+static void setup_rubberband (SchematicView *sv, GdkEventButton *event);
+static void set_tool (SchematicView *sv, SchematicTool tool);
+
+static int dot_equal (gconstpointer a, gconstpointer b);
+static guint dot_hash (gconstpointer key);
+
+static void schematic_view_show_voltmeters (SchematicView *sv, gboolean show);
+
+static void
+new_cmd (GtkWidget *widget, Schematic *sv)
+{
+ Schematic *new_sm;
+ SchematicView *new_sv;
+
+ new_sm = schematic_new ();
+ new_sv = schematic_view_new (new_sm);
+
+ gtk_widget_show_all (new_sv->toplevel);
+}
+
+static void
+properties_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *s;
+ GladeXML *xml;
+ GtkWidget *win;
+ GtkEntry *title, *author;
+ GtkTextView *comments;
+ GtkTextBuffer *buffer;
+ gchar *s_title, *s_author, *s_comments;
+ gint btn;
+
+ s = schematic_view_get_schematic (sv);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/properties.glade", G_FILE_TEST_EXISTS)) {
+ gchar *msg;
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/properties.glade");
+
+ oregano_error_with_title (_("Could not create properties dialog"), msg);
+ g_free (msg);
+ return;
+ }
+
+ xml = glade_xml_new (
+ OREGANO_GLADEDIR "/properties.glade",
+ "properties", NULL
+ );
+
+ win = glade_xml_get_widget (xml, "properties");
+ title = GTK_ENTRY (glade_xml_get_widget (xml, "title"));
+ author = GTK_ENTRY (glade_xml_get_widget (xml, "author"));
+ comments = GTK_TEXT_VIEW (glade_xml_get_widget (xml, "comments"));
+ buffer = gtk_text_view_get_buffer (comments);
+
+ s_title = schematic_get_title (s);
+ s_author = schematic_get_author (s);
+ s_comments = schematic_get_comments (s);
+
+ if (s_title)
+ gtk_entry_set_text (title, s_title);
+ if (s_author)
+ gtk_entry_set_text (author, s_author);
+ if (s_comments)
+ gtk_text_buffer_set_text (buffer, s_comments, strlen(s_comments));
+
+ btn = gtk_dialog_run (GTK_DIALOG (win));
+
+ if (btn == GTK_RESPONSE_OK) {
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ s_title = (gchar *) gtk_entry_get_text (title);
+ s_author = (gchar *) gtk_entry_get_text (author);
+ s_comments = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+ schematic_set_title (s, s_title);
+ schematic_set_author (s, s_author);
+ schematic_set_comments (s, s_comments);
+
+ g_free (s_comments);
+ }
+
+ gtk_widget_destroy (win);
+}
+
+static void
+find_file (GtkButton *btn, GtkEntry *text)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new (
+ _("Export to..."),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ gtk_entry_set_text (text, filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+export_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *s;
+ GladeXML *xml;
+ GtkWidget *win;
+ GtkWidget *w;
+ GtkEntry *file = NULL;
+ GtkComboBox *combo;
+ gint btn = GTK_RESPONSE_NONE;
+ gint formats[5], fc;
+ GtkSpinButton *spinw, *spinh;
+
+ s = schematic_view_get_schematic (sv);
+
+ if (!g_file_test (OREGANO_GLADEDIR "/export.glade", G_FILE_TEST_EXISTS)) {
+ gchar *msg;
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/export.glade");
+
+ oregano_error_with_title (_("Could not create properties dialog"), msg);
+ g_free (msg);
+ return;
+ }
+
+ xml = glade_xml_new (OREGANO_GLADEDIR "/export.glade", "export", NULL);
+
+ win = glade_xml_get_widget (xml, "export");
+ combo = GTK_COMBO_BOX (glade_xml_get_widget (xml, "format"));
+
+ gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (combo)));
+ fc = 0;
+#ifdef CAIRO_HAS_SVG_SURFACE
+ gtk_combo_box_append_text (combo, "Scalar Vector Graphics (SVG)");
+ formats[fc++] = 0;
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+ gtk_combo_box_append_text (combo, "Portable Document Format (PDF)");
+ formats[fc++] = 1;
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+ gtk_combo_box_append_text (combo, "Postscript (PS)");
+ formats[fc++] = 2;
+#endif
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+ gtk_combo_box_append_text (combo, "Portable Network Graphics (PNG)");
+ formats[fc++] = 3;
+#endif
+
+ file = GTK_ENTRY (glade_xml_get_widget (xml, "file"));
+
+ w = glade_xml_get_widget (xml, "find");
+ g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (find_file), file);
+
+ gtk_combo_box_set_active (combo, 0);
+
+ btn = gtk_dialog_run (GTK_DIALOG (win));
+
+ if (btn == GTK_RESPONSE_OK) {
+ int bg = 0;
+ GtkSpinButton *spinw, *spinh;
+ int color_scheme = 0;
+ GtkWidget *w;
+ int i = gtk_combo_box_get_active (combo);
+
+ w = glade_xml_get_widget (xml, "bgwhite");
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
+ bg = 1;
+ w = glade_xml_get_widget (xml, "bgblack");
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
+ bg = 2;
+
+ w = glade_xml_get_widget (xml, "color");
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
+ color_scheme = 1;
+
+ spinw = GTK_SPIN_BUTTON (glade_xml_get_widget (xml, "export_width"));
+ spinh = GTK_SPIN_BUTTON (glade_xml_get_widget (xml, "export_height"));
+
+ schematic_export (s,
+ gtk_entry_get_text (file),
+ gtk_spin_button_get_value_as_int (spinw),
+ gtk_spin_button_get_value_as_int (spinh),
+ bg,
+ color_scheme,
+ formats[i]);
+ }
+
+ gtk_widget_destroy (win);
+}
+
+static void
+page_properties_cmd (GtkWidget *widget, SchematicView *sv)
+{
+sv->priv->page_setup = gtk_print_run_page_setup_dialog (NULL,
+ sv->priv->page_setup,
+ sv->priv->print_settings);
+}
+
+static void
+open_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *new_sm;
+ SchematicView *new_sv;
+ char *fname, *uri;
+ GList *list;
+ GError *error = NULL;
+
+ fname = dialog_open_file (sv);
+ if (fname == NULL)
+ return;
+
+ /*
+ * Repaint the other schematic windows before loading the new file.
+ */
+ new_sv = NULL;
+ for (list = schematic_view_list; list; list = list->next) {
+ if (SCHEMATIC_VIEW (list->data)->priv->empty) {
+ new_sv = SCHEMATIC_VIEW (list->data);
+ }
+ gtk_widget_queue_draw( GTK_WIDGET(SCHEMATIC_VIEW(list->data)->toplevel));
+ }
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ new_sm = schematic_read(fname, &error);
+
+ if (error != NULL) {
+ oregano_error_with_title (_("Could not load file"), error->message);
+ g_error_free (error);
+ }
+
+ if (new_sm) {
+ GtkRecentManager *manager;
+ const gchar *mime;
+ gchar *uri;
+ GtkRecentInfo *item;
+
+ manager = gtk_recent_manager_get_default ();
+ uri = g_strdup_printf ("file://%s", fname);
+ if (!gtk_recent_manager_has_item (manager, uri) &&
+ (!(uri==NULL))) gtk_recent_manager_add_item (manager, uri);
+ if (!new_sv)
+ new_sv = schematic_view_new (new_sm);
+ else
+ schematic_view_load (new_sv, new_sm);
+
+ gtk_widget_show_all (new_sv->toplevel);
+ schematic_set_filename (new_sm, fname);
+ schematic_set_title (new_sm, g_path_get_basename(fname));
+ }
+
+ g_free (fname);
+ g_free (uri);
+}
+
+
+static void
+oregano_recent_open (GtkRecentChooser *chooser,
+ SchematicView *sv)
+{
+ gchar *uri;
+ const gchar *mime;
+ GtkRecentInfo *item;
+ GtkRecentManager *manager;
+ Schematic *new_sm;
+ SchematicView *new_sv;
+ GError *error = NULL;
+
+ uri = gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (chooser));
+ if (!uri) return;
+
+ manager = gtk_recent_manager_get_default ();
+ item = gtk_recent_manager_lookup_item (manager, uri, NULL);
+ if (!item) return;
+
+ mime = gtk_recent_info_get_mime_type (item);
+ if (!mime) {
+ g_warning ("Unrecognized mime type");
+ return;
+ }
+
+ if (!strcmp (mime, "application/x-oregano")) {
+ new_sm = schematic_read(uri, &error);
+ if (error != NULL) {
+ oregano_error_with_title (_("Could not load file"), error->message);
+ g_error_free (error);
+ }
+ if (new_sm) {
+ if (!new_sv)
+ new_sv = schematic_view_new (new_sm);
+ else
+ schematic_view_load (new_sv, new_sm);
+
+ gtk_widget_show_all (new_sv->toplevel);
+ schematic_set_filename (new_sm, uri);
+ schematic_set_title (new_sm, g_path_get_basename(uri));
+ }
+ }
+ else
+ g_warning (_("Unknown type of file can't open"));
+
+ g_free(uri);
+ gtk_recent_info_unref (item);
+}
+
+
+static GtkWidget *
+create_recent_chooser_menu (GtkRecentManager *manager)
+{
+ GtkWidget *menu;
+ GtkRecentFilter *filter;
+ GtkWidget *menuitem;
+
+ menu = gtk_recent_chooser_menu_new_for_manager (manager);
+
+ gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), 10);
+
+ gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (menu), TRUE);
+ gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu),
+ GTK_RECENT_SORT_MRU);
+
+ filter = gtk_recent_filter_new ();
+ gtk_recent_filter_add_mime_type (filter, "application/x-oregano");
+ gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
+ gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (menu), filter);
+ gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (menu), TRUE);
+ g_signal_connect (menu, "item-activated",
+ G_CALLBACK (oregano_recent_open),
+ NULL);
+
+ gtk_widget_show_all (menu);
+
+ return menu;
+}
+
+static void
+display_recent_files (GtkWidget *menu, SchematicView *sv)
+{
+ SchematicViewPriv *priv = sv->priv;
+ GtkWidget *menuitem;
+ GtkWidget *recentmenu;
+ GtkRecentManager *manager = NULL;
+
+ manager = gtk_recent_manager_get_default ();
+ menuitem = gtk_ui_manager_get_widget (priv->ui_manager, "/MainMenu/MenuFile/DisplayRecentFiles");
+ recentmenu = create_recent_chooser_menu (manager);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), recentmenu);
+ gtk_widget_show (menuitem);
+}
+
+static void
+save_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *sm;
+ char *filename;
+ GError *error = NULL;
+ sm = sv->priv->schematic;
+ filename = schematic_get_filename (sm);
+
+ if (filename == NULL || !strcmp (filename, _("Untitled.oregano"))){
+ dialog_save_as (sv);
+ return;
+ } else {
+ if (!schematic_save_file (sm, &error)){
+ oregano_error_with_title (_("Could not save schematic file"), error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+save_as_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ dialog_save_as (sv);
+}
+
+static void
+close_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (can_close (sv)) {
+ g_object_unref(G_OBJECT(sv));
+ }
+}
+
+static void
+select_all_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_view_select_all (sv, TRUE);
+}
+
+static void
+deselect_all_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_view_select_all (sv, FALSE);
+}
+
+static void
+delete_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_view_delete_selection (sv);
+}
+
+static void
+rotate_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ schematic_view_rotate_selection (sv);
+ else if (sv->priv->sheet->state == SHEET_STATE_FLOAT ||
+ sv->priv->sheet->state == SHEET_STATE_FLOAT_START)
+ schematic_view_rotate_ghosts (sv);
+}
+
+static void
+flip_horizontal_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ schematic_view_flip_selection (sv, TRUE);
+ else if (sv->priv->sheet->state == SHEET_STATE_FLOAT ||
+ sv->priv->sheet->state == SHEET_STATE_FLOAT_START)
+ schematic_view_flip_ghosts (sv, TRUE);
+}
+
+static void
+flip_vertical_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ schematic_view_flip_selection (sv, FALSE);
+ else if (sv->priv->sheet->state == SHEET_STATE_FLOAT ||
+ sv->priv->sheet->state == SHEET_STATE_FLOAT_START)
+ schematic_view_flip_ghosts (sv, FALSE);
+}
+
+static void
+object_properties_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Sheet *sheet;
+
+ sheet = sv->priv->sheet;
+
+ if (g_list_length (sheet->priv->selected_objects) == 1)
+ sheet_item_edit_properties (sheet->priv->selected_objects->data);
+}
+
+static void
+copy_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ SheetItem *item;
+ GList *list;
+
+ if (sv->priv->sheet->state != SHEET_STATE_NONE)
+ return;
+
+ schematic_view_clear_ghosts (sv);
+ clipboard_empty ();
+
+ list = schematic_view_get_selection (sv);
+ for (; list; list = list->next) {
+ item = list->data;
+ clipboard_add_object (G_OBJECT (item));
+ }
+
+ if (clipboard_is_empty ())
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), FALSE);
+ else
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), TRUE);
+}
+
+void
+schematic_view_copy_selection (SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ copy_cmd (NULL, sv);
+}
+
+static void
+cut_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (sv->priv->sheet->state != SHEET_STATE_NONE)
+ return;
+
+ copy_cmd (NULL, sv);
+
+ schematic_view_delete_selection (sv);
+
+ if (clipboard_is_empty ())
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), FALSE);
+ else
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), TRUE);
+}
+
+void
+schematic_view_cut_selection (SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ cut_cmd (NULL, sv);
+}
+
+/**
+* Ugly hack (gpointer...) to avoid a .h-file dependancy... :/
+*/
+static void
+paste_objects (gpointer data, gpointer user_data)
+{
+ SchematicView *sv;
+
+ sv = SCHEMATIC_VIEW (user_data);
+ sheet_item_paste (sv, data);
+}
+
+static void
+paste_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ if (sv->priv->sheet->state != SHEET_STATE_NONE)
+ return;
+
+ if (sv->priv->sheet->priv->floating_objects != NULL) {
+ schematic_view_clear_ghosts (sv);
+ }
+
+ schematic_view_select_all (sv, FALSE);
+ clipboard_foreach ((ClipBoardFunction) paste_objects, sv);
+
+ if (sv->priv->sheet->priv->floating_objects != NULL) {
+ part_item_signal_connect_floating_group (sv->priv->sheet, sv);
+ }
+}
+
+void
+schematic_view_paste (SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ paste_cmd (NULL, sv);
+}
+
+static void
+about_cmd (GtkWidget *widget, Schematic *sm)
+{
+ dialog_about ();
+}
+
+static void
+log_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_view_log_show (sv, TRUE);
+}
+
+static void
+show_label_cmd (GtkToggleAction *toggle, SchematicView *sv)
+{
+ gboolean b;
+ GList *item;
+
+ b = gtk_toggle_action_get_active (toggle);
+
+ for (item = sv->priv->items; item; item=item->next) {
+ if (IS_PART_ITEM(item->data))
+ part_item_show_node_labels (PART_ITEM (item->data), b);
+ }
+}
+
+static void
+print_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_print (schematic_view_get_schematic (sv),
+ sv->priv->page_setup,
+ sv->priv->print_settings, FALSE);
+}
+
+static void
+print_preview_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ schematic_print (schematic_view_get_schematic (sv),
+ sv->priv->page_setup,
+ sv->priv->print_settings, TRUE);
+}
+
+static void
+quit_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ GList *list, *copy;
+
+ /*
+ * Duplicate the list as the list is modified during destruction.
+ */
+ copy = g_list_copy (schematic_view_list);
+
+ for (list = copy; list; list = list->next){
+ if (can_close (list->data))
+ g_object_unref (list->data);
+ }
+
+ g_list_free (copy);
+}
+
+static void
+v_clamp_cmd (SchematicView *sv)
+{
+ LibraryPart *library_part;
+ SheetPos pos;
+ Sheet *sheet;
+ Part *part;
+ GList *lib;
+ Library *l;
+
+ set_tool (sv, SCHEMATIC_TOOL_PART);
+ sheet = schematic_view_get_sheet (sv);
+
+ /* Find default lib */
+ for(lib=oregano.libraries; lib; lib=lib->next) {
+ l = (Library *)(lib->data);
+ if (!g_strcasecmp(l->name, "Default")) break;
+ }
+
+ library_part = library_get_part (l, "Test Clamp");
+
+ part = part_new_from_library_part (library_part);
+
+ if (!part) {
+ g_warning ("Clamp not found!");
+ return;
+ }
+ pos.x = pos.y = 0;
+ item_data_set_pos (ITEM_DATA (part), &pos);
+ item_data_set_property (ITEM_DATA (part), "type", "v");
+
+ schematic_view_select_all(sv, FALSE);
+ schematic_view_clear_ghosts(sv);
+ schematic_view_add_ghost_item(sv, ITEM_DATA(part));
+
+ part_item_signal_connect_floating_group (sheet, sv);
+}
+
+static void
+tool_cmd (GtkAction *action, GtkRadioAction *current, SchematicView *sv)
+{
+
+ switch (gtk_radio_action_get_current_value (current)) {
+ case 0:
+ set_tool (sv, SCHEMATIC_TOOL_ARROW);
+ break;
+ case 1:
+ set_tool (sv, SCHEMATIC_TOOL_TEXT);
+ break;
+ case 2:
+ set_tool (sv, SCHEMATIC_TOOL_WIRE);
+ break;
+ case 3:
+ v_clamp_cmd (sv);
+ }
+}
+
+static void
+part_browser_cmd (GtkToggleAction *action, SchematicView *sv)
+{
+ part_browser_toggle_show (sv);
+}
+
+static void
+grid_toggle_snap_cmd (GtkToggleAction *action, SchematicView *sv)
+{
+ sv->priv->grid = !sv->priv->grid;
+
+ grid_snap (sv->priv->sheet->grid, sv->priv->grid);
+ grid_show (sv->priv->sheet->grid, sv->priv->grid);
+}
+
+static void
+voltmeter_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ sv->priv->show_voltmeters = !sv->priv->show_voltmeters;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ sv->priv->show_voltmeters);
+
+ schematic_view_show_voltmeters (sv, sv->priv->show_voltmeters);
+}
+
+static void
+netlist_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *sm;
+ gchar *netlist_name;
+ GError *error = 0;
+ OreganoEngine *engine;
+
+ g_return_if_fail (sv != NULL);
+
+ sm = sv->priv->schematic;
+
+ netlist_name = dialog_netlist_file (sv);
+ if (netlist_name == NULL)
+ return;
+
+ schematic_set_netlist_filename (sm, netlist_name);
+ engine = oregano_engine_factory_create_engine (oregano.engine, sm);
+ oregano_engine_generate_netlist (engine, netlist_name, &error);
+ schematic_view_update_parts (sv);
+
+ g_free (netlist_name);
+ g_object_unref (engine);
+
+ if (error != NULL) {
+ if (g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) ||
+ g_error_matches (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) {
+ oregano_error_with_title (_("Could not create a netlist"), error->message);
+ g_clear_error (&error);
+ }
+ else oregano_error (_("An unexpected error has occurred"));
+
+ return;
+ }
+}
+
+static void
+netlist_view_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ netlist_editor_new_from_schematic_view (sv);
+}
+
+#define ZOOM_MIN 0.35
+#define ZOOM_MAX 3
+
+static void
+zoom_check (SchematicView *sv)
+{
+ double zoom;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet_get_zoom (sv->priv->sheet, &zoom);
+
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/StandartToolbar/ZoomIn"), zoom < ZOOM_MAX);
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/StandartToolbar/ZoomOut"), zoom > ZOOM_MIN);
+}
+
+static void
+zoom_in_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet_change_zoom (sv->priv->sheet, 1.1);
+ zoom_check (sv);
+}
+
+static void
+zoom_out_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet_change_zoom (sv->priv->sheet, 0.9);
+ zoom_check (sv);
+}
+
+static void
+zoom_cmd (GtkAction *action, GtkRadioAction *current, SchematicView *sv)
+{
+ switch (gtk_radio_action_get_current_value (current)) {
+ case 0:
+ g_object_set(G_OBJECT(sv->priv->sheet), "zoom", 0.50, NULL);
+ break;
+ case 1:
+ g_object_set(G_OBJECT(sv->priv->sheet), "zoom", 0.75, NULL);
+ break;
+ case 2:
+ g_object_set(G_OBJECT (sv->priv->sheet), "zoom", 1.0, NULL);
+ break;
+ case 3:
+ g_object_set(G_OBJECT(sv->priv->sheet), "zoom", 1.25, NULL);
+ break;
+ case 4:
+ g_object_set(G_OBJECT(sv->priv->sheet), "zoom", 1.5, NULL);
+ break;
+ }
+ zoom_check (sv);
+}
+
+static void
+simulate_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ Schematic *sm;
+
+ sm = sv->priv->schematic;
+
+ simulation_show (NULL, sv);
+
+ schematic_view_update_parts (sv);
+ return;
+}
+
+GType
+schematic_view_get_type(void)
+{
+ static GType schematic_view_type = 0;
+
+ if (!schematic_view_type) {
+ static const GTypeInfo schematic_view_info = {
+ sizeof(SchematicViewClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)schematic_view_class_init,
+ NULL,
+ NULL,
+ sizeof(SchematicView),
+ 0,
+ (GInstanceInitFunc)schematic_view_init,
+ NULL
+ };
+
+ schematic_view_type = g_type_register_static(G_TYPE_OBJECT,
+ "SchematicView", &schematic_view_info, 0);
+ }
+
+ return schematic_view_type;
+}
+
+static void
+schematic_view_class_init (SchematicViewClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ schematic_view_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SchematicViewClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = schematic_view_finalize;
+ object_class->dispose = schematic_view_dispose;
+
+ stipple = gdk_bitmap_create_from_data(NULL, stipple_pattern, 2, 2);
+}
+
+static void
+schematic_view_init (SchematicView *sv)
+{
+ sv->priv = g_new0 (SchematicViewPriv, 1);
+
+ sv->priv->preserve_selection_items = NULL;
+ sv->priv->rubberband = g_new0 (RubberbandInfo, 1);
+ sv->priv->rubberband->state = RUBBER_NO;
+ sv->priv->items = NULL;
+ sv->priv->empty = TRUE;
+ sv->priv->schematic = NULL;
+ sv->priv->page_setup = NULL;
+ sv->priv->print_settings = gtk_print_settings_new ();
+ sv->priv->grid = TRUE;
+
+ sv->priv->show_voltmeters = FALSE;
+ sv->priv->voltmeter_items = NULL;
+ sv->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, g_free);
+}
+
+static void
+schematic_view_finalize(GObject *object)
+{
+ SchematicView *sv = SCHEMATIC_VIEW (object);
+
+ if (sv->priv) {
+ g_hash_table_destroy (sv->priv->dots);
+ g_free(sv->priv->rubberband);
+
+ g_free (sv->priv);
+ sv->priv = NULL;
+ }
+
+ if (sv->toplevel) {
+ gtk_widget_destroy (GTK_WIDGET (sv->toplevel));
+ sv->toplevel = NULL;
+ }
+
+ G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+schematic_view_dispose(GObject *object)
+{
+ SchematicView *sv = SCHEMATIC_VIEW(object);
+ GList *list;
+
+ schematic_view_list = g_list_remove (schematic_view_list, sv);
+
+ /* Disconnect sheet's events */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sv->priv->sheet),
+ G_CALLBACK(sheet_event_callback), sv);
+
+ /* Disconnect focus signal */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel),
+ G_CALLBACK(set_focus), sv);
+
+ /* Disconnect schematic dot_removed signal */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sv->priv->schematic),
+ G_CALLBACK(dot_removed_callback), sv);
+
+ /* Disconnect destroyed item signal */
+ for(list=sv->priv->items; list; list=list->next)
+ g_signal_handlers_disconnect_by_func (G_OBJECT (list->data),
+ G_CALLBACK(item_destroy_callback), sv);
+
+ /* Disconnect destroy event from toplevel */
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel),
+ G_CALLBACK(delete_event), sv);
+
+ if (sv->priv) {
+ g_object_unref(G_OBJECT(sv->priv->schematic));
+ }
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+/**
+* Set up a mini icon for the window.
+*/
+static void
+realized (GtkWidget *widget)
+{
+ GdkPixmap *icon;
+ GdkBitmap *icon_mask;
+
+ icon = gdk_pixmap_create_from_xpm_d (widget->window,
+ &icon_mask, NULL, mini_icon_xpm);
+ set_small_icon (widget->window, icon, icon_mask);
+}
+
+static void
+dot_added_callback (Schematic *schematic, SheetPos *pos, SchematicView *sv)
+{
+ /* GnomeCanvasItem *dot_item; */
+ NodeItem *node_item;
+
+ SheetPos *key;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ node_item = g_hash_table_lookup (sv->priv->dots, pos);
+ if (node_item == NULL) {
+ node_item = NODE_ITEM (
+ gnome_canvas_item_new (
+ gnome_canvas_root (GNOME_CANVAS (sv->priv->sheet)),
+ node_item_get_type (),
+ "x", pos->x,
+ "y", pos->y,
+ NULL));
+ }
+
+ node_item_show_dot (node_item, TRUE);
+
+ key = g_new0 (SheetPos, 1);
+ key->x = pos->x;
+ key->y = pos->y;
+
+ g_hash_table_insert (sv->priv->dots, key, node_item);
+}
+
+static void
+dot_removed_callback (Schematic *schematic, SheetPos *pos, SchematicView *sv)
+{
+ gpointer *node_item; /* GnomeCanvasItem* */
+ gpointer *orig_key; /* SheetPos* */
+ gboolean found;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ found = g_hash_table_lookup_extended (
+ sv->priv->dots,
+ pos,
+ (gpointer) &orig_key,
+ (gpointer) &node_item);
+
+ if (found) {
+ gtk_object_destroy(GTK_OBJECT (node_item));
+ g_hash_table_remove(sv->priv->dots, pos);
+ } else
+ g_warning ("No dot to remove!");
+}
+
+static void
+show_help (GtkWidget *widget, SchematicView *sv)
+{
+ GError *error = NULL;
+
+ if (!gnome_help_display_uri("ghelp:oregano",&error)) {
+ printf("Error %s\n", error->message);
+ g_error_free(error);
+ }
+}
+
+#include "schematic-view-menu.h"
+
+SchematicView *
+schematic_view_new (Schematic *schematic)
+{
+ SchematicView *sv;
+ SchematicViewPriv *priv;
+ GtkAdjustment *vadj, *hadj;
+ GtkWidget *w, *hbox, *vbox;
+ GtkWidget *toolbar;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GtkAccelGroup *accel_group;
+ GtkWidget *menubar;
+ GError *error;
+
+ g_return_val_if_fail (schematic != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL);
+
+ sv = SCHEMATIC_VIEW(g_object_new(schematic_view_get_type(), NULL));
+
+ schematic_view_list = g_list_prepend (schematic_view_list, sv);
+
+ sv->toplevel = gnome_app_new ("Oregano", "Oregano");
+
+ w = gnome_appbar_new (FALSE, TRUE, GNOME_PREFERENCES_NEVER);
+ gnome_app_set_statusbar (GNOME_APP (sv->toplevel), w);
+
+ sv->priv->sheet = SHEET (sheet_new (10000, 10000));
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ hbox = gtk_hbox_new (FALSE, 0);
+
+ w = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (sv->priv->sheet));
+ vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (sv->priv->sheet));
+ hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (sv->priv->sheet));
+ vadj->step_increment = 10;
+ hadj->step_increment = 10;
+ gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (w), vadj);
+ gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (w), hadj);
+
+ gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
+
+ g_signal_connect (G_OBJECT (sv->priv->sheet), "event", G_CALLBACK(sheet_event_callback), sv);
+
+ priv = sv->priv;
+ priv->log_window = NULL;
+
+ gtk_box_pack_start (GTK_BOX (hbox), part_browser_create (sv), FALSE, FALSE, 0);
+
+ priv->action_group = action_group = gtk_action_group_new ("MenuActions");
+ gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), sv);
+ gtk_action_group_add_radio_actions (action_group, zoom_entries, G_N_ELEMENTS (zoom_entries), 2, G_CALLBACK (zoom_cmd), sv);
+ gtk_action_group_add_radio_actions (action_group, tools_entries, G_N_ELEMENTS (tools_entries), 0, G_CALLBACK (tool_cmd), sv);
+ gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), sv);
+
+ priv->ui_manager = ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
+ accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+ gtk_window_add_accel_group (GTK_WINDOW (sv->toplevel), accel_group);
+
+ error = NULL;
+ if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error)) {
+ g_message ("building menus failed: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+ menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
+
+ // Upgrade the menu bar with the recent files used by oregano
+ display_recent_files (menubar, sv);
+ gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/StandartToolbar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
+
+ // Fill the window
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gnome_app_set_contents (GNOME_APP (sv->toplevel), vbox);
+
+ gtk_window_set_focus (GTK_WINDOW (sv->toplevel), GTK_WIDGET (sv->priv->sheet));
+ gtk_widget_grab_focus (GTK_WIDGET (sv->priv->sheet));
+
+ g_signal_connect_after (G_OBJECT (sv->toplevel), "set_focus", G_CALLBACK (set_focus), sv);
+ g_signal_connect (G_OBJECT (sv->toplevel), "delete_event", G_CALLBACK (delete_event), sv);
+ g_signal_connect (G_OBJECT (sv->toplevel), "realize", G_CALLBACK (realized), NULL);
+
+ setup_dnd (sv);
+
+ /* Set default sensitive for items */
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, "/MainMenu/MenuEdit/ObjectProperties"), FALSE);
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, "/MainMenu/MenuEdit/Paste"), FALSE);
+
+ /*
+ * Set the window size to something reasonable. Stolen from Gnumeric.
+ */
+ {
+ int sx, sy;
+
+ gtk_window_set_policy (GTK_WINDOW (sv->toplevel), TRUE, TRUE, FALSE);
+ sx = MAX (gdk_screen_width () - 64, 640);
+ sy = MAX (gdk_screen_height () - 64, 480);
+ sx = (sx * 3) / 4;
+ sy = (sy * 3) / 4;
+ gtk_window_set_default_size (GTK_WINDOW (sv->toplevel),
+ sx, sy);
+ }
+
+ /*
+ * Hash table that keeps maps coordinate to a specific dot.
+ */
+ priv->dots = g_hash_table_new_full (dot_hash, dot_equal, g_free, NULL);
+
+ g_signal_connect_object(G_OBJECT (sv->priv->sheet),
+ "reset_tool",
+ G_CALLBACK (reset_tool_cb),
+ G_OBJECT (sv),
+ 0);
+
+ schematic_view_load (sv, schematic);
+
+ if (!schematic_get_title (sv->priv->schematic)) {
+ gtk_window_set_title (
+ GTK_WINDOW (sv->toplevel),
+ _("Untitled.oregano")
+ );
+ } else {
+ gtk_window_set_title (
+ GTK_WINDOW (sv->toplevel),
+ schematic_get_title (sv->priv->schematic)
+ );
+ }
+ schematic_set_filename (sv->priv->schematic, _("Untitled.oregano"));
+ schematic_set_netlist_filename (sv->priv->schematic, _("Untitled.netlist"));
+
+ return sv;
+}
+
+void
+schematic_view_load (SchematicView *sv, Schematic *sm)
+{
+ GList *list;
+ g_return_if_fail (sv->priv->empty != FALSE);
+ g_return_if_fail (sm != NULL);
+
+ if (sv->priv->schematic) g_object_unref (G_OBJECT (sv->priv->schematic));
+
+ sv->priv->schematic = sm;
+
+ g_signal_connect_object(G_OBJECT (sm),
+ "title_changed",
+ G_CALLBACK(title_changed_callback),
+ G_OBJECT (sv),
+ 0);
+ g_signal_connect_object(G_OBJECT (sm),
+ "item_data_added",
+ G_CALLBACK(item_data_added_callback),
+ G_OBJECT (sv),
+ 0);
+ g_signal_connect_object(G_OBJECT (sm),
+ "dot_added",
+ G_CALLBACK(dot_added_callback),
+ G_OBJECT (sv),
+ 0);
+ g_signal_connect_object(G_OBJECT (sm),
+ "dot_removed",
+ G_CALLBACK(dot_removed_callback),
+ G_OBJECT (sv),
+ 0);
+
+ list = schematic_get_items (sm);
+
+ for (; list; list = list->next)
+ g_signal_emit_by_name(G_OBJECT (sm), "item_data_added", list->data);
+
+ list = node_store_get_node_positions (schematic_get_store (sm));
+ for (; list; list = list->next)
+ dot_added_callback (sm, list->data, sv);
+
+ g_list_free (list);
+}
+
+/*
+* Tidy up: remove the item from the item list and selected items list.
+*/
+static void
+item_destroy_callback (SheetItem *item, SchematicView *sv)
+{
+ Sheet *sheet;
+
+ sv->priv->items = g_list_remove (sv->priv->items, item);
+
+ sheet = sheet_item_get_sheet (item);
+
+ /*
+ * Remove the object from the selected-list before destroying.
+ */
+
+ /* FIXME: optimize by checking if the item is selected first. */
+ sheet->priv->selected_objects = g_list_remove (sheet->priv->selected_objects, item);
+ sheet->priv->floating_objects = g_list_remove (sheet->priv->floating_objects, item);
+}
+
+static void
+item_selection_changed_callback (SheetItem *item, gboolean selected,
+ SchematicView *sv)
+{
+ guint length;
+
+ /* FIXME! : don't touch sheet->priv directly!!! */
+ if (selected) {
+ sv->priv->sheet->priv->selected_objects =
+ g_list_prepend ( sv->priv->sheet->priv->selected_objects, item);
+ } else {
+ sv->priv->sheet->priv->selected_objects = g_list_remove ( sv->priv->sheet->priv->selected_objects, item);
+ }
+
+ length = g_list_length (sv->priv->sheet->priv->selected_objects);
+ if (length && item_data_has_properties (sheet_item_get_data (item)))
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/ObjectProperties"), TRUE);
+ else
+ gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/ObjectProperties"), FALSE);
+}
+
+/**
+* An ItemData got added; create an Item and set up the neccessary handlers.
+*/
+static void
+item_data_added_callback (Schematic *schematic, ItemData *data,
+ SchematicView *sv)
+{
+ NodeStore *store;
+ Sheet *sheet;
+ SheetItem *item;
+
+ store = schematic_get_store (schematic);
+ sheet = schematic_view_get_sheet (sv);
+
+ item = sheet_item_factory_create_sheet_item (sv, data);
+ if (item != NULL) {
+ sheet_item_place (item, sv);
+
+ g_object_set (G_OBJECT (item), "action_group", sv->priv->action_group, NULL);
+ /*
+ * Hook onto the destroy signal so that we can perform some
+ * cleaning magic before destroying the item (remove it from
+ * lists etc).
+ */
+ g_signal_connect (
+ G_OBJECT (item),
+ "destroy",
+ G_CALLBACK (item_destroy_callback),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT (item),
+ "selection_changed",
+ G_CALLBACK (item_selection_changed_callback),
+ sv);
+
+ sv->priv->items = g_list_prepend (sv->priv->items, item);
+ sv->priv->empty = FALSE;
+ if (sv->priv->tool == SCHEMATIC_TOOL_PART)
+ schematic_view_reset_tool (sv);
+ }
+}
+
+void
+schematic_view_add_ghost_item (SchematicView *sv, ItemData *data)
+{
+ Sheet *sheet;
+ SheetItem *item;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = schematic_view_get_sheet (sv);
+
+ item = sheet_item_factory_create_sheet_item (sv, data);
+
+ sheet->priv->floating_objects =
+ g_list_prepend (sheet->priv->floating_objects,item);
+}
+
+static void
+title_changed_callback (Schematic *schematic, char *new_title, SchematicView *sv)
+{
+ g_return_if_fail (schematic != NULL);
+ g_return_if_fail (IS_SCHEMATIC (schematic));
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ gtk_window_set_title (GTK_WINDOW (sv->toplevel), new_title);
+}
+
+static void
+set_focus (GtkWindow *window, GtkWidget *focus, SchematicView *sv)
+{
+ g_return_if_fail(sv->priv != NULL);
+ g_return_if_fail(sv->priv->sheet != NULL);
+
+ if (!window->focus_widget)
+ /* gtk_window_set_focus (GTK_WINDOW (sv->toplevel), GTK_WIDGET (sv->priv->sheet)); */
+ gtk_widget_grab_focus(GTK_WIDGET (sv->priv->sheet));
+}
+
+static int
+delete_event(GtkWidget *widget, GdkEvent *event, SchematicView *sv)
+{
+ if (can_close (sv)) {
+ g_object_unref(G_OBJECT(sv));
+
+ if (schematic_count() == 0)
+ bonobo_main_quit();
+
+ return FALSE;
+ } else
+ return TRUE;
+}
+
+static int
+can_close (SchematicView *sv)
+{
+ GtkWidget *dialog;
+ gchar *text, *filename;
+ GError *error = NULL;
+ gint result;
+
+ if (!schematic_is_dirty (schematic_view_get_schematic (sv)))
+ return TRUE;
+
+ filename = schematic_get_filename (sv->priv->schematic);
+ text = g_strdup_printf (
+ _("<span weight=\"bold\" size=\"large\">Save changes to schematic %s before closing?</span>\n\n"
+ "If you don't save, all changes since you last saved will be permanently lost."),
+ filename ? g_basename (filename) : NULL );
+
+ dialog = gtk_message_dialog_new_with_markup (
+ NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ _(text), NULL);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("Close _without Saving"),
+ GTK_RESPONSE_NO,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_YES,
+ NULL);
+
+ g_free (text);
+
+ result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ switch (result) {
+ case GTK_RESPONSE_YES:
+ schematic_save_file (schematic_view_get_schematic (sv), &error);
+ break;
+ case GTK_RESPONSE_NO:
+ schematic_set_dirty (schematic_view_get_schematic (sv), FALSE);
+ break;
+ case GTK_RESPONSE_CANCEL:
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+run_context_menu (SchematicView *sv, GdkEventButton *event)
+{
+ GtkWidget *menu;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ menu = gtk_ui_manager_get_widget (sv->priv->ui_manager, "/MainPopup");
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sv, event->button, event->time);
+}
+
+Sheet *
+schematic_view_get_sheet (SchematicView *sv)
+{
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ return sv->priv->sheet;
+}
+
+Schematic *
+schematic_view_get_schematic (SchematicView *sv)
+{
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ return sv->priv->schematic;
+}
+
+
+/*
+* FIXME: move these signals to schematic-view instead.
+*/
+void
+schematic_view_reset_tool (SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ g_signal_emit_by_name (G_OBJECT (sv->priv->sheet), "reset_tool");
+}
+
+void
+schematic_view_cancel (SchematicView *sv)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ g_signal_emit_by_name (G_OBJECT (sv->priv->sheet), "cancel");
+}
+
+/* Debugging: */
+extern void node_store_dump_wires (NodeStore *store);
+
+static int
+sheet_event_callback (GtkWidget *widget, GdkEvent *event, SchematicView *sv)
+{
+ Sheet *sheet;
+
+ sheet = sv->priv->sheet;
+
+ switch (event->type) {
+ case GDK_3BUTTON_PRESS:
+ /*
+ * We don't not care about triple clicks on the sheet.
+ */
+ return FALSE;
+ case GDK_2BUTTON_PRESS:
+ /*
+ * The sheet does not care about double clicks, but invoke the
+ * canvas event handler and see if an item picks up the event.
+ */
+ if ((*GTK_WIDGET_CLASS (sheet_parent_class)->button_press_event) (
+ widget, (GdkEventButton *)event))
+ return TRUE;
+ else
+ return FALSE;
+ case GDK_BUTTON_PRESS:
+ /*
+ * If we are in the middle of something else, don't interfere
+ * with that.
+ */
+ if (sheet->state != SHEET_STATE_NONE)
+ return FALSE;
+
+ if ((* GTK_WIDGET_CLASS
+ (sheet_parent_class)->button_press_event) (widget, (GdkEventButton *) event))
+ return TRUE;
+
+ if (event->button.button == 3) {
+ run_context_menu (sv, (GdkEventButton *) event);
+ return TRUE;
+ }
+
+ if (event->button.button == 1) {
+ if (!(event->button.state & GDK_SHIFT_MASK))
+ schematic_view_select_all (sv, FALSE);
+
+ setup_rubberband (sv, (GdkEventButton *) event);
+ return TRUE;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 4 || event->button.button == 5)
+ return TRUE;
+
+ if (event->button.button == 1 &&
+ sv->priv->rubberband->state == RUBBER_YES) {
+ stop_rubberband (sv, (GdkEventButton *) event);
+ return TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS
+ (sheet_parent_class)->button_release_event != NULL)
+ return GTK_WIDGET_CLASS
+ (sheet_parent_class)->button_release_event (
+ widget, (GdkEventButton *) event);
+
+ break;
+
+ case GDK_SCROLL:
+ if (((GdkEventScroll *)event)->direction == GDK_SCROLL_UP) {
+ double zoom;
+ sheet_get_zoom (sv->priv->sheet, &zoom);
+ if (zoom < ZOOM_MAX)
+ zoom_in_cmd (widget, sv);
+ } else if (((GdkEventScroll *)event)->direction == GDK_SCROLL_DOWN) {
+ double zoom;
+ sheet_get_zoom (sv->priv->sheet, &zoom);
+ if (zoom > ZOOM_MIN)
+ zoom_out_cmd (widget, sv);
+ }
+ break;
+ case GDK_KEY_PRESS:
+ switch (event->key.keyval) {
+ case GDK_R:
+ case GDK_r:
+ if (sheet->state == SHEET_STATE_NONE)
+ schematic_view_rotate_selection (sv);
+ break;
+ /* case GDK_f:
+ if (sheet->state == SHEET_STATE_NONE)
+ schematic_view_flip_selection (sv, TRUE);
+ break;
+ case GDK_F:
+ if (sheet->state == SHEET_STATE_NONE)
+ schematic_view_flip_selection (sv, FALSE);
+ break;*/
+ case GDK_Home:
+ break;
+ case GDK_End:
+ break;
+ case GDK_Left:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, -20, 0);
+ break;
+ case GDK_Up:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, 0, -20);
+ break;
+ case GDK_Right:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, 20, 0);
+ break;
+ case GDK_Down:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, 0, 20);
+ break;
+ case GDK_Page_Up:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, 0, -120);
+ break;
+ case GDK_Page_Down:
+ if (event->key.state & GDK_MOD1_MASK)
+ sheet_scroll (sheet, 0, 120);
+ break;
+ case GDK_l:
+/* part_browser_place_selected_part (sheet->schematic); */
+ break;
+ case GDK_space:
+
+ node_store_dump_wires (
+ schematic_get_store (sv->priv->schematic));
+ return TRUE;
+
+ break;
+ case GDK_Escape:
+ g_signal_emit_by_name (G_OBJECT (sheet), "cancel");
+ break;
+ case GDK_Delete:
+ schematic_view_delete_selection (sv);
+ break;
+ default:
+ return FALSE;
+ }
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+setup_dnd (SchematicView *sv)
+{
+ static GtkTargetEntry dnd_types[] = {
+ { "text/uri-list", 0, DRAG_URI_INFO },
+ { "x-application/oregano-part", 0, DRAG_PART_INFO }
+ };
+ static gint dnd_num_types = sizeof (dnd_types) / sizeof (dnd_types[0]);
+
+ gtk_drag_dest_set (GTK_WIDGET (sv->priv->sheet),
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_DROP,
+ dnd_types, dnd_num_types, GDK_ACTION_MOVE);
+
+ g_signal_connect (G_OBJECT (sv->priv->sheet), "drag_data_received",
+ G_CALLBACK (data_received),
+ "koko");
+}
+
+static void
+data_received (GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *selection_data, guint info, guint32 time,
+ SchematicView *sv)
+{
+ gchar **files;
+ GError *error = NULL;
+
+ /*
+ * Extract the filenames from the URI-list we recieved.
+ */
+
+ switch (info) {
+ case DRAG_PART_INFO:
+ part_browser_dnd (selection_data, x, y);
+ break;
+ case DRAG_URI_INFO:
+ files = g_strsplit (selection_data->data, "\n", -1);
+ if (files) {
+ int i=0;
+ while (files[i]) {
+ Schematic *new_sm = NULL;
+ int l = strlen(files[i]);
+ /* Algo queda mal al final luego del split, agrego un \0 */
+ files[i][l-1] = '\0';
+
+ if (l <= 0) {
+ /* Empty file name, ignore! */
+ i++;
+ continue;
+ }
+
+ gchar *fname = files[i];
+
+ new_sm = schematic_read (fname, &error);
+ if (new_sm) {
+ SchematicView *new_view;
+ new_view = schematic_view_new (new_sm);
+ if (new_view) {
+ gtk_widget_show_all (new_view->toplevel);
+ schematic_set_filename (new_sm, fname);
+ }
+ // schematic_set_title (new_sm, fname);
+ while (gtk_events_pending ()) /* Show something. */
+ gtk_main_iteration ();
+ }
+ i++;
+ }
+ }
+ }
+ gtk_drag_finish (context, TRUE, TRUE, time);
+}
+
+static void
+setup_rubberband (SchematicView *sv, GdkEventButton *event)
+{
+ double x, y;
+ double wx, wy;
+
+ x = event->x; //the x coordinate of the pointer relative to the window.
+ y = event->y; //the y coordinate of the pointer relative to the window.
+
+ /* Need this for zoomed views */
+ gnome_canvas_window_to_world (
+ GNOME_CANVAS (sv->priv->sheet),
+ x, y,
+ &wx, &wy
+ );
+ x = wx;
+ y = wy;
+
+ sv->priv->rubberband->start_x = x;
+ sv->priv->rubberband->start_y = y;
+
+ sv->priv->rubberband->state = RUBBER_YES;
+ sv->priv->rubberband->click_start_state = event->state;
+
+
+ sv->priv->rubberband->rectangle = gnome_canvas_item_new (
+ sv->priv->sheet->object_group,
+ gnome_canvas_rect_get_type (),
+ "x1", x,
+ "y1", y,
+ "x2", x,
+ "y2", y,
+ "outline_stipple", stipple,
+ "outline_color", "black",
+ "width_pixels", 1,
+ NULL);
+
+ gnome_canvas_item_grab (GNOME_CANVAS_ITEM (sv->priv->sheet->grid),
+ (GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK),
+ NULL, event->time);
+
+ /* Mark all the selected objects to preserve their selected state
+ * if SHIFT is pressed while rubberbanding.
+ */
+ if (event->state & GDK_SHIFT_MASK) {
+ GList *list;
+ for (list = sv->priv->sheet->priv->selected_objects; list;
+ list = list->next) {
+ sheet_item_set_preserve_selection (
+ SHEET_ITEM (list->data), TRUE);
+ }
+ /* Save the list so that we can remove the preserve_selection
+ flags later. */
+ sv->priv->preserve_selection_items =
+ g_list_copy (sv->priv->sheet->priv->selected_objects);
+ }
+
+ sv->priv->rubberband->timeout_id = gtk_timeout_add (
+ 15,
+ (gpointer) rubberband_timeout_cb,
+ (gpointer) sv);
+}
+
+static void
+stop_rubberband (SchematicView *sv, GdkEventButton *event)
+{
+ GList *list;
+
+ gtk_idle_remove (sv->priv->rubberband->timeout_id);
+ sv->priv->rubberband->state = RUBBER_NO;
+
+ if (sv->priv->preserve_selection_items != NULL) {
+ for (list = sv->priv->preserve_selection_items; list;
+ list = list->next)
+ sheet_item_set_preserve_selection (
+ SHEET_ITEM (list->data), FALSE);
+ g_list_free (sv->priv->preserve_selection_items);
+ sv->priv->preserve_selection_items = NULL;
+ }
+
+ gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (sv->priv->sheet->grid), event->time);
+ gtk_object_destroy (GTK_OBJECT (sv->priv->rubberband->rectangle));
+}
+
+static int
+rubberband_timeout_cb (SchematicView *sv)
+{
+ static double x1_old = 0, y1_old = 0, x2_old = 0, y2_old = 0;
+ int _x, _y;
+ double x, y;
+ double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ double dx, dy, tx, ty;
+ GList *list;
+ SheetPos p1, p2;
+
+ /* Obtains the current pointer position and modifier state.
+ * The position is given in coordinates relative to window.
+ */
+ gdk_window_get_pointer (GTK_WIDGET (sv->priv->sheet)->window, &_x, &_y, NULL);
+
+ x = _x;
+ y = _y;
+
+ gnome_canvas_get_scroll_offsets (
+ GNOME_CANVAS (sv->priv->sheet),
+ &_x, &_y
+ );
+
+ x += _x;
+ y += _y;
+
+ /* Need this for zoomed views */
+ gnome_canvas_window_to_world (
+ GNOME_CANVAS (sv->priv->sheet),
+ x, y,
+ &tx, &ty
+ );
+ x = tx;
+ y = ty;
+
+ if (x < sv->priv->rubberband->start_x) {
+ x1 = x;
+ x2 = sv->priv->rubberband->start_x;
+ } else {
+ x1 = sv->priv->rubberband->start_x;
+ x2 = x;
+ }
+
+ if (y < sv->priv->rubberband->start_y) {
+ y1 = y;
+ y2 = sv->priv->rubberband->start_y;
+ } else {
+ y1 = sv->priv->rubberband->start_y;
+ y2 = y;
+ }
+
+
+ p1.x = x1;
+ p1.y = y1;
+ p2.x = x2;
+ p2.y = y2;
+
+ /*
+ * Scroll the sheet if needed.
+ */
+ /* Need FIX */
+ {
+ int width, height;
+ int dx = 0, dy = 0;
+
+ gdk_window_get_pointer (GTK_WIDGET (sv->priv->sheet)->window, &_x, &_y, NULL);
+
+ width = GTK_WIDGET (sv->priv->sheet)->allocation.width;
+ height = GTK_WIDGET (sv->priv->sheet)->allocation.height;
+
+ if (_x < 0)
+ dx = -1;
+ else if (_x > width)
+ dx = 1;
+
+ if (_y < 0)
+ dy = -1;
+ else if (_y > height)
+ dy = 1;
+
+ if (!(_x > 0 && _x < width && _y > 0 && _y < height))
+ sheet_scroll (sv->priv->sheet, dx * 5, dy * 5);
+ }
+
+ dx = fabs ((x1 - x2) - (x1_old - x2_old));
+ dy = fabs ((y1 - y2) - (y1_old - y2_old));
+ if (dx > 1.0 || dy > 1.0) {
+ /* Save old state */
+ x1_old = x1;
+ y1_old = y1;
+ x2_old = x2;
+ y2_old = y2;
+
+ for (list = sv->priv->items; list; list = list->next) {
+ sheet_item_select_in_area (list->data, &p1, &p2);
+ }
+
+ gnome_canvas_item_set (sv->priv->rubberband->rectangle,
+ "x1", (double) x1,
+ "y1", (double) y1,
+ "x2", (double) x2,
+ "y2", (double) y2,
+ NULL);
+ }
+
+ return TRUE;
+}
+
+static void
+set_tool (SchematicView *sv, SchematicTool tool)
+{
+ /*
+ * Switch from this tool...
+ */
+ switch (sv->priv->tool) {
+ case SCHEMATIC_TOOL_ARROW:
+ /*
+ * In case we are handling a floating object,
+ * cancel that so that we can change tool.
+ */
+ sheet_item_cancel_floating (sv);
+ break;
+ case SCHEMATIC_TOOL_WIRE:
+ if (sv->priv->create_wire_context) {
+ create_wire_exit (sv->priv->create_wire_context);
+ sv->priv->create_wire_context = NULL;
+ }
+ break;
+ case SCHEMATIC_TOOL_TEXT:
+ textbox_item_cancel_listen (sv);
+ break;
+ case SCHEMATIC_TOOL_PART:
+ sheet_item_cancel_floating (sv);
+ default:
+ break;
+ }
+
+ /*
+ * ...to this tool.
+ */
+ switch (tool) {
+ case SCHEMATIC_TOOL_ARROW:
+ cursor_set_widget (GTK_WIDGET (sv->priv->sheet), OREGANO_CURSOR_LEFT_PTR);
+ sv->priv->sheet->state = SHEET_STATE_NONE;
+ break;
+ case SCHEMATIC_TOOL_WIRE:
+ cursor_set_widget (GTK_WIDGET (sv->priv->sheet),
+ OREGANO_CURSOR_PENCIL);
+ sv->priv->sheet->state = SHEET_STATE_WIRE;
+ sv->priv->create_wire_context = create_wire_initiate (sv);
+ break;
+ case SCHEMATIC_TOOL_TEXT:
+ cursor_set_widget (GTK_WIDGET (sv->priv->sheet), OREGANO_CURSOR_CARET);
+ sv->priv->sheet->state = SHEET_STATE_TEXTBOX_WAIT;
+
+ textbox_item_listen (sv);
+ break;
+ case SCHEMATIC_TOOL_PART:
+ cursor_set_widget (GTK_WIDGET (sv->priv->sheet), OREGANO_CURSOR_LEFT_PTR);
+ default:
+ break;
+ }
+
+ sv->priv->tool = tool;
+}
+
+static void
+reset_tool_cb (Sheet *sheet, SchematicView *sv)
+{
+ set_tool (sv, SCHEMATIC_TOOL_ARROW);
+
+ gtk_radio_action_set_current_value (GTK_RADIO_ACTION (gtk_ui_manager_get_action (sv->priv->ui_manager, "/StandartToolbar/Arrow")), 0);
+}
+
+gpointer
+schematic_view_get_browser (SchematicView *sv)
+{
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ return sv->priv->browser;
+}
+
+void
+schematic_view_set_browser (SchematicView *sv, gpointer p)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sv->priv->browser = p;
+}
+
+void
+schematic_view_delete_selection (SchematicView *sv)
+{
+ GList *list, *copy;
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = sv->priv->sheet;
+
+ if (sheet->state != SHEET_STATE_NONE)
+ return;
+
+ copy = g_list_copy (sheet->priv->selected_objects);
+
+ for (list = copy; list; list = list->next) {
+ gtk_object_destroy(GTK_OBJECT(list->data));
+ }
+
+ g_list_free (copy);
+ g_list_free (sheet->priv->selected_objects);
+ sheet->priv->selected_objects = NULL;
+}
+
+static void
+rotate_items (SchematicView *sv, GList *items)
+{
+ GList *list, *item_data_list;
+ SheetPos center, b1, b2;
+ Sheet *sheet;
+
+ sheet = sv->priv->sheet;
+
+ item_data_list = NULL;
+ for (list = items; list; list = list->next) {
+ item_data_list = g_list_prepend (item_data_list,
+ sheet_item_get_data (list->data));
+ }
+
+ item_data_list_get_absolute_bbox (item_data_list, &b1, &b2);
+
+ center.x = (b2.x - b1.x) / 2 + b1.x;
+ center.y = (b2.y - b1.y) / 2 + b1.y;
+
+ snap_to_grid (sheet->grid, &center.x, &center.y);
+
+ for (list = item_data_list; list; list = list->next) {
+ ItemData *item_data = list->data;
+
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ item_data_unregister (item_data);
+
+ item_data_rotate (item_data, 90, &center);
+
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ item_data_register (item_data);
+ }
+
+ g_list_free (item_data_list);
+}
+
+void
+schematic_view_rotate_selection (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = sv->priv->sheet;
+
+ if (sheet->priv->selected_objects != NULL)
+ rotate_items (sv, sheet->priv->selected_objects);
+}
+
+void
+schematic_view_rotate_ghosts (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = sv->priv->sheet;
+
+ if (sheet->priv->floating_objects != NULL)
+ rotate_items (sv, sheet->priv->floating_objects);
+}
+
+static void
+flip_items (SchematicView *sv, GList *items, gboolean horizontal)
+{
+ GList *list, *item_data_list;
+ SheetPos center, b1, b2;
+ SheetPos after, delta;
+ Sheet *sheet;
+ gboolean got_first = FALSE;
+
+ sheet = sv->priv->sheet;
+
+ item_data_list = NULL;
+ for (list = items; list; list = list->next) {
+ item_data_list = g_list_prepend (item_data_list,
+ sheet_item_get_data (list->data));
+ }
+
+ item_data_list_get_absolute_bbox (item_data_list, &b1, &b2);
+
+ center.x = (b2.x - b1.x) / 2 + b1.x;
+ center.y = (b2.y - b1.y) / 2 + b1.y;
+
+ for (list = item_data_list; list; list = list->next) {
+ ItemData *item_data = list->data;
+
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ item_data_unregister (item_data);
+
+ item_data_flip (item_data, horizontal, &center);
+
+ /*
+ * Make sure we snap to grid.
+ */
+ if (!got_first) {
+ SheetPos off;
+
+ item_data_get_pos (item_data, &off);
+ item_data_get_pos (item_data, &after);
+
+ snap_to_grid (sheet->grid, &off.x, &off.y);
+
+ delta.x = off.x - after.x;
+ delta.y = off.y - after.y;
+
+ got_first = TRUE;
+ }
+ item_data_move (item_data, &delta);
+
+ if (sv->priv->sheet->state == SHEET_STATE_NONE)
+ item_data_register (item_data);
+ }
+
+ g_list_free (item_data_list);
+}
+
+void
+schematic_view_flip_selection (SchematicView *sv, gboolean horizontal)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = sv->priv->sheet;
+
+ if (sheet->priv->selected_objects != NULL)
+ flip_items (sv, sheet->priv->selected_objects, horizontal);
+}
+
+void
+schematic_view_flip_ghosts (SchematicView *sv, gboolean horizontal)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = sv->priv->sheet;
+
+ if (sheet->priv->floating_objects != NULL)
+ flip_items (sv, sheet->priv->floating_objects, horizontal);
+}
+
+GList *
+schematic_view_get_selection (SchematicView *sv)
+{
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ return sv->priv->sheet->priv->selected_objects;
+}
+
+GList *
+schematic_view_get_items (SchematicView *sv)
+{
+ g_return_val_if_fail (sv != NULL, NULL);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), NULL);
+
+ return sv->priv->items;
+}
+
+void
+schematic_view_clear_ghosts (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ sheet = sv->priv->sheet;
+
+ if (sheet->priv->floating_objects == NULL) return;
+
+ g_assert (sheet->state != SHEET_STATE_FLOAT);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (sheet->priv->floating_group),
+ "x", 0.0, "y", 0.0, NULL);
+
+ /* FIXME: should destroy ghosts here. */
+ g_print("FIXME : Destroy ghosts here!\n");
+
+ g_list_free (sheet->priv->floating_objects);
+ sheet->priv->floating_objects = NULL;
+}
+
+void
+schematic_view_set_parent (SchematicView *sv, GtkDialog *dialog)
+{
+ /*gnome_dialog_set_parent (dialog, GTK_WINDOW (sv->toplevel));*/
+}
+
+void
+schematic_view_select_all (SchematicView *sv, gboolean select)
+{
+ Sheet *sheet;
+ GList *list;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = schematic_view_get_sheet (sv);
+
+ for (list = sv->priv->items; list; list = list->next)
+ sheet_item_select (SHEET_ITEM (list->data), select);
+
+ if (!select) {
+ g_list_free (sheet->priv->selected_objects);
+ sheet->priv->selected_objects = NULL;
+ }
+}
+
+static gboolean
+log_window_delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv)
+{
+ sv->priv->log_window = NULL;
+ return FALSE;
+}
+
+static void
+log_window_destroy_event (GtkWidget *widget, SchematicView *sv)
+{
+ sv->priv->log_window = NULL;
+}
+
+static void
+log_window_close_cb (GtkWidget *widget, SchematicView *sv)
+{
+ gtk_widget_destroy (sv->priv->log_window);
+ sv->priv->log_window = NULL;
+}
+
+static void
+log_window_clear_cb (GtkWidget *widget, SchematicView *sv)
+{
+ GtkTextTagTable *tag;
+ GtkTextBuffer *buf;
+
+ tag = gtk_text_tag_table_new ();
+ buf = gtk_text_buffer_new (GTK_TEXT_TAG_TABLE (tag));
+
+ schematic_log_clear (sv->priv->schematic);
+
+ gtk_text_view_set_buffer (GTK_TEXT_VIEW (sv->priv->log_text),
+ GTK_TEXT_BUFFER (buf));
+}
+
+static void
+log_updated_callback (Schematic *sm, SchematicView *sv)
+{
+ schematic_view_log_show (sv, FALSE);
+}
+
+void
+schematic_view_log_show (SchematicView *sv, gboolean explicit)
+{
+ GtkWidget *w;
+ gchar *msg;
+ Schematic *sm;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sm = schematic_view_get_schematic (sv);
+
+ if (sv->priv->log_window == NULL) {
+ /*
+ * Create the log window if not already done.
+ */
+
+ if (!explicit && !oregano.show_log)
+ return;
+
+ if (!g_file_test (OREGANO_GLADEDIR "/log-window.glade", G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/log-window.glade");
+
+ oregano_error_with_title ( _("Could not create the log window"), msg);
+ g_free (msg);
+ return;
+ }
+
+
+ sv->priv->log_gui = glade_xml_new (
+ OREGANO_GLADEDIR "/log-window.glade",
+ NULL, NULL);
+
+ if (!sv->priv->log_gui) {
+ oregano_error (_("Could not create the log window"));
+ return;
+ }
+
+ sv->priv->log_window = glade_xml_get_widget (sv->priv->log_gui,
+ "log-window");
+ sv->priv->log_text = GTK_TEXT_VIEW (
+ glade_xml_get_widget (sv->priv->log_gui,
+ "log-text"));
+
+ /* gtk_window_set_policy (GTK_WINDOW (sv->priv->log_window),
+ TRUE, TRUE, FALSE); */
+ gtk_window_set_default_size (GTK_WINDOW (sv->priv->log_window),
+ 500, 250);
+
+ /*
+ * Delete event.
+ */
+ g_signal_connect (
+ G_OBJECT (sv->priv->log_window), "delete_event",
+ G_CALLBACK (log_window_delete_event), sv);
+
+ g_signal_connect (
+ G_OBJECT (sv->priv->log_window), "destroy_event",
+ G_CALLBACK (log_window_destroy_event), sv);
+
+ w = glade_xml_get_widget (sv->priv->log_gui, "close-button");
+ g_signal_connect (G_OBJECT (w),
+ "clicked", G_CALLBACK (log_window_close_cb), sv);
+
+ w = glade_xml_get_widget (sv->priv->log_gui, "clear-button");
+ g_signal_connect (G_OBJECT (w),
+ "clicked", G_CALLBACK (log_window_clear_cb), sv);
+ g_signal_connect(G_OBJECT (sm),
+ "log_updated", G_CALLBACK(log_updated_callback), sv);
+ } else {
+ gdk_window_raise (sv->priv->log_window->window);
+ }
+
+ gtk_text_view_set_buffer (
+ sv->priv->log_text,
+ schematic_get_log_text (sm)
+ );
+
+
+ gtk_widget_show_all (sv->priv->log_window);
+}
+
+/** FIXME: only have one window for each schematic, not one per view. */
+gboolean
+schematic_view_log_window_exists (SchematicView *sv)
+{
+ if (sv->priv->log_window != NULL) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static guint
+dot_hash (gconstpointer key)
+{
+ SheetPos *sp = (SheetPos *) key;
+ int x, y;
+
+ x = (int)rint (sp->x) % 256;
+ y = (int)rint (sp->y) % 256;
+
+ return (y << 8) | x;
+}
+
+#define HASH_EPSILON 1e-2
+
+static int
+dot_equal (gconstpointer a, gconstpointer b)
+{
+ SheetPos *spa, *spb;
+
+ g_return_val_if_fail (a!=NULL, 0);
+ g_return_val_if_fail (b!=NULL, 0);
+
+ spa = (SheetPos *) a;
+ spb = (SheetPos *) b;
+
+ if (fabs (spa->y - spb->y) > HASH_EPSILON)
+ return 0;
+
+ if (fabs (spa->x - spb->x) > HASH_EPSILON)
+ return 0;
+
+ return 1;
+}
+
+#define OP_VALUE_FONT "-*-helvetica-medium-r-*-*-*-80-*-*-*-*-*-*"
+
+/**
+* Temporary hack to test the OP analysis visualization.
+* FIXME: solve similar to the connection dots.
+*/
+void
+schematic_view_show_op_values (SchematicView *sv, OreganoEngine *engine)
+{
+ GList *nodes, *list;
+ NodeStore *store;
+ Node *node;
+ GnomeCanvasItem *item, *group;
+ double value;
+ char *text, *tmp;
+ gboolean got;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (engine != NULL);
+
+ return;
+
+ store = schematic_get_store (sv->priv->schematic);
+
+ /*
+ * Iterate over the Nodes and find their resp. operation point.
+ */
+ nodes = node_store_get_nodes (store);
+ for (list = nodes; list; list = list->next) {
+ node = list->data;
+
+ got = 0; //sim_engine_get_op_value (engine, node->netlist_node_name, &value);
+ if (!got) {
+ tmp = g_strdup_printf ("V(%s)",
+ node->netlist_node_name);
+ //got = sim_engine_get_op_value (engine, tmp, &value);
+ } else
+ tmp = g_strdup (node->netlist_node_name);
+
+ if (got) {
+ /* Don't have more than one meter per node. */
+ if (g_hash_table_lookup (sv->priv->voltmeter_nodes,
+ tmp) != NULL)
+ continue;
+
+ g_hash_table_insert (sv->priv->voltmeter_nodes, tmp,
+ "there");
+
+ text = g_strdup_printf ("%.3f", value);
+
+ group = gnome_canvas_item_new (
+ gnome_canvas_root (
+ GNOME_CANVAS (sv->priv->sheet)),
+ gnome_canvas_group_get_type (),
+ "x", node->key.x - 20.0,
+ "y", node->key.y - 20.0,
+ NULL);
+
+ sv->priv->voltmeter_items = g_list_prepend (
+ sv->priv->voltmeter_items, group);
+
+ item = gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (group),
+ gnome_canvas_rect_get_type (),
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", 40.0,
+ "y2", 10.0,
+ "fill_color", "light gray",
+ "outline_color", "black",
+ NULL);
+
+ gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (group),
+ gnome_canvas_text_get_type (),
+ "x", 2.0,
+ "y", 1.0,
+ "anchor", GTK_ANCHOR_NW,
+ "text", text,
+ "fill_color", "black",
+ "font", OP_VALUE_FONT,
+ NULL);
+
+ g_free (text);
+ } else
+ g_free (tmp);
+
+ }
+
+ g_list_free (nodes);
+
+ schematic_view_show_voltmeters (sv, sv->priv->show_voltmeters);
+}
+
+void
+schematic_view_clear_op_values (SchematicView *sv)
+{
+ GList *list;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ for (list = sv->priv->voltmeter_items; list; list = list->next) {
+ gtk_object_destroy (GTK_OBJECT (list->data));
+ }
+
+ g_list_free (sv->priv->voltmeter_items);
+ sv->priv->voltmeter_items = NULL;
+
+ /* DONE (NOT TESTED): free the keys. - */
+ g_hash_table_destroy (sv->priv->voltmeter_nodes);
+ sv->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash,
+ g_str_equal, g_free, NULL);
+}
+
+static void
+schematic_view_show_voltmeters (SchematicView *sv, gboolean show)
+{
+ GList *list;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ for (list = sv->priv->voltmeter_items; list; list = list->next) {
+ if (show)
+ gnome_canvas_item_show (
+ GNOME_CANVAS_ITEM (list->data));
+ else
+ gnome_canvas_item_hide (
+ GNOME_CANVAS_ITEM (list->data));
+ }
+}
+
+void
+schematic_view_update_parts (SchematicView *sv)
+{
+ GList *list;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ for (list = sv->priv->items; list; list = list->next) {
+ if (IS_PART_ITEM (list->data))
+ part_item_update_node_label (PART_ITEM (list->data));
+ }
+}
+
diff --git a/src/schematic-view.h b/src/schematic-view.h
new file mode 100644
index 0000000..5d02156
--- /dev/null
+++ b/src/schematic-view.h
@@ -0,0 +1,127 @@
+/*
+ * schematic-view.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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __SCHEMATIC_VIEW_H
+#define __SCHEMATIC_VIEW_H
+
+#include <gtk/gtk.h>
+#include "schematic.h"
+#include "sheet.h"
+
+typedef enum {
+ DRAG_URI_INFO,
+ DRAG_PART_INFO
+} DragTypes;
+
+#define TYPE_SCHEMATIC_VIEW (schematic_view_get_type ())
+#define SCHEMATIC_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_SCHEMATIC_VIEW, SchematicView))
+#define SCHEMATIC_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_SCHEMATIC_VIEW, SchematicViewClass))
+#define IS_SCHEMATIC_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_SCHEMATIC_VIEW))
+#define IS_SCHEMATIC_VIEW_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_SCHEMATIC_VIEW, SchematicViewClass))
+
+typedef struct _SchematicView SchematicView;
+typedef struct _SchematicViewClass SchematicViewClass;
+typedef struct _SchematicViewPriv SchematicViewPriv;
+
+struct _SchematicView
+{
+ GObject parent;
+
+ GtkWidget *toplevel;
+
+ gpointer corba_server;
+ SchematicViewPriv *priv;
+};
+
+struct _SchematicViewClass
+{
+ GObjectClass parent_class;
+
+ /* Signals go here */
+ void (*changed) (SchematicView *schematic_view);
+};
+
+
+GType schematic_view_get_type (void);
+void schematic_view_load (SchematicView *sv, Schematic *sm);
+SchematicView *schematic_view_new (Schematic *schematic);
+Sheet *schematic_view_get_sheet (SchematicView *sv);
+Schematic *schematic_view_get_schematic (SchematicView *sv);
+
+/*
+ * Signal emission wrappers.
+ */
+void schematic_view_reset_tool (SchematicView *sv);
+void schematic_view_cancel (SchematicView *sv);
+
+/*
+ * Misc.
+ */
+void schematic_view_set_browser (SchematicView *sv, gpointer p);
+gpointer schematic_view_get_browser (SchematicView *sv);
+void schematic_view_add_ghost_item (SchematicView *sv,
+ ItemData *data);
+void schematic_view_delete_selection (SchematicView *sv);
+void schematic_view_rotate_selection (SchematicView *sv);
+void schematic_view_rotate_ghosts (SchematicView *sv);
+void schematic_view_flip_selection (SchematicView *sv,
+ gboolean horizontal);
+void schematic_view_flip_ghosts (SchematicView *sv,
+ gboolean horizontal);
+GList * schematic_view_get_selection (SchematicView *sv);
+void schematic_view_clear_ghosts (SchematicView *sv);
+void schematic_view_set_parent (SchematicView *sv, GtkDialog *dialog);
+GList * schematic_view_get_items (SchematicView *sv);
+void schematic_view_select_all (SchematicView *sv, gboolean select);
+void schematic_view_update_parts (SchematicView *sv);
+/*
+ * Clipboard operations.
+ */
+void schematic_view_copy_selection (SchematicView *sv);
+void schematic_view_cut_selection (SchematicView *sv);
+void schematic_view_paste (SchematicView *sv);
+
+/*
+ * Logging.
+ */
+void schematic_view_log_show (SchematicView *sv, gboolean explicit);
+gboolean schematic_view_log_window_exists (SchematicView *sv);
+
+/*
+ * Voltmeter.
+ */
+/*
+void schematic_view_show_op_values (SchematicView *sv,
+ SimEngine *engine);
+*/
+
+void schematic_view_clear_op_values (SchematicView *sv);
+
+#endif /* __SCHEMATIC_VIEW_H */
+
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..a5b1685
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,274 @@
+/*
+ * settings.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 <unistd.h>
+#include <glib.h>
+#include <gnome.h>
+#include <glade/glade.h>
+#include <stdio.h>
+#include "main.h"
+#include "string.h"
+#include "settings.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "dialogs.h"
+#include "oregano-utils.h"
+#include "oregano-config.h"
+
+/* Engines Titles */
+static const gchar*
+engine[] = {
+ "gnucap",
+ "ngspice"
+};
+typedef struct {
+ Schematic *sm;
+ GtkWidget *pbox; /* Property box */
+
+ GtkWidget *w_show_splash;
+ GtkWidget *w_show_log;
+
+ GtkWidget *w_compress_files;
+ GtkWidget *w_engine;
+} Settings;
+#define SETTINGS(x) ((Settings*)(x))
+
+GtkWidget *engine_path;
+GtkWidget *button[2];
+static void
+change_modeldir_cb (GtkFileChooser *chooser, gpointer user_data)
+{
+ gchar *model_dir;
+
+ model_dir = gtk_file_chooser_get_uri (chooser);
+}
+
+static void
+change_librarydir_cb (GtkFileChooser *chooser, gpointer user_data)
+{
+ gchar *library_dir;
+
+ library_dir = gtk_file_chooser_get_uri (chooser);
+}
+
+static void
+change_engine_path_cb (GtkFileChooser *chooser,
+ gpointer user_data)
+{
+ gchar *engine_path;
+ gboolean has_changed = FALSE;
+ Settings *s;
+ s = SETTINGS (user_data);
+
+ int engine_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (s->w_engine), "id"));
+ engine_path = gtk_file_chooser_get_filename (chooser);
+}
+
+static void
+apply_callback (GtkWidget *w, Settings *s)
+{
+ oregano.engine = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (s->w_engine), "id"));
+ oregano.compress_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->w_compress_files));
+ oregano.show_log = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->w_show_log ));
+ oregano.show_splash = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->w_show_splash ));
+
+ oregano_config_save ();
+
+ gtk_widget_destroy (s->pbox);
+ s->pbox = NULL;
+}
+
+static gboolean
+delete_event_callback (GtkWidget *w, GdkEvent *event, Settings *s)
+{
+ apply_callback(w, s);
+}
+
+static void
+set_engine_name (GtkWidget *w, Settings *s)
+{
+ int engine_id;
+ s->w_engine = w;
+ engine_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (s->w_engine), "id"));
+ if (g_find_program_in_path (engine[engine_id]) != NULL) {
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(engine_path),
+ g_find_program_in_path (engine[engine_id]));
+ }
+ else
+ {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button[engine_id])) ){
+ GString *msg = g_string_new(_("Engine <span weight=\"bold\" size=\"large\">"));
+ msg = g_string_append (msg, engine[engine_id]);
+ msg = g_string_append (msg, _("</span> not found\nThe engine is unable to locate the external program."));
+ oregano_warning_with_title (_("Warning"), msg->str);
+ g_string_free (msg, TRUE);
+ engine_id = (engine_id +1) % 2;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[engine_id]), TRUE);
+ }
+ else gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[engine_id]), FALSE);
+ }
+}
+
+gpointer
+settings_new (Schematic *sm)
+{
+ Settings *s;
+
+ s = g_new0 (Settings, 1);
+ s->sm = sm;
+
+ return s;
+}
+
+void
+settings_show (GtkWidget *widget, SchematicView *sv)
+{
+ gint i;
+ GtkWidget *engine_group = NULL;
+ GtkWidget *w, *pbox, *toplevel;
+ GtkWidget *w0;
+ GladeXML *gui = NULL;
+ Settings *s;
+ Schematic *sm;
+ int engine_id = oregano.engine;
+
+ gchar *fname, *library_dir, *model_dir;
+ struct dirent *libentry;
+ Library *library;
+
+ g_return_if_fail (sv != NULL);
+
+ sm = schematic_view_get_schematic (sv);
+ s = schematic_get_settings (sm);
+
+ /* Only allow one instance of the property box per schematic. */
+ if (GTK_WIDGET(SETTINGS (s)->pbox)){
+ gdk_window_raise (GTK_WIDGET (SETTINGS (s)->pbox)->window);
+ return;
+ }
+
+ if (!g_file_test (OREGANO_GLADEDIR "/settings.glade", G_FILE_TEST_EXISTS)) {
+ gchar *msg;
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/settings.glade");
+ oregano_error_with_title (_("Could not create settings dialog"), msg);
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/settings.glade", NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create settings dialog"));
+ return;
+ }
+
+ w = toplevel = glade_xml_get_widget (gui, "toplevel");
+ if (!w) {
+ oregano_error (_("Could not create settings dialog"));
+ return;
+ }
+ g_signal_connect (G_OBJECT (w), "delete_event",
+ G_CALLBACK (delete_event_callback), s);
+
+ pbox = toplevel;
+ s->pbox = GTK_WIDGET (pbox);
+
+ w = glade_xml_get_widget (gui, "close_bt");
+ g_signal_connect (G_OBJECT (w), "clicked",
+ G_CALLBACK (apply_callback), s);
+
+ w = glade_xml_get_widget (gui, "splash-enable");
+ s->w_show_splash = w;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), oregano.show_splash);
+
+ w = glade_xml_get_widget (gui, "compress-enable");
+ s->w_compress_files = w;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+ oregano.compress_files);
+
+ w = glade_xml_get_widget (gui, "log-enable");
+ s->w_show_log = w;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), oregano.show_log);
+
+ w = glade_xml_get_widget (gui, "grid-size");
+ gtk_widget_set_sensitive (w, FALSE);
+ w = glade_xml_get_widget (gui, "realtime-enable");
+ gtk_widget_set_sensitive (w, FALSE);
+
+
+ w = glade_xml_get_widget (gui, "engine_table");
+ for (i = 0; i < OREGANO_ENGINE_COUNT; i++) {
+ OreganoEngine *engine;
+
+ if (engine_group)
+ button[i] = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (engine_group), engines[i]);
+ else
+ button[i] = engine_group = gtk_radio_button_new_with_label_from_widget (NULL, engines[i]);
+
+ /* Check if the engine is available */
+ g_object_set_data (G_OBJECT (button[i]), "id", GUINT_TO_POINTER (i));
+
+ gtk_table_attach (GTK_TABLE (w), button[i], 0, 1, i, i+1,
+ GTK_EXPAND|GTK_FILL, GTK_SHRINK, 6, 6);
+ g_signal_connect (G_OBJECT (button[i]), "clicked",
+ G_CALLBACK (set_engine_name), s);
+ s->w_engine = GINT_TO_POINTER (oregano.engine);
+
+ }
+ /* Libraries directory */
+ w = glade_xml_get_widget (gui, "library-path-entry");
+ gtk_file_chooser_set_action (GTK_FILE_CHOOSER(w),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(w),
+ OREGANO_LIBRARYDIR);
+ g_signal_connect (G_OBJECT (w), "selection-changed",
+ G_CALLBACK (change_librarydir_cb), w0);
+
+ /* Models directory */
+
+ w = glade_xml_get_widget (gui, "model-path-entry");
+ gtk_file_chooser_set_action (GTK_FILE_CHOOSER(w),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(w),
+ OREGANO_MODELDIR);
+ g_signal_connect (G_OBJECT (w), "selection-changed",
+ G_CALLBACK (change_modeldir_cb), w0);
+
+ /* Engine localisation */
+ engine_path = glade_xml_get_widget (gui, "engine-path");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[oregano.engine]), TRUE);
+ set_engine_name(button[oregano.engine], s);
+ g_signal_connect (G_OBJECT (engine_path), "file-set",
+ G_CALLBACK (change_engine_path_cb), s);
+
+
+ gtk_widget_show_all (toplevel);
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 0000000..bf36c49
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,42 @@
+/*
+ * settings.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 __SETTINGS_H
+#define __SETTINGS_H
+
+#include <gtk/gtk.h>
+#include "schematic.h"
+#include "schematic-view.h"
+#include "engine.h"
+
+void settings_show (GtkWidget *widget, SchematicView *sv);
+gpointer settings_new (Schematic *sm);
+
+#endif
diff --git a/src/sheet/Makefile.am b/src/sheet/Makefile.am
new file mode 100644
index 0000000..37291fc
--- /dev/null
+++ b/src/sheet/Makefile.am
@@ -0,0 +1,28 @@
+oreganodir = $(datadir)/oregano
+INCLUDES = \
+ $(OREGANO_CFLAGS) -I$(top_srcdir)/src -I$(top_srcdir)/src/model \
+ -DOREGANO_GLADEDIR=\""$(oreganodir)/glade"\" \
+ -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \
+ -DOREGANO_MODELDIR=\""$(oreganodir)/models"\"
+
+noinst_LIBRARIES = libsheet.a
+
+libsheet_a_SOURCES = \
+ grid.c \
+ grid.h \
+ node-item.c \
+ node-item.h \
+ part-item.c \
+ part-item.h \
+ sheet.c \
+ sheet.h \
+ sheet-item.c \
+ sheet-item.h \
+ sheet-item-factory.c \
+ sheet-item-factory.h \
+ sheet-private.h \
+ textbox-item.c \
+ textbox-item.h \
+ wire-item.c \
+ wire-item.h
+libsheet_a_LIBADD = libsheet.a
diff --git a/src/sheet/grid.c b/src/sheet/grid.c
new file mode 100644
index 0000000..9a06ee7
--- /dev/null
+++ b/src/sheet/grid.c
@@ -0,0 +1,394 @@
+/*
+ * grid.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 <math.h>
+#include "grid.h"
+#include "sheet-private.h"
+
+#define ROUND(x) (floor((x)+0.5))
+
+enum {
+ ARG_0,
+ ARG_COLOR,
+ ARG_SPACING,
+ ARG_SNAP
+};
+
+typedef struct {
+ guint snap : 1;
+ guint visible : 1;
+ GdkColor color;
+ gdouble spacing;
+ gdouble cached_zoom;
+ GdkGC *gc;
+} GridPriv;
+
+static void grid_class_init (GridClass *class);
+static void grid_init (Grid *grid);
+static void grid_destroy (GtkObject *object);
+static void grid_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+static void grid_get_property (GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec);
+static void grid_update (GnomeCanvasItem *item, gdouble *affine,
+ ArtSVP *clip_path, gint flags);
+static void grid_realize (GnomeCanvasItem *item);
+static void grid_unrealize (GnomeCanvasItem *item);
+static void grid_draw (GnomeCanvasItem *grid, GdkDrawable *drawable,
+ gint x, gint y, gint width, gint height);
+static double grid_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx,
+ gint cy, GnomeCanvasItem **actual_item);
+
+static GnomeCanvasItemClass *grid_parent_class;
+
+GType
+grid_get_type (void)
+{
+ static GType grid_type = 0;
+
+ if (!grid_type) {
+ static const GTypeInfo grid_info = {
+ sizeof (GridClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) grid_class_init,
+ NULL,
+ NULL,
+ sizeof (Grid),
+ 0,
+ (GInstanceInitFunc) grid_init,
+ NULL
+ };
+
+ grid_type = g_type_register_static(GNOME_TYPE_CANVAS_ITEM,
+ "Grid", &grid_info, 0);
+ }
+
+ return grid_type;
+}
+
+static void
+grid_class_init (GridClass *class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ GnomeCanvasItemClass *item_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ gtk_object_class = GTK_OBJECT_CLASS(class);
+ item_class = GNOME_CANVAS_ITEM_CLASS(class);
+
+ gtk_object_class->destroy = grid_destroy;
+
+ grid_parent_class = g_type_class_peek_parent (class);
+
+ object_class->set_property = grid_set_property;
+ object_class->get_property = grid_get_property;
+
+ g_object_class_install_property(
+ object_class,
+ ARG_COLOR,
+ g_param_spec_string("color", "Grid::color", "the color",
+ "black", G_PARAM_WRITABLE)
+ );
+ g_object_class_install_property(
+ object_class,
+ ARG_SPACING,
+ g_param_spec_double("spacing", "Grid::spacing",
+ "the grid spacing", 0.0f, 100.0f, 10.0f,
+ G_PARAM_READWRITE)
+ );
+ g_object_class_install_property(
+ object_class,
+ ARG_SNAP,
+ g_param_spec_boolean("snap", "Grid::snap", "snap to grid?",
+ TRUE,G_PARAM_READWRITE)
+ );
+
+ item_class->update = grid_update;
+ item_class->realize = grid_realize;
+ item_class->unrealize = grid_unrealize;
+ item_class->draw = grid_draw;
+ item_class->point = grid_point;
+}
+
+static void
+grid_init (Grid *grid)
+{
+ GridPriv *priv;
+
+ priv = g_new0 (GridPriv, 1);
+
+ grid->priv = priv;
+
+ priv->spacing = 10.0;
+ priv->snap = TRUE;
+ priv->visible = TRUE;
+}
+
+static void
+grid_destroy (GtkObject *object)
+{
+ Grid *grid;
+ GridPriv *priv;
+
+ grid = GRID (object);
+ priv = grid->priv;
+
+ if (GTK_OBJECT_CLASS(grid_parent_class)->destroy) {
+ GTK_OBJECT_CLASS(grid_parent_class)->destroy(object);
+ }
+}
+
+inline void
+snap_to_grid (Grid *grid, gdouble *x, gdouble *y)
+{
+ GridPriv *priv;
+ gdouble spacing;
+
+ priv = grid->priv;
+ spacing = priv->spacing;
+
+ if (priv->snap) {
+ if (x) *x = ROUND((*x) / spacing) * spacing;
+ if (y) *y = ROUND((*y) / spacing) * spacing;
+ }
+}
+
+static void
+grid_set_property (GObject *object, guint prop_id, const GValue *value,
+ GParamSpec *spec)
+{
+ GnomeCanvasItem *item;
+ Grid *grid;
+ GridPriv *priv;
+ GdkColor color;
+
+ item = GNOME_CANVAS_ITEM (object);
+ grid = GRID (object);
+ priv = grid->priv;
+
+ switch (prop_id){
+ case ARG_COLOR:
+ if (gnome_canvas_get_color (item->canvas,
+ g_value_get_string(value), &color)) {
+ priv->color = color;
+ } else {
+ color.pixel = 0;
+ priv->color = color;
+ }
+ if (priv->gc)
+ gdk_gc_set_foreground (priv->gc, &color);
+ break;
+
+ case ARG_SPACING:
+ priv->spacing = g_value_get_double(value);
+ break;
+
+ case ARG_SNAP:
+ priv->snap = g_value_get_boolean (value);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+grid_get_property (GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec)
+{
+ Grid *grid;
+ GridPriv *priv;
+
+ grid = GRID (object);
+ priv = grid->priv;
+
+ switch (prop_id){
+ case ARG_SPACING:
+ g_value_set_double(value, priv->spacing);
+ break;
+ case ARG_SNAP:
+ g_value_set_boolean(value, priv->snap);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(grid, prop_id, spec);
+ break;
+ }
+}
+
+void
+grid_show (Grid *grid, gboolean show)
+{
+ GridPriv *priv;
+
+ priv = grid->priv;
+
+ priv->visible = show;
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (grid));
+}
+
+gint
+grid_is_snap (Grid *grid)
+{
+ GridPriv *priv;
+
+ priv = grid->priv;
+
+ return priv->snap;
+}
+
+gint
+grid_is_show (Grid *grid)
+{
+ GridPriv *priv;
+
+ priv = grid->priv;
+
+ return priv->visible;
+}
+
+void
+grid_snap (Grid *grid, gboolean snap)
+{
+ GridPriv *priv;
+
+ priv = grid->priv;
+
+ priv->snap = snap;
+}
+
+static void
+grid_update (GnomeCanvasItem *item, gdouble *affine, ArtSVP *clip_path,
+ gint flags)
+{
+ Grid *grid;
+ GridPriv *priv;
+
+ grid = GRID (item);
+
+ priv = grid->priv;
+
+ priv->cached_zoom = SHEET(item->canvas)->priv->zoom;
+
+ if (grid_parent_class->update)
+ (* grid_parent_class->update) (item, affine, clip_path, flags);
+
+ gdk_gc_set_foreground (priv->gc, &priv->color);
+ gnome_canvas_update_bbox (item, 0, 0, INT_MAX, INT_MAX);
+}
+
+static void
+grid_realize (GnomeCanvasItem *item)
+{
+ Grid *grid;
+ GridPriv *priv;
+
+ grid = GRID (item);
+
+ priv = grid->priv;
+
+ if (grid_parent_class->realize)
+ (* grid_parent_class->realize) (item);
+
+ priv->gc = gdk_gc_new (item->canvas->layout.bin_window);
+}
+
+static void
+grid_unrealize (GnomeCanvasItem *item)
+{
+ Grid *grid;
+ GridPriv *priv;
+
+ grid = GRID (item);
+ priv = grid->priv;
+
+ g_object_unref (priv->gc);
+
+ if (grid_parent_class->unrealize)
+ (* grid_parent_class->unrealize) (item);
+}
+
+inline static gint
+start_coord (long c, long g)
+{
+ long m;
+
+ if (c > 0){
+ m = c % g;
+ if (m == 0)
+ return m;
+ else
+ return g - m;
+ } else
+ return (-c) % g;
+}
+
+static void
+grid_draw (GnomeCanvasItem *canvas, GdkDrawable *drawable, gint x, gint y,
+ gint width, gint height)
+{
+ GridPriv *priv;
+ Grid *grid;
+ GdkGC *gc;
+ gdouble gx, gy, sgx, sgy;
+ gdouble spacing;
+
+ grid = GRID(canvas);
+ priv = grid->priv;
+
+ if (!priv->visible)
+ return;
+
+ spacing = priv->spacing * priv->cached_zoom * 10000;
+ gc = priv->gc;
+
+ sgx = start_coord (x * 10000, spacing) - spacing;
+ sgy = start_coord (y * 10000, spacing) - spacing;
+
+ for (gx = sgx; gx <= width * 10000; gx += spacing)
+ for (gy = sgy; gy <= height * 10000; gy += spacing) {
+ gdk_draw_point (drawable, gc, rint (gx/10000 + 0.45),
+ rint (gy/10000 + 0.45));
+ }
+}
+
+static double
+grid_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, gint cy,
+ GnomeCanvasItem **actual_item)
+{
+ /*
+ * The grid is everywhere. (That's a bug).
+ */
+ *actual_item = item;
+ return 0.0;
+}
+
diff --git a/src/sheet/grid.h b/src/sheet/grid.h
new file mode 100644
index 0000000..3835e9d
--- /dev/null
+++ b/src/sheet/grid.h
@@ -0,0 +1,61 @@
+/*
+ * grid.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 __GRID_H
+#define __GRID_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#define GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, grid_get_type (), Grid))
+#define GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, grid_get_type (), GridClass))
+#define IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, grid_get_type ()))
+
+typedef struct _Grid Grid;
+typedef struct _GridClass GridClass;
+
+struct _Grid {
+ GnomeCanvasItem item;
+
+ gpointer priv;
+};
+
+struct _GridClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+GType grid_get_type (void);
+void snap_to_grid(Grid *grid, double *x, double *y);
+
+void grid_show (Grid *grid, gboolean snap);
+void grid_snap (Grid *grid, gboolean snap);
+gint grid_is_show (Grid *grid);
+gint grid_is_snap (Grid *grid);
+
+#endif
+
diff --git a/src/sheet/node-item.c b/src/sheet/node-item.c
new file mode 100644
index 0000000..a4319bf
--- /dev/null
+++ b/src/sheet/node-item.c
@@ -0,0 +1,112 @@
+/*
+ * node-item.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 <gnome.h>
+#include "node-item.h"
+
+static void node_item_init (NodeItem *item);
+static void node_item_class_init (NodeItemClass *klass);
+
+struct _NodeItemPriv {
+ GnomeCanvasItem *dot_item;
+};
+
+static GnomeCanvasGroupClass *parent_class = NULL;
+
+
+GType
+node_item_get_type (void)
+{
+ static GType item_type = 0;
+
+ if (!item_type)
+ {
+ static const GTypeInfo item_info =
+ {
+ sizeof (NodeItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) node_item_class_init,
+ NULL,
+ NULL,
+ sizeof (NodeItem),
+ 0,
+ (GInstanceInitFunc) node_item_init,
+ NULL
+ };
+
+ item_type = g_type_register_static(GNOME_TYPE_CANVAS_GROUP, "NodeItem",
+ &item_info, 0);
+ }
+
+ return item_type;
+}
+
+static void
+node_item_class_init (NodeItemClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void
+node_item_init (NodeItem *item)
+{
+ item->priv = g_new0 (NodeItemPriv, 1);
+}
+
+void
+node_item_show_dot (NodeItem *item, gboolean show)
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_NODE_ITEM (item));
+
+ if (show) {
+ if (item->priv->dot_item == NULL) {
+ item->priv->dot_item = gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_ellipse_get_type (),
+ "x1", -2.0,
+ "y1", -2.0,
+ "x2", 2.0,
+ "y2", 2.0,
+ "fill_color", "black",
+ NULL);
+ }
+
+ gnome_canvas_item_show (item->priv->dot_item);
+ } else {
+ if (item->priv->dot_item != NULL)
+ gnome_canvas_item_hide (item->priv->dot_item);
+ }
+} \ No newline at end of file
diff --git a/src/sheet/node-item.h b/src/sheet/node-item.h
new file mode 100644
index 0000000..141c72c
--- /dev/null
+++ b/src/sheet/node-item.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * node-item.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 __NODE_ITEM_H__
+#define __NODE_ITEM_H__
+
+#include <gtk/gtk.h>
+#include <gnome.h>
+
+#define TYPE_NODE_ITEM (node_item_get_type ())
+#define NODE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NODE_ITEM, NodeItem))
+#define NODE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_NODE_ITEM, NodeItemClass))
+#define IS_NODE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NODE_ITEM))
+#define IS_NODE_ITEM_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_NODE_ITEM, NodeItemClass))
+
+
+typedef struct _NodeItem NodeItem;
+typedef struct _NodeItemClass NodeItemClass;
+typedef struct _NodeItemPriv NodeItemPriv;
+
+struct _NodeItem
+{
+ GnomeCanvasGroup parent;
+ NodeItemPriv *priv;
+};
+
+struct _NodeItemClass
+{
+ GnomeCanvasGroupClass parent_class;
+};
+
+
+GType node_item_get_type (void);
+GtkWidget *node_item_new (void);
+void node_item_show_dot (NodeItem *item, gboolean show);
+
+#endif /* __NODE_ITEM_H__ */
diff --git a/src/sheet/part-item.c b/src/sheet/part-item.c
new file mode 100644
index 0000000..cdad022
--- /dev/null
+++ b/src/sheet/part-item.c
@@ -0,0 +1,1632 @@
+/*
+ * part-item.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 <glade/glade.h>
+#include <math.h>
+#include <string.h>
+#include "main.h"
+#include "schematic-view.h"
+#include "sheet-private.h"
+#include "sheet-item.h"
+#include "part-item.h"
+#include "part-private.h"
+#include "part-property.h"
+#include "load-library.h"
+#include "load-common.h"
+#include "part-label.h"
+#include "stock.h"
+#include "dialogs.h"
+
+
+#define NORMAL_COLOR "red"
+#define LABEL_COLOR "dark cyan"
+#define SELECTED_COLOR "green"
+#define O_DEBUG 0
+
+static void part_item_class_init(PartItemClass *klass);
+static void part_item_init(PartItem *gspart);
+static void part_item_destroy(GtkObject *object);
+static void part_item_moved(SheetItem *sheet_item);
+
+static void edit_properties (SheetItem *object);
+
+static void selection_changed (PartItem *item, gboolean select,
+ gpointer user_data);
+static int select_idle_callback (PartItem *item);
+static int deselect_idle_callback (PartItem *item);
+
+static void update_canvas_labels (PartItem *part_item);
+
+static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2);
+inline static void get_cached_bounds (PartItem *item, SheetPos *p1,
+ SheetPos *p2);
+static void show_labels (SheetItem *sheet_item, gboolean show);
+static void part_item_paste (SchematicView *sv, ItemData *data);
+
+static void part_rotated_callback (ItemData *data, int angle, SheetItem *item);
+static void part_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item);
+
+static void part_moved_callback (ItemData *data, SheetPos *pos,
+ SheetItem *item);
+
+static void part_item_place (SheetItem *item, SchematicView *sv);
+static void part_item_place_ghost (SheetItem *item, SchematicView *sv);
+
+static void create_canvas_items (GnomeCanvasGroup *group,
+ LibraryPart *library_part);
+static void create_canvas_labels (PartItem *item, Part *part);
+static void create_canvas_label_nodes (PartItem *item, Part *part);
+
+static void part_item_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *spec);
+static void part_item_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+
+enum {
+ ARG_0,
+ ARG_NAME,
+ ARG_SYMNAME,
+ ARG_LIBNAME,
+ ARG_REFDES,
+ ARG_TEMPLATE,
+ ARG_MODEL
+};
+
+struct _PartItemPriv {
+ guint cache_valid : 1;
+ guint highlight : 1;
+
+ GnomeCanvasGroup *label_group;
+ GSList *label_items;
+
+ GnomeCanvasGroup *node_group;
+ GSList *label_nodes;
+
+ /*
+ * Cached bounding box. This is used to make
+ * the rubberband selection a bit faster.
+ */
+ SheetPos bbox_start;
+ SheetPos bbox_end;
+};
+
+typedef struct {
+ GtkDialog *dialog;
+ PartItem *part_item;
+
+ /* List of GtkEntry's */
+ GList *widgets;
+} PartPropDialog;
+
+static PartPropDialog *prop_dialog = NULL;
+static SheetItemClass *parent_class = NULL;
+
+static const char *part_item_context_menu =
+"<ui>"
+" <popup name='ItemMenu'>"
+" <menuitem action='ObjectProperties'/>"
+" </popup>"
+"</ui>";
+static GtkActionEntry action_entries[] = {
+ {"ObjectProperties", GTK_STOCK_PROPERTIES, N_("_Object Properties..."), NULL, N_("Modify the object's properties"), NULL}//G_CALLBACK (object_properties_cmd)}
+};
+
+enum {
+ ANCHOR_NORTH,
+ ANCHOR_SOUTH,
+ ANCHOR_WEST,
+ ANCHOR_EAST
+};
+
+GtkAnchorType part_item_get_anchor_from_part (Part *part)
+{
+ int anchor_h, anchor_v;
+ int angle;
+ IDFlip flip;
+
+ flip = part_get_flip (part);
+ angle = part_get_rotation (part);
+
+ switch (angle) {
+ case 0:
+ anchor_h = ANCHOR_SOUTH;
+ anchor_v = ANCHOR_WEST;
+ break;
+ case 90:
+ anchor_h = ANCHOR_NORTH;
+ anchor_v = ANCHOR_WEST;
+ /* Invert Rotation */
+ if (flip & ID_FLIP_HORIZ)
+ flip = ID_FLIP_VERT;
+ else if (flip & ID_FLIP_VERT)
+ flip = ID_FLIP_HORIZ;
+ break;
+ }
+
+ if (flip & ID_FLIP_HORIZ) {
+ anchor_v = ANCHOR_EAST;
+ }
+ if (flip & ID_FLIP_VERT) {
+ anchor_h = ANCHOR_NORTH;
+ }
+
+ if ((anchor_v == ANCHOR_EAST) && (anchor_h == ANCHOR_NORTH))
+ return GTK_ANCHOR_NORTH_EAST;
+ if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_NORTH))
+ return GTK_ANCHOR_NORTH_WEST;
+ if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_SOUTH))
+ return GTK_ANCHOR_SOUTH_WEST;
+
+ return GTK_ANCHOR_SOUTH_EAST;
+}
+
+GType
+part_item_get_type ()
+{
+ static GType part_item_type = 0;
+
+ if (!part_item_type) {
+ static const GTypeInfo part_item_info = {
+ sizeof(PartItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) part_item_class_init,
+ NULL,
+ NULL,
+ sizeof (PartItem),
+ 0,
+ (GInstanceInitFunc) part_item_init,
+ NULL
+ };
+ part_item_type = g_type_register_static(TYPE_SHEET_ITEM,
+ "PartItem", &part_item_info, 0);
+ }
+ return part_item_type;
+}
+
+static void
+part_item_class_init (PartItemClass *part_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ SheetItemClass *sheet_item_class;
+
+ object_class = G_OBJECT_CLASS(part_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(part_item_class);
+ sheet_item_class = SHEET_ITEM_CLASS(part_item_class);
+ parent_class = g_type_class_peek_parent(part_item_class);
+
+ object_class->set_property = part_item_set_property;
+ object_class->get_property = part_item_get_property;
+
+ g_object_class_install_property (
+ object_class,
+ ARG_NAME,
+ g_param_spec_pointer ("name",
+ "PartItem::name",
+ "name",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (
+ object_class,
+ ARG_SYMNAME,
+ g_param_spec_pointer ("symbol_name",
+ "PartItem::symbol_name",
+ "symbol name",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (
+ object_class,
+ ARG_REFDES,
+ g_param_spec_pointer ("refdes",
+ "PartItem::refdes",
+ "refdes",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (
+ object_class,
+ ARG_LIBNAME,
+ g_param_spec_pointer ("library_name",
+ "PartItem::library_name",
+ "library_name",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (
+ object_class,
+ ARG_TEMPLATE,
+ g_param_spec_pointer ("template",
+ "PartItem::template",
+ "template",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+ g_object_class_install_property (
+ object_class,
+ ARG_MODEL,
+ g_param_spec_pointer ("model",
+ "PartItem::model",
+ "model",
+ G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+ gtk_object_class->destroy = part_item_destroy;
+
+ sheet_item_class->moved = part_item_moved;
+ sheet_item_class->is_in_area = is_in_area;
+ sheet_item_class->show_labels = show_labels;
+ sheet_item_class->paste = part_item_paste;
+ sheet_item_class->edit_properties = edit_properties;
+ sheet_item_class->selection_changed = (gpointer) selection_changed;
+
+ sheet_item_class->place = part_item_place;
+ sheet_item_class->place_ghost = part_item_place_ghost;
+}
+
+static void
+part_item_init (PartItem *item)
+{
+ PartItemPriv *priv;
+
+ priv = g_new0 (PartItemPriv, 1);
+
+ priv->highlight = FALSE;
+ priv->cache_valid = FALSE;
+
+ item->priv = priv;
+
+ sheet_item_add_menu (SHEET_ITEM (item), part_item_context_menu,
+ action_entries, G_N_ELEMENTS (action_entries));
+}
+
+static void
+part_item_set_property (GObject *object, guint propety_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PartItem *part_item;
+ PartItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_PART_ITEM(object));
+
+ part_item = PART_ITEM(object);
+ priv = part_item->priv;
+}
+
+static void
+part_item_get_property (GObject *object, guint propety_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PartItem *part_item;
+ PartItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_PART_ITEM (object));
+
+ part_item = PART_ITEM (object);
+ priv = part_item->priv;
+}
+
+static void
+part_item_destroy(GtkObject *object)
+{
+ PartItem *item;
+ PartItemPriv *priv;
+ /*
+ * Unused variable
+ Part *part;
+ */
+ ArtPoint *old;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_PART_ITEM (object));
+
+ /*
+ * Free the stored coordinate that lets us rotate circles.
+ */
+ old = g_object_get_data(G_OBJECT(object), "hack");
+ if (old)
+ g_free (old);
+
+ item = PART_ITEM (object);
+ priv = item->priv;
+
+ if (priv) {
+ if (priv->label_group) {
+ /* GnomeCanvasGroups utiliza GtkObject todavia */
+ gtk_object_destroy(GTK_OBJECT(priv->label_group));
+ priv->label_group = NULL;
+ }
+
+ g_free (priv);
+ item->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy) {
+ GTK_OBJECT_CLASS (parent_class)->destroy(object);
+ }
+}
+
+static void
+part_item_set_label_items (PartItem *item, GSList *item_list)
+{
+ PartItemPriv *priv;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_PART_ITEM (item));
+
+ priv = item->priv;
+
+ if (priv->label_items)
+ g_slist_free (priv->label_items);
+
+ priv->label_items = item_list;
+}
+
+/*
+ * part_item_moved
+ *
+ * "moved" signal handler. Invalidates the bounding box cache.
+ */
+static void
+part_item_moved (SheetItem *sheet_item)
+{
+ PartItem *part_item;
+
+ part_item = PART_ITEM (sheet_item);
+ part_item->priv->cache_valid = FALSE;
+}
+
+PartItem *
+part_item_new (Sheet *sheet, Part *part)
+{
+ PartItem *item;
+ PartItemPriv *priv;
+
+ g_return_val_if_fail(sheet != NULL, NULL);
+ g_return_val_if_fail(IS_SHEET (sheet), NULL);
+ g_return_val_if_fail(part != NULL, NULL);
+ g_return_val_if_fail(IS_PART (part), NULL);
+
+ item = PART_ITEM(gnome_canvas_item_new(
+ sheet->object_group,
+ TYPE_PART_ITEM,
+ "data", part,
+ NULL));
+
+ priv = item->priv;
+
+ priv->label_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(
+ GNOME_CANVAS_GROUP(item),
+ GNOME_TYPE_CANVAS_GROUP,
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ priv->node_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new(
+ GNOME_CANVAS_GROUP(item),
+ GNOME_TYPE_CANVAS_GROUP,
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->node_group));
+
+ g_signal_connect_object(G_OBJECT(part),
+ "rotated",
+ G_CALLBACK (part_rotated_callback),
+ G_OBJECT(item),
+ 0);
+ g_signal_connect_object(G_OBJECT(part),
+ "flipped",
+ G_CALLBACK(part_flipped_callback),
+ G_OBJECT(item),
+ 0);
+ g_signal_connect_object(G_OBJECT(part),
+ "moved",
+ G_CALLBACK(part_moved_callback),
+ G_OBJECT(item),
+ 0);
+
+ return item;
+}
+
+static void
+update_canvas_labels (PartItem *item)
+{
+ PartItemPriv *priv;
+ Part *part;
+ GSList *labels, *label_items;
+ GnomeCanvasItem *canvas_item;
+
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(IS_PART_ITEM (item));
+
+ priv = item->priv;
+ part = PART(sheet_item_get_data(SHEET_ITEM(item)));
+
+ label_items = priv->label_items;
+
+ /* Put the label of each item */
+ for (labels = part_get_labels (part); labels;
+ labels = labels->next, label_items = label_items->next) {
+ char *text;
+ PartLabel *label = (PartLabel*) labels->data;
+ g_assert (label_items != NULL);
+ canvas_item = label_items->data;
+
+ text = part_property_expand_macros (part, label->text);
+ gnome_canvas_item_set (canvas_item, "text", text, NULL);
+ g_free (text);
+ }
+}
+
+void
+part_item_update_node_label (PartItem *item)
+{
+ PartItemPriv *priv;
+ Part *part;
+ GSList *labels;
+ GnomeCanvasItem *canvas_item;
+ Pin *pins;
+ gint num_pins, i;
+
+
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(IS_PART_ITEM (item));
+ priv = item->priv;
+ part = PART(sheet_item_get_data(SHEET_ITEM(item)));
+
+ g_return_if_fail( IS_PART(part) );
+
+ /* Put the label of each node */
+ num_pins = part_get_num_pins(part);
+ pins = part_get_pins(part);
+ labels = priv->label_nodes;
+ for (i=0; i<num_pins; i++, labels=labels->next) {
+ int x, y;
+ char *txt;
+ x = pins[i].offset.x;
+ y = pins[i].offset.y;
+
+ txt = g_strdup_printf("%d", pins[i].node_nr);
+ canvas_item = labels->data;
+ gnome_canvas_item_set (canvas_item, "text", txt, NULL);
+
+ g_free(txt);
+ }
+}
+
+static void
+prop_dialog_destroy (GtkWidget *widget, PartPropDialog *prop_dialog)
+{
+ g_free (prop_dialog);
+}
+
+static void
+prop_dialog_response(GtkWidget *dialog, gint response,
+ PartPropDialog *prop_dialog)
+{
+ GSList *props;
+ GList *widget;
+ Property *prop;
+ PartItem *item;
+ PartItemPriv *priv;
+ Part *part;
+ gchar *prop_name;
+ const gchar *prop_value;
+ GtkWidget *w;
+
+ item = prop_dialog->part_item;
+
+ priv = item->priv;
+ part = PART (sheet_item_get_data (SHEET_ITEM (item)));
+
+ for (widget = prop_dialog->widgets; widget;
+ widget = widget->next) {
+ w = widget->data;
+
+ prop_name = g_object_get_data (G_OBJECT (w), "user");
+ prop_value = gtk_entry_get_text (GTK_ENTRY (w));
+
+ for (props = part_get_properties (part); props; props = props->next) {
+ prop = props->data;
+ if (g_strcasecmp (prop->name, prop_name) == 0) {
+ if (prop->value) g_free (prop->value);
+ prop->value = g_strdup (prop_value);
+ }
+ }
+ g_free (prop_name);
+ }
+
+ update_canvas_labels (item);
+}
+
+static void
+edit_properties_point (PartItem *item)
+{
+ GSList *properties;
+ PartItemPriv *priv;
+ Part *part;
+ char *msg;
+ GladeXML *gui;
+ GtkRadioButton *radio_v, *radio_c;
+ GtkRadioButton *ac_r, *ac_m, *ac_i, *ac_p;
+ GtkCheckButton *chk_db;
+ gint response;
+
+ priv = item->priv;
+ part = PART (sheet_item_get_data (SHEET_ITEM (item)));
+
+ if (!g_file_test(
+ OREGANO_GLADEDIR "/clamp-properties-dialog.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/clamp-properties-dialog.glade");
+ oregano_error_with_title (_("Could not create part properties dialog."), msg);
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/clamp-properties-dialog.glade",
+ NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create part properties dialog."));
+ return;
+ }
+
+ prop_dialog = g_new0 (PartPropDialog, 1);
+
+ prop_dialog->part_item = item;
+
+ prop_dialog->dialog = GTK_DIALOG (glade_xml_get_widget (gui, "clamp-properties-dialog"));
+ gtk_dialog_set_has_separator (prop_dialog->dialog, FALSE);
+
+ radio_v = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_v"));
+ radio_c = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_c"));
+
+ /* FIXME: Deactivated up to finalisation of the analysis by the backend */
+ gtk_widget_set_sensitive (GTK_WIDGET (radio_c), FALSE);
+
+ ac_r = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_r"));
+ ac_m = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_m"));
+ ac_p = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_p"));
+ ac_i = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_i"));
+
+ chk_db = GTK_CHECK_BUTTON (glade_xml_get_widget (gui, "check_db"));
+
+ /* Setup GUI from properties */
+ for (properties = part_get_properties (part); properties;
+ properties = properties->next) {
+ Property *prop;
+ prop = properties->data;
+ if (prop->name) {
+ if (!g_strcasecmp (prop->name, "internal"))
+ continue;
+
+ if (!g_strcasecmp (prop->name, "type")) {
+ if (!g_strcasecmp (prop->value, "v")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_v), TRUE);
+ } else {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_c), TRUE);
+ }
+ } else if (!g_strcasecmp (prop->name, "ac_type")) {
+ if (!g_strcasecmp (prop->value, "m")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_m), TRUE);
+ } else if (!g_strcasecmp (prop->value, "i")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_i), TRUE);
+ } else if (!g_strcasecmp (prop->value, "p")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_p), TRUE);
+ } else if (!g_strcasecmp (prop->value, "r")) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_r), TRUE);
+ }
+ } else if (!g_strcasecmp (prop->name, "ac_db")) {
+ if (!g_strcasecmp (prop->value, "true"))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chk_db), TRUE);
+ }
+ }
+ }
+
+ response = gtk_dialog_run(prop_dialog->dialog);
+
+ /* Save properties from GUI */
+ for (properties = part_get_properties (part); properties;
+ properties = properties->next) {
+ Property *prop;
+ prop = properties->data;
+
+ if (prop->name) {
+ if (!g_strcasecmp (prop->name, "internal"))
+ continue;
+
+ if (!g_strcasecmp (prop->name, "type")) {
+ g_free (prop->value);
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_v))) {
+ prop->value = g_strdup ("v");
+ } else {
+ prop->value = g_strdup ("i");
+ }
+ } else if (!g_strcasecmp (prop->name, "ac_type")) {
+ g_free (prop->value);
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_m))) {
+ prop->value = g_strdup ("m");
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_i))) {
+ prop->value = g_strdup ("i");
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_p))) {
+ prop->value = g_strdup ("p");
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_r))) {
+ prop->value = g_strdup ("r");
+ }
+ } else if (!g_strcasecmp (prop->name, "ac_db")) {
+ g_free (prop->value);
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chk_db)))
+ prop->value = g_strdup ("true");
+ else
+ prop->value = g_strdup ("false");
+ }
+ }
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog));
+}
+
+static void
+edit_properties (SheetItem *object)
+{
+ GSList *properties;
+ PartItem *item;
+ PartItemPriv *priv;
+ Part *part;
+ char *internal, *msg;
+ GladeXML *gui;
+ GtkTable *prop_table;
+ GtkNotebook *notebook;
+ gint response, y = 0;
+ gboolean has_model;
+ gchar *model_name = NULL;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_PART_ITEM (object));
+
+ item = PART_ITEM (object);
+ priv = item->priv;
+ part = PART (sheet_item_get_data (SHEET_ITEM (item)));
+
+ internal = part_get_property (part, "internal");
+ if (internal) {
+ if (g_strcasecmp (internal, "ground") == 0) {
+ g_free (internal);
+ return;
+ }
+ /* Hack!! */
+ if (g_strcasecmp (internal, "point") == 0) {
+ edit_properties_point (item);
+ return;
+ }
+ }
+
+ g_free (internal);
+
+ if (!g_file_test(
+ OREGANO_GLADEDIR "/part-properties-dialog.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/part-properties-dialog.glade");
+ oregano_error_with_title (_("Could not create part properties dialog."), msg);
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/part-properties-dialog.glade",
+ NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create part properties dialog."));
+ return;
+ }
+
+ prop_dialog = g_new0 (PartPropDialog, 1);
+
+ prop_dialog->part_item = item;
+
+ prop_dialog->dialog = GTK_DIALOG ( glade_xml_get_widget (gui, "part-properties-dialog"));
+
+ prop_table = GTK_TABLE (glade_xml_get_widget (gui, "prop_table"));
+ notebook = GTK_NOTEBOOK (glade_xml_get_widget (gui, "notebook"));
+
+ g_signal_connect (prop_dialog->dialog, "destroy",
+ (GCallback) prop_dialog_destroy, prop_dialog);
+
+ prop_dialog->widgets = NULL;
+ has_model = FALSE;
+ for (properties = part_get_properties (part); properties;
+ properties = properties->next) {
+ Property *prop;
+
+ prop = properties->data;
+ if (prop->name) {
+ GtkWidget *entry;
+ GtkWidget *label;
+ gchar *temp=NULL;
+
+ if (!g_strcasecmp (prop->name, "internal"))
+ continue;
+
+ if (!g_strcasecmp (prop->name, "model")) {
+ has_model = TRUE;
+ model_name = g_strdup (prop->value);
+ }
+
+
+ /* Find the Refdes and replace by their real value */
+ temp = prop->name;
+ if (!g_ascii_strcasecmp (temp, "Refdes")) temp = _("Designation");
+ if (!g_ascii_strcasecmp (temp, "Template")) temp = _("Template");
+ if (!g_ascii_strcasecmp (temp, "Res")) temp = _("Resistor");
+ if (!g_ascii_strcasecmp (temp, "Cap")) temp = _("Capacitor");
+ if (!g_ascii_strcasecmp (temp, "Ind")) temp = _("Inductor");
+ label = gtk_label_new (temp);
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), prop->value);
+ g_object_set_data (G_OBJECT (entry), "user", g_strdup (prop->name));
+
+ gtk_table_attach (
+ prop_table, label,
+ 0, 1, y, y+1,
+ GTK_FILL|GTK_SHRINK,
+ GTK_FILL|GTK_SHRINK,
+ 8, 8);
+
+ gtk_table_attach (
+ prop_table, entry,
+ 1, 2, y, y+1,
+ GTK_EXPAND|GTK_FILL,
+ GTK_FILL|GTK_SHRINK,
+ 8, 8);
+
+ y++;
+ gtk_widget_show (label);
+ gtk_widget_show (entry);
+
+ prop_dialog->widgets = g_list_prepend (prop_dialog->widgets, entry);
+ }
+ }
+
+ if (!has_model) {
+ gtk_notebook_remove_page (notebook, 1);
+ } else {
+ GtkTextBuffer *txtbuffer;
+ GtkTextView *txtmodel;
+ gchar *filename, *str;
+ GError *read_error = NULL;
+
+ txtmodel = GTK_TEXT_VIEW (glade_xml_get_widget (gui, "txtmodel"));
+ txtbuffer = gtk_text_buffer_new (NULL);
+
+ filename = g_strdup_printf ("%s/%s.model", OREGANO_MODELDIR, model_name);
+ if (g_file_get_contents (filename, &str, NULL, &read_error)) {
+ gtk_text_buffer_set_text (txtbuffer, str, -1);
+ g_free (str);
+ } else {
+ gtk_text_buffer_set_text (txtbuffer, read_error->message, -1);
+ g_error_free (read_error);
+ }
+
+ g_free (filename);
+ g_free (model_name);
+
+ gtk_text_view_set_buffer (txtmodel, txtbuffer);
+ }
+
+ gtk_dialog_set_default_response (prop_dialog->dialog, 1);
+
+ response = gtk_dialog_run(prop_dialog->dialog);
+
+ prop_dialog_response (GTK_WIDGET (prop_dialog->dialog), response, prop_dialog);
+
+ gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog));
+}
+
+/* Matrix Multiplication
+ Tried art_affine_multiply, but that didn't work */
+static void matrix_mult(double* a,double* b,double* c){
+ c[0]=a[0]*b[0]+a[1]*b[2];
+ c[2]=a[2]*b[0]+a[3]*b[2];
+ c[1]=a[0]*b[1]+a[1]*b[3];
+ c[3]=a[2]*b[1]+a[3]*b[3];
+}
+
+static void
+part_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
+{
+ double affine[6];
+ double affine_scale[6];
+ double affine_rotate[6];
+
+ GList *list;
+ GSList *label_items;
+ GtkAnchorType anchor;
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *canvas_item;
+ PartItem *item;
+ PartItemPriv *priv;
+ Part *part;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_PART_ITEM (sheet_item));
+
+ item = PART_ITEM (sheet_item);
+ group = GNOME_CANVAS_GROUP (item);
+ part = PART(data);
+
+ priv = item->priv;
+
+ if (angle!=0) {
+ // check if the part is flipped
+ if ((part->priv->flip & 1) && (part->priv->flip & 2)) {
+ // Nothing to do in this case
+ art_affine_rotate (affine, angle);
+ } else {
+ if ((part->priv->flip & 1) || (part->priv->flip & 2)) {
+ // mirror over point (0,0)
+ art_affine_scale(affine_scale,-1,-1);
+ art_affine_rotate(affine_rotate,angle);
+ /* Matrix multiplication */
+ /* Don't use art_affine_multiply() here !*/
+ matrix_mult(affine_rotate,affine_scale,affine);
+ affine[4]=affine_rotate[4];
+ affine[5]=affine_rotate[5];
+ } else
+ art_affine_rotate (affine, angle);
+ }
+ } else {
+ // angle==0: Nothing has changed, therefore do nothing
+ art_affine_scale(affine,1,1);
+ }
+
+ for (list = group->item_list; list; list = list->next) {
+ canvas_item = GNOME_CANVAS_ITEM (list->data);
+
+ gnome_canvas_item_affine_relative (canvas_item, affine);
+ }
+
+ /*
+ * Get the right anchor for the labels. This is needed since the
+ * canvas don't know how to rotate text and since we rotate the
+ * label_group instead of the labels directly.
+ */
+
+ switch (part_get_rotation (part)) {
+ case 0:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ break;
+ case 90:
+ anchor = GTK_ANCHOR_NORTH_WEST;
+ break;
+ case 180:
+ anchor = GTK_ANCHOR_NORTH_EAST;
+ break;
+ case 270:
+ anchor = GTK_ANCHOR_SOUTH_EAST;
+ break;
+ default:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ break;
+ }
+
+
+ for (label_items = priv->label_items; label_items;
+ label_items = label_items->next) {
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (label_items->data),
+ "anchor", anchor,
+ NULL);
+ }
+
+ for (label_items = priv->label_nodes; label_items;
+ label_items = label_items->next) {
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (label_items->data),
+ "anchor", anchor,
+ NULL);
+ }
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ priv->cache_valid = FALSE;
+}
+
+static void
+part_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item)
+{
+ GList *list;
+ GSList *label;
+ GtkAnchorType anchor;
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *canvas_item;
+ PartItem *item;
+ PartItemPriv *priv;
+ Part *part;
+ IDFlip flip;
+ double affine[6];
+ double affine_rotate[6];
+ double affine_scale[6];
+ int angle;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_PART_ITEM (sheet_item));
+
+ item = PART_ITEM (sheet_item);
+ group = GNOME_CANVAS_GROUP (item);
+ part = PART (data);
+ flip = part_get_flip (part);
+
+ priv = item->priv;
+
+ if (horizontal)
+ art_affine_scale (affine, -1, 1);
+ else
+ art_affine_scale (affine, 1, -1);
+
+ angle=part_get_rotation(part);
+ if (angle!=0) {
+ switch (angle){
+ case 90: art_affine_rotate(affine_rotate,180);
+ memcpy(affine_scale,affine,6*sizeof(double));
+ break;
+ case 180: //don't do a thing
+ art_affine_rotate(affine_rotate,0);
+ memcpy(affine_scale,affine,6*sizeof(double));
+ break;
+ case 270: art_affine_rotate(affine_rotate,0);
+ // only to reset the memory of affine_scale, just a hack
+ memcpy(affine_scale,affine,6*sizeof(double));
+ // switch the flipping from horizontal to vertical and vice versa
+ affine_scale[0]=affine[3];
+ affine_scale[3]=affine[0];
+ }
+ matrix_mult(affine_scale,affine_rotate,affine);
+ }
+
+
+ for (list = group->item_list; list; list = list->next) {
+ canvas_item = GNOME_CANVAS_ITEM (list->data);
+
+ gnome_canvas_item_affine_relative (canvas_item, affine);
+ }
+
+ anchor = part_item_get_anchor_from_part (part);
+
+ for (label = item->priv->label_items; label; label = label->next) {
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (label->data),
+ "anchor", anchor,
+ NULL);
+ }
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ priv->cache_valid = FALSE;
+}
+
+/*
+ Wherefore this function?
+ Formerly, the above defined callback functions were called after
+ reading data from file or from clipboard, in both cases there was neither
+ a change in flip nor rotate, but the above functions only work for
+ data which has changed. The "Not Changed, Just Redraw" was missing here.
+ */
+static void
+part_arrange_canvas_item (ItemData *data, SheetItem *sheet_item)
+{
+ double affine[6];
+ double affine_scale[6];
+ double affine_rotate[6];
+ GList *list;
+ GSList *label_items;
+ GtkAnchorType anchor;
+ GnomeCanvasGroup *group;
+ GnomeCanvasItem *canvas_item;
+ PartItem *item;
+ PartItemPriv *priv;
+ Part *part;
+ int angle;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_PART_ITEM (sheet_item));
+
+ item = PART_ITEM (sheet_item);
+ group = GNOME_CANVAS_GROUP (item);
+ part = PART(data);
+
+ priv = item->priv;
+
+ angle=part_get_rotation(part);
+
+ if (angle!=0) {
+ if ((part->priv->flip & ID_FLIP_HORIZ)
+ && (part->priv->flip & ID_FLIP_VERT)) {
+ art_affine_rotate(affine,angle+180);
+ } else { if ((part->priv->flip & ID_FLIP_HORIZ)
+ || (part->priv->flip & ID_FLIP_VERT)) {
+ if (part->priv->flip & ID_FLIP_HORIZ)
+ art_affine_scale(affine_scale,-1,1);
+ else
+ art_affine_scale(affine_scale,1,-1);
+
+ art_affine_rotate(affine_rotate,angle);
+
+ matrix_mult(affine_rotate,affine_scale,affine);
+ affine[4]=affine_rotate[4];
+ affine[5]=affine_rotate[5];
+ } else
+ art_affine_rotate (affine, angle);
+ }
+ } else {
+ art_affine_scale(affine,1,1); //default
+ if ((part->priv->flip & ID_FLIP_HORIZ)
+ && (part->priv->flip & ID_FLIP_VERT))
+ art_affine_scale(affine,-1,-1);
+ if ((part->priv->flip & ID_FLIP_HORIZ)
+ && !(part->priv->flip & ID_FLIP_VERT))
+ art_affine_scale(affine,-1,1);
+ if (!(part->priv->flip & ID_FLIP_HORIZ)
+ && (part->priv->flip & ID_FLIP_VERT))
+ art_affine_scale(affine,1,-1);
+ }
+
+ for (list = group->item_list; list; list = list->next) {
+ canvas_item = GNOME_CANVAS_ITEM (list->data);
+ gnome_canvas_item_affine_relative (canvas_item, affine);
+ }
+
+ /*
+ * Get the right anchor for the labels. This is needed since the
+ * canvas don't know how to rotate text and since we rotate the
+ * label_group instead of the labels directly.
+ */
+ switch (part_get_rotation (part)) {
+ case 0:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ break;
+ case 90:
+ anchor = GTK_ANCHOR_NORTH_WEST;
+ break;
+ case 180:
+ anchor = GTK_ANCHOR_NORTH_EAST;
+ break;
+ case 270:
+ anchor = GTK_ANCHOR_SOUTH_EAST;
+ break;
+ default:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ break;
+ }
+
+
+ for (label_items = priv->label_items; label_items;
+ label_items = label_items->next) {
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (label_items->data),
+ "anchor", anchor,
+ NULL);
+ }
+
+ for (label_items = priv->label_nodes; label_items;
+ label_items = label_items->next) {
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (label_items->data),
+ "anchor", anchor,
+ NULL);
+ }
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ priv->cache_valid = FALSE;
+}
+
+void
+part_item_signal_connect_floating_group (Sheet *sheet, SchematicView *sv)
+{
+ g_return_if_fail (sheet != NULL);
+ g_return_if_fail (IS_SHEET (sheet));
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet->state = SHEET_STATE_FLOAT_START;
+
+ /* FIXME: clean all this mess with floating groups etc... */
+ if (sheet->priv->float_handler_id != 0)
+ return;
+
+ sheet->priv->float_handler_id = g_signal_connect(
+ G_OBJECT (sheet),
+ "event",
+ G_CALLBACK(sheet_item_floating_event),
+ sv);
+}
+
+void
+part_item_signal_connect_floating (PartItem *item)
+{
+ Sheet *sheet;
+
+ sheet = sheet_item_get_sheet (SHEET_ITEM (item));
+ sheet->state = SHEET_STATE_FLOAT_START;
+
+ g_signal_connect (
+ G_OBJECT (item),
+ "double_clicked",
+ G_CALLBACK(edit_properties),
+ item);
+}
+
+static void
+selection_changed (PartItem *item, gboolean select, gpointer user_data)
+{
+ g_object_ref (G_OBJECT (item));
+ if (select)
+ gtk_idle_add ((gpointer) select_idle_callback, item);
+ else
+ gtk_idle_add ((gpointer) deselect_idle_callback, item);
+}
+
+static int
+select_idle_callback (PartItem *item)
+{
+ PartItemPriv *priv;
+ GnomeCanvasItem *canvas_item;
+ GList *list;
+
+ priv = item->priv;
+
+ if (priv->highlight) {
+ g_object_unref(G_OBJECT (item));
+ return FALSE;
+ }
+
+ for (list = GNOME_CANVAS_GROUP (item)->item_list; list;
+ list = list->next){
+ canvas_item = GNOME_CANVAS_ITEM (list->data);
+ if (GNOME_IS_CANVAS_LINE (canvas_item))
+ gnome_canvas_item_set (canvas_item, "fill_color", SELECTED_COLOR, NULL);
+ else if (GNOME_IS_CANVAS_ELLIPSE (canvas_item))
+ gnome_canvas_item_set (canvas_item, "outline_color", SELECTED_COLOR, NULL);
+ else if (GNOME_IS_CANVAS_TEXT (canvas_item))
+ gnome_canvas_item_set (canvas_item, "fill_color", SELECTED_COLOR, NULL);
+ }
+
+ priv->highlight = TRUE;
+
+ g_object_unref(G_OBJECT(item));
+ return FALSE;
+}
+
+static int
+deselect_idle_callback (PartItem *item)
+{
+ GList *list;
+ GnomeCanvasItem *canvas_item;
+ PartItemPriv *priv;
+
+ priv = item->priv;
+
+ if (!priv->highlight) {
+ g_object_unref (G_OBJECT (item));
+ return FALSE;
+ }
+
+ for (list = GNOME_CANVAS_GROUP (item)->item_list; list;
+ list = list->next){
+ canvas_item = GNOME_CANVAS_ITEM (list->data);
+ if (GNOME_IS_CANVAS_LINE (canvas_item))
+ gnome_canvas_item_set (canvas_item, "fill_color", NORMAL_COLOR, NULL);
+ else if (GNOME_IS_CANVAS_ELLIPSE (canvas_item))
+ gnome_canvas_item_set (canvas_item, "outline_color", NORMAL_COLOR, NULL);
+ else if (GNOME_IS_CANVAS_TEXT (canvas_item))
+ gnome_canvas_item_set (canvas_item, "fill_color", LABEL_COLOR, NULL);
+ }
+
+ priv->highlight = FALSE;
+
+ g_object_unref(G_OBJECT(item));
+ return FALSE;
+}
+
+static gboolean
+is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2)
+{
+ PartItem *item;
+ SheetPos bbox_start, bbox_end;
+
+ item = PART_ITEM (object);
+
+ get_cached_bounds (item, &bbox_start, &bbox_end);
+
+ if ((p1->x < bbox_start.x) &&
+ (p2->x > bbox_end.x) &&
+ (p1->y < bbox_start.y) &&
+ (p2->y > bbox_end.y))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+show_labels (SheetItem *sheet_item, gboolean show)
+{
+ PartItem *item;
+ PartItemPriv *priv;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_PART_ITEM (sheet_item));
+
+ item = PART_ITEM (sheet_item);
+ priv = item->priv;
+
+ if (show)
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->label_group));
+ else
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->label_group));
+}
+
+/**
+ * Retrieves the bounding box. We use a caching scheme for this
+ * since it's too expensive to calculate it every time we need it.
+ */
+inline static void
+get_cached_bounds (PartItem *item, SheetPos *p1, SheetPos *p2)
+{
+ PartItemPriv *priv;
+ priv = item->priv;
+
+ if (!priv->cache_valid) {
+ SheetPos start_pos, end_pos;
+ gdouble x1, y1, x2, y2;
+
+ /*
+ * Hide the labels, then get bounding box, then show them
+ * again.
+ */
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->label_group));
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (item),
+ &x1, &y1, &x2, &y2);
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->label_group));
+
+ start_pos.x = x1;
+ start_pos.y = y1;
+ end_pos.x = x2;
+ end_pos.y = y2;
+
+ priv->bbox_start = start_pos;
+ priv->bbox_end = end_pos;
+ priv->cache_valid = TRUE;
+ }
+
+ memcpy (p1, &priv->bbox_start, sizeof (SheetPos));
+ memcpy (p2, &priv->bbox_end, sizeof (SheetPos));
+}
+
+static void
+part_item_paste (SchematicView *sv, ItemData *data)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_PART (data));
+
+ schematic_view_add_ghost_item (sv, data);
+}
+
+/**
+ * FIXME: make this the default constructor for PartItem (rename to
+ * part_item_new).
+ */
+PartItem *
+part_item_new_from_part (Sheet *sheet, Part *part)
+{
+ Library *library;
+ LibraryPart *library_part;
+ PartPriv *priv;
+ PartItem *item;
+
+ priv = part->priv;
+
+ library = priv->library;
+ library_part = library_get_part (library, priv->name);
+
+ /*
+ * Create the PartItem canvas item.
+ */
+ item = part_item_new (sheet, part);
+
+ create_canvas_items (GNOME_CANVAS_GROUP (item), library_part);
+ create_canvas_labels (item, part);
+ create_canvas_label_nodes(item, part);
+
+ part_arrange_canvas_item(ITEM_DATA (part), SHEET_ITEM (item));
+ return item;
+}
+
+void
+part_item_create_canvas_items_for_preview (GnomeCanvasGroup *group,
+ LibraryPart *library_part)
+{
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+ g_return_if_fail (library_part != NULL);
+
+ create_canvas_items (group, library_part);
+}
+
+static void
+create_canvas_items (GnomeCanvasGroup *group, LibraryPart *library_part)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasPoints *points;
+ GSList *objects;
+ LibrarySymbol *symbol;
+ SymbolObject *object;
+
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+ g_return_if_fail (library_part != NULL);
+
+ 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;
+ }
+
+ for (objects = symbol->symbol_objects; objects;
+ objects = objects->next) {
+ object = (SymbolObject *)(objects->data);
+ switch (object->type){
+ case SYMBOL_OBJECT_LINE:
+ points = object->u.uline.line;
+ item = gnome_canvas_item_new (
+ group,
+ gnome_canvas_line_get_type (),
+ "points", points,
+ "fill_color", NORMAL_COLOR,
+ "width_pixels", 0,
+ "cap_style", GDK_CAP_BUTT,
+ NULL);
+ if (object->u.uline.spline) {
+ gnome_canvas_item_set (
+ item,
+ "smooth", TRUE,
+ "spline_steps", 5,
+ NULL);
+ }
+ break;
+ case SYMBOL_OBJECT_ARC:
+ item = gnome_canvas_item_new (
+ group,
+ gnome_canvas_ellipse_get_type (),
+ "x1", object->u.arc.x1,
+ "y1", object->u.arc.y1,
+ "x2", object->u.arc.x2,
+ "y2", object->u.arc.y2,
+ "outline_color", NORMAL_COLOR,
+ "width_pixels", 0,
+ NULL);
+ break;
+ case SYMBOL_OBJECT_TEXT:
+ item = gnome_canvas_item_new (
+ group,
+ gnome_canvas_text_get_type (),
+ "text",object->u.text.str,
+ "x", (double) object->u.text.x,
+ "y", (double) object->u.text.y,
+ "fill_color", LABEL_COLOR,
+ "font", "Sans 8",
+ NULL);
+ break;
+ default:
+ g_warning ("Unknown symbol object.\n");
+ continue;
+ }
+ }
+}
+
+static void
+create_canvas_labels (PartItem *item, Part *part)
+{
+ GnomeCanvasItem *canvas_item;
+ GSList *list, *labels, *item_list;
+ GnomeCanvasGroup *group;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_PART_ITEM (item));
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (IS_PART (part));
+
+ labels = part_get_labels (part);
+ group = item->priv->label_group;
+ item_list = NULL;
+
+ for (list = labels; list; list = list->next) {
+ PartLabel *label = list->data;
+ char *text;
+
+ text = part_property_expand_macros (part, label->text);
+
+ canvas_item = gnome_canvas_item_new (
+ group,
+ gnome_canvas_text_get_type (),
+ "x", (double) label->pos.x,
+ "y", (double) label->pos.y,
+ "text", text,
+ "anchor", GTK_ANCHOR_SOUTH_WEST,
+ "fill_color", LABEL_COLOR,
+ "font", "Sans 8",
+ NULL);
+
+ item_list = g_slist_prepend (item_list, canvas_item);
+
+ g_free (text);
+ }
+ item_list = g_slist_reverse (item_list);
+ part_item_set_label_items (item, item_list);
+}
+
+
+static void
+create_canvas_label_nodes (PartItem *item, Part *part)
+{
+ GnomeCanvasItem *canvas_item;
+ GSList *item_list;
+ GnomeCanvasGroup *group;
+ Pin *pins;
+ int num_pins, i;
+ SheetPos p1, p2;
+ GtkAnchorType anchor;
+ int w, h;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_PART_ITEM (item));
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (IS_PART (part));
+
+ num_pins = part_get_num_pins(part);
+ pins = part_get_pins(part);
+ group = item->priv->node_group;
+ item_list = NULL;
+
+ get_cached_bounds (item, &p1, &p2);
+
+ w = p2.x - p1.x;
+ h = p2.y - p1.y;
+
+ switch (part_get_rotation (part)) {
+ case 0:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ break;
+ case 90:
+ anchor = GTK_ANCHOR_NORTH_WEST;
+ break;
+ case 180:
+ anchor = GTK_ANCHOR_NORTH_EAST;
+ break;
+ case 270:
+ anchor = GTK_ANCHOR_SOUTH_EAST;
+ break;
+ default:
+ anchor = GTK_ANCHOR_SOUTH_WEST;
+ }
+
+ for (i=0; i<num_pins; i++) {
+ int x, y;
+ char *txt;
+ x = pins[i].offset.x;
+ y = pins[i].offset.y;
+
+ txt = g_strdup_printf("%d", pins[i].node_nr);
+ canvas_item = gnome_canvas_item_new (
+ group,
+ gnome_canvas_text_get_type (),
+ "x", (double) x,
+ "y", (double) y,
+ "text", txt,
+ "anchor", anchor,
+ "fill_color", "black",
+ "font", "Sans 8",
+ NULL);
+
+ item_list = g_slist_prepend (item_list, canvas_item);
+ g_free(txt);
+ }
+ item_list = g_slist_reverse (item_list);
+ item->priv->label_nodes = item_list;
+}
+
+
+/**
+ * This is called when the part data was moved. Update the view accordingly.
+ */
+static void
+part_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item)
+{
+ PartItem *part_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_PART_ITEM (item));
+
+ if (pos == NULL)
+ return;
+
+ part_item = PART_ITEM (item);
+
+ /*
+ * Move the canvas item and invalidate the bbox cache.
+ */
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y);
+ part_item->priv->cache_valid = FALSE;
+}
+
+static void
+part_item_place (SheetItem *item, SchematicView *sv)
+{
+ g_signal_connect (
+ G_OBJECT(item),
+ "event",
+ G_CALLBACK(sheet_item_event),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT(item),
+ "double_clicked",
+ G_CALLBACK(edit_properties),
+ item);
+}
+
+static void
+part_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+// part_item_signal_connect_placed (PART_ITEM (item));
+}
+
+
+void
+part_item_show_node_labels (PartItem *part, gboolean b)
+{
+ PartItemPriv *priv;
+
+ priv = part->priv;
+
+ if (b)
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->node_group));
+ else
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->node_group));
+}
+
diff --git a/src/sheet/part-item.h b/src/sheet/part-item.h
new file mode 100644
index 0000000..0ead795
--- /dev/null
+++ b/src/sheet/part-item.h
@@ -0,0 +1,70 @@
+/*
+ * part-item.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_ITEM_H
+#define __PART_ITEM_H
+
+typedef struct _PartItem PartItem;
+typedef struct _PartItemClass PartItemClass;
+typedef struct _PartItemPriv PartItemPriv;
+
+#include "sheet-item.h"
+#include "load-library.h"
+#include "load-common.h"
+#include "part.h"
+
+#define TYPE_PART_ITEM (part_item_get_type ())
+#define PART_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_PART_ITEM, PartItem))
+#define PART_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_PART_ITEM, PartItemClass))
+#define IS_PART_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_PART_ITEM))
+#define PART_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_PART_ITEM, PartItemClass))
+
+#include "schematic-view.h"
+
+struct _PartItem {
+ SheetItem parent_object;
+ PartItemPriv *priv;
+};
+
+struct _PartItemClass {
+ SheetItemClass parent_class;
+};
+
+GType part_item_get_type (void);
+PartItem *part_item_new (Sheet *sheet, Part *part);
+PartItem *part_item_new_from_part (Sheet *sheet, Part *part);
+void part_item_signal_connect_floating (PartItem *item);
+void part_item_signal_connect_floating_group (Sheet *sheet,
+ SchematicView *schematic_view);
+void part_item_create_canvas_items_for_preview (GnomeCanvasGroup *group,
+ LibraryPart *library_part);
+void part_item_update_node_label (PartItem *part);
+void part_item_show_node_labels (PartItem *part, gboolean b);
+
+#endif
diff --git a/src/sheet/sheet-item-factory.c b/src/sheet/sheet-item-factory.c
new file mode 100644
index 0000000..97f31b2
--- /dev/null
+++ b/src/sheet/sheet-item-factory.c
@@ -0,0 +1,70 @@
+/*
+ * sheet-item-factory.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 "schematic-view.h"
+#include "sheet-item-factory.h"
+#include "wire-item.h"
+#include "part-item.h"
+#include "textbox-item.h"
+
+/*
+ * Create a SheetItem from an ItemData object. This is a bit ugly.
+ * It could be beautified by having a method that creates the item.
+ * E.g. sheet_item->new_from_data (data);
+ */
+
+SheetItem *
+sheet_item_factory_create_sheet_item (SchematicView *sv, ItemData *data)
+{
+ Sheet *sheet;
+ SheetItem *item;
+
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (IS_ITEM_DATA (data), NULL);
+
+ sheet = schematic_view_get_sheet (sv);
+ item = NULL;
+
+ /*
+ * Pick the right model.
+ */
+ if (IS_PART(data)) {
+ item = SHEET_ITEM(part_item_new_from_part(sheet, PART(data)));
+ } else if (IS_WIRE(data)) {
+ item = SHEET_ITEM(wire_item_new(sheet, WIRE(data)));
+ } else if (IS_TEXTBOX(data)) {
+ item = SHEET_ITEM(textbox_item_new(sheet, TEXTBOX(data)));
+ } else
+ g_warning ("Unknown Item type.");
+
+ return item;
+}
+
+
diff --git a/src/sheet/sheet-item-factory.h b/src/sheet/sheet-item-factory.h
new file mode 100644
index 0000000..4a6825b
--- /dev/null
+++ b/src/sheet/sheet-item-factory.h
@@ -0,0 +1,38 @@
+/*
+ * sheet-item-factory.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_ITEM_FACTORY_H
+#define __SHEET_ITEM_FACTORY_H
+
+#include "item-data.h"
+#include "sheet-item.h"
+
+SheetItem *sheet_item_factory_create_sheet_item (SchematicView *sv, ItemData *data);
+
+#endif /* __SHEET_ITEM_FACTORY_H */
diff --git a/src/sheet/sheet-item.c b/src/sheet/sheet-item.c
new file mode 100644
index 0000000..35120e3
--- /dev/null
+++ b/src/sheet/sheet-item.c
@@ -0,0 +1,1222 @@
+/*
+ * sheet-item.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 <math.h>
+#include "main.h"
+#include "sheet-private.h"
+#include "sheet-item.h"
+#include "stock.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "config.h"
+
+static void sheet_item_class_init (SheetItemClass * klass);
+
+static void sheet_item_init (SheetItem *item);
+
+static void sheet_item_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+
+static void sheet_item_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *spec);
+
+static void sheet_item_destroy (GtkObject *object);
+
+static void sheet_item_run_menu (SheetItem *item, SchematicView *sv,
+ GdkEventButton *event);
+
+static GnomeCanvasGroupClass *sheet_item_parent_class = NULL;
+extern GObject *clipboard_data_get_item_data ();
+extern GObjectClass *clipboard_data_get_item_class ();
+
+struct _SheetItemPriv {
+ guint selected : 1;
+ guint preserve_selection : 1;
+ ItemData *data;
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+};
+
+enum {
+ ARG_0,
+ ARG_DATA,
+ ARG_SHEET,
+ ARG_ACTION_GROUP
+};
+
+enum {
+ MOVED,
+ PLACED,
+ SELECTION_CHANGED,
+ MOUSE_OVER,
+ DOUBLE_CLICKED,
+
+ LAST_SIGNAL
+};
+
+static guint so_signals[LAST_SIGNAL] = { 0 };
+/*
+ * This is the upper part of the object popup menu. It contains actions
+ * that are the same for all objects, e.g. parts and wires.
+ */
+static const char *sheet_item_context_menu =
+"<ui>"
+" <popup name='ItemMenu'>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Delete'/>"
+" <separator/>"
+" <menuitem action='Rotate'/>"
+" <menuitem action='FlipH'/>"
+" <menuitem action='FlipV'/>"
+" <separator/>"
+" </popup>"
+"</ui>";
+
+static GtkActionEntry action_entries[] = {
+ {"Copy", GTK_STOCK_COPY, N_("_Copy"), "<control>C", NULL, NULL},
+ {"Cut", GTK_STOCK_CUT, N_("C_ut"), "<control>X", NULL, NULL},
+ {"Delete", GTK_STOCK_DELETE, N_("_Delete"), "<control>D", N_("Delete the selection"), NULL},
+ {"Rotate", STOCK_PIXMAP_ROTATE, N_("_Rotate"), "<control>R", N_("Rotate the selection clockwise"), NULL},
+ {"FlipH", NULL, N_("Flip _horizontally"), "<control>F", N_("Flip the selection horizontally"), NULL},
+ {"FlipV", NULL, N_("Flip _vertically"), "<control><shift>F", N_("Flip the selection vertically"), NULL}
+};
+GType
+sheet_item_get_type ()
+{
+ static GType sheet_item_type = 0;
+
+ if (!sheet_item_type) {
+ static const GTypeInfo sheet_item_info = {
+ sizeof (SheetItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) sheet_item_class_init,
+ NULL,
+ NULL,
+ sizeof (SheetItem),
+ 0,
+ (GInstanceInitFunc)sheet_item_init,
+ NULL
+ };
+ sheet_item_type = g_type_register_static(GNOME_TYPE_CANVAS_GROUP,
+ "SheetItem",
+ &sheet_item_info, 0);
+ }
+ return sheet_item_type;
+}
+
+static void
+sheet_item_class_init (SheetItemClass *sheet_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+
+ object_class = G_OBJECT_CLASS(sheet_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(sheet_item_class);
+ sheet_item_parent_class = g_type_class_peek_parent(sheet_item_class);
+
+ object_class->set_property = sheet_item_set_property;
+ object_class->get_property = sheet_item_get_property;
+
+ g_object_class_install_property(
+ object_class,
+ ARG_DATA,
+ g_param_spec_pointer("data", "SheetItem::data", "the data",
+ G_PARAM_READWRITE)
+ );
+ g_object_class_install_property(
+ object_class,
+ ARG_SHEET,
+ g_param_spec_pointer("sheet", "SheetItem::sheet", "the sheet",
+ G_PARAM_READABLE)
+ );
+ g_object_class_install_property(
+ object_class,
+ ARG_ACTION_GROUP,
+ g_param_spec_pointer("action_group", "SheetItem::action_group", "action group",
+ G_PARAM_READWRITE)
+ );
+
+ gtk_object_class->destroy = sheet_item_destroy;
+
+ sheet_item_class->is_in_area = NULL;
+ sheet_item_class->show_labels = NULL;
+ sheet_item_class->paste = NULL;
+
+ sheet_item_class->moved = NULL;
+ sheet_item_class->selection_changed = NULL;
+ sheet_item_class->mouse_over = NULL;
+
+ so_signals[PLACED] =
+ g_signal_new ("placed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ so_signals[MOVED] = g_signal_new ("moved",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetItemClass, moved),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ so_signals[SELECTION_CHANGED] = g_signal_new ("selection_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetItemClass, selection_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ so_signals[MOUSE_OVER] = g_signal_new ("mouse_over",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetItemClass, mouse_over),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ so_signals[DOUBLE_CLICKED] = g_signal_new ("double_clicked",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+sheet_item_init (SheetItem *item)
+{
+ GError *error = NULL;
+
+ item->priv = g_new0 (SheetItemPriv, 1);
+ item->priv->selected = FALSE;
+ item->priv->preserve_selection = FALSE;
+ item->priv->data = NULL;
+ item->priv->ui_manager = NULL;
+ item->priv->action_group = NULL;
+
+ item->priv->ui_manager = gtk_ui_manager_new ();
+ item->priv->action_group = gtk_action_group_new ("action_group");
+ gtk_action_group_set_translation_domain (item->priv->action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (item->priv->action_group,
+ action_entries,
+ G_N_ELEMENTS (action_entries),
+ NULL);
+ gtk_ui_manager_insert_action_group (item->priv->ui_manager, item->priv->action_group, 0);
+
+ if (!gtk_ui_manager_add_ui_from_string (item->priv->ui_manager, sheet_item_context_menu, -1, &error)) {
+ g_message ("building menus failed: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void
+sheet_item_set_property (GObject *object, guint prop_id, const GValue *value,
+ GParamSpec *spec)
+{
+ SheetItem *sheet_item;
+ SheetPos pos;
+
+ sheet_item = SHEET_ITEM (object);
+
+ switch (prop_id) {
+ case ARG_DATA:
+ if (sheet_item->priv->data) {
+ g_warning ("Cannot set ItemData after creation.");
+ break;
+ }
+ sheet_item->priv->data = g_value_get_pointer (value);
+ item_data_get_pos (sheet_item->priv->data, &pos);
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (object),
+ "x", pos.x,
+ "y", pos.y,
+ NULL);
+ break;
+ case ARG_ACTION_GROUP:
+ sheet_item->priv->action_group = g_value_get_pointer (value);
+ gtk_ui_manager_insert_action_group (sheet_item->priv->ui_manager, sheet_item->priv->action_group, 0);
+ default:
+ break;
+ }
+}
+
+static void
+sheet_item_get_property (GObject *object, guint prop_id, GValue *value,
+ GParamSpec *spec)
+{
+ SheetItem *sheet_item;
+
+ sheet_item = SHEET_ITEM (object);
+
+ switch (prop_id) {
+ case ARG_DATA:
+ g_value_set_pointer(value, sheet_item->priv->data);
+ break;
+ case ARG_SHEET:
+ g_value_set_pointer (value, sheet_item_get_sheet (sheet_item));
+ break;
+ case ARG_ACTION_GROUP:
+ g_value_set_pointer (value, sheet_item->priv->action_group);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(sheet_item, prop_id, spec);
+ break;
+ }
+}
+
+static void
+sheet_item_destroy (GtkObject *object)
+{
+ SheetItem *sheet_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (object));
+
+ sheet_item = SHEET_ITEM (object);
+
+ if (sheet_item->priv) {
+ g_object_unref(G_OBJECT(sheet_item->priv->data));
+ g_free(sheet_item->priv);
+ sheet_item->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS(sheet_item_parent_class)->destroy) {
+ GTK_OBJECT_CLASS(sheet_item_parent_class)->destroy(object);
+ }
+}
+
+static void
+sheet_item_run_menu (SheetItem *item, SchematicView *sv, GdkEventButton *event)
+{
+ GtkWidget *menu;
+
+ menu = gtk_ui_manager_get_widget (item->priv->ui_manager, "/ItemMenu");
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sv, event->button, event->time);
+}
+
+static int
+scroll_timeout_callback (Sheet *sheet)
+{
+ int width, height;
+ int x, y, dx = 0, dy = 0;
+
+ /* Get the current mouse position so that we can decide if the pointer is
+ inside the viewport. */
+ gdk_window_get_pointer (GTK_WIDGET (sheet)->window, &x, &y, NULL);
+
+ width = GTK_WIDGET (sheet)->allocation.width;
+ height = GTK_WIDGET (sheet)->allocation.height;
+
+ if (x < 0)
+ dx = -1;
+ else if (x > width)
+ dx = 1;
+
+ if (y < 0)
+ dy = -1;
+ else if (y > height)
+ dy = 1;
+
+ if (!(x > 0 && x < width && y > 0 && y < height))
+ sheet_scroll (sheet, dx*5, dy*5);
+
+ return TRUE;
+}
+
+
+/*
+ * sheet_item_event
+ *
+ * Event handler for a SheetItem
+ */
+int
+sheet_item_event (SheetItem *sheet_item, const GdkEvent *event, SchematicView *sv)
+{
+ SheetItemClass *class;
+ GnomeCanvas *canvas;
+ Sheet *sheet;
+ SheetPriv *priv;
+ GList *list;
+ SheetPos delta;
+ /* Remember the last position of the mouse cursor. */
+ static double last_x, last_y;
+ /* Mouse cursor position in window coordinates, snapped to the grid spacing. */
+ double snapped_x, snapped_y;
+ /* Move the selected item(s) by this movement. */
+ double dx, dy;
+ /* The selected group's bounding box in window resp. canvas coordinates. */
+ double x1, y1, x2, y2;
+ static double bb_x1, bb_y1, bb_x2, bb_y2;
+ int cx1, cy1, cx2, cy2;
+ /* The sheet's width and the its viewport's width. */
+ guint sheet_width, sheet_height;
+
+ g_return_val_if_fail (sheet_item != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET_ITEM (sheet_item), FALSE);
+ g_return_val_if_fail (sv != NULL, FALSE);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), FALSE);
+
+ sheet = schematic_view_get_sheet (sv);
+ priv = sheet->priv;
+ canvas = GNOME_CANVAS (sheet);
+
+ switch (event->type){
+ case GDK_ENTER_NOTIFY:
+ /*
+ * Debugging...
+ */
+ if (event->crossing.state & GDK_CONTROL_MASK)
+ g_signal_emit_by_name (G_OBJECT (sheet_item), "mouse_over");
+ return TRUE;
+
+ case GDK_BUTTON_PRESS:
+ /* Grab focus to sheet for correct use of events */
+ gtk_widget_grab_focus (GTK_WIDGET (sheet));
+ switch (event->button.button){
+ case 1:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ sheet->state = SHEET_STATE_DRAG_START;
+
+ last_x = event->button.x;
+ last_y = event->button.y;
+ snap_to_grid (sheet->grid, &last_x, &last_y);
+ break;
+ case 3:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+
+ if (sheet->state != SHEET_STATE_NONE)
+ return TRUE;
+
+ /*
+ * Bring up a context menu for right button clicks.
+ */
+ if (!sheet_item->priv->selected &&
+ !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK))
+ schematic_view_select_all (sv, FALSE);
+
+ //if (!sheet_item->priv->selected)
+ sheet_item_select (sheet_item, TRUE);
+
+ class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS(sheet_item));
+ sheet_item_run_menu ( sheet_item, sv, (GdkEventButton *) event);
+ break;
+ default:
+ return FALSE;
+ }
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ /*
+ * Do not interfere with object dragging.
+ */
+ if (sheet->state == SHEET_STATE_DRAG)
+ return FALSE;
+
+ switch (event->button.button){
+ case 1:
+ if (sheet->state == SHEET_STATE_DRAG_START)
+ sheet->state = SHEET_STATE_NONE;
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ g_signal_emit_by_name (G_OBJECT (sheet_item), "double_clicked");
+ break;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ case GDK_3BUTTON_PRESS:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ return TRUE;
+
+ case GDK_BUTTON_RELEASE:
+ switch (event->button.button){
+ case 1:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ return TRUE;
+
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+
+ if (sheet->state == SHEET_STATE_DRAG_START) {
+ sheet->state = SHEET_STATE_NONE;
+
+ if (!(event->button.state & GDK_SHIFT_MASK))
+ schematic_view_select_all (sv, FALSE);
+
+ sheet_item_select (sheet_item, TRUE);
+
+ return TRUE;
+ }
+
+ g_object_get (G_OBJECT (priv->selected_group),
+ "x", &delta.x,
+ "y", &delta.y,
+ NULL);
+
+ gtk_timeout_remove (priv->scroll_timeout_id); // Tricky...
+
+ sheet->state = SHEET_STATE_NONE;
+ gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (sheet_item), event->button.time);
+
+
+ /*
+ * HACK :(
+ * FIXME: fix this later. The problem is that we don't want to
+ * update the current view, since it acts as the controller and
+ * already is updated. It's not really a problem, but an ugly hack.
+ */
+ gnome_canvas_item_move (
+ GNOME_CANVAS_ITEM (priv->selected_group),
+ -delta.x, -delta.y
+ );
+
+ /*
+ * Make sure the objects are reparented back to the object
+ * group _before_ we register them. Otherwise they will get
+ * incorrect coordinates.
+ */
+ for (list = priv->selected_objects; list; list = list->next) {
+ sheet_item_reparent (SHEET_ITEM (list->data), sheet->object_group);
+ }
+
+ /*
+ * FIXME: this is not the best way to solve this. Ideally, the
+ * moving would take care of re-registering items.
+ */
+ for (list = priv->selected_objects; list; list = list->next) {
+ ItemData *item_data;
+
+ item_data = SHEET_ITEM (list->data)->priv->data;
+ item_data_move (item_data, &delta);
+ item_data_register (item_data);
+ }
+
+ break;
+ case GDK_KEY_PRESS:
+ switch (event->key.keyval) {
+ case GDK_r:
+ schematic_view_rotate_selection (sv);
+ {
+ int x, y;
+
+ gdk_window_get_pointer (GDK_WINDOW (GTK_WIDGET (sheet)->window), &x, &y, NULL);
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet), x, y, &snapped_x, &snapped_y);
+
+ /*
+ * Center the objects around the mouse pointer.
+ */
+ gnome_canvas_item_get_bounds (
+ GNOME_CANVAS_ITEM (priv->floating_group),
+ &x1, &y1, &x2, &y2
+ );
+
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - (x1 + (x2 - x1) / 2);
+ dy = snapped_y - (y1 + (y2 - y1) / 2);
+ snap_to_grid (sheet->grid, &dx, &dy);
+
+ gnome_canvas_item_move (
+ GNOME_CANVAS_ITEM (priv->floating_group),
+ dx, dy
+ );
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ return FALSE;
+
+ if (sheet->state == SHEET_STATE_DRAG_START) {
+ sheet->state = SHEET_STATE_DRAG;
+
+ /*
+ * Update the selection if needed.
+ */
+ if (!sheet_item->priv->selected){
+ if (!(event->motion.state & GDK_SHIFT_MASK))
+ schematic_view_select_all (sv, FALSE);
+ sheet_item_select (sheet_item, TRUE);
+ }
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (priv->selected_group),
+ "x", 0.0, "y", 0.0, NULL
+ );
+
+ /*
+ * Reparent the selected objects so that we can
+ * move them efficiently.
+ */
+ for (list = priv->selected_objects; list; list = list->next){
+ ItemData *item_data;
+
+ item_data = SHEET_ITEM (list->data)->priv->data;
+ item_data_unregister (item_data);
+ sheet_item_reparent (SHEET_ITEM (list->data), priv->selected_group);
+ }
+
+ gnome_canvas_item_grab (GNOME_CANVAS_ITEM (sheet_item),
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK,
+ NULL, event->button.time);
+
+ gnome_canvas_item_get_bounds (
+ GNOME_CANVAS_ITEM (priv->selected_group),
+ &bb_x1, &bb_y1, &bb_x2, &bb_y2
+ );
+
+ /*
+ * Start the autoscroll timeout.
+ */
+ priv->scroll_timeout_id =
+ g_timeout_add (50, (void *) scroll_timeout_callback, sheet);
+ }
+
+ snapped_x = event->motion.x;
+ snapped_y = event->motion.y;
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - last_x;
+ dy = snapped_y - last_y;
+
+ x1 = bb_x1 + dx;
+ x2 = bb_x2 + dx;
+ y1 = bb_y1 + dy;
+ y2 = bb_y2 + dy;
+
+ gnome_canvas_w2c (canvas, x1, y1, &cx1, &cy1);
+ gnome_canvas_w2c (canvas, x2, y2, &cx2, &cy2);
+ sheet_get_size_pixels (sheet, &sheet_width, &sheet_height);
+
+ /* Check that we don't move outside the sheet... horizontally: */
+ if (cx1 <= 0){ /* leftmost edge */
+ dx = dx - x1;
+ snap_to_grid (sheet->grid, &dx, NULL);
+ snapped_x = last_x + dx;
+ } else if (cx2 >= sheet_width){ /* rightmost edge */
+ dx = dx - (x2 - sheet_width / priv->zoom);
+ snap_to_grid (sheet->grid, &dx, NULL);
+ snapped_x = last_x + dx;
+ }
+
+ /* And vertically: */
+ if (cy1 <= 0){ /* upper edge */
+ dy = dy - y1;
+ snap_to_grid (sheet->grid, NULL, &dy);
+ snapped_y = last_y + dy;
+ } else if (cy2 >= sheet_height){ /* lower edge */
+ dy = dy - (y2 - sheet_height / priv->zoom);
+ snap_to_grid (sheet->grid, NULL, &dy);
+ snapped_y = last_y + dy;
+ }
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+
+ if (dx != 0 || dy != 0)
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->selected_group), dx, dy);
+
+ /* Update the bounding box. */
+ bb_x1 += dx;
+ bb_x2 += dx;
+ bb_y1 += dy;
+ bb_y2 += dy;
+
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Cancel the placement of floating items and remove them.
+ */
+void
+sheet_item_cancel_floating (SchematicView *sv)
+{
+ GnomeCanvasGroup *group;
+ Sheet *sheet;
+ GList *list;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = schematic_view_get_sheet (sv);
+ group = sheet->priv->floating_group;
+ if (group == NULL)
+ return;
+
+ if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START)
+ return;
+
+ g_signal_handler_disconnect(G_OBJECT (sheet), sheet->priv->float_handler_id);
+
+ gtk_object_destroy (GTK_OBJECT (group));
+
+ /*
+ * If the state is _START, the items are not yet put in the
+ * object_group. This means we have to destroy them one by one.
+ */
+ if (sheet->state == SHEET_STATE_FLOAT_START) {
+ for (list = sheet->priv->floating_objects; list; list = list->next) {
+ gtk_object_destroy (GTK_OBJECT (list->data));
+ }
+ }
+
+ sheet->priv->floating_group = GNOME_CANVAS_GROUP (
+ gnome_canvas_item_new (
+ sheet->object_group,
+ gnome_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL)
+ );
+
+ sheet->priv->float_handler_id = 0;
+ sheet->state = SHEET_STATE_NONE;
+ schematic_view_clear_ghosts (sv);
+}
+
+/*
+ * Event handler for a "floating" group of objects.
+ */
+int
+sheet_item_floating_event (Sheet *sheet, const GdkEvent *event, SchematicView *schematic_view)
+{
+ GnomeCanvas *canvas;
+ SheetPriv *priv;
+ GList *list;
+ static SheetPos delta, tmp;
+ static int control_key_down = 0;
+
+ /* Remember the last position of the mouse cursor. */
+ static double last_x, last_y;
+
+ /* Mouse cursor position in window coordinates, snapped to the grid
+ spacing. */
+ double snapped_x, snapped_y;
+
+ /* Move the selected item(s) by this movement. */
+ double dx, dy;
+
+ /* The sheet is scrolled by (canvas coordinates): */
+ int offset_x, offset_y;
+
+ /* The selected group's bounding box in window resp. canvas coordinates. */
+ double x1, y1, x2, y2;
+ int cx1, cy1, cx2, cy2;
+
+ /* The sheet's width and the its viewport's width. */
+ guint sheet_width, sheet_height;
+
+ g_return_val_if_fail (sheet != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET (sheet), FALSE);
+ g_return_val_if_fail (schematic_view != NULL, FALSE);
+ g_return_val_if_fail (IS_SCHEMATIC_VIEW (schematic_view), FALSE);
+ /* assert? */
+ g_return_val_if_fail (sheet->priv->floating_objects != NULL, FALSE);
+
+ priv = sheet->priv;
+ canvas = GNOME_CANVAS (sheet);
+
+ switch (event->type) {
+ case GDK_BUTTON_RELEASE:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (sheet->state != SHEET_STATE_FLOAT)
+ return TRUE;
+
+ switch (event->button.button) {
+ case 2:
+ case 4:
+ case 5:
+ return FALSE;
+
+ case 1:
+ control_key_down = event->button.state & GDK_CONTROL_MASK;
+
+ /* Continue adding if CTRL is pressed */
+ if (!control_key_down) {
+ sheet->state = SHEET_STATE_NONE;
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ g_signal_handler_disconnect ( G_OBJECT (sheet), sheet->priv->float_handler_id);
+ sheet->priv->float_handler_id = 0;
+ }
+
+ g_object_get (G_OBJECT (sheet->priv->floating_group),
+ "x", &tmp.x,
+ "y", &tmp.y,
+ NULL);
+
+ delta.x = tmp.x - delta.x;
+ delta.y = tmp.y - delta.y;
+
+ for (list = priv->floating_objects; list; list = list->next) {
+ SheetItem *floating_item;
+ ItemData *floating_data;
+ /*
+ * Destroy the ghost item and place a real item.
+ */
+ floating_item = list->data;
+ if (!control_key_down)
+ floating_data = sheet_item_get_data (floating_item);
+ else
+ floating_data = item_data_clone (sheet_item_get_data (floating_item));
+
+ g_object_ref (G_OBJECT (floating_data));
+
+ item_data_move (floating_data, &delta);
+ schematic_add_item (schematic_view_get_schematic (schematic_view),
+ floating_data);
+
+ if (!control_key_down)
+ gtk_object_destroy (GTK_OBJECT(floating_item));
+ }
+
+ if (!control_key_down) {
+ g_list_free (sheet->priv->floating_objects);
+ sheet->priv->floating_objects = NULL;
+ } else {
+ gtk_object_set (GTK_OBJECT (sheet->priv->floating_group),
+ "x", tmp.x,
+ "y", tmp.y,
+ NULL);
+ }
+ delta.x = 0;
+ delta.y = 0;
+ break;
+
+ case 3:
+ /*
+ * Cancel the "float-placement" for button-3 clicks.
+ */
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ sheet_item_cancel_floating (schematic_view);
+ break;
+ }
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ return TRUE;
+
+ case GDK_MOTION_NOTIFY:
+ if (sheet->state != SHEET_STATE_FLOAT &&
+ sheet->state != SHEET_STATE_FLOAT_START)
+ return FALSE;
+
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+
+ if (sheet->state == SHEET_STATE_FLOAT_START) {
+ sheet->state = SHEET_STATE_FLOAT;
+
+ /*
+ * Reparent the selected objects so that we can
+ * move them efficiently.
+ */
+ for (list = priv->floating_objects; list; list = list->next) {
+ sheet_item_reparent (SHEET_ITEM (list->data), priv->floating_group);
+ }
+
+ gtk_object_get (GTK_OBJECT (sheet->priv->floating_group),
+ "x", &delta.x,
+ "y", &delta.y,
+ NULL);
+
+ /*
+ * Center the objects around the mouse pointer.
+ */
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group),
+ &x1, &y1, &x2, &y2);
+
+ gnome_canvas_window_to_world (canvas, event->motion.x, event->motion.y,
+ &snapped_x, &snapped_y);
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - (x1 + (x2 - x1) / 2);
+ dy = snapped_y - (y1 + (y2 - y1) / 2);
+ snap_to_grid (sheet->grid, &dx, &dy);
+
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group),
+ dx, dy);
+
+ x1 += dx;
+ y1 += dy;
+ x2 += dx;
+ y2 += dy;
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+
+ return TRUE;
+ }
+
+ /*
+ * We have to convert from window to world coordinates here,
+ * since we get the coordinates relative the sheet and not the
+ * item like we do in sheet_item_event ().
+ */
+ gnome_canvas_window_to_world (canvas, event->motion.x, event->motion.y,
+ &snapped_x, &snapped_y);
+
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ /* Calculate which amount to move the selected objects by. */
+ dx = snapped_x - last_x;
+ dy = snapped_y - last_y;
+
+ /* We need the bounding box to check that we don't move anything
+ off-sheet. */
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group),
+ &x1, &y1, &x2, &y2);
+
+ x1 += dx;
+ x2 += dx;
+ y1 += dy;
+ y2 += dy;
+
+ gnome_canvas_get_scroll_offsets (canvas, &offset_x, &offset_y);
+ gnome_canvas_w2c (canvas, x1, y1, &cx1, &cy1);
+ gnome_canvas_w2c (canvas, x2, y2, &cx2, &cy2);
+ sheet_get_size_pixels (sheet, &sheet_width, &sheet_height);
+
+ /* Check that we don't move outside the sheet horizontally */
+ if (cx1 <= 0){ /* leftmost edge */
+ dx = dx - x1;
+ snap_to_grid (sheet->grid, &dx, NULL);
+ snapped_x = last_x + dx;
+ } else if (cx2 >= sheet_width){ /* rightmost edge */
+ dx = dx - (x2 - sheet_width/sheet->priv->zoom);
+ snap_to_grid (sheet->grid, &dx, NULL);
+ snapped_x = last_x + dx;
+ }
+
+ /* And vertically */
+ if (cy1 <= 0){ /* upper edge */
+ dy = dy - y1;
+ snap_to_grid (sheet->grid, NULL, &dy);
+ snapped_y = last_y + dy;
+ } else if (cy2 >= sheet_height){ /* lower edge */
+ dy = dy - (y2 - sheet_height/sheet->priv->zoom);
+ snap_to_grid (sheet->grid, NULL, &dy);
+ snapped_y = last_y + dy;
+ }
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+
+ if (dx != 0 || dy != 0){
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group),
+ dx, dy);
+ }
+ break;
+
+ case GDK_KEY_PRESS:
+ switch (event->key.keyval) {
+ case GDK_r:
+ schematic_view_rotate_ghosts (schematic_view);
+ {
+ int x, y;
+
+ gdk_window_get_pointer (GDK_WINDOW (GTK_WIDGET (sheet)->window),
+ &x, &y, NULL);
+ gnome_canvas_window_to_world (GNOME_CANVAS (sheet), x, y,
+ &snapped_x, &snapped_y);
+
+ /*
+ * Center the objects around the mouse pointer.
+ */
+ gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group),
+ &x1, &y1, &x2, &y2);
+
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - (x1 + (x2 - x1) / 2);
+ dy = snapped_y - (y1 + (y2 - y1) / 2);
+ snap_to_grid (sheet->grid, &dx, &dy);
+
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group),
+ dx, dy);
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+sheet_item_select (SheetItem *item, gboolean select)
+{
+ g_return_val_if_fail (item != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE);
+
+ if ((item->priv->selected && select) ||
+ (!item->priv->selected && !select)) {
+ return FALSE;
+ }
+
+ g_signal_emit_by_name (G_OBJECT (item), "selection_changed", select);
+ item->priv->selected = select;
+
+ return TRUE;
+}
+
+void
+sheet_item_select_in_area (SheetItem *item, SheetPos *p1, SheetPos *p2)
+{
+ SheetItemClass *si_class;
+ gboolean in_area;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (item));
+ g_return_if_fail (p1 != NULL);
+ g_return_if_fail (p2 != NULL);
+
+ si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item));
+ in_area = si_class->is_in_area (item, p1, p2);
+
+ if (in_area && !item->priv->selected)
+ sheet_item_select (item, TRUE);
+ else if (!in_area && item->priv->selected &&
+ !item->priv->preserve_selection)
+ sheet_item_select (item, FALSE);
+}
+
+/*
+ * sheet_item_reparent ()
+ *
+ * Reparent a sheet object without moving it on the sheet.
+ */
+void
+sheet_item_reparent (SheetItem *object, GnomeCanvasGroup *group)
+{
+ double x1, y1, x2, y2;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (object));
+ g_return_if_fail (group != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+
+ g_object_get (G_OBJECT (object),
+ "x", &x1,
+ "y", &y1,
+ NULL);
+
+ gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (object), &x1, &y1);
+
+ gnome_canvas_item_reparent (GNOME_CANVAS_ITEM (object), group);
+
+ g_object_get (G_OBJECT (object), "x", &x2, "y", &y2, NULL);
+
+ gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (object), &x2, &y2);
+
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (object), x1 - x2, y1 - y2);
+
+ /* This is needed because of a bug (?) in gnome-libs. */
+ gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (object));
+}
+
+void
+sheet_item_edit_properties (SheetItem *item)
+{
+ SheetItemClass *si_class;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (item));
+
+ si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item));
+
+ if (si_class->edit_properties)
+ si_class->edit_properties (item);
+}
+
+void
+sheet_item_rotate (SheetItem *sheet_item, int angle, SheetPos *center)
+{
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (sheet_item));
+
+ item_data_rotate (sheet_item->priv->data, angle, center);
+}
+
+void
+sheet_item_paste (SchematicView *schematic_view, ClipboardData *data)
+{
+ SheetItemClass *item_class;
+ ItemDataClass *id_class;
+ ItemData *item_data, *clone;
+
+ g_return_if_fail (schematic_view != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (schematic_view));
+ g_return_if_fail (data != NULL);
+
+ item_data = ITEM_DATA (clipboard_data_get_item_data (data));
+
+ id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (item_data));
+ if (id_class->clone == NULL)
+ return;
+
+ /*
+ * Duplicate the data for the item and paste the item on the sheet.
+ */
+ item_class = SHEET_ITEM_CLASS (clipboard_data_get_item_class (data));
+ if (item_class->paste) {
+ clone = id_class->clone (item_data);
+ item_class->paste (schematic_view, clone);
+ }
+}
+
+ItemData *
+sheet_item_get_data (SheetItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ g_return_val_if_fail (IS_SHEET_ITEM (item), NULL);
+
+ return item->priv->data;
+}
+
+Sheet *
+sheet_item_get_sheet (SheetItem *item)
+{
+ g_return_val_if_fail (item != NULL, NULL);
+ g_return_val_if_fail (IS_SHEET_ITEM (item), NULL);
+
+ return SHEET (GNOME_CANVAS_ITEM (item)->canvas);
+}
+
+gboolean
+sheet_item_get_selected (SheetItem *item)
+{
+ g_return_val_if_fail (item != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE);
+
+ return item->priv->selected;
+}
+
+gboolean
+sheet_item_get_preserve_selection (SheetItem *item)
+{
+ g_return_val_if_fail (item != NULL, FALSE);
+ g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE);
+
+ return item->priv->preserve_selection;
+}
+
+void
+sheet_item_set_preserve_selection (SheetItem *item, gboolean set)
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (item));
+
+ item->priv->preserve_selection = set;
+}
+
+void
+sheet_item_place (SheetItem *item, SchematicView *sv)
+{
+ SheetItemClass *si_class;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (item));
+
+ si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item));
+
+ if (si_class->place)
+ si_class->place (item, sv);
+}
+
+void
+sheet_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+ SheetItemClass *si_class;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_SHEET_ITEM (item));
+
+ si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item));
+
+ if (si_class->place_ghost)
+ si_class->place_ghost (item, sv);
+}
+
+void
+sheet_item_add_menu (SheetItem *item, const char *menu,
+ const GtkActionEntry *action_entries, int nb_entries)
+{
+ GError *error = NULL;
+ gtk_action_group_add_actions (item->priv->action_group,
+ action_entries,
+ nb_entries,
+ NULL);
+
+ if (!gtk_ui_manager_add_ui_from_string (item->priv->ui_manager, menu, -1, &error)) {
+ g_message ("building menus failed: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+}
diff --git a/src/sheet/sheet-item.h b/src/sheet/sheet-item.h
new file mode 100644
index 0000000..d7d544f
--- /dev/null
+++ b/src/sheet/sheet-item.h
@@ -0,0 +1,129 @@
+/*
+ * sheet-item.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_ITEM_H
+#define __SHEET_ITEM_H
+
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <libgnomecanvas/libgnomecanvas.h>
+#include "sheet-pos.h"
+#include "item-data.h"
+
+
+#define TYPE_SHEET_ITEM (sheet_item_get_type())
+#define SHEET_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, sheet_item_get_type (), SheetItem))
+#define SHEET_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, sheet_item_get_type (), SheetItemClass))
+#define IS_SHEET_ITEM(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, TYPE_SHEET_ITEM, SheetItemClass))
+
+typedef struct _SheetItem SheetItem;
+typedef struct _SheetItemClass SheetItemClass;
+typedef struct _SheetItemPriv SheetItemPriv;
+
+#include "schematic-view.h"
+#include "sheet.h"
+#include "clipboard.h"
+
+struct _SheetItem {
+ GnomeCanvasGroup canvas_group;
+ SheetItemPriv *priv;
+};
+
+struct _SheetItemClass {
+ GnomeCanvasGroupClass parent_class;
+
+ /*
+ * Methods.
+ */
+ gboolean (*is_in_area) (SheetItem *item, SheetPos *p1, SheetPos *p2);
+ void (*show_labels) (SheetItem *sheet_item, gboolean show);
+ void (*edit_properties) (SheetItem *item);
+ void (*paste) (SchematicView *schematic_view,
+ ItemData *data);
+ void (*place) (SheetItem *item, SchematicView *sv);
+ void (*place_ghost) (SheetItem *item, SchematicView *sv);
+
+ /*
+ * Signal handlers.
+ */
+ void (*moved) (SheetItem *item);
+ void (*selection_changed)(SheetItem *item);
+ void (*mouse_over) (SheetItem *item);
+};
+
+GType sheet_item_get_type (void);
+
+void sheet_item_select_all (SchematicView *sv,
+ gboolean select);
+
+gboolean sheet_item_select (SheetItem *item,
+ gboolean select);
+
+Sheet *sheet_item_get_sheet (SheetItem *item);
+
+int sheet_item_event (SheetItem *sheet_item,
+ const GdkEvent *event,
+ SchematicView *sv);
+
+int sheet_item_floating_event (Sheet *sheet,
+ const GdkEvent *event,
+ SchematicView *schematic_view);
+
+void sheet_item_reparent (SheetItem *item,
+ GnomeCanvasGroup *group);
+
+void sheet_item_cancel_floating (SchematicView *sv);
+
+void sheet_item_edit_properties (SheetItem *item);
+
+ItemData *sheet_item_get_data (SheetItem *item);
+
+void sheet_item_paste (SchematicView *schematic_view,
+ ClipboardData *data);
+
+void sheet_item_rotate (SheetItem *sheet_item,
+ int angle, SheetPos *center);
+
+gboolean sheet_item_get_selected (SheetItem *item);
+
+gboolean sheet_item_get_preserve_selection (SheetItem *item);
+
+void sheet_item_set_preserve_selection (SheetItem *item,
+ gboolean set);
+
+void sheet_item_select_in_area (SheetItem *item,
+ SheetPos *p1,
+ SheetPos *p2);
+
+void sheet_item_place (SheetItem *item, SchematicView *sv);
+void sheet_item_place_ghost (SheetItem *item, SchematicView *sv);
+void sheet_item_add_menu (SheetItem *item, const char *menu,
+ const GtkActionEntry *action_entries, int nb_entries);
+
+#endif
diff --git a/src/sheet/sheet-private.h b/src/sheet/sheet-private.h
new file mode 100644
index 0000000..14025ee
--- /dev/null
+++ b/src/sheet/sheet-private.h
@@ -0,0 +1,54 @@
+/*
+ * sheet-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 __SHEET_PRIVATE_H
+#define __SHEET_PRIVATE_H
+
+#include "sheet.h"
+
+struct _SheetPriv {
+
+ int wire_handler_id; /* Keeps the current signal handler for wire
+ creation. */
+ int float_handler_id; /* Keeps the signal handler for floating objects. */
+ int scroll_timeout_id;
+
+ double zoom;
+ gulong width;
+ gulong height;
+
+ void *current_object; /* SheetItem */
+
+ GnomeCanvasGroup *selected_group;
+ GnomeCanvasGroup *floating_group;
+ GList *selected_objects;
+ GList *floating_objects;
+};
+
+#endif
diff --git a/src/sheet/sheet.c b/src/sheet/sheet.c
new file mode 100644
index 0000000..aa73760
--- /dev/null
+++ b/src/sheet/sheet.c
@@ -0,0 +1,469 @@
+/*
+ * sheet.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 <gdk/gdkprivate.h>
+#include "sheet-private.h"
+#include "sheet-item.h"
+#include "node-store.h"
+#include "wire-item.h"
+#include "part-item.h"
+#include "grid.h"
+
+static void sheet_class_init (SheetClass *klass);
+static void sheet_init (Sheet *sheet);
+static void sheet_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *spec);
+static void sheet_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *spec);
+static void sheet_destroy (GtkObject *object);
+static void sheet_set_zoom (const Sheet *sheet, double zoom);
+static void sheet_realized (GtkWidget *widget, gpointer data);
+
+enum {
+ SELECTION_CHANGED,
+ BUTTON_PRESS,
+ CONTEXT_CLICK,
+ CANCEL,
+ RESET_TOOL,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GnomeCanvasClass *sheet_parent_class = NULL;
+
+enum {
+ ARG_0,
+ ARG_ZOOM
+};
+
+GType
+sheet_get_type ()
+{
+ static GType sheet_type = 0;
+
+ if (!sheet_type) {
+ static const GTypeInfo sheet_info = {
+ sizeof(SheetClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)sheet_class_init,
+ NULL,
+ NULL,
+ sizeof(Sheet),
+ 0,
+ (GInstanceInitFunc)sheet_init,
+ NULL
+ };
+
+ sheet_type = g_type_register_static(GNOME_TYPE_CANVAS,
+ "Sheet", &sheet_info, 0);
+ }
+ return sheet_type;
+}
+
+static void
+sheet_class_init (SheetClass *sheet_class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = G_OBJECT_CLASS (sheet_class);
+ widget_class = GTK_WIDGET_CLASS (sheet_class);
+ sheet_parent_class = g_type_class_peek(GNOME_TYPE_CANVAS);
+
+ object_class->set_property = sheet_set_property;
+ object_class->get_property = sheet_get_property;
+
+ g_object_class_install_property(
+ object_class,
+ ARG_ZOOM,
+ g_param_spec_double("zoom", "Sheet::zoom", "the zoom factor",
+ 0.01f, 10.0f, 1.0f, G_PARAM_READWRITE)
+ );
+
+ //object_class->destroy = sheet_destroy;
+
+ /* Signals. */
+ signals[SELECTION_CHANGED] = g_signal_new ("selection_changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SheetClass, selection_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[BUTTON_PRESS] = g_signal_new ("button_press",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetClass, button_press),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ /* TODO deberia ser boolean? */
+ G_TYPE_NONE, 1, GDK_TYPE_EVENT);
+ signals[CONTEXT_CLICK] = g_signal_new ("context_click",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetClass, context_click),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
+ signals[CONTEXT_CLICK] = g_signal_new ("cancel",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetClass, cancel),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[RESET_TOOL] = g_signal_new ("reset_tool",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SheetClass, reset_tool),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+sheet_init (Sheet *sheet)
+{
+ sheet->priv = g_new0 (SheetPriv, 1);
+ sheet->priv->zoom = 1.0;
+ sheet->priv->selected_objects = NULL;
+ sheet->priv->selected_group = NULL;
+ sheet->priv->floating_group = NULL;
+ sheet->priv->wire_handler_id = 0;
+ sheet->priv->float_handler_id = 0;
+
+ sheet->state = SHEET_STATE_NONE;
+
+/* GNOME_CANVAS (sheet)->aa = TRUE;*/
+}
+
+void
+sheet_get_zoom (const Sheet *sheet, gdouble *zoom)
+{
+ *zoom = sheet->priv->zoom;
+}
+
+static void
+sheet_set_zoom (const Sheet *sheet, double zoom)
+{
+ gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet), zoom);
+ sheet->priv->zoom = zoom;
+}
+
+void
+sheet_change_zoom (const Sheet *sheet, gdouble rate)
+{
+ sheet->priv->zoom *= rate;
+
+ gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet), sheet->priv->zoom);
+}
+
+static void
+sheet_realized (GtkWidget *widget, gpointer data)
+{
+ GdkWindow *window;
+ GdkColormap *colormap;
+ GtkStyle *style;
+
+ /*
+ * We set the background pixmap to NULL so that X won't clear
+ * exposed areas and thus be faster.
+ */
+ gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE);
+
+ window = widget->window;
+
+ /*
+ * Set the background to white.
+ */
+ style = gtk_style_copy (widget->style);
+ colormap = gtk_widget_get_colormap (widget);
+ gdk_color_white (colormap, &style->bg[GTK_STATE_NORMAL]);
+ gtk_widget_set_style (widget, style);
+ gtk_style_unref (style);
+}
+
+GtkWidget *
+sheet_new (int width, int height)
+{
+ GnomeCanvas *sheet_canvas;
+ GnomeCanvasGroup *sheet_group;
+ GnomeCanvasPoints *points;
+ Sheet *sheet;
+ GtkWidget *sheet_widget;
+
+ /* Creo el Canvas con Anti-aliasing */
+ sheet = SHEET(g_object_new(TYPE_SHEET, NULL));
+
+ g_signal_connect(G_OBJECT(sheet),
+ "realize", G_CALLBACK(sheet_realized), 0);
+
+ sheet_canvas = GNOME_CANVAS(sheet);
+ sheet_group = GNOME_CANVAS_GROUP(sheet_canvas->root);
+ sheet_widget = GTK_WIDGET(sheet);
+
+ gnome_canvas_set_scroll_region (sheet_canvas,
+ 0, 0, width + 20, height + 20);
+
+ /* Since there is no API for this (yet), tweak it directly. */
+ sheet_canvas->close_enough = 6.0;
+
+ sheet->priv->width = width;
+ sheet->priv->height = height;
+
+ /* Create the dot grid. */
+ sheet->grid = GRID (gnome_canvas_item_new (
+ sheet_group,
+ grid_get_type (),
+ "color", "dark gray",
+ "spacing", 10.0,
+ "snap", TRUE,
+ NULL));
+
+ grid_show (sheet->grid, TRUE);
+
+ /* Everything outside the sheet should be gray. */
+ /* top */
+ gnome_canvas_item_new (
+ sheet_group,
+ gnome_canvas_rect_get_type (),
+ "fill_color", "gray",
+ "outline_color", NULL,
+ "width_pixels", (int)1,
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", (double)width + 20,
+ "y2", 20.0,
+ NULL);
+
+ gnome_canvas_item_new (
+ sheet_group,
+ gnome_canvas_rect_get_type (),
+ "fill_color", "gray",
+ "outline_color", NULL,
+ "width_pixels", (int)1,
+ "x1", 0.0,
+ "y1", (double)height,
+ "x2", (double)width + 20,
+ "y2", (double)height + 20,
+ NULL);
+
+ /* right */
+ gnome_canvas_item_new (
+ sheet_group,
+ gnome_canvas_rect_get_type (),
+ "fill_color", "gray",
+ "outline_color", NULL,
+ "width_pixels", (int)1,
+ "x1", 0.0,
+ "y1", 0.0,
+ "x2", 20.0,
+ "y2", (double)height + 20,
+ NULL);
+
+ gnome_canvas_item_new (
+ sheet_group,
+ gnome_canvas_rect_get_type (),
+ "fill_color", "gray",
+ "outline_color", NULL,
+ "width_pixels", (int)1,
+ "x1", (double)width,
+ "y1", 0.0,
+ "x2", (double)width + 20,
+ "y2", (double)height + 20,
+ NULL);
+
+ /* Draw a thin black border around the sheet. */
+ points = gnome_canvas_points_new (5);
+ points->coords[0] = 20.0;
+ points->coords[1] = 20.0;
+ points->coords[2] = width;
+ points->coords[3] = 20.0;
+ points->coords[4] = width;
+ points->coords[5] = height;
+ points->coords[6] = 20.0;
+ points->coords[7] = height;
+ points->coords[8] = 20.0;
+ points->coords[9] = 20.0;
+ gnome_canvas_item_new (
+ sheet_group,
+ gnome_canvas_line_get_type (),
+ "fill_color", "black",
+ "width_pixels", (int)1,
+ "points", points,
+ NULL);
+
+ gnome_canvas_points_free (points);
+
+ /* Finally, create the object group that holds all objects. */
+ sheet->object_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new (
+ gnome_canvas_root(GNOME_CANVAS(sheet_canvas)),
+ GNOME_TYPE_CANVAS_GROUP,
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ sheet->priv->selected_group = GNOME_CANVAS_GROUP (gnome_canvas_item_new (
+ sheet->object_group,
+ GNOME_TYPE_CANVAS_GROUP,
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ sheet->priv->floating_group = GNOME_CANVAS_GROUP (gnome_canvas_item_new (
+ sheet->object_group,
+ gnome_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+
+ return sheet_widget;
+}
+
+static void
+sheet_set_property (GObject *object,
+ guint prop_id, const GValue *value, GParamSpec *spec)
+{
+ const Sheet *sheet = SHEET (object);
+
+ switch (prop_id) {
+ case ARG_ZOOM:
+ sheet_set_zoom (sheet, g_value_get_double(value));
+ break;
+ }
+}
+
+static void
+sheet_get_property (GObject *object,
+ guint prop_id, GValue *value, GParamSpec *spec)
+{
+ const Sheet *sheet = SHEET (object);
+
+ switch (prop_id) {
+ case ARG_ZOOM:
+ g_value_set_double(value, sheet->priv->zoom);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(sheet, prop_id, spec);
+ break;
+ }
+}
+
+static void
+sheet_destroy (GtkObject *object)
+{
+ Sheet *sheet;
+ SheetPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_SHEET (object));
+
+ sheet = SHEET (object);
+ priv = sheet->priv;
+
+ if (priv) {
+ g_list_free (priv->selected_objects);
+ g_list_free (priv->floating_objects);
+
+ /*
+ * We need to destroy this first so that things get destroyed in the
+ * right order. Label groups are destroyed in their parent parts'
+ * destroy handler, so when the sheet gets destroyed, label groups
+ * will screw things up if this is not done.
+ */
+ gtk_object_destroy (GTK_OBJECT (sheet->object_group));
+
+ g_free (priv);
+ sheet->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (sheet_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (sheet_parent_class)->destroy) (object);
+}
+
+void
+sheet_scroll (const Sheet *sheet, int delta_x, int delta_y)
+{
+ GtkAdjustment *hadj, *vadj;
+ GtkAllocation *allocation;
+ gfloat vnew, hnew;
+ gfloat hmax, vmax;
+ const SheetPriv *priv = sheet->priv;
+
+ hadj = GTK_LAYOUT (sheet)->hadjustment;
+ vadj = GTK_LAYOUT (sheet)->vadjustment;
+
+ allocation = &GTK_WIDGET (sheet)->allocation;
+
+ if (priv->width > allocation->width)
+ hmax = (gfloat) (priv->width - allocation->width);
+ else
+ hmax = 0.0;
+
+ if (priv->height > allocation->height)
+ vmax = (gfloat) (priv->height - allocation->height);
+ else
+ vmax = 0.0;
+
+ hnew = CLAMP (hadj->value + (gfloat) delta_x, 0.0, hmax);
+ vnew = CLAMP (vadj->value + (gfloat) delta_y, 0.0, vmax);
+
+ if (hnew != hadj->value) {
+ hadj->value = hnew;
+ g_signal_emit_by_name (G_OBJECT (hadj), "value_changed");
+ }
+ if (vnew != vadj->value) {
+ vadj->value = vnew;
+ g_signal_emit_by_name (G_OBJECT (vadj), "value_changed");
+ }
+}
+
+void
+sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height)
+{
+ *width = sheet->priv->width * sheet->priv->zoom;
+ *height = sheet->priv->height * sheet->priv->zoom;
+}
+
+void
+sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog)
+{
+/* gtk_window_set_transient_for (
+ GTK_WINDOW (dialog),
+ GTK_WINDOW (SCHEMATIC (sheet->schematic)->toplevel)
+ );*/
+}
diff --git a/src/sheet/sheet.h b/src/sheet/sheet.h
new file mode 100644
index 0000000..d880743
--- /dev/null
+++ b/src/sheet/sheet.h
@@ -0,0 +1,92 @@
+/*
+ * sheet.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_H
+#define __SHEET_H
+
+#include <libgnomecanvas/libgnomecanvas.h>
+#include <gtk/gtkdialog.h>
+#include "grid.h"
+
+typedef struct _Sheet Sheet;
+typedef struct _SheetPriv SheetPriv;
+typedef struct _SheetClass SheetClass;
+
+#define TYPE_SHEET (sheet_get_type())
+#define SHEET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_SHEET, Sheet))
+#define SHEET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, TYPE_SHEET, SheetClass))
+#define IS_SHEET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_SHEET))
+
+typedef enum {
+ SHEET_STATE_NONE,
+ SHEET_STATE_DRAG_START,
+ SHEET_STATE_DRAG,
+ SHEET_STATE_FLOAT_START,
+ SHEET_STATE_FLOAT,
+ SHEET_STATE_WIRE,
+ SHEET_STATE_TEXTBOX_WAIT,
+ SHEET_STATE_TEXTBOX_START,
+ SHEET_STATE_TEXTBOX_CREATING
+} SheetState;
+
+struct _Sheet {
+ GnomeCanvas parent_canvas;
+ SheetState state;
+ GnomeCanvasGroup *object_group;
+ Grid *grid;
+ SheetPriv *priv;
+};
+
+struct _SheetClass {
+ GnomeCanvasClass parent_class;
+
+ void (*selection_changed) (Sheet *sheet);
+ gint (*button_press) (Sheet *sheet, GdkEventButton *event);
+ void (*context_click) (Sheet *sheet,
+ const char *name, gpointer data);
+ void (*cancel) (Sheet *sheet);
+ void (*reset_tool) (Sheet *sheet);
+};
+
+GType sheet_get_type (void);
+GtkWidget *sheet_new (int width, int height);
+void sheet_scroll (const Sheet *sheet, int dx, int dy);
+void sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height);
+int sheet_get_num_selected_items (const Sheet *sheet);
+gpointer sheet_get_first_selected_item (const Sheet *sheet);
+GSList *sheet_get_selected_items (const Sheet *sheet);
+void sheet_change_zoom (const Sheet *sheet, double rate);
+void sheet_get_zoom (const Sheet *sheet, gdouble *zoom);
+void sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog);
+void sheet_delete_selected_items (const Sheet *sheet);
+void sheet_rotate_selected_items (const Sheet *sheet);
+void sheet_rotate_floating_items (const Sheet *sheet);
+void sheet_reset_floating_items (const Sheet *sheet);
+
+#endif
diff --git a/src/sheet/textbox-item.c b/src/sheet/textbox-item.c
new file mode 100644
index 0000000..2354c55
--- /dev/null
+++ b/src/sheet/textbox-item.c
@@ -0,0 +1,699 @@
+/*
+ * textbox-item.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.
+ */
+/*
+ * TextboxItem object: the graphical representation of a textbox.
+ *
+ * Author:
+ * Richard Hult (rhult@hem2.passagen.se)
+ *
+ * (C) 1999, 2000 Richard Hult, http://www.dtek.chalmers.se/~d4hult/oregano/
+ */
+
+#include <math.h>
+#include <gnome.h>
+#include <glade/glade.h>
+#include "cursors.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "textbox-item.h"
+#include "textbox.h"
+#include "dialogs.h"
+
+#define NORMAL_COLOR "black"
+#define SELECTED_COLOR "green"
+#define TEXTBOX_FONT "Arial 10"
+
+static void textbox_item_class_init (TextboxItemClass *klass);
+static void textbox_item_init (TextboxItem *item);
+static void textbox_item_destroy (GtkObject *object);
+static void textbox_item_moved (SheetItem *object);
+
+static void textbox_rotated_callback (ItemData *data, int angle,
+ SheetItem *sheet_item);
+static void textbox_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item);
+static void textbox_moved_callback (ItemData *data, SheetPos *pos,
+ SheetItem *item);
+static void textbox_text_changed_callback (ItemData *data, gchar *new_text,
+ SheetItem *item);
+static void textbox_font_changed_callback (ItemData *data, gchar *new_font,
+ SheetItem *item);
+
+static void textbox_item_paste (SchematicView *sv, ItemData *data);
+static void selection_changed (TextboxItem *item, gboolean select,
+ gpointer user_data);
+static int select_idle_callback (TextboxItem *item);
+static int deselect_idle_callback (TextboxItem *item);
+static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2);
+inline static void get_cached_bounds (TextboxItem *item, SheetPos *p1,
+ SheetPos *p2);
+
+static void textbox_item_place (SheetItem *item, SchematicView *sv);
+static void textbox_item_place_ghost (SheetItem *item, SchematicView *sv);
+
+static void edit_cmd (GtkWidget *widget, SchematicView *sv);
+static void edit_textbox (SheetItem *sheet_item);
+
+typedef struct {
+ GtkDialog *dialog;
+ GtkFontSelection *font;
+ GtkEntry *entry;
+} TextboxPropDialog;
+
+static TextboxPropDialog *prop_dialog = NULL;
+static SheetItemClass *textbox_item_parent_class = NULL;
+
+/* Use EDIT!! */
+static const char *textbox_item_context_menu =
+"<ui>"
+" <popup name='ItemMenu'>"
+" <menuitem action='EditText'/>"
+" </popup>"
+"</ui>";
+
+static GtkActionEntry action_entries[] = {
+ {"EditText", GTK_STOCK_PROPERTIES, N_("_Edit the text..."), NULL,
+ N_("Edit the text"),G_CALLBACK (edit_cmd)}
+};
+
+enum {
+ TEXTBOX_ITEM_ARG_0,
+ TEXTBOX_ITEM_ARG_NAME
+};
+
+struct _TextboxItemPriv {
+ guint cache_valid : 1;
+
+ guint highlight : 1;
+
+ // FIXME: More members.
+ GnomeCanvasItem *text_canvas_item;
+
+ /*
+ * Cached bounding box. This is used to make
+ * the rubberband selection a bit faster.
+ */
+ SheetPos bbox_start;
+ SheetPos bbox_end;
+};
+
+GType
+textbox_item_get_type ()
+{
+ static GType textbox_item_type = 0;
+
+ if (!textbox_item_type) {
+ static const GTypeInfo textbox_item_info = {
+ sizeof(TextboxItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)textbox_item_class_init,
+ NULL,
+ NULL,
+ sizeof(TextboxItem),
+ 0,
+ (GInstanceInitFunc)textbox_item_init,
+ NULL
+ };
+
+ textbox_item_type = g_type_register_static(TYPE_SHEET_ITEM,
+ "TextboxItem", &textbox_item_info, 0);
+ }
+ return textbox_item_type;
+}
+
+static void
+textbox_item_class_init (TextboxItemClass *textbox_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ SheetItemClass *sheet_item_class;
+
+ object_class = G_OBJECT_CLASS(textbox_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(textbox_item_class);
+ sheet_item_class = SHEET_ITEM_CLASS(textbox_item_class);
+ textbox_item_parent_class =
+ g_type_class_peek_parent(textbox_item_class);
+
+ gtk_object_class->destroy = textbox_item_destroy;
+
+ sheet_item_class->moved = textbox_item_moved;
+ sheet_item_class->paste = textbox_item_paste;
+ sheet_item_class->is_in_area = is_in_area;
+ sheet_item_class->selection_changed = (gpointer) selection_changed;
+ sheet_item_class->edit_properties = edit_textbox;
+ sheet_item_class->place = textbox_item_place;
+ sheet_item_class->place_ghost = textbox_item_place_ghost;
+}
+
+static void
+textbox_item_init (TextboxItem *item)
+{
+ TextboxItemPriv *priv;
+
+ priv = g_new0 (TextboxItemPriv, 1);
+ item->priv = priv;
+
+ priv->highlight = FALSE;
+ priv->cache_valid = FALSE;
+
+ sheet_item_add_menu (SHEET_ITEM (item), textbox_item_context_menu,
+ action_entries, G_N_ELEMENTS (action_entries));
+}
+
+static void
+textbox_item_destroy (GtkObject *object)
+{
+ TextboxItem *textbox;
+ TextboxItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (object));
+
+ textbox = TEXTBOX_ITEM (object);
+ priv = textbox->priv;
+
+ if (priv) {
+ if (priv->text_canvas_item) {
+ gtk_object_destroy(GTK_OBJECT(priv->text_canvas_item));
+ }
+ textbox->priv = NULL;
+ g_free (priv);
+ }
+
+ if (GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy){
+ GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy(object);
+ }
+}
+
+/*
+ * textbox_item_moved
+ *
+ * "moved" signal handler. Invalidates the bounding box cache.
+ */
+static void
+textbox_item_moved (SheetItem *object)
+{
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+
+ item = TEXTBOX_ITEM (object);
+ priv = item->priv;
+
+ priv->cache_valid = FALSE;
+}
+
+TextboxItem *
+textbox_item_new (Sheet *sheet, Textbox *textbox)
+{
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+ SheetPos pos;
+
+ g_return_val_if_fail(sheet != NULL, NULL);
+ g_return_val_if_fail(IS_SHEET(sheet), NULL);
+
+ item_data_get_pos(ITEM_DATA(textbox), &pos);
+
+ item = TEXTBOX_ITEM(gnome_canvas_item_new (
+ sheet->object_group,
+ textbox_item_get_type(),
+ "data", textbox,
+ "x", (double) pos.x,
+ "y", (double) pos.y,
+ NULL));
+
+ priv = item->priv;
+
+ priv->text_canvas_item = gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_text_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ "text", textbox_get_text (textbox),
+ "fill_color", NORMAL_COLOR,
+ "anchor", GTK_ANCHOR_SW,
+ "font", TEXTBOX_FONT,
+ NULL);
+
+ g_signal_connect_object(G_OBJECT (textbox),
+ "rotated", G_CALLBACK(textbox_rotated_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "flipped", G_CALLBACK(textbox_flipped_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "moved", G_CALLBACK(textbox_moved_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "text_changed", G_CALLBACK(textbox_text_changed_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "font_changed", G_CALLBACK(textbox_font_changed_callback),
+ G_OBJECT(item), 0);
+
+ textbox_update_bbox (textbox);
+
+ return item;
+}
+
+void
+textbox_item_signal_connect_placed (TextboxItem *textbox_item,
+ SchematicView *sv)
+{
+ g_signal_connect (
+ G_OBJECT (textbox_item),
+ "event",
+ G_CALLBACK(sheet_item_event),
+ sv);
+}
+
+static void
+textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
+{
+ TextboxItem *item;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+
+ item->priv->cache_valid = FALSE;
+}
+
+static void
+textbox_flipped_callback (ItemData *data,
+ gboolean horizontal, SheetItem *sheet_item)
+{
+ TextboxItem *item;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+
+ item->priv->cache_valid = FALSE;
+}
+
+static int
+select_idle_callback (TextboxItem *item)
+{
+ SheetPos bbox_start, bbox_end;
+ TextboxItemPriv *priv = item->priv;
+
+ get_cached_bounds (item, &bbox_start, &bbox_end);
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item),
+ "fill_color", SELECTED_COLOR, NULL);
+
+ priv->highlight = TRUE;
+
+ return FALSE;
+}
+
+static int
+deselect_idle_callback (TextboxItem *item)
+{
+ TextboxItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item),
+ "fill_color", NORMAL_COLOR, NULL);
+
+ priv->highlight = FALSE;
+
+ return FALSE;
+}
+
+static void
+selection_changed (TextboxItem *item, gboolean select, gpointer user_data)
+{
+ if (select)
+ gtk_idle_add ((gpointer) select_idle_callback, item);
+ else
+ gtk_idle_add ((gpointer) deselect_idle_callback, item);
+}
+
+static gboolean
+is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2)
+{
+ TextboxItem *item;
+ SheetPos bbox_start, bbox_end;
+
+ item = TEXTBOX_ITEM (object);
+
+ get_cached_bounds (item, &bbox_start, &bbox_end);
+
+ if (p1->x < bbox_start.x &&
+ p2->x > bbox_end.x &&
+ p1->y < bbox_start.y &&
+ p2->y > bbox_end.y)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Retrieves the bounding box. We use a caching scheme for this
+ * since it's too expensive to calculate it every time we need it.
+ */
+inline static void
+get_cached_bounds (TextboxItem *item, SheetPos *p1, SheetPos *p2)
+{
+ PangoFontDescription *font;
+ PangoFontMetrics *font_metric;
+ int width;
+ int rbearing;
+ int lbearing;
+ int ascent, descent;
+ SheetPos pos;
+
+ TextboxItemPriv *priv;
+ priv = item->priv;
+
+ if (!priv->cache_valid) {
+ SheetPos start_pos, end_pos;
+
+ font = pango_font_description_from_string(TEXTBOX_FONT);
+
+ item_data_get_pos (sheet_item_get_data (SHEET_ITEM (item)),
+ &pos);
+
+ start_pos.x = pos.x;
+ start_pos.y = pos.y-5;// - font->ascent;
+ end_pos.x = pos.x+5; // + rbearing;
+ end_pos.y = pos.y+5; // + font->descent;
+
+ priv->bbox_start = start_pos;
+ priv->bbox_end = end_pos;
+ priv->cache_valid = TRUE;
+ pango_font_description_free(font);
+ }
+
+ memcpy (p1, &priv->bbox_start, sizeof (SheetPos));
+ memcpy (p2, &priv->bbox_end, sizeof (SheetPos));
+
+}
+
+static void
+textbox_item_paste (SchematicView *sv, ItemData *data)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ schematic_view_add_ghost_item (sv, data);
+}
+
+/**
+ * This is called when the textbox data was moved. Update the view accordingly.
+ */
+static void
+textbox_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ if (pos == NULL)
+ return;
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ /*
+ * Move the canvas item and invalidate the bbox cache.
+ */
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y);
+ textbox_item->priv->cache_valid = FALSE;
+}
+
+static void
+textbox_text_changed_callback (ItemData *data,
+ gchar *new_text, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM ( textbox_item->priv->text_canvas_item ),
+ "text", new_text, NULL );
+
+}
+
+static void
+textbox_font_changed_callback (ItemData *data,
+ gchar *new_font, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+ g_return_if_fail (new_font != NULL);
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (textbox_item->priv->text_canvas_item),
+ "font", new_font, NULL);
+}
+
+static void
+textbox_item_place (SheetItem *item, SchematicView *sv)
+{
+ textbox_item_signal_connect_placed (TEXTBOX_ITEM (item), sv);
+
+ g_signal_connect (
+ G_OBJECT (item),
+ "double_clicked",
+ G_CALLBACK(edit_textbox),
+ item);
+}
+
+static void
+textbox_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+// textbox_item_signal_connect_placed (TEXTBOX_ITEM (item));
+}
+
+static gboolean
+create_textbox_event (Sheet *sheet, GdkEvent *event, SchematicView *sv)
+{
+ switch (event->type) {
+ case GDK_3BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ return TRUE;
+
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 4 || event->button.button == 5)
+ return FALSE;
+
+ if (event->button.button == 1) {
+ if (sheet->state == SHEET_STATE_TEXTBOX_WAIT)
+ sheet->state = SHEET_STATE_TEXTBOX_START;
+
+ return TRUE;
+ } else
+ return FALSE;
+
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 4 || event->button.button == 5)
+ return FALSE;
+
+ if (sheet->state == SHEET_STATE_TEXTBOX_START) {
+ Textbox *textbox;
+ SheetPos pos;
+
+ sheet->state = SHEET_STATE_NONE;
+
+ pos.x = event->button.x;
+ pos.y = event->button.y;
+
+ textbox = textbox_new (NULL);
+ item_data_set_pos (ITEM_DATA (textbox), &pos);
+ textbox_set_text (textbox, _("Label"));
+
+ schematic_add_item (schematic_view_get_schematic (sv),
+ ITEM_DATA (textbox));
+
+ g_signal_emit_by_name(G_OBJECT(sheet),
+ "reset_tool", NULL);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sheet),
+ G_CALLBACK(create_textbox_event), sv);
+ }
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+ void
+textbox_item_cancel_listen (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = schematic_view_get_sheet (sv);
+
+ sheet->state = SHEET_STATE_NONE;
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), G_CALLBACK(create_textbox_event), sv);
+}
+
+void
+textbox_item_listen (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+// schematic_view_disconnect_handler (sv);
+ sheet = schematic_view_get_sheet (sv);
+
+ /*
+ * Connect to a signal handler that will
+ * let the user create a new textbox.
+ */
+ sheet->state = SHEET_STATE_TEXTBOX_WAIT;
+ g_signal_connect(G_OBJECT(sheet),
+ "event", G_CALLBACK(create_textbox_event), sv);
+}
+
+/*
+ * Go through the properties and commit the changes.
+ */
+static void
+edit_dialog_ok(TextboxItem *item)
+{
+ TextboxItemPriv *priv;
+ Textbox *textbox;
+ const gchar *value;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ priv = item->priv;
+ textbox = TEXTBOX (sheet_item_get_data (SHEET_ITEM (item)));
+
+ value = gtk_entry_get_text(GTK_ENTRY(prop_dialog->entry));
+
+ textbox_set_text(textbox, value);
+ textbox_set_font(textbox,
+ gtk_font_selection_get_font_name(prop_dialog->font));
+}
+
+static void
+edit_textbox (SheetItem *sheet_item)
+{
+ Sheet *sheet;
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+ Textbox *textbox;
+ char *msg, *value;
+ GladeXML *gui;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+ priv = item->priv;
+ textbox = TEXTBOX (sheet_item_get_data (sheet_item));
+
+ if (!g_file_test (OREGANO_GLADEDIR "/textbox-properties-dialog.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/textbox-properties-dialog.glade");
+ oregano_error (_("Could not create textbox properties dialog"));
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (
+ OREGANO_GLADEDIR "/textbox-properties-dialog.glade",
+ NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create textbox properties dialog"));
+ return;
+ }
+
+ prop_dialog = g_new0 (TextboxPropDialog, 1);
+ prop_dialog->dialog = GTK_DIALOG (
+ glade_xml_get_widget (gui, "textbox-properties-dialog"));
+ prop_dialog->font = GTK_FONT_SELECTION (
+ glade_xml_get_widget (gui, "font_selector"));
+ prop_dialog->entry = GTK_ENTRY (glade_xml_get_widget (gui, "entry"));
+
+ value = textbox_get_font (textbox);
+ gtk_font_selection_set_font_name (
+ GTK_FONT_SELECTION (prop_dialog->font), value);
+
+ value = textbox_get_text(textbox);
+ gtk_entry_set_text (GTK_ENTRY (prop_dialog->entry), value);
+
+ sheet = sheet_item_get_sheet (SHEET_ITEM (item));
+ sheet_dialog_set_parent (sheet, (GtkDialog*) prop_dialog->dialog);
+
+ gtk_dialog_set_default_response (
+ GTK_DIALOG (prop_dialog->dialog), GTK_RESPONSE_OK);
+
+ gtk_dialog_run( GTK_DIALOG (prop_dialog->dialog));
+
+ edit_dialog_ok(item);
+
+ /* Clean the dialog */
+ gtk_widget_destroy(GTK_WIDGET(prop_dialog->dialog));
+ prop_dialog = NULL;
+}
+
+static void
+edit_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ GList *list;
+
+ list = schematic_view_get_selection (sv);
+ if ((list != NULL) && IS_TEXTBOX_ITEM (list->data))
+ edit_textbox (list->data);
+}
diff --git a/src/sheet/textbox-item.h b/src/sheet/textbox-item.h
new file mode 100644
index 0000000..74edfff
--- /dev/null
+++ b/src/sheet/textbox-item.h
@@ -0,0 +1,67 @@
+/*
+ * textbox-item.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_ITEM_H
+#define __TEXTBOX_ITEM_H
+
+#include <gtk/gtk.h>
+#include "schematic-view.h"
+#include "sheet-item.h"
+#include "textbox.h"
+
+#define TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, textbox_item_get_type (), TextboxItem)
+#define TEXTBOX_ITEM_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, textbox_item_get_type (), TextboxItemClass)
+#define IS_TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, textbox_item_get_type ())
+
+typedef struct _TextboxItemPriv TextboxItemPriv;
+
+typedef enum {
+ TEXTBOX_DIR_NONE = 0,
+ TEXTBOX_DIR_HORIZ = 1,
+ TEXTBOX_DIR_VERT = 2,
+ TEXTBOX_DIR_DIAG = 3
+} TextboxDir;
+
+typedef struct {
+ SheetItem parent_object;
+ TextboxItemPriv *priv;
+} TextboxItem;
+
+typedef struct {
+ SheetItemClass parent_class;
+} TextboxItemClass;
+
+GType textbox_item_get_type (void);
+TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox);
+void textbox_item_signal_connect_placed (TextboxItem *textbox_item, SchematicView *sv);
+void textbox_item_cancel_listen (SchematicView *sv);
+void textbox_item_listen (SchematicView *sv);
+
+
+#endif
diff --git a/src/sheet/wire-item.c b/src/sheet/wire-item.c
new file mode 100644
index 0000000..a5b542d
--- /dev/null
+++ b/src/sheet/wire-item.c
@@ -0,0 +1,916 @@
+/*
+ * wire-item.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 <math.h>
+#include <gnome.h>
+#include "cursors.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "wire-item.h"
+#include "node-store.h"
+#include "wire.h"
+#include "wire-private.h"
+
+#define NORMAL_COLOR "blue"
+#define SELECTED_COLOR "green"
+#define HIGHLIGHT_COLOR "yellow"
+
+#define RESIZER_SIZE 4.0f
+
+static void wire_item_class_init (WireItemClass *klass);
+static void wire_item_init (WireItem *item);
+static void wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+static void wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+static void wire_item_destroy (GtkObject *object);
+static void wire_item_moved (SheetItem *object);
+
+static void wire_rotated_callback (ItemData *data, int angle,
+ SheetItem *sheet_item);
+static void wire_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item);
+static void wire_moved_callback (ItemData *data, SheetPos *pos,
+ SheetItem *item);
+static void wire_changed_callback (Wire *, WireItem *item);
+static void wire_delete_callback (Wire *, WireItem *item);
+
+static void wire_item_paste (SchematicView *sv, ItemData *data);
+static void selection_changed (WireItem *item, gboolean select,
+ gpointer user_data);
+static int select_idle_callback (WireItem *item);
+static int deselect_idle_callback (WireItem *item);
+static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2);
+inline static void get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2);
+
+static void mouse_over_wire_cb (WireItem *item, SchematicView *sv);
+static void highlight_wire_cb (Wire *wire, WireItem *item);
+static int unhighlight_wire (WireItem *item);
+
+static void wire_item_place (SheetItem *item, SchematicView *sv);
+static void wire_item_place_ghost (SheetItem *item, SchematicView *sv);
+
+static SheetItemClass *wire_item_parent_class = NULL;
+
+enum {
+ WIRE_ITEM_ARG_0,
+ WIRE_ITEM_ARG_NAME
+};
+
+enum {
+ WIRE_RESIZER_NONE,
+ WIRE_RESIZER_1,
+ WIRE_RESIZER_2
+};
+
+struct _WireItemPriv {
+ guint cache_valid : 1;
+ guint resize_state;
+ guint highlight : 1;
+ WireDir direction; /* Direction of the wire. */
+
+ GnomeCanvasLine *line;
+ GnomeCanvasRect *resize1;
+ GnomeCanvasRect *resize2;
+
+ /*
+ * Cached bounding box. This is used to make
+ * the rubberband selection a bit faster.
+ */
+ SheetPos bbox_start;
+ SheetPos bbox_end;
+};
+
+GType
+wire_item_get_type ()
+{
+ static GType wire_item_type = 0;
+
+ if (!wire_item_type) {
+ static const GTypeInfo wire_item_info = {
+ sizeof (WireItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)wire_item_class_init,
+ NULL,
+ NULL,
+ sizeof (WireItem),
+ 0,
+ (GInstanceInitFunc)wire_item_init,
+ NULL
+ };
+
+ wire_item_type = g_type_register_static(TYPE_SHEET_ITEM,
+ "WireItem", &wire_item_info, 0);
+ }
+ return wire_item_type;
+}
+
+static void
+wire_item_class_init (WireItemClass *wire_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ SheetItemClass *sheet_item_class;
+
+ object_class = G_OBJECT_CLASS(wire_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(wire_item_class);
+ sheet_item_class = SHEET_ITEM_CLASS(wire_item_class);
+ wire_item_parent_class = g_type_class_peek(TYPE_SHEET_ITEM);
+
+ gtk_object_class->destroy = wire_item_destroy;
+
+ sheet_item_class->moved = wire_item_moved;
+ sheet_item_class->paste = wire_item_paste;
+ sheet_item_class->is_in_area = is_in_area;
+ sheet_item_class->selection_changed = (gpointer) selection_changed;
+ sheet_item_class->place = wire_item_place;
+ sheet_item_class->place_ghost = wire_item_place_ghost;
+}
+
+static void
+wire_item_init (WireItem *item)
+{
+ WireItemPriv *priv;
+
+ priv = g_new0 (WireItemPriv, 1);
+
+ priv->direction = WIRE_DIR_NONE;
+ priv->highlight = FALSE;
+ priv->cache_valid = FALSE;
+
+ item->priv = priv;
+}
+
+static void
+wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ WireItem *wire_item = WIRE_ITEM (object);
+
+ wire_item = WIRE_ITEM (object);
+
+ switch (arg_id) {
+ case WIRE_ITEM_ARG_NAME:
+ break;
+ }
+}
+
+static void
+wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ WireItem *wire_item = WIRE_ITEM (object);
+
+ wire_item = WIRE_ITEM (object);
+
+ switch (arg_id) {
+ case WIRE_ITEM_ARG_NAME:
+ break;
+ default:
+ //arg->type = G_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+wire_item_destroy (GtkObject *object)
+{
+ WireItem *wire;
+ WireItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (object));
+
+ wire = WIRE_ITEM (object);
+ priv = wire->priv;
+
+ if (priv) {
+ if (priv->line) {
+ /* TODO Check if destroy or unref have to be used for
+ * GnomeCanvasItem */
+ gtk_object_destroy (GTK_OBJECT (priv->line));
+ priv->line = NULL;
+ }
+ g_free (priv);
+ wire->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS(wire_item_parent_class)->destroy){
+ GTK_OBJECT_CLASS(wire_item_parent_class)->destroy(object);
+ }
+}
+
+/**
+ * "moved" signal handler. Invalidates the bounding box cache.
+ */
+static void
+wire_item_moved (SheetItem *object)
+{
+ WireItem *item;
+ WireItemPriv *priv;
+
+ item = WIRE_ITEM (object);
+ priv = item->priv;
+
+ priv->cache_valid = FALSE;
+}
+
+WireItem *
+wire_item_new (Sheet *sheet, Wire *wire)
+{
+ WireItem *item;
+ GnomeCanvasPoints *points;
+ WireItemPriv *priv;
+ SheetPos start_pos, length;
+
+ g_return_val_if_fail (sheet != NULL, NULL);
+ g_return_val_if_fail (IS_SHEET (sheet), NULL);
+
+ //g_object_ref (G_OBJECT(wire));
+ /* XXX Ver si hay equivalente gtk_object_sink (GTK_OBJECT (wire)); */
+
+ wire_get_pos_and_length (wire, &start_pos, &length);
+
+ /*
+ * Because of the GnomeCanvasGroup inheritance, a small hack is needed
+ * here. The group starts at the startpoint of the wire, and the line
+ * goes from (0,0) to (length.x, length.y).
+ */
+ item = WIRE_ITEM (gnome_canvas_item_new (
+ sheet->object_group,
+ wire_item_get_type (),
+ "data", wire,
+ "x", (double) start_pos.x,
+ "y", (double) start_pos.y,
+ NULL));
+
+ priv = item->priv;
+
+ priv->resize1 = GNOME_CANVAS_RECT (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_rect_get_type (),
+ "x1", -RESIZER_SIZE,
+ "y1", -RESIZER_SIZE,
+ "x2", RESIZER_SIZE,
+ "y2", RESIZER_SIZE,
+ "fill_color", "red",
+ "fill_color_rgba", 0x3cb37180,
+ "outline_color", "blue",
+ "width_pixels", 1,
+ NULL));
+
+ priv->resize2 = GNOME_CANVAS_RECT (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_rect_get_type (),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ "fill_color", "red",
+ "fill_color_rgba", 0x3cb37180,
+ "outline_color", "blue",
+ "width_pixels", 1,
+ NULL));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize1));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize2));
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ priv->line = GNOME_CANVAS_LINE (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_line_get_type (),
+ "points", points,
+ "fill_color", "blue",
+ "width_pixels", 1,
+ NULL));
+
+ gnome_canvas_points_free (points);
+
+ g_signal_connect_object(G_OBJECT(wire), "rotated",
+ G_CALLBACK(wire_rotated_callback), G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT(wire), "flipped",
+ G_CALLBACK(wire_flipped_callback), G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT(wire), "moved",
+ G_CALLBACK(wire_moved_callback), G_OBJECT(item), 0);
+
+ g_signal_connect (G_OBJECT (wire), "changed", G_CALLBACK (wire_changed_callback), item);
+ g_signal_connect (G_OBJECT (wire), "delete", G_CALLBACK (wire_delete_callback), item);
+ wire_update_bbox (wire);
+
+ return item;
+}
+
+static
+int wire_item_event (WireItem *wire_item, const GdkEvent *event, SchematicView *sv)
+{
+ SheetPos start_pos, length;
+ Wire *wire;
+ Sheet *sheet;
+ GnomeCanvas *canvas;
+ static double last_x, last_y;
+ double dx, dy, zoom;
+ /* The selected group's bounding box in window resp. canvas coordinates. */
+ double x1, y1, x2, y2;
+ static double bb_x1, bb_y1, bb_x2, bb_y2;
+ int cx1, cy1, cx2, cy2;
+ double snapped_x, snapped_y;
+ int sheet_width, sheet_height;
+ SheetPos pos;
+
+ sheet = schematic_view_get_sheet (sv);
+ canvas = GNOME_CANVAS (sheet);
+ g_object_get (G_OBJECT (wire_item), "data", &wire, NULL);
+
+ wire_get_pos_and_length (WIRE (wire), &start_pos, &length);
+ sheet_get_zoom (sheet, &zoom);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ switch (event->button.button) {
+ case 1: {
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ double x, y;
+ x = event->button.x - start_pos.x;
+ y = event->button.y - start_pos.y;
+ if ((x > -RESIZER_SIZE) && (x < RESIZER_SIZE) &&
+ (y > -RESIZER_SIZE) && (y < RESIZER_SIZE)) {
+ gtk_widget_grab_focus (GTK_WIDGET (sheet));
+ sheet->state = SHEET_STATE_DRAG_START;
+ wire_item->priv->resize_state = WIRE_RESIZER_1;
+
+ last_x = event->button.x;
+ last_y = event->button.y;
+ item_data_unregister (ITEM_DATA (wire));
+ return TRUE;
+ }
+ if ((x > (length.x-RESIZER_SIZE)) && (x < (length.x+RESIZER_SIZE)) &&
+ (y > (length.y-RESIZER_SIZE)) && (y < (length.y+RESIZER_SIZE))) {
+ gtk_widget_grab_focus (GTK_WIDGET (sheet));
+ sheet->state = SHEET_STATE_DRAG_START;
+ wire_item->priv->resize_state = WIRE_RESIZER_2;
+
+ last_x = event->button.x;
+ last_y = event->button.y;
+ item_data_unregister (ITEM_DATA (wire));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ break;
+
+ if (wire_item->priv->resize_state == WIRE_RESIZER_NONE)
+ break;
+
+ if (sheet->state == SHEET_STATE_DRAG_START || sheet->state == SHEET_STATE_DRAG) {
+ sheet->state = SHEET_STATE_DRAG;
+
+ snapped_x = event->motion.x;
+ snapped_y = event->motion.y;
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - last_x;
+ dy = snapped_y - last_y;
+
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ if (wire_item->priv->resize_state == WIRE_RESIZER_1) {
+ switch (wire->priv->direction) {
+ case WIRE_DIR_VERT:
+ /* Vertical Wire */
+ pos.y = last_y;
+ length.y -= dy;
+ break;
+ case WIRE_DIR_HORIZ:
+ /* Horizontal Wire */
+ pos.x = last_x;
+ length.x -= dx;
+ break;
+ default:
+ pos.y = last_y;
+ length.y -= dy;
+ pos.x = last_x;
+ length.x -= dx;
+ }
+ } else {
+ switch (wire->priv->direction) {
+ case WIRE_DIR_VERT:
+ /* Vertical Wire */
+ length.y += dy;
+ break;
+ case WIRE_DIR_HORIZ:
+ /* Horizontal Wire */
+ length.x += dx;
+ break;
+ default:
+ length.y += dy;
+ length.x += dx;
+ }
+ }
+ snap_to_grid (sheet->grid, &length.x, &length.y);
+ item_data_set_pos (sheet_item_get_data (SHEET_ITEM (wire_item)), &pos);
+ wire_set_length (wire, &length);
+ return TRUE;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ switch (event->button.button) {
+ case 1:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ break;
+ if (wire_item->priv->resize_state == WIRE_RESIZER_NONE)
+ break;
+
+ g_signal_stop_emission_by_name (G_OBJECT (wire_item), "event");
+
+ //gtk_timeout_remove (priv->scroll_timeout_id); // Esto no esta bien.
+
+ sheet->state = SHEET_STATE_NONE;
+ gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (wire_item), event->button.time);
+
+ wire_item->priv->resize_state = WIRE_RESIZER_NONE;
+ sheet->state = SHEET_STATE_NONE;
+ item_data_register (ITEM_DATA (wire));
+ return TRUE;
+ }
+ break;
+ default:
+ return sheet_item_event (SHEET_ITEM (wire_item), event, sv);
+ }
+ return sheet_item_event (SHEET_ITEM (wire_item), event, sv);
+}
+
+void
+wire_item_signal_connect_placed (WireItem *wire, SchematicView *sv)
+{
+ g_signal_connect (
+ G_OBJECT (wire),
+ "event",
+ G_CALLBACK(wire_item_event),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT (wire),
+ "mouse_over",
+ G_CALLBACK(mouse_over_wire_cb),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT (sheet_item_get_data (SHEET_ITEM (wire))),
+ "highlight",
+ G_CALLBACK(highlight_wire_cb),
+ wire);
+}
+
+static void
+wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
+{
+ WireItem *wire_item;
+ GnomeCanvasPoints *points;
+ SheetPos start_pos, length;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (sheet_item));
+
+ wire_item = WIRE_ITEM (sheet_item);
+
+ wire_get_pos_and_length (WIRE (data), &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item),
+ "x", start_pos.x,
+ "y", start_pos.y,
+ NULL);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (wire_item-> priv->resize2),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ NULL
+ );
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ wire_item->priv->cache_valid = FALSE;
+}
+
+static void
+wire_flipped_callback (ItemData *data,
+ gboolean horizontal, SheetItem *sheet_item)
+{
+ GnomeCanvasPoints *points;
+ WireItem *item;
+ WireItemPriv *priv;
+ SheetPos start_pos, length;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (sheet_item));
+
+ item = WIRE_ITEM (sheet_item);
+ priv = item->priv;
+
+ wire_get_pos_and_length (WIRE (data), &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item),
+ "x", start_pos.x,
+ "y", start_pos.y,
+ NULL);
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ priv->cache_valid = FALSE;
+}
+
+static int
+select_idle_callback (WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", SELECTED_COLOR, NULL);
+
+ priv->highlight = TRUE;
+
+ g_object_unref (G_OBJECT (item));
+ return FALSE;
+}
+
+static int
+deselect_idle_callback (WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", NORMAL_COLOR, NULL);
+
+ priv->highlight = FALSE;
+
+ g_object_unref(G_OBJECT (item));
+ return FALSE;
+}
+
+static void
+selection_changed(WireItem *item, gboolean select, gpointer user_data)
+{
+ g_object_ref(G_OBJECT(item));
+ if (select) {
+ gtk_idle_add ((gpointer) select_idle_callback, item);
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize1));
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize2));
+ } else {
+ gtk_idle_add ((gpointer) deselect_idle_callback, item);
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize1));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize2));
+ }
+}
+
+/**
+ * This function returns the position of the canvas item. It has
+ * nothing to do with where the wire is stored in the sheet node store.
+ */
+void
+wire_item_get_start_pos (WireItem *item, SheetPos *pos)
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+ g_return_if_fail (pos != NULL);
+
+ g_object_get(G_OBJECT(item), "x", &pos->x, "y", &pos->y, NULL);
+}
+
+/**
+ * This function returns the length of the canvas item.
+ */
+void
+wire_item_get_length (WireItem *item, SheetPos *pos)
+{
+ WireItemPriv *priv;
+ GnomeCanvasPoints *points;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+ g_return_if_fail (pos != NULL);
+
+ priv = item->priv;
+
+ g_object_get(G_OBJECT(priv->line), "points", &points, NULL);
+
+ /*
+ * This is not strictly neccessary, since the first point is always
+ * (0,0) but it's more correct and good to have if this changes in the
+ * future.
+ */
+ pos->x = points->coords[2] - points->coords[0];
+ pos->y = points->coords[3] - points->coords[1];
+ gnome_canvas_points_free (points);
+}
+
+static gboolean
+is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2)
+{
+ WireItem *item;
+ SheetPos bbox_start, bbox_end;
+
+ item = WIRE_ITEM (object);
+
+ get_bbox (item, &bbox_start, &bbox_end);
+
+ if (p1->x < bbox_start.x &&
+ p2->x > bbox_end.x &&
+ p1->y < bbox_start.y &&
+ p2->y > bbox_end.y)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Retrieves the bounding box. We use a caching scheme for this
+ * since it's too expensive to calculate it every time we need it.
+ */
+inline static void
+get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2)
+{
+ WireItemPriv *priv;
+ priv = item->priv;
+
+ if (!priv->cache_valid) {
+ SheetPos start_pos, end_pos;
+
+ wire_item_get_start_pos (item, &start_pos);
+ wire_item_get_length (item, &end_pos);
+ end_pos.x += start_pos.x;
+ end_pos.y += start_pos.y;
+
+ priv->bbox_start.x = MIN (start_pos.x, end_pos.x);
+ priv->bbox_start.y = MIN (start_pos.y, end_pos.y);
+ priv->bbox_end.x = MAX (start_pos.x, end_pos.x);
+ priv->bbox_end.y = MAX (start_pos.y, end_pos.y);
+ priv->cache_valid = TRUE;
+ }
+
+ memcpy (p1, &priv->bbox_start, sizeof (SheetPos));
+ memcpy (p2, &priv->bbox_end, sizeof (SheetPos));
+}
+
+static void
+wire_item_paste (SchematicView *sv, ItemData *data)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_WIRE (data));
+
+ schematic_view_add_ghost_item (sv, data);
+}
+
+static void wire_traverse (Wire *wire);
+
+static void
+node_traverse (Node *node)
+{
+ GSList *wires;
+
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (IS_NODE (node));
+
+ if (node_is_visited (node))
+ return;
+
+ node_set_visited (node, TRUE);
+
+ for (wires = node->wires; wires; wires = wires->next) {
+ Wire *wire = wires->data;
+ wire_traverse (wire);
+ }
+}
+
+static void
+wire_traverse (Wire *wire)
+{
+ GSList *nodes;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ if (wire_is_visited (wire))
+ return;
+
+ wire_set_visited (wire, TRUE);
+
+ g_signal_emit_by_name(G_OBJECT (wire), "highlight");
+
+ for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) {
+ Node *node = nodes->data;
+
+ node_traverse (node);
+ }
+}
+
+static void
+node_foreach_reset (gpointer key, gpointer value, gpointer user_data)
+{
+ Node *node = value;
+
+ node_set_visited (node, FALSE);
+}
+
+static void
+mouse_over_wire_cb (WireItem *item, SchematicView *sv)
+{
+ GList *wires;
+ Wire *wire;
+ NodeStore *store;
+ Sheet *sheet;
+
+ sheet = schematic_view_get_sheet (sv);
+
+ if (sheet->state != SHEET_STATE_NONE)
+ return;
+
+ store = schematic_get_store (schematic_view_get_schematic (sv));
+
+ node_store_node_foreach (store, (GHFunc *) node_foreach_reset, NULL);
+ for (wires = store->wires; wires; wires = wires->next) {
+ wire = wires->data;
+ wire_set_visited (wire, FALSE);
+ }
+
+ wire = WIRE (sheet_item_get_data (SHEET_ITEM (item)));
+ wire_traverse (wire);
+}
+
+static void
+highlight_wire_cb (Wire *wire, WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", HIGHLIGHT_COLOR, NULL);
+
+ /*
+ * Guard against removal during the highlighting.
+ */
+ g_object_ref(G_OBJECT (item));
+
+ gtk_timeout_add (1000, (gpointer) unhighlight_wire, item);
+}
+
+static int
+unhighlight_wire (WireItem *item)
+{
+ char *color;
+ WireItemPriv *priv = item->priv;
+
+ color = sheet_item_get_selected (SHEET_ITEM (item)) ?
+ SELECTED_COLOR : NORMAL_COLOR;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", color, NULL);
+
+ g_object_unref (G_OBJECT (item));
+
+ return FALSE;
+}
+
+/**
+ * This is called when the wire data was moved. Update the view accordingly.
+ */
+static void
+wire_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item)
+{
+ WireItem *wire_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+
+ if (pos == NULL)
+ return;
+
+ wire_item = WIRE_ITEM (item);
+
+ /*
+ * Move the canvas item and invalidate the bbox cache.
+ */
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y);
+ wire_item->priv->cache_valid = FALSE;
+}
+
+static void
+wire_item_place (SheetItem *item, SchematicView *sv)
+{
+ wire_item_signal_connect_placed (WIRE_ITEM (item), sv);
+}
+
+static void
+wire_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+// wire_item_signal_connect_placed (WIRE_ITEM (item));
+}
+
+
+static void
+wire_changed_callback (Wire *wire, WireItem *item)
+{
+ SheetPos start_pos, length;
+ GnomeCanvasPoints *points;
+
+ wire_get_pos_and_length (wire, &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize1),
+ "x1", -RESIZER_SIZE,
+ "y1", -RESIZER_SIZE,
+ "x2", RESIZER_SIZE,
+ "y2", RESIZER_SIZE,
+ NULL);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize2),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ NULL);
+}
+
+static void
+wire_delete_callback (Wire *wire, WireItem *item)
+{
+ gtk_object_destroy (GTK_OBJECT (item));
+}
+
+
diff --git a/src/sheet/wire-item.h b/src/sheet/wire-item.h
new file mode 100644
index 0000000..11124cc
--- /dev/null
+++ b/src/sheet/wire-item.h
@@ -0,0 +1,60 @@
+/*
+ * wire-item.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_ITEM_H
+#define __WIRE_ITEM_H
+
+#include <gnome.h>
+
+#include "schematic-view.h"
+#include "sheet-item.h"
+#include "wire.h"
+
+#define WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, wire_item_get_type (), WireItem))
+#define WIRE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, wire_item_get_type (), WireItemClass))
+#define IS_WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, wire_item_get_type ()))
+
+typedef struct _WireItemPriv WireItemPriv;
+
+typedef struct {
+ SheetItem parent_object;
+ WireItemPriv *priv;
+} WireItem;
+
+typedef struct {
+ SheetItemClass parent_class;
+} WireItemClass;
+
+GType wire_item_get_type (void);
+WireItem *wire_item_new (Sheet *sheet, Wire *wire);
+void wire_item_initiate (Sheet *sheet);
+void wire_item_get_start_pos (WireItem *item, SheetPos *pos);
+void wire_item_get_length (WireItem *item, SheetPos *pos);
+
+#endif
diff --git a/src/sim-settings.c b/src/sim-settings.c
new file mode 100644
index 0000000..d8f1e61
--- /dev/null
+++ b/src/sim-settings.c
@@ -0,0 +1,1064 @@
+/*
+ * sim-settings.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 <unistd.h>
+#include <ctype.h>
+#include <gnome.h>
+#include <glade/glade.h>
+#include <gtk/gtkclist.h>
+#include <gtk/gtkentry.h>
+#include "main.h"
+#include "sim-settings.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "dialogs.h"
+#include "oregano-utils.h"
+
+struct _SimSettingsPriv {
+ /* Transient analysis. */
+ GtkWidget *w_main;
+ GtkWidget *w_trans_enable, *w_trans_start, *w_trans_stop;
+ GtkWidget *w_trans_step, *w_trans_step_enable, *w_trans_init_cond, *w_trans_frame;
+ gboolean trans_enable;
+ gboolean trans_init_cond;
+ gchar *trans_start;
+ gchar *trans_stop;
+ gchar *trans_step;
+ gboolean trans_step_enable;
+
+ /* AC */
+ GtkWidget *w_ac_enable, *w_ac_type, *w_ac_npoints, *w_ac_start, *w_ac_stop, *w_ac_frame;
+ gboolean ac_enable;
+ gchar *ac_type;
+ gchar *ac_npoints;
+ gchar *ac_start;
+ gchar *ac_stop;
+
+ /* DC */
+ GtkWidget *w_dc_enable,*w_dc_vin1,*w_dc_start1,*w_dc_stop1,*w_dc_step1,
+ *w_dc_vin2,*w_dc_start2,*w_dc_stop2,*w_dc_step2,*w_dcsweep_frame;
+ gboolean dc_enable;
+ gchar *dc_vin1;
+ gchar *dc_start1,*dc_stop1,*dc_step1;
+ gchar *dc_vin2;
+ gchar *dc_start2,*dc_stop2,*dc_step2;
+
+ /* Fourier analysis. Replace with something sane later. */
+ GtkWidget *w_four_enable,*w_four_freq,*w_four_vout,*w_four_combo,*w_four_add,*w_fourier_frame;
+ gboolean four_enable;
+ gchar *four_freq;
+ gchar *four_vout;
+
+ /* Options */
+ GtkEntry *w_opt_value;
+ GtkTreeView *w_opt_list;
+ GList *options;
+};
+
+
+static SimOption default_options[] = {
+ {"TEMP" ,NULL},
+
+ {"GMIN" ,NULL},
+ {"ABSTOL" ,NULL},
+ {"CHGTOL" ,NULL},
+ {"RELTOL" ,NULL},
+ {"VNTOL" ,NULL},
+
+ {"ITL1" ,NULL},
+ {"ITL2" ,NULL},
+ {"ITL4", NULL},
+
+ {"PIVREL", NULL},
+ {"PIVTOL", NULL},
+
+ {"TNOM", NULL},
+ {"TRTOL", NULL},
+
+ {"DEFAD", NULL},
+ {"DEFAS", NULL},
+ {"DEFL", NULL},
+ {"DEFW", NULL},
+ {NULL, NULL}
+};
+
+
+static void
+set_options_in_list(gchar *key, gchar *val, GtkTreeView *cl)
+{
+ int i;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ char *name;
+ char *value;
+
+ model = gtk_tree_view_get_model(cl);
+
+ i=0;
+ while (gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
+ gtk_tree_model_get(model, &iter, 0, &name, 1, &value, -1);
+ if (!strcmp (name, key) ) {
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, val, -1);
+ }
+ i++;
+ }
+}
+
+static void
+get_options_from_list (SimSettings *s)
+{
+ int i;
+ gchar *name,*value;
+ SimOption *so;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeView *cl = s->priv->w_opt_list;
+
+ /* Empty the list */
+ while (s->priv->options) {
+ so = s->priv->options->data;
+ if (so) {
+ /* Prevent sigfault on NULL */
+ if (so->name) g_free (so->name);
+ if (so->value) g_free (so->value);
+ s->priv->options = g_list_remove (s->priv->options,so);
+ g_free (so);
+ }
+ if (s->priv->options) s->priv->options = s->priv->options->next;
+ }
+
+ model = gtk_tree_view_get_model(cl);
+ i = 0;
+ while (gtk_tree_model_iter_nth_child(model, &iter, NULL, i)) {
+ SimOption *so;
+ gtk_tree_model_get(model, &iter, 0, &name, 1, &value, -1);
+
+ so = g_new0 (SimOption,1);
+ so->name = g_strdup(name);
+ so->value = g_strdup(value);
+ s->priv->options = g_list_append (s->priv->options, so);
+ /* TODO : name y value deben ser liberados ? */
+ i++;
+ }
+}
+
+static gboolean
+select_opt_callback (GtkWidget *widget,
+ GdkEventButton *event, SimSettings *sim)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ char *value;
+
+ if (event->button != 1) return FALSE;
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection(sim->priv->w_opt_list);
+ model = gtk_tree_view_get_model(sim->priv->w_opt_list);
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return FALSE;
+ }
+
+ gtk_tree_model_get(model, &iter, 1, &value, -1);
+
+ if (value)
+ gtk_entry_set_text (sim->priv->w_opt_value, value);
+ else
+ gtk_entry_set_text (sim->priv->w_opt_value, "");
+
+ return FALSE;
+}
+
+static void
+option_setvalue (GtkWidget *w, SimSettings *s)
+{
+ const gchar *value;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ value = gtk_entry_get_text (s->priv->w_opt_value);
+ if (!value) return;
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection(s->priv->w_opt_list);
+ model = gtk_tree_view_get_model(s->priv->w_opt_list);
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, value, -1);
+
+// gnome_property_box_set_modified (GNOME_PROPERTY_BOX (s->pbox), TRUE);
+}
+
+static void
+add_option (GtkWidget *w, SimSettings *s)
+{
+ GtkEntry *entry;
+ GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Add new option"),
+ NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+
+ entry = GTK_ENTRY (gtk_entry_new ());
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), GTK_WIDGET (entry));
+ gtk_widget_show (GTK_WIDGET (entry));
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+ GtkTreeIter iter;
+ SimOption *opt = g_new0 (SimOption, 1);
+ opt->name = g_strdup (gtk_entry_get_text (entry));
+ opt->value = g_strdup ("");
+ /* Warning : don't free opt later, is added to the list */
+ sim_settings_add_option (s, opt);
+
+ gtk_list_store_append (GTK_LIST_STORE (gtk_tree_view_get_model(s->priv->w_opt_list)), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (gtk_tree_view_get_model(s->priv->w_opt_list)), &iter, 0, opt->name, -1);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+
+static void
+option_remove (GtkWidget *w, SimSettings *s)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ /* Get the current selected row */
+ selection = gtk_tree_view_get_selection(s->priv->w_opt_list);
+ model = gtk_tree_view_get_model(s->priv->w_opt_list);
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) {
+ return;
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, "", -1);
+}
+
+static void
+trans_enable_cb (GtkWidget *widget, SimSettings *s)
+{
+ gboolean enable, step_enable;
+
+ enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ step_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_trans_step_enable));
+
+ if (enable)
+ gtk_widget_show (s->priv->w_trans_frame);
+ else
+ gtk_widget_hide (s->priv->w_trans_frame);
+ /*
+ gtk_widget_set_sensitive (s->priv->w_trans_start, enable);
+ gtk_widget_set_sensitive (s->priv->w_trans_stop, enable);
+ gtk_widget_set_sensitive (s->priv->w_trans_step, enable & step_enable);
+ gtk_widget_set_sensitive (s->priv->w_trans_step_enable, enable);
+ gtk_widget_set_sensitive (s->priv->w_trans_init_cond, enable);
+ gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox));
+ */
+}
+
+static void
+trans_step_enable_cb (GtkWidget *widget, SimSettings *s)
+{
+ gboolean enable, step_enable;
+
+ step_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_trans_enable));
+
+ gtk_widget_set_sensitive (s->priv->w_trans_step, step_enable & enable);
+
+// gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox));
+}
+
+static void
+entry_changed_cb (GtkWidget *widget, SimSettings *s)
+{
+// gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox));
+}
+
+static void
+ac_enable_cb (GtkWidget *widget, SimSettings *s)
+{
+ gboolean enable;
+ enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if (enable)
+ gtk_widget_show (s->priv->w_ac_frame);
+ else
+ gtk_widget_hide (s->priv->w_ac_frame);
+/* gtk_widget_set_sensitive (s->priv->w_ac_type,enable);
+ gtk_widget_set_sensitive (s->priv->w_ac_npoints,enable);
+ gtk_widget_set_sensitive (s->priv->w_ac_start,enable);
+ gtk_widget_set_sensitive (s->priv->w_ac_stop,enable);
+
+ gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox)); */
+}
+
+static void
+dc_enable_cb (GtkWidget *widget, SimSettings *s)
+{
+ gboolean enable;
+ enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if (enable)
+ gtk_widget_show (s->priv->w_dcsweep_frame);
+ else
+ gtk_widget_hide (s->priv->w_dcsweep_frame);
+ /*
+ gtk_widget_set_sensitive (s->priv->w_dc_vin1,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_start1,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_stop1,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_step1,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_vin2,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_start2,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_stop2,enable);
+ gtk_widget_set_sensitive (s->priv->w_dc_step2,enable);
+ gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox)); */
+}
+
+static void
+four_enable_cb (GtkWidget *widget, SimSettings *s)
+{
+ gboolean enable;
+ enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if (enable)
+ gtk_widget_show (s->priv->w_fourier_frame);
+ else
+ gtk_widget_hide (s->priv->w_fourier_frame);
+ /*
+ gtk_widget_set_sensitive (s->priv->w_four_freq,enable);
+ gtk_widget_set_sensitive (s->priv->w_four_vout,enable);
+ gtk_widget_set_sensitive (s->priv->w_four_combo,enable);
+ gtk_widget_set_sensitive (s->priv->w_four_add,enable);
+
+ gnome_property_box_changed (GNOME_PROPERTY_BOX (s->pbox));*/
+}
+
+static void
+four_add_vout_cb (GtkWidget *widget, SimSettings *s)
+{
+
+}
+
+static int
+delete_event_cb (GtkWidget *widget, GdkEvent *event, SimSettings *s)
+{
+ s->pbox = NULL;
+ return FALSE;
+}
+
+SimSettings *
+sim_settings_new (Schematic *sm)
+{
+ SimSettings *s;
+
+ s = g_new0 (SimSettings, 1);
+ s->sm = sm;
+
+ s->priv = g_new0 (SimSettingsPriv, 1);
+
+ /*
+ * Set some default settings.
+ */
+ /* transient */
+ s->priv->trans_enable = TRUE;
+ s->priv->trans_start = g_strdup ("0 s");
+ s->priv->trans_stop = g_strdup ("5 ms");
+ s->priv->trans_step = g_strdup ("0.1 ms");
+ s->priv->trans_step_enable = FALSE;
+
+ /* ac */
+ s->priv->ac_enable = FALSE;
+ s->priv->ac_type = g_strdup ("DEC");
+ s->priv->ac_npoints= g_strdup ("50");
+ s->priv->ac_start = g_strdup ("1");
+ s->priv->ac_stop = g_strdup ("1 Meg");
+
+ /* dc */
+ s->priv->dc_enable = FALSE;
+ s->priv->dc_vin1 = g_strdup ("");
+ s->priv->dc_start1 = g_strdup ("");
+ s->priv->dc_stop1 = g_strdup ("");
+ s->priv->dc_step1 = g_strdup ("");
+ s->priv->dc_vin2 = g_strdup ("");
+ s->priv->dc_start2 = g_strdup ("");
+ s->priv->dc_stop2 = g_strdup ("");
+ s->priv->dc_step2 = g_strdup ("");
+
+ /* fourier */
+ s->priv->four_enable = FALSE;
+
+ s->priv->options=0;
+
+ return s;
+}
+
+gboolean
+sim_settings_get_trans (SimSettings *sim_settings)
+{
+ return sim_settings->priv->trans_enable;
+}
+
+gboolean
+sim_settings_get_trans_init_cond (SimSettings *sim_settings)
+{
+ return sim_settings->priv->trans_init_cond;
+}
+
+gdouble
+sim_settings_get_trans_start (SimSettings *sim_settings)
+{
+ gchar *text = sim_settings->priv->trans_start;
+ return oregano_strtod (text, 's');
+}
+
+gdouble
+sim_settings_get_trans_stop (SimSettings *sim_settings)
+{
+ gchar *text = sim_settings->priv->trans_stop;
+ return oregano_strtod (text, 's');
+}
+
+gdouble
+sim_settings_get_trans_step (SimSettings *sim_settings)
+{
+ gchar *text = sim_settings->priv->trans_step;
+ return oregano_strtod (text, 's');
+}
+
+gdouble
+sim_settings_get_trans_step_enable (SimSettings *sim_settings)
+{
+ return sim_settings->priv->trans_step_enable;
+}
+
+void
+sim_settings_set_trans (SimSettings *sim_settings, gboolean enable)
+{
+ sim_settings->priv->trans_enable = enable;
+}
+
+void
+sim_settings_set_trans_start (SimSettings *sim_settings, gchar *str)
+{
+ if (sim_settings->priv->trans_start)
+ g_strdup (sim_settings->priv->trans_start);
+ sim_settings->priv->trans_start = g_strdup (str);
+}
+
+void
+sim_settings_set_trans_init_cond(SimSettings *sim_settings, gboolean enable)
+{
+ sim_settings->priv->trans_init_cond = enable;
+}
+
+void
+sim_settings_set_trans_stop (SimSettings *sim_settings, gchar *str)
+{
+ if (sim_settings->priv->trans_stop)
+ g_strdup (sim_settings->priv->trans_stop);
+ sim_settings->priv->trans_stop = g_strdup (str);
+}
+
+void
+sim_settings_set_trans_step (SimSettings *sim_settings, gchar *str)
+{
+ if (sim_settings->priv->trans_step)
+ g_strdup (sim_settings->priv->trans_step);
+ sim_settings->priv->trans_step = g_strdup (str);
+}
+
+void
+sim_settings_set_trans_step_enable (SimSettings *sim_settings, gboolean enable)
+{
+ sim_settings->priv->trans_step_enable = enable;
+}
+
+gboolean
+sim_settings_get_ac (SimSettings *sim_settings)
+{
+ return sim_settings->priv->ac_enable;
+}
+
+gchar *
+sim_settings_get_ac_type (SimSettings *sim_settings)
+{
+ return sim_settings->priv->ac_type;
+}
+
+gint
+sim_settings_get_ac_npoints (SimSettings *sim_settings)
+{
+ return atoi (sim_settings->priv->ac_npoints);
+}
+
+gdouble
+sim_settings_get_ac_start (SimSettings *sim_settings)
+{
+ return oregano_strtod (sim_settings->priv->ac_start, 's');
+}
+
+gdouble
+sim_settings_get_ac_stop (SimSettings *sim_settings)
+{
+ return oregano_strtod (sim_settings->priv->ac_stop,'s');
+}
+
+void
+sim_settings_set_ac (SimSettings *sim_settings,gboolean enable)
+{
+ sim_settings->priv->ac_enable = enable;
+}
+
+void
+sim_settings_set_ac_type(SimSettings *sim_settings,gchar *str)
+{
+ if ( sim_settings->priv->ac_type )
+ g_free ( sim_settings->priv->ac_type );
+ sim_settings->priv->ac_type = g_strdup (str);
+}
+
+void
+sim_settings_set_ac_npoints(SimSettings *sim_settings,gchar *str)
+{
+ if ( sim_settings->priv->ac_npoints )
+ g_free ( sim_settings->priv->ac_npoints );
+ sim_settings->priv->ac_npoints = g_strdup (str);
+
+}
+
+void
+sim_settings_set_ac_start (SimSettings *sim_settings,gchar *str)
+{
+ if (sim_settings->priv->ac_start)
+ g_free (sim_settings->priv->ac_start);
+ sim_settings->priv->ac_start = g_strdup (str);
+}
+
+void
+sim_settings_set_ac_stop(SimSettings *sim_settings,gchar *str)
+{
+ if (sim_settings->priv->ac_stop)
+ g_free (sim_settings->priv->ac_stop);
+ sim_settings->priv->ac_stop = g_strdup (str);
+}
+
+gboolean
+sim_settings_get_dc (SimSettings *sim_settings)
+{
+ return sim_settings->priv->dc_enable;
+}
+
+gchar*
+sim_settings_get_dc_vsrc (SimSettings *sim_settings,gint i)
+{
+ gchar *tmp = (i==0 ? sim_settings->priv->dc_vin1 : sim_settings->priv->dc_vin2);
+ return (tmp && *tmp ? tmp : NULL);
+}
+
+gdouble
+sim_settings_get_dc_start (SimSettings *sim_settings, gint i)
+{
+ return oregano_strtod ( i==0
+ ? sim_settings->priv->dc_start1
+ : sim_settings->priv->dc_start2, 's');
+}
+
+gdouble
+sim_settings_get_dc_stop (SimSettings *sim_settings,gint i)
+{
+ return oregano_strtod ( i==0
+ ? sim_settings->priv->dc_stop1
+ : sim_settings->priv->dc_stop2, 's');
+}
+
+gdouble
+sim_settings_get_dc_step (SimSettings *sim_settings,gint i)
+{
+ return oregano_strtod ( i==0
+ ? sim_settings->priv->dc_step1
+ : sim_settings->priv->dc_step2, 's');
+}
+
+void
+sim_settings_set_dc (SimSettings *sim_settings, gboolean enable)
+{
+ sim_settings->priv->dc_enable = enable;
+}
+
+void
+sim_settings_set_dc_vsrc (SimSettings *sim_settings, gint i, gchar *str)
+{
+ gchar **val = (i==0
+ ? &sim_settings->priv->dc_vin1
+ : &sim_settings->priv->dc_vin2);
+ if ( *val ) g_free (*val);
+ *val = g_strdup (str);
+}
+
+void
+sim_settings_set_dc_start (SimSettings *sim_settings, gint i, gchar *str) {
+ gchar **val = (i==0
+ ? &sim_settings->priv->dc_start1
+ : &sim_settings->priv->dc_start2);
+ if ( *val ) g_free (*val);
+ *val = g_strdup (str);
+}
+
+void
+sim_settings_set_dc_stop (SimSettings *sim_settings, gint i, gchar *str)
+{
+ gchar **val = (i==0
+ ? &sim_settings->priv->dc_stop1
+ : &sim_settings->priv->dc_stop2);
+ if (*val) g_free (*val);
+ *val = g_strdup (str);
+}
+
+void
+sim_settings_set_dc_step (SimSettings *sim_settings, gint i, gchar *str)
+{
+ gchar **val = (i==0
+ ? &sim_settings->priv->dc_step1
+ : &sim_settings->priv->dc_step2);
+ if (*val) g_free (*val);
+ *val = g_strdup (str);
+}
+
+static void
+response_callback(GtkDialog *dlg, gint id, Schematic *sm)
+{
+ gint page;
+ SimSettings *s;
+
+ s = schematic_get_sim_settings (sm);
+
+ g_object_get(s->notebook, "page", &page, NULL);
+
+ /* Trans */
+ s->priv->trans_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_trans_enable));
+
+ if (s->priv->trans_start) g_free (s->priv->trans_start);
+ s->priv->trans_start = gtk_editable_get_chars (GTK_EDITABLE (s->priv->w_trans_start), 0, -1);
+
+ if (s->priv->trans_stop) g_free (s->priv->trans_stop);
+ s->priv->trans_stop = gtk_editable_get_chars (GTK_EDITABLE (s->priv->w_trans_stop), 0, -1);
+
+ if (s->priv->trans_step) g_free (s->priv->trans_step);
+ s->priv->trans_step = gtk_editable_get_chars (GTK_EDITABLE (s->priv->w_trans_step), 0, -1);
+
+ s->priv->trans_step_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_trans_step_enable));
+
+ s->priv->trans_init_cond = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_trans_init_cond));
+
+ /* DC */
+ s->priv->dc_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->priv->w_dc_enable));
+ if (s->priv->dc_vin1) g_free (s->priv->dc_vin1);
+ s->priv->dc_vin1 = g_strdup (gtk_entry_get_text(GTK_ENTRY(GTK_COMBO (s->priv->w_dc_vin1)->entry)));
+
+ if (s->priv->dc_start1) g_free (s->priv->dc_start1);
+ s->priv->dc_start1 = g_strdup (gtk_entry_get_text (GTK_ENTRY (s->priv->w_dc_start1)));
+
+ if (s->priv->dc_stop1) g_free(s->priv->dc_stop1);
+ s->priv->dc_stop1 = g_strdup(gtk_entry_get_text (GTK_ENTRY (s->priv->w_dc_stop1)));
+
+ if (s->priv->dc_step1) g_free(s->priv->dc_step1);
+ s->priv->dc_step1 = g_strdup(gtk_entry_get_text(GTK_ENTRY (s->priv->w_dc_step1)));
+
+ if (s->priv->dc_vin2) g_free(s->priv->dc_vin2);
+ s->priv->dc_vin2 = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO (s->priv->w_dc_vin2)->entry)));
+
+ if (s->priv->dc_start2) g_free(s->priv->dc_start2);
+ s->priv->dc_start2 = g_strdup(gtk_entry_get_text(GTK_ENTRY (s->priv->w_dc_start2)));
+
+ if (s->priv->dc_stop2) g_free(s->priv->dc_stop2);
+ s->priv->dc_stop2 = g_strdup(gtk_entry_get_text(GTK_ENTRY (s->priv->w_dc_stop2)));
+
+ if (s->priv->dc_step2) g_free(s->priv->dc_step2);
+ s->priv->dc_step2 = g_strdup(gtk_entry_get_text(GTK_ENTRY (s->priv->w_dc_step2)));
+
+ /* AC */
+ s->priv->ac_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (s->priv->w_ac_enable));
+
+ if (s->priv->ac_type) g_free(s->priv->ac_type);
+ s->priv->ac_type = g_strdup(gtk_entry_get_text(GTK_ENTRY( s->priv->w_ac_type)));
+
+ if (s->priv->ac_npoints) g_free (s->priv->ac_npoints);
+ s->priv->ac_npoints = g_strdup(gtk_entry_get_text(GTK_ENTRY( s->priv->w_ac_npoints)));
+
+ if (s->priv->ac_start) g_free (s->priv->ac_start);
+ s->priv->ac_start = g_strdup(gtk_entry_get_text(GTK_ENTRY( s->priv->w_ac_start)));
+
+ if (s->priv->ac_stop) g_free (s->priv->ac_stop);
+ s->priv->ac_stop = g_strdup(gtk_entry_get_text(GTK_ENTRY( s->priv->w_ac_stop)));
+
+ /* Options */
+ get_options_from_list (s);
+ gtk_widget_hide(GTK_WIDGET(dlg));
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ s->pbox = NULL;
+ s->notebook = NULL;
+
+ /* Schematic is dirty now ;-) */
+ schematic_set_dirty (sm, TRUE);
+}
+
+void
+sim_settings_show (GtkWidget *widget, SchematicView *sv)
+{
+ int i;
+ GtkWidget *toplevel, *w, *pbox;
+ GtkTreeView *opt_list;
+ GtkCellRenderer *cell_option, *cell_value;
+ GtkTreeViewColumn *column_option, *column_value;
+ GtkListStore *opt_model;
+ GtkTreeIter iter;
+ GladeXML *gui;
+ SimSettings *s;
+ Schematic *sm;
+ GList *list;
+ GList *items,*sources=NULL,*ltmp;
+
+ g_return_if_fail (sv != NULL);
+
+ sm = schematic_view_get_schematic (sv);
+ s = schematic_get_sim_settings (sm);
+
+ /* Only allow one instance of the property box per schematic. */
+ if (s->pbox){
+ gdk_window_raise (GTK_WIDGET (s->pbox)->window);
+ return;
+ }
+
+ if (!g_file_test (OREGANO_GLADEDIR "/sim-settings.glade", G_FILE_TEST_EXISTS)) {
+ gchar *msg;
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/sim-settings.glade");
+ oregano_error_with_title (_("Could not create simulation settings dialog"), msg);
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/sim-settings.glade", "toplevel", NULL);
+ if (!gui) {
+ oregano_error (_("Could not create simulation settings dialog"));
+ return;
+ }
+
+ toplevel = glade_xml_get_widget (gui, "toplevel");
+ if (!toplevel) {
+ oregano_error (_("Could not create simulation settings dialog"));
+ return;
+ }
+
+ pbox = toplevel;
+ s->pbox = GTK_WIDGET (pbox);
+ s->notebook = GTK_NOTEBOOK (glade_xml_get_widget (gui, "notebook"));
+ g_signal_connect (G_OBJECT (pbox),
+ "delete_event", G_CALLBACK(delete_event_cb), s);
+
+ /* Prepare options list */
+ s->priv->w_opt_value = GTK_ENTRY (glade_xml_get_widget (gui, "opt_value"));
+ opt_list = s->priv->w_opt_list = GTK_TREE_VIEW(glade_xml_get_widget (gui,
+ "option_list"));
+
+ /* Grab the frames */
+ s->priv->w_trans_frame = GTK_WIDGET (glade_xml_get_widget (gui, "trans_frame"));
+ s->priv->w_ac_frame = GTK_WIDGET (glade_xml_get_widget (gui, "ac_frame"));
+ s->priv->w_dcsweep_frame = GTK_WIDGET (glade_xml_get_widget (gui, "dcsweep_frame"));
+ s->priv->w_fourier_frame = GTK_WIDGET (glade_xml_get_widget (gui, "fourier_frame"));
+
+ /* Create the Columns */
+ cell_option = gtk_cell_renderer_text_new();
+ cell_value = gtk_cell_renderer_text_new();
+ column_option = gtk_tree_view_column_new_with_attributes(N_("Option"),
+ cell_option, "text", 0, NULL);
+ column_value = gtk_tree_view_column_new_with_attributes(N_("Value"),
+ cell_value, "text", 1, NULL);
+
+ /* Create the model */
+ opt_model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model(opt_list, GTK_TREE_MODEL(opt_model));
+ gtk_tree_view_append_column(opt_list, column_option);
+ gtk_tree_view_append_column(opt_list, column_value);
+
+ if (s->priv->options == NULL) {
+ /* Load defaults */
+ for (i = 0; default_options[i].name; i++) {
+ gtk_list_store_append(opt_model, &iter);
+ gtk_list_store_set(opt_model, &iter, 0, default_options[i].name, -1);
+ }
+ } else {
+ /* Load schematic options */
+ list = s->priv->options;
+ while (list) {
+ SimOption *so = list->data;
+ if (so) {
+ gtk_list_store_append(opt_model, &iter);
+ gtk_list_store_set(opt_model, &iter, 0, so->name, -1);
+ }
+ list = list->next;
+ }
+ }
+
+ /* Set the optinos already stored */
+ list = s->priv->options;
+ while (list) {
+ SimOption *so = list->data;
+ if (so)
+ set_options_in_list (so->name, so->value, opt_list);
+ list = list->next;
+ }
+
+ g_signal_connect(G_OBJECT (opt_list),
+ "button_release_event", G_CALLBACK(select_opt_callback), s);
+
+ w = glade_xml_get_widget (gui, "opt_accept");
+ g_signal_connect (G_OBJECT(w),"clicked", G_CALLBACK(option_setvalue), s);
+ w = glade_xml_get_widget (gui, "opt_remove");
+ g_signal_connect (G_OBJECT(w),"clicked", G_CALLBACK(option_remove), s);
+ w = glade_xml_get_widget (gui, "add_option");
+ g_signal_connect (G_OBJECT(w),"clicked", G_CALLBACK(add_option), s);
+
+ /* Setup callbacks. */
+ g_signal_connect (G_OBJECT (pbox),
+ "response", G_CALLBACK(response_callback), sm);
+
+ /* Transient */
+ w = glade_xml_get_widget (gui, "trans_start");
+ if (s->priv->trans_start) gtk_entry_set_text (GTK_ENTRY (w),
+ s->priv->trans_start);
+ s->priv->w_trans_start = w;
+ g_signal_connect(G_OBJECT (w),
+ "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "trans_stop");
+ if (s->priv->trans_stop)
+ gtk_entry_set_text (GTK_ENTRY (w), s->priv->trans_stop);
+ s->priv->w_trans_stop = w;
+ g_signal_connect(G_OBJECT (w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "trans_step");
+ if (s->priv->trans_step)
+ gtk_entry_set_text (GTK_ENTRY (w), s->priv->trans_step);
+ s->priv->w_trans_step = w;
+ g_signal_connect(G_OBJECT (w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "trans_enable");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->priv->trans_enable);
+ s->priv->w_trans_enable = w;
+
+ w = glade_xml_get_widget (gui, "trans_step_enable");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+ s->priv->trans_step_enable);
+ s->priv->w_trans_step_enable = w;
+
+ w = glade_xml_get_widget (gui, "trans_init_cond");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+ s->priv->trans_init_cond);
+ s->priv->w_trans_init_cond = w;
+
+ g_signal_connect(G_OBJECT(s->priv->w_trans_enable),
+ "clicked", G_CALLBACK(trans_enable_cb), s);
+
+ g_signal_connect(G_OBJECT(s->priv->w_trans_step_enable),
+ "clicked", G_CALLBACK(trans_step_enable_cb), s);
+
+ /* AC */
+ w = glade_xml_get_widget (gui, "ac_enable");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->priv->ac_enable);
+ s->priv->w_ac_enable=w;
+ g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK(ac_enable_cb), s);
+
+ w = glade_xml_get_widget (gui, "ac_type");
+ if ( s->priv->ac_type )
+ gtk_entry_set_text ( GTK_ENTRY (GTK_COMBO (w)->entry),
+ s->priv->ac_type);
+ s->priv->w_ac_type = GTK_COMBO (w)->entry;
+ g_signal_connect(G_OBJECT (GTK_COMBO (w)->entry),
+ "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "ac_npoints");
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->ac_npoints);
+ s->priv->w_ac_npoints = w;
+ g_signal_connect(G_OBJECT (w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "ac_start");
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->ac_start);
+ s->priv->w_ac_start = w;
+ g_signal_connect(G_OBJECT (w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "ac_stop");
+ gtk_entry_set_text(GTK_ENTRY (w), s->priv->ac_stop);
+ s->priv->w_ac_stop = w;
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ /* DC */
+ /* Get list of sources */
+
+ items = node_store_get_parts (schematic_get_store (sm));
+ for ( ; items; items = items->next ) {
+ gchar *temp = part_get_property (items->data,"template");
+ gchar *name = part_get_property (items->data,"refdes");
+ if (temp) {
+ gchar c = tolower(*temp);
+ if (c=='v' || c=='i' || c=='e' ||
+ c=='f' || c=='g' || c=='h' || c=='b' ) {
+ gchar *vsrc = g_strdup_printf ("%c_%s",*temp,name);
+ sources = g_list_prepend (sources,vsrc);
+ }
+ }
+ }
+
+ w = glade_xml_get_widget (gui, "dc_enable");
+ s->priv->w_dc_enable = w;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->priv->dc_enable);
+ g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(dc_enable_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_vin1");
+ s->priv->w_dc_vin1 = w;
+ if (sources)
+ gtk_combo_set_popdown_strings (GTK_COMBO (w), sources);
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (w)->entry),s->priv->dc_vin1);
+ g_signal_connect(G_OBJECT(GTK_COMBO(w)->entry),
+ "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_vin2");
+ s->priv->w_dc_vin2 = w;
+ if (sources)
+ gtk_combo_set_popdown_strings (GTK_COMBO (w),sources);
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (w)->entry),s->priv->dc_vin2);
+ g_signal_connect(G_OBJECT(GTK_COMBO(w)->entry),
+ "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_start1");
+ s->priv->w_dc_start1 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_start1);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_stop1");
+ s->priv->w_dc_stop1 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_stop1);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_step1");
+ s->priv->w_dc_step1 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_step1);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_start2");
+ s->priv->w_dc_start2 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_start2);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "dc_stop2");
+ s->priv->w_dc_stop2 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_stop2);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+ w = glade_xml_get_widget (gui, "dc_step2");
+ s->priv->w_dc_step2 = w;
+ gtk_entry_set_text ( GTK_ENTRY (w), s->priv->dc_step2);
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ for (ltmp = sources; ltmp; ltmp = ltmp->next)
+ g_free (ltmp->data);
+ g_list_free (sources);
+
+ /* Fourier */
+ w = glade_xml_get_widget (gui, "fourier_enable");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->priv->four_enable);
+ s->priv->w_four_enable = w;
+ g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(four_enable_cb), s);
+
+ w = glade_xml_get_widget (gui, "fourier_freq");
+ s->priv->w_four_freq = w;
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "fourier_vout");
+ s->priv->w_four_vout = w;
+ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "fourier_select_out");
+ s->priv->w_four_combo = w;
+ g_signal_connect (G_OBJECT (GTK_COMBO (w)->entry),
+ "changed", G_CALLBACK(entry_changed_cb), s);
+
+ w = glade_xml_get_widget (gui, "fourier_add");
+ s->priv->w_four_add = w;
+ g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(four_add_vout_cb), s);
+
+ gtk_widget_show_all (toplevel);
+
+ ac_enable_cb (s->priv->w_ac_enable,s);
+ four_enable_cb (s->priv->w_four_enable, s);
+ dc_enable_cb(s->priv->w_dc_enable,s);
+ trans_enable_cb (s->priv->w_trans_enable, s);
+ trans_step_enable_cb (s->priv->w_trans_step_enable, s);
+}
+
+GList *
+sim_settings_get_options (SimSettings *s)
+{
+ return s->priv->options;
+}
+
+void
+sim_settings_add_option (SimSettings *s, SimOption *opt)
+{
+ GList *list=s->priv->options;
+
+ /* Remove the option if already in the list. */
+ while (list) {
+ SimOption *so=list->data;
+ if (so && !strcmp (opt->name,so->name)) {
+ GList * tmp_list;
+ g_free (so->name);
+ g_free (so->value);
+ tmp_list = g_list_remove (s->priv->options, so);
+ g_free (so);
+ }
+ list = list->next;
+ }
+ s->priv->options = g_list_append (s->priv->options, opt);
+}
+
diff --git a/src/sim-settings.h b/src/sim-settings.h
new file mode 100644
index 0000000..be2a3a0
--- /dev/null
+++ b/src/sim-settings.h
@@ -0,0 +1,169 @@
+/*
+ * sim-settings.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 __SIM_SETTINGS_H
+#define __SIM_SETTINGS_H
+
+#include <gtk/gtk.h>
+
+typedef struct _SimSettings SimSettings;
+
+#include "schematic-view.h"
+#include "schematic.h"
+
+typedef struct _SimSettingsPriv SimSettingsPriv;
+
+struct _SimSettings {
+ Schematic *sm;
+ GtkWidget *pbox;
+ GtkNotebook *notebook;
+ SimSettingsPriv *priv;
+};
+
+typedef struct _SimOption {
+ gchar *name;
+ gchar *value;
+} SimOption;
+
+void sim_settings_show (GtkWidget *widget,
+ SchematicView *sv);
+
+SimSettings *sim_settings_new (Schematic *sm);
+
+/**
+ *
+ */
+gdouble sim_settings_get_trans_start (SimSettings *sim_settings);
+
+gdouble sim_settings_get_trans_stop (SimSettings *sim_settings);
+
+gdouble sim_settings_get_trans_step (SimSettings *sim_settings);
+
+gboolean sim_settings_get_trans (SimSettings *sim_settings);
+
+gdouble sim_settings_get_trans_step_enable (SimSettings *sim_settings);
+
+/**
+ *
+ */
+gboolean sim_settings_get_trans_init_cond (SimSettings *sim_settings);
+
+void sim_settings_set_trans_start (SimSettings *sim_settings,
+ gchar *str);
+
+void sim_settings_set_trans_stop (SimSettings *sim_settings,
+ gchar *str);
+
+void sim_settings_set_trans_step (SimSettings *sim_settings,
+ gchar *str);
+
+void sim_settings_set_trans (SimSettings *sim_settings,
+ gboolean enable);
+
+void sim_settings_set_trans_step_enable (SimSettings *sim_settings,
+ gboolean enable);
+
+void sim_settings_set_trans_init_cond(SimSettings *sim_settings,
+ gboolean enable);
+
+/**
+ *
+ */
+gboolean sim_settings_get_dc (SimSettings *);
+
+gchar *sim_settings_get_dc_vsrc (SimSettings *,gint);
+
+gdouble sim_settings_get_dc_start (SimSettings *, gint);
+
+gdouble sim_settings_get_dc_stop (SimSettings *,gint);
+
+gdouble sim_settings_get_dc_step (SimSettings *,gint);
+
+/**
+ *
+ */
+void sim_settings_set_dc (SimSettings *,
+ gboolean);
+
+void sim_settings_set_dc_vsrc (SimSettings * ,
+ gint,
+ gchar *);
+
+void sim_settings_set_dc_start (SimSettings *,
+ gint,
+ gchar *);
+
+void sim_settings_set_dc_stop (SimSettings *,
+ gint,
+ gchar *);
+
+void sim_settings_set_dc_step (SimSettings *,
+ gint,
+ gchar *);
+
+/**
+ *
+ */
+gboolean sim_settings_get_ac (SimSettings *);
+
+gchar *sim_settings_get_ac_type (SimSettings *);
+
+gint sim_settings_get_ac_npoints (SimSettings *);
+
+gdouble sim_settings_get_ac_start (SimSettings *);
+
+gdouble sim_settings_get_ac_stop (SimSettings *);
+
+/**
+ *
+ */
+void sim_settings_set_ac (SimSettings *,
+ gboolean);
+
+void sim_settings_set_ac_type (SimSettings *,
+ gchar *);
+
+void sim_settings_set_ac_npoints (SimSettings *,
+ gchar *);
+
+void sim_settings_set_ac_start (SimSettings *,
+ gchar *);
+
+void sim_settings_set_ac_stop (SimSettings *,
+ gchar *);
+
+/**
+ *
+ */
+GList *sim_settings_get_options (SimSettings *sim_settings);
+
+void sim_settings_add_option (SimSettings *,
+ SimOption *);
+
+#endif
diff --git a/src/simulation.c b/src/simulation.c
new file mode 100644
index 0000000..c6ae240
--- /dev/null
+++ b/src/simulation.c
@@ -0,0 +1,264 @@
+/*
+ * simulation.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 <locale.h>
+#include <glade/glade.h>
+
+#include "main.h"
+#include "simulation.h"
+#include "schematic.h"
+#include "schematic-view.h"
+#include "dialogs.h"
+#include "oregano-utils.h"
+#include "oregano-config.h"
+#include "plot.h"
+#include "gnucap.h"
+
+
+typedef struct {
+ Schematic *sm;
+ SchematicView *sv;
+ GtkDialog *dialog;
+ OreganoEngine *engine;
+ GtkProgressBar *progress;
+ GtkLabel *progress_label;
+ int progress_timeout_id;
+} Simulation;
+
+static int progress_bar_timeout_cb (Simulation *s);
+static void cancel_cb (GtkWidget *widget, gint arg1, Simulation *s);
+static void engine_done_cb (OreganoEngine *engine, Simulation *s);
+static void engine_aborted_cb (OreganoEngine *engine, Simulation *s);
+static gboolean simulate_cmd (Simulation *s);
+
+static int
+delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ return FALSE;
+}
+
+/* TODO : This gpointer sucks */
+gpointer
+simulation_new (Schematic *sm)
+{
+ Simulation *s;
+
+ s = g_new0 (Simulation, 1);
+ s->sm = sm;
+ s->sv = NULL;
+
+ return s;
+}
+
+void
+simulation_show (GtkWidget *widget, SchematicView *sv)
+{
+ GtkWidget *w;
+ GladeXML *gui;
+ Simulation *s;
+ Schematic *sm;
+
+ g_return_if_fail (sv != NULL);
+
+ sm = schematic_view_get_schematic (sv);
+ s = schematic_get_simulation (sm);
+
+ /* Only allow one instance of the dialog box per schematic. */
+ if (s->dialog){
+ gdk_window_raise (GTK_WIDGET (s->dialog)->window);
+ return;
+ }
+
+ if (!g_file_test (OREGANO_GLADEDIR "/simulation.glade",
+ G_FILE_TEST_EXISTS)) {
+ oregano_error (_("Could not create simulation dialog"));
+ return;
+ }
+
+ gui = glade_xml_new (OREGANO_GLADEDIR "/simulation.glade", "toplevel", NULL);
+
+ if (!gui) {
+ oregano_error (_("Could not create simulation dialog"));
+ return;
+ }
+
+ w = glade_xml_get_widget (gui, "toplevel");
+ if (!w) {
+ oregano_error (_("Could not create simulation dialog"));
+ return;
+ }
+
+ s->dialog = GTK_DIALOG (w);
+ g_signal_connect (G_OBJECT (w), "delete_event", G_CALLBACK (delete_event_cb), s);
+
+ w = glade_xml_get_widget (gui, "progressbar");
+ s->progress = GTK_PROGRESS_BAR (w);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress), 0.0);
+
+ w = glade_xml_get_widget (gui, "progress_label");
+ s->progress_label = GTK_LABEL (w);
+
+ g_signal_connect (G_OBJECT (s->dialog), "response", G_CALLBACK (cancel_cb), s);
+
+ gtk_widget_show_all (GTK_WIDGET (s->dialog));
+
+ s->sv = sv;
+ simulate_cmd (s);
+}
+
+static int
+progress_bar_timeout_cb (Simulation *s)
+{
+ gchar *str;
+ double p;
+ g_return_val_if_fail (s != NULL, FALSE);
+
+ oregano_engine_get_progress (s->engine, &p);
+
+ if (p >= 1) p = 0;
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress), p);
+
+ str = g_strdup_printf (_("Progress: <b>%s</b>"),
+ oregano_engine_get_current_operation (s->engine));
+ gtk_label_set_markup (s->progress_label, str);
+ g_free (str);
+
+ return TRUE;
+}
+
+static void
+engine_done_cb (OreganoEngine *engine, Simulation *s)
+{
+ if (s->progress_timeout_id != 0) {
+ g_source_remove (s->progress_timeout_id);
+ s->progress_timeout_id = 0;
+
+ /* Make sure that the progress bar is completed, just for good looks. */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress), 1.0);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (s->dialog));
+ s->dialog = NULL;
+
+ plot_show (s->engine);
+
+ if (oregano_engine_has_warnings (s->engine))
+ schematic_view_log_show (s->sv, FALSE);
+
+ schematic_view_clear_op_values (s->sv);
+ schematic_view_show_op_values (s->sv, s->engine);
+
+ /* I don't need the engine anymore. The plot
+ * window own his reference to the engine */
+ g_object_unref (s->engine);
+ s->engine = NULL;
+
+}
+
+static void
+engine_aborted_cb (OreganoEngine *engine, Simulation *s)
+{
+ GtkWidget *dialog;
+ int answer;
+
+ if (s->progress_timeout_id != 0) {
+ g_source_remove (s->progress_timeout_id);
+ s->progress_timeout_id = 0;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (s->dialog));
+ s->dialog = NULL;
+
+ if (!schematic_view_log_window_exists (s->sv)) {
+ dialog = gtk_message_dialog_new_with_markup (
+ GTK_WINDOW (s->sv->toplevel),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_YES_NO,
+ _("<span weight=\"bold\" size=\"large\">The simulation was aborted due to an error.</span>\n\n"
+ "Would you like to view the error log?"));
+
+ answer = gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ if (answer == GTK_RESPONSE_YES) {
+ schematic_view_log_show (s->sv, TRUE);
+ }
+ } else {
+ oregano_error (_("The simulation was aborted due to an error"));
+
+ schematic_view_log_show (s->sv, FALSE);
+ }
+}
+
+static void
+cancel_cb (GtkWidget *widget, gint arg1, Simulation *s)
+{
+ g_return_if_fail (s != NULL);
+
+ if (s->progress_timeout_id != 0) {
+ g_source_remove (s->progress_timeout_id);
+ s->progress_timeout_id = 0;
+ }
+
+ if (s->engine)
+ oregano_engine_stop (s->engine);
+
+ gtk_widget_destroy (GTK_WIDGET (s->dialog));
+ s->dialog = NULL;
+ s->sv = NULL;
+}
+
+static gboolean
+simulate_cmd (Simulation *s)
+{
+ OreganoEngine *engine;
+
+ if (s->engine != NULL) {
+ g_object_unref (G_OBJECT (s->engine));
+ s->engine = NULL;
+ }
+
+ engine = oregano_engine_factory_create_engine (oregano.engine, s->sm);
+ s->engine = engine;
+
+ s->progress_timeout_id = g_timeout_add (100, (GSourceFunc)progress_bar_timeout_cb, s);
+
+ g_signal_connect (G_OBJECT (engine), "done", G_CALLBACK (engine_done_cb), s);
+ g_signal_connect (G_OBJECT (engine), "aborted", G_CALLBACK (engine_aborted_cb), s);
+
+ /*TODO: separar generate list del start */
+ oregano_engine_start (engine);
+
+ return TRUE;
+}
+
diff --git a/src/simulation.h b/src/simulation.h
new file mode 100644
index 0000000..3f4e23e
--- /dev/null
+++ b/src/simulation.h
@@ -0,0 +1,136 @@
+/*
+ * simulation.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 __SIMULATION_H
+#define __SIMULATION_H
+
+#include <gtk/gtk.h>
+#include "schematic.h"
+#include "schematic-view.h"
+
+typedef struct _SimulationData SimulationData;
+
+typedef enum {
+ OP_POINT ,
+ TRANSIENT ,
+ DC_TRANSFER ,
+ AC ,
+ TRANSFER ,
+ DISTORTION ,
+ NOISE ,
+ POLE_ZERO ,
+ SENSITIVITY ,
+ ANALYSIS_UNKNOWN
+} AnalysisType;
+
+
+#define INFINITE 1e50f
+
+typedef enum {
+ FUNCTION_MINUS = 0,
+ FUNCTION_TRANSFER
+} SimulationFunctionType;
+
+typedef struct _SimulationFunction {
+ SimulationFunctionType type;
+ guint first;
+ guint second;
+} SimulationFunction;
+
+struct _SimulationData {
+ AnalysisType type;
+ gboolean binary;
+ gint state;
+ gint n_variables;
+ gint n_points;
+ gchar **var_names;
+ gchar **var_units;
+ GArray **data;
+ gdouble *min_data;
+ gdouble *max_data;
+ gint got_var;
+ gint got_points;
+ gint num_data;
+
+ /* Functions typeof SimulationFunction */
+ GList *functions;
+};
+
+
+typedef struct {
+ SimulationData sim_data;
+ int state;
+} SimOp;
+
+/* Placeholder for something real later. */
+typedef struct {
+ SimulationData sim_data;
+ double freq;
+ GList *out_var;
+} SimFourier;
+
+typedef struct {
+ SimulationData sim_data;
+ int state;
+ double sim_length;
+ double step_size;
+} SimTransient;
+
+typedef struct {
+ SimulationData sim_data;
+ int state;
+ double sim_length;
+ double start,stop;
+} SimAC;
+
+typedef struct {
+ SimulationData sim_data;
+ int state;
+ double sim_length;
+ double start1,stop1,step1;
+ double start2,stop2,step2;
+} SimDC;
+
+
+typedef union {
+ SimOp op;
+ SimTransient transient;
+ SimFourier fourier;
+ SimAC ac;
+ SimDC dc;
+} Analysis;
+
+void simulation_show (GtkWidget *widget, SchematicView *sv);
+gpointer simulation_new (Schematic *sm);
+gchar *sim_engine_analysis_name(SimulationData *);
+
+#define SIM_DATA(obj) ((SimulationData *)(obj))
+#define ANALYSIS(obj) ((Analysis *)(obj))
+
+#endif /* __SIMULATION_H */
diff --git a/src/smallicon.c b/src/smallicon.c
new file mode 100644
index 0000000..16d2e12
--- /dev/null
+++ b/src/smallicon.c
@@ -0,0 +1,66 @@
+/*
+ * smallicon.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
+ *
+ *
+ * http://www.dtek.chalmers.se/~d4hult/oregano/
+ *
+ * Copyright (C) 1999,2000 Richard Hult
+ *
+ * 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 <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+#include <X11/Xlib.h>
+
+#include "smallicon.h"
+
+void
+set_small_icon(GdkWindow *window,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask)
+{
+#ifdef GNOME2_CONVERSION_COMPLETE
+ GdkAtom icon_atom;
+ long data[2];
+
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (pixmap != NULL);
+
+ data[0] = ((GdkPixmapPrivate *)pixmap)->xwindow;
+ if (mask)
+ data[1] = ((GdkPixmapPrivate *)mask)->xwindow;
+ else
+ data[1] = 0;
+
+ icon_atom = gdk_atom_intern ("KWM_WIN_ICON", FALSE);
+ gdk_property_change (window, icon_atom, icon_atom,
+ 32,GDK_PROP_MODE_REPLACE,
+ (guchar *)data, 2);
+
+#endif
+}
diff --git a/src/smallicon.h b/src/smallicon.h
new file mode 100644
index 0000000..304d73e
--- /dev/null
+++ b/src/smallicon.h
@@ -0,0 +1,33 @@
+/*
+ * smallicon.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.
+ */
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+void set_small_icon(GdkWindow *window, GdkPixmap *pixmap, GdkBitmap *mask);
diff --git a/src/spice2to3.awk b/src/spice2to3.awk
new file mode 100644
index 0000000..32f7a46
--- /dev/null
+++ b/src/spice2to3.awk
@@ -0,0 +1,294 @@
+#==================================================== -*- AWK -*- =====
+# spice2to3.awk
+#
+# This awk program file is used to convert Spice2 input files to
+# Spice3 format.
+#
+# Specifically, nonlinear dependent sources are converted from
+# the polynomial model to the more general BXXXXX nonlinear
+# dependent source model used in Spice3.
+#
+#
+# Usage:
+# awk -f spice2to3.awk [comments=1] inputfile > outputfile
+#
+# If the optional comments=1 parameter is specified, the original
+# input lines that are converted will be saved as comments in the
+# output file.
+#
+# Limitations:
+# - Cubic and higher order terms are not handled except for
+# the special case of a single variable
+# - Initial conditions are not handled (IC=...)
+# - Continued lines are not handled
+# - This program uses features from GNU awk
+#
+#
+# Mike McCarrick Feb 1995
+# mmccarri@apti.com APTI, Wash DC
+#======================================================================
+
+BEGIN {
+ IGNORECASE = 1;
+}
+
+
+#----------------------------------------------------------------------
+# Utility functions
+#----------------------------------------------------------------------
+
+function min(a,b) {
+ return (a < b) ? a : b;
+}
+
+function max(a,b) {
+ return (a > b) ? a : b;
+}
+
+
+# Turn the original input line into a comment
+function make_comment(inputline) {
+ print "*SPICE2:";
+ print "*" inputline;
+ print "*SPICE3:";
+}
+
+
+# Get leading whitespace from the input line
+function get_leader(inputline)
+{
+ if ((i = match(inputline, "[^ \t]")) != 0)
+ return substr(inputline, 1, i-1);
+ else
+ return "";
+}
+
+
+# Get trailing comments from the input line
+function get_trailer(inputline)
+{
+ if ((i = index(inputline, ";")) != 0)
+ return substr(inputline, i, length(inputline)-i+1);
+ else
+ return "";
+}
+
+
+# Canonicalize the input line
+function canonicalize(inputline)
+{
+ # Strip parentheses and commas from the input line
+ gsub(/\(/, " ", inputline);
+ gsub(/\)/, " ", inputline);
+ gsub(/,/, " ", inputline);
+
+ # chop off any trailing comments
+ if ((i = index(inputline, ";")) != 0)
+ inputline = substr(inputline, 1, i-1);
+
+ return inputline;
+}
+
+
+# Convert numerical values to canonical form (the scale factors are
+# not recognized for B devices at least in 3f4)
+function convert_num(strval)
+{
+ sub(/T/, "E12", strval);
+ sub(/G/, "E9", strval);
+ sub(/MEG/, "E6", strval);
+ sub(/K/, "E3", strval);
+ sub(/M/, "E-3", strval);
+ sub(/U/, "E-6", strval);
+ sub(/N/, "E-9", strval);
+ sub(/P/, "E-12", strval);
+ sub(/F/, "E-15", strval);
+
+ return strval;
+}
+
+
+
+#----------------------------------------------------------------------
+# E: Voltage controlled voltage source
+# G: Voltage controlled current source
+# H: Current controlled voltage source
+# F: Current controlled current source
+#----------------------------------------------------------------------
+/^ *[EGHF].+POLY/ {
+
+ # Save the old line as a comment
+ origline = $0;
+ if (comments != 0)
+ make_comment(origline);
+
+ # Save any leading whitespace and trailing comments
+ leader = get_leader($0);
+ trailer = get_trailer($0);
+
+ # canonicalize the input line
+ $0 = canonicalize($0);
+
+ # Print the new source name with its node connections
+ printf("%sB%s %s %s ", leader, $1, $2, $3);
+
+ # The format differs for voltage vs current controlled sources
+ stype = toupper(substr($1, 1, 1));
+ if (stype == "E" || stype == "H")
+ printf("V = ");
+ else
+ printf("I = ");
+ nvars = $5;
+ inodes = 6;
+ if (stype == "E" || stype == "G")
+ {
+ v_controlled = 1;
+ nnodes = 2 * nvars;
+ }
+ else
+ {
+ v_controlled = 0;
+ nnodes = nvars;
+ }
+
+ icoeff = inodes + nnodes;
+ ncoeff = NF - icoeff + 1;
+ plusflag = 0;
+
+ # Constant term
+ if ($icoeff != 0.0)
+ {
+ printf("%s", convert_num($icoeff));
+ plusflag = 1;
+ }
+ icoeff++;
+ ncoeff--;
+
+ # Linear terms
+ nlinear = min(ncoeff, nvars);
+ for (ic = 0; ic < nlinear; ic++)
+ {
+ if ((val = convert_num($(icoeff + ic))) != 0.0)
+ {
+ if (plusflag)
+ printf(" + ");
+ if (v_controlled)
+ {
+ ip = inodes + 2 * ic;
+ im = ip + 1;
+ if (val != 1.0)
+ printf("%s*V(%s,%s)", val, $ip, $im);
+ else
+ printf("V(%s,%s)", $ip, $im);
+ }
+ else
+ {
+ is = inodes + ic;
+ if (val != 1.0)
+ printf("%s*I(%s)", val, $is);
+ else
+ printf("I(%s)", $is);
+ }
+ plusflag = 1;
+ }
+ }
+ icoeff += ic;
+ ncoeff -= ic;
+
+
+ # Quadratic terms
+ nquad = min(ncoeff, nvars*(nvars+1)/2);
+ for (n1 = ic = 0; n1 < nvars; n1++)
+ {
+ for (n2 = 0; n2 <= n1; n2++)
+ {
+ if (ic > nquad)
+ break;
+
+ if ((val = convert_num($(icoeff + ic))) != 0.0)
+ {
+ if (plusflag)
+ printf(" + ");
+ if (v_controlled)
+ {
+ ip1 = inodes + 2 * n1;
+ im1 = ip1 + 1;
+ ip2 = inodes + 2 * n2;
+ im2 = ip2 + 1;
+ if (val != 1.0)
+ printf("%s*V(%s,%s)*V(%s,%s)", val, $ip1, $im1, $ip2, $im2);
+ else
+ printf("V(%s,%s)*V(%s,%s)", $ip1, $im1, $ip2, $im2);
+ }
+ else
+ {
+ is1 = inodes + n1;
+ is2 = inodes + n2;
+ if (val != 1.0)
+ printf("%s*I(%s)*I(%s)", val, $is1, $is2);
+ else
+ printf("I(%s)*I(%s)", $is1, $is2);
+ }
+ plusflag = 1;
+ }
+ ic++;
+ }
+ }
+
+ icoeff += ic;
+ ncoeff -= ic;
+
+ # Cubic and higher order terms are handled only for a single variable
+ if (ncoeff > 0 && nvars > 1)
+ {
+ print "Warning: the following source line contains" > "/dev/stderr";
+ print "polynomial terms greater than second order." > "/dev/stderr";
+ print "Convert this line manually:" > "/dev/stderr";
+ print origline > "/dev/stderr";
+ # Add a warning message comment in the output file
+ printf(" ; ***ERROR CONVERTING SPICE2 ENTRY\n");
+ next;
+ }
+
+ # Single variable higher-order terms
+ for (ic = 0; ic < ncoeff; ic++)
+ {
+ if ((val = convert_num($(icoeff + ic))) != 0.0)
+ {
+ if (plusflag)
+ printf(" + ");
+ if (v_controlled)
+ {
+ ip = inodes + 2 * ic;
+ im = ip + 1;
+ if (val != 1.0)
+ printf("%s*V(%s,%s)^%d", val, $ip, $im, ic+3);
+ else
+ printf("V(%s,%s)^%d", $ip, $im, ic+3);
+ }
+ else
+ {
+ is = inodes + ic;
+ if (val != 1.0)
+ printf("%s*I(%s)^%d", val, $is, ic+3);
+ else
+ printf("I(%s)^%d", $is, ic+3);
+ }
+ plusflag = 1;
+ }
+ }
+
+ # Add trailing comments to the output line
+ printf(" %s\n", trailer);
+ next;
+
+}
+
+
+
+#----------------------------------------------------------------------
+# Default: just print out the line
+#----------------------------------------------------------------------
+{
+ print $0;
+}
diff --git a/src/splash.c b/src/splash.c
new file mode 100644
index 0000000..65094f2
--- /dev/null
+++ b/src/splash.c
@@ -0,0 +1,118 @@
+/*
+ * splash.c
+ *
+ *
+ * Author:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 2003-2008 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 <unistd.h>
+#include <glade/glade.h>
+#include "splash.h"
+#include "splash.xpm"
+#include "dialogs.h"
+
+
+/* TODO : If we support this, we need to know how to stop the g_timeout :-/ */
+static void
+oregano_splash_destroy (GtkWidget *w, GdkEvent *event, Splash *sp)
+{
+ if ((event->type == GDK_BUTTON_PRESS) && (event->button.button == 1)) {
+ if (sp->can_destroy)
+ gtk_widget_destroy (GTK_WIDGET (sp->win));
+ }
+}
+
+Splash *
+oregano_splash_new ()
+{
+ GladeXML *gui;
+ Splash *sp;
+ GtkImage *img;
+ GtkEventBox *event;
+ GdkPixbuf *logo;
+ gchar *msg;
+
+ if (!g_file_test (OREGANO_GLADEDIR "/splash.glade", G_FILE_TEST_EXISTS) ||
+ !g_file_test (OREGANO_GLADEDIR "/splash.xpm", G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The files %s or %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/splash.glade", OREGANO_GLADEDIR "/splash.xpm");
+ oregano_error_with_title (_("Could not create textbox properties dialog"), msg);
+ g_free (msg);
+ return;
+ }
+ gui = glade_xml_new (OREGANO_GLADEDIR "/splash.glade", NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create textbox properties dialog"));
+ return;
+ }
+
+ sp = g_new0 (Splash, 1);
+ sp->can_destroy = FALSE;
+
+ sp->win = GTK_WINDOW (glade_xml_get_widget (gui, "splash"));
+ sp->lbl = GTK_LABEL (glade_xml_get_widget (gui, "label"));
+ sp->progress = glade_xml_get_widget (gui, "pbar");
+
+ event = GTK_EVENT_BOX (glade_xml_get_widget (gui, "event"));
+ sp->event = GTK_WIDGET (event);
+
+ // Replaced with TimeOut!
+ //g_signal_connect (G_OBJECT (event), "button_press_event", G_CALLBACK (oregano_splash_destroy), sp);
+
+ gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (sp->progress), 0.07);
+ gtk_widget_show_all (GTK_WIDGET (sp->win));
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ return sp;
+}
+
+gboolean
+oregano_splash_free (Splash *sp)
+{
+ /* Need to disconnect the EventBox Widget! */
+ g_signal_handlers_disconnect_by_func (sp->event, oregano_splash_destroy, sp);
+ gtk_widget_destroy (GTK_WIDGET (sp->win));
+ g_free (sp);
+ return FALSE;
+}
+
+void
+oregano_splash_step (Splash *sp, char *s)
+{
+ int i;
+ gtk_label_set_text (sp->lbl, s);
+ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (sp->progress));
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+
+void
+oregano_splash_done (Splash *sp, char *s)
+{
+ gtk_label_set_text (sp->lbl, s);
+ sp->can_destroy = TRUE;
+ g_timeout_add (2000, (GSourceFunc)(oregano_splash_free), sp);
+}
+
diff --git a/src/splash.h b/src/splash.h
new file mode 100644
index 0000000..e689f34
--- /dev/null
+++ b/src/splash.h
@@ -0,0 +1,46 @@
+/*
+ * splash.h
+ *
+ *
+ * Author:
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ *
+ * Copyright (C) 2003-2008 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 _OREGANO_SPLASH_H_
+#define _OREGANO_SPLASH_H_
+
+#include <gtk/gtk.h>
+
+typedef struct _Splash Splash;
+
+struct _Splash {
+ GtkWindow *win;
+ GtkWidget *progress;
+ GtkWidget *event;
+ GtkLabel *lbl;
+ gboolean can_destroy;
+};
+
+Splash *oregano_splash_new ();
+gboolean oregano_splash_free (Splash *);
+void oregano_splash_step (Splash *, char *s);
+void oregano_splash_done (Splash *, char *s);
+
+#endif //_OREGANO_SPLASH_H_
diff --git a/src/stock.c b/src/stock.c
new file mode 100644
index 0000000..8b42e34
--- /dev/null
+++ b/src/stock.c
@@ -0,0 +1,92 @@
+/*
+ * stock.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.
+ */
+/*
+ * Stock icon code, stolen from:
+ * Eye of Gnome image viewer - stock icons
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico@gimp.org>
+ */
+
+#include <gnome.h>
+#include "stock.h"
+#include "stock/sim-settings.xpm"
+#include "stock/rotate.xpm"
+#include "stock/zoom_in.xpm"
+#include "stock/zoom_out.xpm"
+#include "stock/plot.xpm"
+#include "stock/part-browser.xpm"
+#include "stock/grid.xpm"
+#include "stock/arrow.xpm"
+#include "stock/text.xpm"
+#include "stock/wire.xpm"
+#include "stock/voltmeter.xpm"
+#include "stock/vclamp.xpm"
+#include "stock/zoom_pan.xpm"
+#include "stock/zoom_region.xpm"
+
+static void
+add_stock_entry (const gchar *stock_id, char **xpm_data)
+{
+ static GtkIconFactory *factory = NULL;
+ GdkPixbuf *pixbuf;
+ GtkIconSet *icon_set;
+
+ if (!factory) {
+ factory =gtk_icon_factory_new ();
+ gtk_icon_factory_add_default (factory);
+ }
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **)xpm_data);
+ icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
+ gtk_icon_factory_add (factory, stock_id, icon_set);
+ gtk_icon_set_unref (icon_set);
+ g_object_unref (G_OBJECT (pixbuf));
+}
+
+void
+stock_init (void)
+{
+ add_stock_entry (STOCK_PIXMAP_SIM_SETTINGS, sim_settings_xpm);
+ add_stock_entry (STOCK_PIXMAP_ROTATE, rotate_xpm);
+ add_stock_entry (STOCK_PIXMAP_ZOOM_IN, zoom_in_xpm);
+ add_stock_entry (STOCK_PIXMAP_ZOOM_OUT, zoom_out_xpm);
+ add_stock_entry (STOCK_PIXMAP_PLOT, plot_xpm);
+ add_stock_entry (STOCK_PIXMAP_PART_BROWSER, part_browser_xpm);
+ add_stock_entry (STOCK_PIXMAP_GRID, grid_xpm);
+ add_stock_entry (STOCK_PIXMAP_ARROW, arrow_xpm);
+ add_stock_entry (STOCK_PIXMAP_TEXT, text_xpm);
+ add_stock_entry (STOCK_PIXMAP_WIRE, wire_xpm);
+ add_stock_entry (STOCK_PIXMAP_VOLTMETER, voltmeter_xpm);
+ add_stock_entry (STOCK_PIXMAP_V_CLAMP, vclamp_xpm);
+ add_stock_entry (STOCK_PIXMAP_ZOOM_PAN, zoom_pan_xpm);
+ add_stock_entry (STOCK_PIXMAP_ZOOM_REGION, zoom_region_xpm);
+}
diff --git a/src/stock.h b/src/stock.h
new file mode 100644
index 0000000..d736f98
--- /dev/null
+++ b/src/stock.h
@@ -0,0 +1,53 @@
+/*
+ * stock.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 __STOCK_H
+#define __STOCK_H
+
+/* Stock names */
+
+#define STOCK_PIXMAP_SIM_SETTINGS "OREGANO_stock_sim_settings"
+#define STOCK_PIXMAP_ROTATE "OREGANO_stock_rotate"
+#define STOCK_PIXMAP_ZOOM_IN "OREGANO_stock_zoom_in"
+#define STOCK_PIXMAP_ZOOM_OUT "OREGANO_stock_zoom_out"
+#define STOCK_PIXMAP_PLOT "OREGANO_stock_plot"
+#define STOCK_PIXMAP_PART_BROWSER "OREGANO_stock_part_browser"
+#define STOCK_PIXMAP_GRID "OREGANO_stock_grid"
+#define STOCK_PIXMAP_ARROW "OREGANO_stock_arrow"
+#define STOCK_PIXMAP_TEXT "OREGANO_stock_text"
+#define STOCK_PIXMAP_WIRE "OREGANO_stock_wire"
+#define STOCK_PIXMAP_VOLTMETER "OREGANO_stock_voltmeter"
+#define STOCK_PIXMAP_V_CLAMP "OREGANO_stock_v_clamp"
+#define STOCK_PIXMAP_ZOOM_PAN "OREGANO_stock_zoom_pan"
+#define STOCK_PIXMAP_ZOOM_REGION "OREGANO_stock_zoom_region"
+
+void stock_init (void);
+
+#endif
diff --git a/src/stock/Makefile.am b/src/stock/Makefile.am
new file mode 100644
index 0000000..e4a0270
--- /dev/null
+++ b/src/stock/Makefile.am
@@ -0,0 +1,18 @@
+EXTRA_DIST = \
+ README \
+ arrow.xpm \
+ grid.xcf \
+ grid.xpm \
+ part-browser.xcf \
+ part-browser.xpm \
+ plot.xcf \
+ plot.xpm \
+ plot2.xcf \
+ rotate.xpm \
+ sim-settings.xpm \
+ text.xpm \
+ voltmeter.xpm \
+ wire.xpm \
+ zoom_in.xpm \
+ zoom_out.xpm
+
diff --git a/src/stock/arrow.xpm b/src/stock/arrow.xpm
new file mode 100644
index 0000000..a0e8abc
--- /dev/null
+++ b/src/stock/arrow.xpm
@@ -0,0 +1,64 @@
+/* XPM */
+static char * arrow_xpm[] = {
+"24 24 37 1",
+" c None",
+". c #000000",
+"+ c #DADADA",
+"@ c #FBFBFB",
+"# c #D7D7D7",
+"$ c #F9F9F9",
+"% c #D4D4D4",
+"& c #F6F6F6",
+"* c #D1D1D1",
+"= c #FCFCFC",
+"- c #FAFAFA",
+"; c #F7F7F7",
+"> c #F3F3F3",
+", c #CFCFCF",
+"' c #FDFDFD",
+") c #F8F8F8",
+"! c #F4F4F4",
+"~ c #F0F0F0",
+"{ c #CCCCCC",
+"] c #F5F5F5",
+"^ c #F2F2F2",
+"/ c #EEEEEE",
+"( c #CACACA",
+"_ c #EFEFEF",
+": c #EBEBEB",
+"< c #C9C9C9",
+"[ c #FEFEFE",
+"} c #EDEDED",
+"| c #969696",
+"1 c #B2B2B2",
+"2 c #FFFFFF",
+"3 c #888888",
+"4 c #DCDCDC",
+"5 c #A3A3A3",
+"6 c #BFBFBF",
+"7 c #D2D2D2",
+"8 c #D6D6D6",
+" ",
+" ",
+" . ",
+" .. ",
+" .+. ",
+" .@#. ",
+" .@$%. ",
+" .@$&*. ",
+" .=-;>,. ",
+" .'@)!~{. ",
+" .'@$]^/(. ",
+" .'=-;>_:<. ",
+" .['@)!~}(|. ",
+" .['=$&*1.. ",
+" .2['@)3. ",
+" .245.#&.. ",
+" .6....;7. ",
+" ... .8]. ",
+" .;7. ",
+" .8]. ",
+" ... ",
+" ",
+" ",
+" "};
diff --git a/src/stock/clamp.xpm b/src/stock/clamp.xpm
new file mode 100644
index 0000000..3ac42eb
--- /dev/null
+++ b/src/stock/clamp.xpm
@@ -0,0 +1,30 @@
+/* XPM */
+static char * clamp_xpm[] = {
+"24 24 3 1",
+" c None",
+". c #000000",
+"+ c #BE5959",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" . ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" ... ",
+" . ... ",
+" .. ... ",
+" .++... ",
+" .+++. ",
+" .++++. ",
+" .+++++.. ",
+" .+++.. ",
+" .+... ",
+" .. ",
+" ",
+" "};
diff --git a/src/stock/grid.xcf b/src/stock/grid.xcf
new file mode 100644
index 0000000..768f69d
--- /dev/null
+++ b/src/stock/grid.xcf
Binary files differ
diff --git a/src/stock/grid.xpm b/src/stock/grid.xpm
new file mode 100644
index 0000000..37910c6
--- /dev/null
+++ b/src/stock/grid.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char * grid_xpm[] = {
+"24 24 29 1",
+" c None",
+". c #000000",
+"+ c #E9E9E9",
+"@ c #FFFFFF",
+"# c #B1B1B1",
+"$ c #FBFBFB",
+"% c #BABABA",
+"& c #E4E4E4",
+"* c #ECECEC",
+"= c #B9B9B9",
+"- c #BCBCBC",
+"; c #E7E7E7",
+"> c #B8B8B8",
+", c #969696",
+"' c #7F7F7F",
+") c #707070",
+"! c #737373",
+"~ c #ABABAB",
+"{ c #EBEBEB",
+"] c #7B7B7B",
+"^ c #858585",
+"/ c #747474",
+"( c #E5E5E5",
+"_ c #C3C3C3",
+": c #BFBFBF",
+"< c #757575",
+"[ c #7E7E7E",
+"} c #BBBBBB",
+"| c #A3A3A3",
+" ",
+" ",
+" ",
+" ................. ",
+" .+@@@#$$@@@%@@@&. ",
+" .@*************=. ",
+" .@***-***;*-***>. ",
+" .,**-'-***-'-**). ",
+" .@***-*****-***-. ",
+" .@*************-. ",
+" .@***-*****-***-. ",
+" .,**-'-***-'-**!. ",
+" .@***~*****-*{*-. ",
+" .@*************>. ",
+" .@***-****+-***-. ",
+" .,**-]-***-^-**/. ",
+" .@***-(**;*-***>. ",
+" .@*****{{******-. ",
+" .+__:<-----[>-}|. ",
+" ................. ",
+" ",
+" ",
+" ",
+" "};
diff --git a/src/stock/part-browser.xcf b/src/stock/part-browser.xcf
new file mode 100644
index 0000000..4aa45b3
--- /dev/null
+++ b/src/stock/part-browser.xcf
Binary files differ
diff --git a/src/stock/part-browser.xpm b/src/stock/part-browser.xpm
new file mode 100644
index 0000000..89031d8
--- /dev/null
+++ b/src/stock/part-browser.xpm
@@ -0,0 +1,427 @@
+/* XPM */
+static char * part_browser_xpm[] = {
+"32 32 392 2",
+" c None",
+". c #525251",
+"+ c #606360",
+"@ c #5F625F",
+"# c #60635F",
+"$ c #60625F",
+"% c #5F625E",
+"& c #494B48",
+"* c #C7CCC6",
+"= c #A8B3A5",
+"- c #9CA899",
+"; c #AEB6A6",
+"> c #AAB4A4",
+", c #ABB4A5",
+"' c #9AA797",
+") c #ACB5A5",
+"! c #9DAA9A",
+"~ c #B3BAAA",
+"{ c #9BA897",
+"] c #849081",
+"^ c #252924",
+"/ c #B7C0B5",
+"( c #6F8066",
+"_ c #67775B",
+": c #6B7B5E",
+"< c #5F7155",
+"[ c #6C7B5E",
+"} c #647659",
+"| c #5C6F53",
+"1 c #808A6D",
+"2 c #647559",
+"3 c #54694D",
+"4 c #50664A",
+"5 c #485C43",
+"6 c #171E16",
+"7 c #91997F",
+"8 c #576B4F",
+"9 c #BAC2B8",
+"0 c #B2BAB0",
+"a c #B3BAB1",
+"b c #A5ADA3",
+"c c #697466",
+"d c #586B4F",
+"e c #738064",
+"f c #778367",
+"g c #88947E",
+"h c #B6BDB4",
+"i c #929990",
+"j c #596954",
+"k c #171D15",
+"l c #B7C1B5",
+"m c #889278",
+"n c #6D7C64",
+"o c #6F7E6B",
+"p c #D1D1D1",
+"q c #A6A6A6",
+"r c #979797",
+"s c #6F6F6F",
+"t c #414540",
+"u c #4C5A49",
+"v c #4E6047",
+"w c #8F9883",
+"x c #8B9589",
+"y c #C9C9C9",
+"z c #989898",
+"A c #515151",
+"B c #444B42",
+"C c #4E5E49",
+"D c #55664D",
+"E c #1B2018",
+"F c #9DA287",
+"G c #697660",
+"H c #5F695C",
+"I c #D5D5D5",
+"J c #BCBCBC",
+"K c #C8C8C8",
+"L c #7B7B7B",
+"M c #3A3D39",
+"N c #424D3F",
+"O c #4D5E46",
+"P c #727E6C",
+"Q c #818880",
+"R c #CACACA",
+"S c #C0C0C0",
+"T c #C5C5C5",
+"U c #4C4C4C",
+"V c #3F463D",
+"W c #424F3E",
+"X c #55684D",
+"Y c #9EA388",
+"Z c #738068",
+"` c #ABABAB",
+" . c #A7A7A7",
+".. c #6D6D6D",
+"+. c #3E423D",
+"@. c #4F5D4B",
+"#. c #4E6147",
+"$. c #929B85",
+"%. c #8C9689",
+"&. c #C7C7C7",
+"*. c #A5A5A5",
+"=. c #A1A1A1",
+"-. c #4B4B4B",
+";. c #464F43",
+">. c #4F5F4A",
+",. c #596B50",
+"'. c #65735D",
+"). c #5D675A",
+"!. c #7E7E7E",
+"~. c #3F433E",
+"{. c #424E40",
+"]. c #495B42",
+"^. c #687564",
+"/. c #80877F",
+"(. c #C6C6C6",
+"_. c #D3D3D3",
+":. c #CDCDCD",
+"<. c #434A41",
+"[. c #42503F",
+"}. c #56694E",
+"|. c #B8C1B6",
+"1. c #9FA489",
+"2. c #6F7D6B",
+"3. c #D0D0D0",
+"4. c #A9A9A9",
+"5. c #B3B3B3",
+"6. c #6B6B6B",
+"7. c #4D5A49",
+"8. c #4E6247",
+"9. c #929A85",
+"0. c #8C968A",
+"a. c #C4C4C4",
+"b. c #AAAAAA",
+"c. c #494949",
+"d. c #444B41",
+"e. c #67745E",
+"f. c #9F9F9F",
+"g. c #ACACAC",
+"h. c #606060",
+"i. c #383B37",
+"j. c #414B3E",
+"k. c #4A5C43",
+"l. c #697564",
+"m. c #7C837A",
+"n. c #BFBFBF",
+"o. c #A2A2A2",
+"p. c #454545",
+"q. c #3E453D",
+"r. c #414E3D",
+"s. c #A4A98D",
+"t. c #6C7B63",
+"u. c #B1B1B1",
+"v. c #666666",
+"w. c #3B3F3B",
+"x. c #4A5747",
+"y. c #4C5F46",
+"z. c #74836E",
+"A. c #899387",
+"B. c #BDBDBD",
+"C. c #C2C2C2",
+"D. c #3E443C",
+"E. c #505F4B",
+"F. c #5A6C51",
+"G. c #54654B",
+"H. c #191F17",
+"I. c #B6BFB3",
+"J. c #A4A78C",
+"K. c #68765F",
+"L. c #5D675B",
+"M. c #787878",
+"N. c #3E473B",
+"O. c #475941",
+"P. c #6B7966",
+"Q. c #7D837B",
+"R. c #919191",
+"S. c #414141",
+"T. c #3E453C",
+"U. c #4B5444",
+"V. c #57694F",
+"W. c #A0A589",
+"X. c #697860",
+"Y. c #677564",
+"Z. c #ADADAD",
+"`. c #333732",
+" + c #505D4D",
+".+ c #64725F",
+"++ c #677261",
+"@+ c #858B7F",
+"#+ c #ACACAA",
+"$+ c #A3A3A3",
+"%+ c #939393",
+"&+ c #3F3F3F",
+"*+ c #434C41",
+"=+ c #646F59",
+"-+ c #5B6D52",
+";+ c #999F85",
+">+ c #67755F",
+",+ c #5F6A5C",
+"'+ c #646464",
+")+ c #575757",
+"!+ c #3A3A3A",
+"~+ c #636361",
+"{+ c #8F8C83",
+"]+ c #6E6A5F",
+"^+ c #736E64",
+"/+ c #797469",
+"(+ c #8C867A",
+"_+ c #A8A6A0",
+":+ c #747372",
+"<+ c #313131",
+"[+ c #4E5249",
+"}+ c #505948",
+"|+ c #586A4F",
+"1+ c #6A765C",
+"2+ c #989F84",
+"3+ c #3B4239",
+"4+ c #2E352C",
+"5+ c #2C312A",
+"6+ c #5C5C56",
+"7+ c #878275",
+"8+ c #47473A",
+"9+ c #4B5644",
+"0+ c #596A53",
+"a+ c #515F4B",
+"b+ c #3D4136",
+"c+ c #5D594A",
+"d+ c #7A7362",
+"e+ c #36352D",
+"f+ c #384634",
+"g+ c #6C7B5F",
+"h+ c #667359",
+"i+ c #161D15",
+"j+ c #8C967B",
+"k+ c #788467",
+"l+ c #717F63",
+"m+ c #69785C",
+"n+ c #757D67",
+"o+ c #807D6E",
+"p+ c #505243",
+"q+ c #5C6C56",
+"r+ c #9AA791",
+"s+ c #ACB7A3",
+"t+ c #99A58B",
+"u+ c #708265",
+"v+ c #6E765F",
+"w+ c #676654",
+"x+ c #656251",
+"y+ c #5C654E",
+"z+ c #707D61",
+"A+ c #66775A",
+"B+ c #5A6950",
+"C+ c #61755C",
+"D+ c #848D70",
+"E+ c #495D43",
+"F+ c #8C8E7F",
+"G+ c #4D4A3E",
+"H+ c #74806A",
+"I+ c #9CAD96",
+"J+ c #D3DACC",
+"K+ c #B4C3B0",
+"L+ c #90A386",
+"M+ c #7A8F70",
+"N+ c #7B8A6D",
+"O+ c #4E5F47",
+"P+ c #787363",
+"Q+ c #484C3C",
+"R+ c #616F56",
+"S+ c #7F8A6C",
+"T+ c #68785C",
+"U+ c #465A41",
+"V+ c #929187",
+"W+ c #414637",
+"X+ c #97A48C",
+"Y+ c #B8C6B4",
+"Z+ c #CFD8C7",
+"`+ c #9BAF94",
+" @ c #869B7B",
+".@ c #7B9071",
+"+@ c #808F71",
+"@@ c #4E6248",
+"#@ c #9C9C8D",
+"$@ c #5A584C",
+"%@ c #5C6A51",
+"&@ c #B9B697",
+"*@ c #738164",
+"=@ c #69755A",
+"-@ c #9B988D",
+";@ c #4D5644",
+">@ c #D9D8C5",
+",@ c #C7D1BF",
+"'@ c #EBE9D8",
+")@ c #9EAF91",
+"!@ c #D8D6BC",
+"~@ c #8D9D7F",
+"{@ c #C1BEA3",
+"]@ c #667059",
+"^@ c #9A9786",
+"/@ c #656157",
+"(@ c #A3A084",
+"_@ c #656F56",
+":@ c #DACFAF",
+"<@ c #7E896C",
+"[@ c #747D62",
+"}@ c #A29D93",
+"|@ c #495041",
+"1@ c #DFDAC9",
+"2@ c #B4BFA9",
+"3@ c #F1ECD9",
+"4@ c #9DAD8F",
+"5@ c #EBE5CD",
+"6@ c #92A083",
+"7@ c #CEC7AE",
+"8@ c #606753",
+"9@ c #9B9587",
+"0@ c #545145",
+"a@ c #C0B79A",
+"b@ c #6E755D",
+"c@ c #4B4F4A",
+"d@ c #364133",
+"e@ c #766F5F",
+"f@ c #424839",
+"g@ c #736D5D",
+"h@ c #3D4133",
+"i@ c #746F62",
+"j@ c #474538",
+"k@ c #787267",
+"l@ c #596050",
+"m@ c #8C8677",
+"n@ c #555C4B",
+"o@ c #878171",
+"p@ c #4D5544",
+"q@ c #666154",
+"r@ c #393B33",
+"s@ c #A19C91",
+"t@ c #3A392E",
+"u@ c #666052",
+"v@ c #3C3F32",
+"w@ c #1F271C",
+"x@ c #030403",
+"y@ c #36332B",
+"z@ c #615A4C",
+"A@ c #1A1814",
+"B@ c #020202",
+"C@ c #000000",
+"D@ c #010101",
+"E@ c #6F6E6B",
+"F@ c #7A7467",
+"G@ c #201E19",
+"H@ c #554F42",
+"I@ c #3E3930",
+"J@ c #474237",
+"K@ c #2E2C27",
+"L@ c #212121",
+"M@ c #464645",
+"N@ c #96948D",
+"O@ c #726C60",
+"P@ c #3D382F",
+"Q@ c #555149",
+"R@ c #312D25",
+"S@ c #5E584A",
+"T@ c #7C7769",
+"U@ c #ABA89F",
+"V@ c #A5A199",
+"W@ c #948F82",
+"X@ c #686358",
+"Y@ c #302C26",
+"Z@ c #524C3F",
+"`@ c #ACA8A0",
+" # c #AAA8A1",
+".# c #777572",
+"+# c #4F493D",
+"@# c #585245",
+"## c #4F493E",
+"$# c #27241F",
+"%# c #999690",
+"&# c #938E86",
+"*# c #2F2C26",
+"=# c #0C0B09",
+"-# c #2B2721",
+";# c #98938D",
+"># c #A7A39D",
+",# c #2D2922",
+"'# c #8E8A84",
+")# c #ABA7A1",
+"!# c #34322C",
+"~# c #9A9690",
+"{# c #B3B0AB",
+"]# c #2C2922",
+"^# c #8A8780",
+"/# c #33312C",
+"(# c #7D7970",
+"_# c #47433C",
+" . + @ @ # # $ % # # $ $ @ # # @ @ # # @ & ",
+" * = - ; > > , ' ) ! ~ { ! ! ! ! ' ! ! ! ] ^ ",
+" / ( _ : < < [ } | 1 2 3 4 4 4 4 4 4 4 4 5 6 ",
+" / 7 _ 8 9 0 a b c d e f g h a a i j 4 4 5 k ",
+" l m n o p q r s t u v w x y z r A B C d D E ",
+" l F G H I J K L M N O P Q R S T U V W X D E ",
+" / Y Z o p ` ...+.@.#.$.%.&.*.=.-.;.>.,.D E ",
+" / Y '.).p p &.!.~.{.].^./.(._.:.-.<.[.}.D E ",
+" |.1.n 2.3.4.5.6.~.7.8.9.0.a.` b.c.d.>.,.D E ",
+" / 1.e.).&.f.g.h.i.j.k.l.m.n.o.z p.q.r.}.D E ",
+" |.s.t.2.K S u.v.w.x.y.z.A.B.C.q p.D.E.F.G.H. ",
+" I.J.K.L.y z u.M.i.N.O.P.Q.B.R.a.S.T.U.V.G.H. ",
+" |.W.X.Y.R Z.*.h.`. +.+++@+#+$+%+&+*+=+-+D E ",
+" / ;+>+,+` '+)+!+~+{+]+^+/+(+_+:+<+[+}+|+1+E ",
+" |.2+4 4 3+4+5+6+7+8+9+0+a+b+c+d+e+f+g+4 h+i+ ",
+" I.j+k+g+l+m+n+o+p+q+r+s+t+u+v+w+x+y+z+A+B+i+ ",
+" |.C+D+4 g+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+4 5 i+ ",
+" |.C+S+4 T+U+V+W+X+Y+Z+`+ @.@+@@@#@$@%@4 5 i+ ",
+" / C+&@*@&@=@-@;@>@,@'@)@!@~@{@]@^@/@(@_@5 i+ ",
+" |.C+:@<@:@[@}@|@1@2@3@4@5@6@7@8@9@0@a@b@5 i+ ",
+" c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@ ",
+" y@z@A@B@C@ C@D@E@F@G@C@ ",
+" H@I@J@K@ L@M@N@O@P@Q@ ",
+" R@S@T@U@V@W@X@Y@Z@`@ #.# ",
+" +#@###$# %#&#*#=# ",
+" -#;#>#G@=# ",
+" ,#'#)#!#=# ",
+" ,#~#{#G@=# ",
+" ]#^#)#/# ",
+" ]#(#_# ",
+" ",
+" "};
diff --git a/src/stock/plot.xcf b/src/stock/plot.xcf
new file mode 100644
index 0000000..04510f4
--- /dev/null
+++ b/src/stock/plot.xcf
Binary files differ
diff --git a/src/stock/plot.xpm b/src/stock/plot.xpm
new file mode 100644
index 0000000..23c48cb
--- /dev/null
+++ b/src/stock/plot.xpm
@@ -0,0 +1,209 @@
+/* XPM */
+static char * plot_xpm[] = {
+"32 32 174 2",
+" c None",
+". c #141414",
+"+ c #3B3B3B",
+"@ c #404040",
+"# c #545454",
+"$ c #555555",
+"% c #F6F6F6",
+"& c #FFFFFF",
+"* c #FAFAFA",
+"= c #D6D6D6",
+"- c #878787",
+"; c #242424",
+"> c #FEFEFE",
+", c #E6E6E6",
+"' c #F0F0F0",
+") c #A0A0A0",
+"! c #000000",
+"~ c #F8F8F8",
+"{ c #D8D8D8",
+"] c #F5F5F5",
+"^ c #979797",
+"/ c #252525",
+"( c #FDFDFD",
+"_ c #F7F7F7",
+": c #C9C9C9",
+"< c #E0E0E0",
+"[ c #888888",
+"} c #212121",
+"| c #FDFDFC",
+"1 c #FCFCFC",
+"2 c #BFBFBF",
+"3 c #E1E1E1",
+"4 c #C8C8C8",
+"5 c #C2C2C2",
+"6 c #8E8E8E",
+"7 c #FBFBFB",
+"8 c #F1F1F1",
+"9 c #FCFCFB",
+"0 c #F6F6F5",
+"a c #A9A9A9",
+"b c #666666",
+"c c #5D5D5D",
+"d c #565656",
+"e c #5A5A5A",
+"f c #848484",
+"g c #535353",
+"h c #D5D5D5",
+"i c #EAEAEA",
+"j c #C7C7C6",
+"k c #A6A6A6",
+"l c #9A9A9A",
+"m c #6C6C6C",
+"n c #F9F9F9",
+"o c #EEEEEE",
+"p c #DFDFDF",
+"q c #AEAEAE",
+"r c #C4C4C4",
+"s c #DEDEDE",
+"t c #505050",
+"u c #C3C3C3",
+"v c #DDDDDD",
+"w c #F8F8F7",
+"x c #F7F7F6",
+"y c #ECECEB",
+"z c #BEBEBD",
+"A c #DCDCDB",
+"B c #F4F4F3",
+"C c #FEFEFD",
+"D c #EDEDED",
+"E c #C0C0C0",
+"F c #DADADA",
+"G c #F2F2F2",
+"H c #F5F5F4",
+"I c #4E4E4E",
+"J c #C0C0BF",
+"K c #DADAD9",
+"L c #F0F0EF",
+"M c #F3F3F2",
+"N c #E8E8E7",
+"O c #BBBBBA",
+"P c #D7D7D6",
+"Q c #4B6983",
+"R c #E4E4E3",
+"S c #EBEBEA",
+"T c #F2F2F1",
+"U c #3A5165",
+"V c #E7E7E6",
+"W c #BABABA",
+"X c #7590AE",
+"Y c #CFCFCE",
+"Z c #E6E6E5",
+"` c #455668",
+" . c #263440",
+".. c #EEEEED",
+"+. c #C3C3C2",
+"@. c #F1F1F0",
+"#. c #4D4D4D",
+"$. c #6985A1",
+"%. c #CDCDCC",
+"&. c #EDEDEC",
+"*. c #64809D",
+"=. c #3E4F61",
+"-. c #2D3E4E",
+";. c #EFEFEE",
+">. c #EFEFEF",
+",. c #E3E3E3",
+"'. c #64809B",
+"). c #5D7A94",
+"!. c #607C99",
+"~. c #5B7793",
+"{. c #495F75",
+"]. c #CACACA",
+"^. c #E2E2E2",
+"/. c #EBEBEB",
+"(. c #E5E5E4",
+"_. c #68849F",
+":. c #5B7894",
+"<. c #54718C",
+"[. c #D6D6D5",
+"}. c #C2C2C1",
+"|. c #EDEDEB",
+"1. c #4C4C4B",
+"2. c #6C88A2",
+"3. c #5F7B97",
+"4. c #263442",
+"5. c #B5B5B3",
+"6. c #D7D7D5",
+"7. c #EAEAE8",
+"8. c #EAEAE9",
+"9. c #DFDFDE",
+"0. c #1A2026",
+"a. c #67849E",
+"b. c #7692AC",
+"c. c #A2A2A1",
+"d. c #C1C1C0",
+"e. c #E1E1DF",
+"f. c #9D9D9C",
+"g. c #CBCBC9",
+"h. c #E4E4E2",
+"i. c #E6E6E4",
+"j. c #E5E5E3",
+"k. c #C0C0BE",
+"l. c #DADAD8",
+"m. c #E2E2E0",
+"n. c #D5D5D3",
+"o. c #989897",
+"p. c #959594",
+"q. c #494948",
+"r. c #949493",
+"s. c #979795",
+"t. c #969694",
+"u. c #A0A09F",
+"v. c #BABAB9",
+"w. c #F3F3F3",
+"x. c #E3E3E1",
+"y. c #C5C5C3",
+"z. c #B5B5B4",
+"A. c #BCBCBB",
+"B. c #C1C1BF",
+"C. c #D0D0CE",
+"D. c #DBDBD9",
+"E. c #DFDFDD",
+"F. c #D9D9D7",
+"G. c #D2D2D1",
+"H. c #D9D9D8",
+"I. c #D8D8D6",
+"J. c #DEDEDC",
+"K. c #E0E0DE",
+"L. c #E9E9E9",
+"M. c #B6B6B5",
+"N. c #323232",
+"O. c #C5C5C4",
+"P. c #C1C1C1",
+"Q. c #979796",
+". + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # $ ",
+"+ % & & & & & & & & & & & & & & & & & * = - ; ",
+"@ & & & & & & & & & & & > > > > > > > * , ' ) ! ",
+"@ & & & & & & & & & > > > > > > > > > ~ { & ] ^ / ",
+"@ & & & & & & & > > > > > > > > > ( ( _ : ( ( < [ } ",
+"@ & & & & > ( > > > > > > ( ( ( | 1 1 % 2 3 = 4 5 6 ",
+"@ & & & 7 ! 8 ~ ( > > > ( ( | 1 1 1 9 0 a b c d e f ! ",
+"@ & & & g ! h i 7 > ( ( 1 1 1 1 9 * * _ ' = j k l m ! ",
+"@ & * n o ! 5 p % * * * * * * * * * * * * * * * * q ! ",
+"@ & * * 8 ! r s % * * * * * * * * * * n * * * * * 5 ! ",
+"@ > n n t ! u v ] n n n n n n n n n n n n n n n n r ! ",
+"@ > w x y ! z A B w w w w w w w w w w w w w w w w r ! ",
+"@ C % % D ! E F G % % % % % % % % % % % % % % % % r ! ",
+"@ ( H H I ! J K L M B H H H H H B M M H H H H H H u ! ",
+"@ ( B M N ! O P Q R S T B B B T U R S L B B B B B u ! ",
+"@ 1 T T V ! W Q X Q Y Z L T L Q ` .Y Z ..T T T T +.! ",
+"@ 1 @.@.#.! Q X $.Q Q %.R &.Q X *.=.-.%.R ..;.L @.+.! ",
+"@ 1 >.o ,.! X $.'.).Q Q : Q X !.~.{.U -.].^./.o >.5 ! ",
+"@ 7 ....(.! $.'._.).Q Q Q X ~.:.<.Q U U -.Q [.Z &.}.! ",
+"@ * |.|.1.! '.'.2.Q Q ).).*.3.Q <.Q Q U U 4.5.6.7.}.! ",
+"@ x S 8.9.0.a.<.Q <.).b.2.3.<.<.Q Q Q Q Q Q c.Y V d.! ",
+"@ 0 7.7.e.! Q Q Q Q Q Q Q Q <.Q Q Q Q Q Q Q f.g.h.E ! ",
+"@ B i.j.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! k.l.J ! ",
+"@ B h.m.n.! o.p.q.r.s.q.r.s.q.r.s.q.r.s.q.t.u.v.n.J ! ",
+"@ w.x.m.K y.z.O k.A.B.B.A.B.B.A.B.B.A.B.B.A.+.C.D.J ! ",
+"@ M e.e.E.F.G.6.H.I.H.H.I.H.H.I.H.H.I.H.H.I.l.J.K.J ! ",
+"+ L.e.e.e.K.E.K.e.K.e.e.K.e.e.K.e.e.K.e.e.K.e.e.e.M.! ",
+"N.O.P.P.d.d.d.d.d.d.d.d.E E E E E E E E E E E E M.Q.! ",
+" ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ",
+" ",
+" ",
+" "};
diff --git a/src/stock/plot2.xcf b/src/stock/plot2.xcf
new file mode 100644
index 0000000..1071ccf
--- /dev/null
+++ b/src/stock/plot2.xcf
Binary files differ
diff --git a/src/stock/rotate.xpm b/src/stock/rotate.xpm
new file mode 100644
index 0000000..78b2090
--- /dev/null
+++ b/src/stock/rotate.xpm
@@ -0,0 +1,62 @@
+/* XPM */
+static char * rotate_xpm[] = {
+"16 16 43 1",
+" c None",
+". c #000000",
+"+ c #F3F3F5",
+"@ c #ECECEE",
+"# c #939CA2",
+"$ c #8D959C",
+"% c #E0E0E1",
+"& c #87929D",
+"* c #8A95A0",
+"= c #9299A1",
+"- c #D2D2D3",
+"; c #8F99A4",
+"> c #939DA8",
+", c #A2AAB0",
+"' c #CED0D1",
+") c #ECEEEE",
+"! c #97A2AC",
+"~ c #9CA6B0",
+"{ c #A9AFB6",
+"] c #CFD0D2",
+"^ c #A0A5AB",
+"/ c #A0AAB4",
+"( c #A3ADB7",
+"_ c #AFB5BB",
+": c #C3C4C5",
+"< c #3A3A3A",
+"[ c #A8AEB2",
+"} c #A7B1BA",
+"| c #AAB4BD",
+"1 c #B3BAC0",
+"2 c #939393",
+"3 c #666666",
+"4 c #AFB3B6",
+"5 c #AEB8C1",
+"6 c #8D8D8D",
+"7 c #B2B6BB",
+"8 c #ABB4BD",
+"9 c #909090",
+"0 c #B5BAC0",
+"a c #B2BBC3",
+"b c #959595",
+"c c #B8B8B8",
+"d c #9C9C9C",
+" ",
+" .. ",
+" . ",
+" .. . ",
+" .++. ... ",
+" .@#$%. . ",
+" .@#&*=-. ",
+" .@#&*;>,'. ",
+".)#&*;>!~{]. ",
+" .^*;>!~/(_:. ",
+" <[>!~/(}|12. ",
+" 34~/(}|56. ",
+" <78}|59. ",
+" .0a5b. ",
+" .cd. ",
+" .. "};
diff --git a/src/stock/sim-settings.xpm b/src/stock/sim-settings.xpm
new file mode 100644
index 0000000..3bcfe43
--- /dev/null
+++ b/src/stock/sim-settings.xpm
@@ -0,0 +1,187 @@
+/* XPM */
+static char * sim_settings_xpm[] = {
+"24 24 160 2",
+" c None",
+". c #000000",
+"+ c #E5E5E5",
+"@ c #CECECE",
+"# c #CDCDCD",
+"$ c #DCCB94",
+"% c #DCDCDC",
+"& c #C7C7C7",
+"* c #8E7D45",
+"= c #CFCFCF",
+"- c #C1C1C1",
+"; c #B2A97E",
+"> c #CCC9B7",
+", c #BAB7AB",
+"' c #898989",
+") c #E3E3DF",
+"! c #AB5959",
+"~ c #D9D9D9",
+"{ c #878787",
+"] c #DBDBDB",
+"^ c #B0A77C",
+"/ c #B5AC80",
+"( c #4A4634",
+"_ c #CAC6B4",
+": c #CEC9B4",
+"< c #CECBBA",
+"[ c #A84F4F",
+"} c #A7A7A7",
+"| c #9B9B9B",
+"1 c #B7AE81",
+"2 c #BDB486",
+"3 c #343124",
+"4 c #CFCCBB",
+"5 c #F5F5F5",
+"6 c #F3F3F3",
+"7 c #A65757",
+"8 c #6E6E6E",
+"9 c #ADA57B",
+"0 c #B4AC80",
+"a c #7C7557",
+"b c #4C4836",
+"c c #D0CCB9",
+"d c #CBBEAC",
+"e c #A35252",
+"f c #C48B8B",
+"g c #AA6C6C",
+"h c #B1A87D",
+"i c #B9B082",
+"j c #BEB586",
+"k c #4C4936",
+"l c #867F7F",
+"m c #A65353",
+"n c #633434",
+"o c #CFCBBA",
+"p c #D0CCBB",
+"q c #332D18",
+"r c #C6C6C6",
+"s c #7A7356",
+"t c #BAB183",
+"u c #7F7F7F",
+"v c #D1CEBB",
+"w c #D3D0BE",
+"x c #D4D0BE",
+"y c #4B4735",
+"z c #7F7554",
+"A c #554B29",
+"B c #494533",
+"C c #2B2B2B",
+"D c #E4E4E4",
+"E c #D6D6D0",
+"F c #C0C0BB",
+"G c #828279",
+"H c #4A4735",
+"I c #D7D4C1",
+"J c #D8D4C1",
+"K c #D9D5C1",
+"L c #E2DFCB",
+"M c #B7A155",
+"N c #96854B",
+"O c #504C38",
+"P c #4E4B37",
+"Q c #C3BFA9",
+"R c #AEAA90",
+"S c #63635B",
+"T c #A7A79A",
+"U c #818174",
+"V c #D4D0BC",
+"W c #DAD6C1",
+"X c #D9D6C1",
+"Y c #D4D6C1",
+"Z c #58543E",
+"` c #54503B",
+" . c #B5B194",
+".. c #B3B093",
+"+. c #B4B095",
+"@. c #64645C",
+"#. c #C8C8C8",
+"$. c #C2B889",
+"%. c #504D39",
+"&. c #DDD9C4",
+"*. c #B9B598",
+"=. c #A9A587",
+"-. c #A19D80",
+";. c #C0C1AA",
+">. c #D7D3BA",
+",. c #D6D2BA",
+"'. c #D5D1B9",
+"). c #828282",
+"!. c #C2B98A",
+"~. c #514D39",
+"{. c #D2CEBA",
+"]. c #DFDBC5",
+"^. c #D3CFB9",
+"/. c #C0BDB0",
+"(. c #DFDBC6",
+"_. c #DEDAC5",
+":. c #DCD8C4",
+"<. c #C8C4B0",
+"[. c #524F3A",
+"}. c #BBB284",
+"|. c #CAC18F",
+"1. c #534F3B",
+"2. c #BCB9AD",
+"3. c #D3D0BD",
+"4. c #BDBBAD",
+"5. c #524E3A",
+"6. c #69644A",
+"7. c #BCB385",
+"8. c #C3BA8A",
+"9. c #CAC08F",
+"0. c #534F3A",
+"a. c #D8D4C0",
+"b. c #DCD9C4",
+"c. c #BBB8AC",
+"d. c #DAD6C2",
+"e. c #CCC8B3",
+"f. c #CCC290",
+"g. c #C4BA8A",
+"h. c #C8BE8D",
+"i. c #CDC491",
+"j. c #DDD9C5",
+"k. c #D4CFB6",
+"l. c #6E684D",
+"m. c #CEC592",
+"n. c #C5BC8C",
+"o. c #CEC491",
+"p. c #CFC592",
+"q. c #C1B888",
+"r. c #BEB486",
+"s. c #C6BC8C",
+"t. c #C8BF8D",
+"u. c #C9C08F",
+"v. c #C9BF8E",
+"w. c #C7BE8D",
+"x. c #C5BB8B",
+"y. c #C0B788",
+"z. c #B2A97D",
+"A. c #C2B989",
+"B. c #BFB687",
+"C. c #B3AA7E",
+" ",
+" . . ",
+" . . + @ . . ",
+" . . # + + + . . $ . ",
+" . . . % + + + + + & . . $ * . ",
+" . . . . # + + + + = + - + + . . $ * . ",
+" . ; . > , ' ) + ! + ~ + = { + ] . $ * . ",
+". ^ / ( _ : < ' + [ } + | { + + . $ * . ",
+". . 1 2 3 4 ' 5 6 7 + 8 + + + . $ * . ",
+". 9 0 a b c d e f g + ' ' + . $ * . ~ . ",
+". . h i j k + l m n o 4 p . $ q . = + r . ",
+" . s . t s u v ' + w x y z A B C D E F G . ",
+" . . . H I J K L M N O P Q R S T T U . ",
+" . V W X Y Z ` ...+.@.T T #.. ",
+" . $.%.&.*.=.-.;.>.,.'.' ).. . ",
+" . i !.~.{.].^./.K (._.:.<.[.. ",
+" . }.!.|.1._.2.3.4.(._.5.6.j . ",
+" . 7.8.9.0.a.b.c.d.e.6.f.g.}.. ",
+" . . h.i.` j.k.0.l.m.h.. . ",
+" . n.9.o.[.0.p.f.h.q.. ",
+" . r.8.s.t.u.v.w.x.y.}.z.. ",
+" . j . . A.$.B.. . C.. ",
+" . . }.t 1 . . ",
+" . . . "};
diff --git a/src/stock/text.xpm b/src/stock/text.xpm
new file mode 100644
index 0000000..e2d19ff
--- /dev/null
+++ b/src/stock/text.xpm
@@ -0,0 +1,30 @@
+/* XPM */
+static char * text_xpm[] = {
+"24 24 3 1",
+" c None",
+". c #000000",
+"+ c #777777",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ........... ",
+" ..++...++..+ ",
+" .++ ...+ .+ ",
+" + ...+ + ",
+" +..+ ",
+" +..+ ",
+" +..+ ",
+" ...+ ",
+" ..+ ",
+" +..+ ",
+" +..+ ",
+" ...+ ",
+" ..... ",
+" +++++ ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/src/stock/vclamp.xpm b/src/stock/vclamp.xpm
new file mode 100644
index 0000000..9629725
--- /dev/null
+++ b/src/stock/vclamp.xpm
@@ -0,0 +1,88 @@
+/* XPM */
+static char * vclamp_xpm[] = {
+"24 24 61 1",
+" c None",
+". c #000000",
+"+ c #2C2C2C",
+"@ c #3F4040",
+"# c #535353",
+"$ c #101010",
+"% c #262625",
+"& c #404040",
+"* c #595A5A",
+"= c #70706F",
+"- c #1A1A1A",
+"; c #656666",
+"> c #7F7F7F",
+", c #8F8F8F",
+"' c #2C2C2B",
+") c #262526",
+"! c #3F403F",
+"~ c #7F7F80",
+"{ c #9A9999",
+"] c #A5A5A5",
+"^ c #ABACAC",
+"/ c #403F40",
+"( c #40403F",
+"_ c #80807F",
+": c #BFBFBF",
+"< c #BFC0BF",
+"[ c #C0BFBF",
+"} c #535453",
+"| c #5A5A5A",
+"1 c #656566",
+"2 c #FFFFFF",
+"3 c #E5E5E5",
+"4 c #DAD9DA",
+"5 c #D3D3D4",
+"6 c #706F6F",
+"7 c #808080",
+"8 c #EFEFEF",
+"9 c #FF003C",
+"0 c #908F90",
+"a c #A5A5A6",
+"b c #D9D9D9",
+"c c #2B2A2A",
+"d c #555555",
+"e c #ACACAC",
+"f c #D3D3D3",
+"g c #070707",
+"h c #030303",
+"i c #2B2B2B",
+"j c #010101",
+"k c #020202",
+"l c #2B2B2A",
+"m c #807F80",
+"n c #AAAAAA",
+"o c #2A2A2B",
+"p c #D4D5D4",
+"q c #2A2A2A",
+"r c #807F7F",
+"s c #D4D4D4",
+"t c #7F8080",
+"u c #D5D4D5",
+"v c #D5D4D4",
+" ",
+" ",
+" ..... ",
+" ..+@#.. ",
+" ..$%&*=.. ",
+" ..$.-&;>,.. ",
+" .')-.!~{]^. ",
+" .@/(&_:<[:. ",
+" .}|1_:2345. ",
+" ...67{:328.. ",
+" ..99.0a:b8..99 ",
+" ..cd99.e:fgh9922",
+" ..id7992jkk. 992 ",
+" ..ldmn992 992 ",
+" ..od>np299 9922 ",
+" ..qdrnp2.992 992 ",
+" ..ldmns2. 99 9922 ",
+" ..dtnu2. 992 992 ",
+" ....nv2. 992 992 ",
+" .....2. 999922 ",
+" ...... 99992 ",
+" ... 9922 ",
+" 992 ",
+" 22 "};
diff --git a/src/stock/voltmeter.xpm b/src/stock/voltmeter.xpm
new file mode 100644
index 0000000..369f2b1
--- /dev/null
+++ b/src/stock/voltmeter.xpm
@@ -0,0 +1,30 @@
+/* XPM */
+static char * voltmeter_xpm[] = {
+"24 24 3 1",
+" c None",
+". c #000000",
+"+ c #5373E8",
+" ",
+" ",
+" ",
+" ",
+" . . ",
+" + . . ",
+" + . . ",
+" + . . ",
+" + . . ",
+" + . ",
+" + ",
+" + ",
+" . ",
+" ... ",
+" +++.....+++++ ",
+" ... ",
+" . ",
+" + ",
+" + ",
+" + ",
+" + ",
+" ",
+" ",
+" "};
diff --git a/src/stock/wire.xpm b/src/stock/wire.xpm
new file mode 100644
index 0000000..4e205bc
--- /dev/null
+++ b/src/stock/wire.xpm
@@ -0,0 +1,184 @@
+/* XPM */
+static char * wire_xpm[] = {
+"32 31 150 2",
+" c None",
+". c #E0B5AE",
+"+ c #DEB2AA",
+"@ c #D39F90",
+"# c #C88974",
+"$ c #BD7659",
+"% c #E1B6AF",
+"& c #DFB3AC",
+"* c #D9A79C",
+"= c #CD9280",
+"- c #C17C63",
+"; c #B56848",
+"> c #DCAEA4",
+", c #D19A8A",
+"' c #C6846E",
+") c #BA6F51",
+"! c #AF5B36",
+"~ c #A64B21",
+"{ c #DDB0A7",
+"] c #D5A294",
+"^ c #CA8C78",
+"/ c #BE775C",
+"( c #B26240",
+"_ c #A74D24",
+": c #A14216",
+"< c #E6C28E",
+"[ c #DAB08C",
+"} c #CE9482",
+"| c #C27F66",
+"1 c #B66A4A",
+"2 c #AB552E",
+"3 c #9F4013",
+"4 c #9C3A0B",
+"5 c #F3E538",
+"6 c #DEDE05",
+"7 c #CAC80C",
+"8 c #C5B11D",
+"9 c #B27B28",
+"0 c #A24C1A",
+"a c #9D3C0A",
+"b c #9C3909",
+"c c #F5F600",
+"d c #D5D900",
+"e c #C9CE00",
+"f c #C3C501",
+"g c #A59208",
+"h c #945709",
+"i c #9E5A05",
+"j c #0000FF",
+"k c #FEFE00",
+"l c #E9EC00",
+"m c #CED300",
+"n c #CACF00",
+"o c #B7BB00",
+"p c #8F8F00",
+"q c #857600",
+"r c #9F7602",
+"s c #FDFD00",
+"t c #DCDF00",
+"u c #A7AA00",
+"v c #828300",
+"w c #8F9000",
+"x c #ABAC00",
+"y c #F7F700",
+"z c #D0D400",
+"A c #929400",
+"B c #7A7A00",
+"C c #9B9C00",
+"D c #B4B500",
+"E c #FCFC00",
+"F c #ECEE00",
+"G c #C5CA00",
+"H c #7B7C00",
+"I c #787800",
+"J c #DFE200",
+"K c #C6CB00",
+"L c #AEB200",
+"M c #797900",
+"N c #808000",
+"O c #FFFF00",
+"P c #EEF000",
+"Q c #BFC400",
+"R c #9A9C00",
+"S c #818100",
+"T c #8C8D00",
+"U c #E3E600",
+"V c #B4B700",
+"W c #898A00",
+"X c #A3A600",
+"Y c #7C7D00",
+"Z c #9C9D00",
+"` c #F4F600",
+" . c #CCD100",
+".. c #C3C800",
+"+. c #919300",
+"@. c #ADAF00",
+"#. c #FBFB00",
+"$. c #E6E900",
+"%. c #B4B800",
+"&. c #838400",
+"*. c #7E7F00",
+"=. c #F4F400",
+"-. c #D8D900",
+";. c #C9CB00",
+">. c #C9CD00",
+",. c #A0A300",
+"'. c #DCB400",
+"). c #C3A000",
+"!. c #C1AA00",
+"~. c #C2B900",
+"{. c #8B8C00",
+"]. c #D89F00",
+"^. c #D29700",
+"/. c #B88300",
+"(. c #B58300",
+"_. c #917A00",
+":. c #8F8E00",
+"<. c #D59800",
+"[. c #CC9200",
+"}. c #B58100",
+"|. c #A87800",
+"1. c #9F7700",
+"2. c #9D9600",
+"3. c #C78E00",
+"4. c #B78200",
+"5. c #AF7C00",
+"6. c #A27300",
+"7. c #A47800",
+"8. c #AF8E00",
+"9. c #C18A00",
+"0. c #B58000",
+"a. c #A77700",
+"b. c #A37400",
+"c. c #BB8500",
+"d. c #AE7C00",
+"e. c #AD7B00",
+"f. c #9B6F40",
+"g. c #986C28",
+"h. c #A57501",
+"i. c #3626BC",
+"j. c #402DA1",
+"k. c #775446",
+"l. c #1811E0",
+"m. c #291DC1",
+"n. c #4E00B0",
+"o. c #4B1DA2",
+"p. c #C90035",
+"q. c #FF0000",
+"r. c #E60017",
+"s. c #9A274F",
+" ",
+" ",
+" . + @ # $ ",
+" % & * = - ; ",
+" . > , ' ) ! ~ ",
+" . { ] ^ / ( _ : ",
+" < [ } | 1 2 3 4 ",
+" 5 6 7 8 9 0 a b ",
+" c d e f g h i ",
+" j j j j j j j k l m n o p q r ",
+" j s t n n u v w x ",
+" j k y z n n A B C D ",
+" j E F n e G H I ",
+" j y J n K L M N ",
+" j O P d n Q R S T ",
+" j k U m n V W T ",
+" j O d n n X Y Z ",
+" j k ` .n ..+.M @. ",
+" j #.$.n n %.&.*. ",
+" j =.-.;.>.,.H *. ",
+" j '.).!.~.{.N ",
+" j ].^././.(._.:. ",
+" j <.[./.}.|.1.2. ",
+" j <.3.4.5.6.7.8. ",
+" j <.9.0.a.b. ",
+" j <.c.d.b.e. ",
+" j f.g.h.|. ",
+" j i.j.k. ",
+" j l.m. ",
+" n. o. ",
+" p. q. q. r.s. "};
diff --git a/src/stock/zoom_in.xpm b/src/stock/zoom_in.xpm
new file mode 100644
index 0000000..b78f85e
--- /dev/null
+++ b/src/stock/zoom_in.xpm
@@ -0,0 +1,36 @@
+/* XPM */
+static char * zoom_in_xpm[] = {
+"24 24 9 1",
+" c None",
+". c #020204",
+"+ c #B5B5B6",
+"@ c #D0D0D1",
+"# c #9A9A98",
+"$ c #E8E8E9",
+"% c #8F8F91",
+"& c #6E6E6E",
+"* c #5A5A5C",
+" ",
+" ",
+" ",
+" ... ",
+" ..+@+.. ",
+" .#@$$$@#. ",
+" .%$$$.#$+&. ",
+" .@$$$.#$@#. ",
+" .+$$$$.+$$#&. ",
+" .@$.......@&. ",
+" .+$##+.&%&@*. ",
+" .@$$$.&#$&. ",
+" .#+@@.&$%*. ",
+" ..%#+#@%*. ",
+" .#...&&*.. ",
+" ..#. ... ",
+" .$.. ",
+" .$... ",
+" .$... ",
+" .#.. ",
+" .. ",
+" ",
+" ",
+" "};
diff --git a/src/stock/zoom_out.xpm b/src/stock/zoom_out.xpm
new file mode 100644
index 0000000..8923760
--- /dev/null
+++ b/src/stock/zoom_out.xpm
@@ -0,0 +1,36 @@
+/* XPM */
+static char * zoom_out_xpm[] = {
+"24 24 9 1",
+" c None",
+". c #020204",
+"+ c #B5B5B6",
+"@ c #D0D0D1",
+"# c #9A9A98",
+"$ c #E8E8E9",
+"% c #8F8F91",
+"& c #6E6E6E",
+"* c #5A5A5C",
+" ",
+" ",
+" ",
+" ... ",
+" ..+@+.. ",
+" .#@$$$@#. ",
+" .%$$$$$$+&. ",
+" .@$$$$$$@#. ",
+" .+$$$$$$$@#&. ",
+" .@$.......+&. ",
+" .+$@####%&+*. ",
+" .@$$$@+#$&. ",
+" .#+@@+#$%*. ",
+" ..%#+#@%*. ",
+" .#...&&*.. ",
+" ..#. ... ",
+" .$.. ",
+" .$... ",
+" .$... ",
+" .#.. ",
+" .. ",
+" ",
+" ",
+" "};
diff --git a/src/stock/zoom_pan.xpm b/src/stock/zoom_pan.xpm
new file mode 100644
index 0000000..b609067
--- /dev/null
+++ b/src/stock/zoom_pan.xpm
@@ -0,0 +1,270 @@
+/* XPM */
+static char * zoom_pan_xpm[] = {
+"24 24 243 2",
+" c None",
+". c #000000",
+"+ c #0E0E0E",
+"@ c #282828",
+"# c #616161",
+"$ c #D3D3D3",
+"% c #F6F6F6",
+"& c #FFFFFF",
+"* c #F9F9F9",
+"= c #F9F9F8",
+"- c #E7E7E7",
+"; c #E4E4E4",
+"> c #CDCDCD",
+", c #1F1F1F",
+"' c #EFEFEE",
+") c #AFAFAE",
+"! c #E9E9E9",
+"~ c #B3B3B3",
+"{ c #383838",
+"] c #FEFEFE",
+"^ c #FDFDFD",
+"/ c #EEEEEE",
+"( c #A4A4A3",
+"_ c #F0F0F0",
+": c #F4F4F4",
+"< c #ABABAB",
+"[ c #050505",
+"} c #FCFCFB",
+"| c #FBFBFB",
+"1 c #F2F2F2",
+"2 c #9E9E9E",
+"3 c #FAFAF9",
+"4 c #F8F8F7",
+"5 c #F7F7F6",
+"6 c #D3D3D2",
+"7 c #B9B7B6",
+"8 c #838381",
+"9 c #848381",
+"0 c #6A6763",
+"a c #F6F6F5",
+"b c #8F8F8E",
+"c c #9F9E9D",
+"d c #D8D8D8",
+"e c #D9D7D5",
+"f c #C8C8C6",
+"g c #B4B4B2",
+"h c #716F6B",
+"i c #8F8F8F",
+"j c #878686",
+"k c #EEEDEC",
+"l c #C3BEA2",
+"m c #A29C86",
+"n c #D6D5D3",
+"o c #30302F",
+"p c #6C6C6A",
+"q c #7C7970",
+"r c #A6A48D",
+"s c #D7D4B7",
+"t c #979796",
+"u c #E9E2BF",
+"v c #D1C8AB",
+"w c #606060",
+"x c #636050",
+"y c #E8E1BD",
+"z c #7C7870",
+"A c #F8F7F6",
+"B c #F5F4F3",
+"C c #C6C4A9",
+"D c #DAD7B9",
+"E c #666666",
+"F c #1C1B1B",
+"G c #E7DFBC",
+"H c #D0C5A8",
+"I c #1E1E1E",
+"J c #C0B798",
+"K c #CFC7A5",
+"L c #0D0D0D",
+"M c #88837A",
+"N c #F6F5F4",
+"O c #F5F5F3",
+"P c #F2F1F0",
+"Q c #737262",
+"R c #615F52",
+"S c #EDEACA",
+"T c #D1CCB1",
+"U c #E7DCBB",
+"V c #CFC3A5",
+"W c #5C5849",
+"X c #E4D9B2",
+"Y c #8C846D",
+"Z c #131313",
+"` c #F4F3F2",
+" . c #F3F2F1",
+".. c #F0F0EE",
+"+. c #E2E2E0",
+"@. c #A5A38D",
+"#. c #DED8BD",
+"$. c #96907D",
+"%. c #E6DBBA",
+"&. c #BCB192",
+"*. c #E1D3AD",
+"=. c #514B3D",
+"-. c #040404",
+";. c #847B66",
+">. c #E4D8B5",
+",. c #E8E8E6",
+"'. c #575756",
+"). c #424141",
+"!. c #EBE4C7",
+"~. c #D3CDB1",
+"{. c #CFC2A4",
+"]. c #E1D3AC",
+"^. c #CAB997",
+"/. c #6B6350",
+"(. c #E0CCA9",
+"_. c #6C6654",
+":. c #898988",
+"<. c #D5D4D3",
+"[. c #C4BEA6",
+"}. c #EAE3C4",
+"|. c #E8E1C0",
+"1. c #E7DDBB",
+"2. c #DCCEAF",
+"3. c #E2D1AF",
+"4. c #DFCEA8",
+"5. c #C9B894",
+"6. c #DEC7A3",
+"7. c #C6B394",
+"8. c #ECECEA",
+"9. c #EEEBCB",
+"0. c #7D7B6A",
+"a. c #4F4F4E",
+"b. c #504D43",
+"c. c #EAE2C4",
+"d. c #BBB49A",
+"e. c #E8DDBE",
+"f. c #C0B59F",
+"g. c #E2D2B1",
+"h. c #DFCDA7",
+"i. c #D6C39C",
+"j. c #DCC49F",
+"k. c #CBB494",
+"l. c #81745F",
+"m. c #E9E8E6",
+"n. c #EDEACB",
+"o. c #868273",
+"p. c #EBE4C9",
+"q. c #9B988F",
+"r. c #EBE1C7",
+"s. c #918C82",
+"t. c #DFD2B6",
+"u. c #9F998C",
+"v. c #CFB997",
+"w. c #D4B998",
+"x. c #C4B08F",
+"y. c #E5E4E1",
+"z. c #747472",
+"A. c #D9D3BA",
+"B. c #8C8977",
+"C. c #EEE6CF",
+"D. c #A5A49E",
+"E. c #D6CEB6",
+"F. c #9C9993",
+"G. c #DFCEB3",
+"H. c #99958D",
+"I. c #D5C0A1",
+"J. c #C7B08F",
+"K. c #776C57",
+"L. c #E3E2DF",
+"M. c #DAD9D6",
+"N. c #3B3B3A",
+"O. c #959380",
+"P. c #ECE6CA",
+"Q. c #D2CCB3",
+"R. c #DAD4BC",
+"S. c #EDE6CC",
+"T. c #929088",
+"U. c #D4CAAD",
+"V. c #A6A29C",
+"W. c #D1BF9F",
+"X. c #918A7C",
+"Y. c #B29E80",
+"Z. c #B19F7F",
+"`. c #57544E",
+" + c #EDEDED",
+".+ c #E1E0DD",
+"++ c #DDDBD8",
+"@+ c #D2D1CE",
+"#+ c #636155",
+"$+ c #ECE7CA",
+"%+ c #D9D2B9",
+"&+ c #EBE5C7",
+"*+ c #C1BBA5",
+"=+ c #E7DDBC",
+"-+ c #BEBAB2",
+";+ c #CCBA9B",
+">+ c #776B57",
+",+ c #B29F80",
+"'+ c #51483A",
+")+ c #7B776D",
+"!+ c #D1D1D1",
+"~+ c #E1DFDB",
+"{+ c #DFDDD9",
+"]+ c #D9D8D4",
+"^+ c #8D8C8A",
+"/+ c #212121",
+"(+ c #C5BFA8",
+"_+ c #E7DCBD",
+":+ c #CBC2B0",
+"<+ c #CAB99A",
+"[+ c #C4B290",
+"}+ c #9F9174",
+"|+ c #8A8885",
+"1+ c #807B72",
+"2+ c #999891",
+"3+ c #A39E92",
+"4+ c #A49E93",
+"5+ c #A29C91",
+"6+ c #9D978B",
+"7+ c #79746C",
+"8+ c #252421",
+"9+ c #1B1A17",
+"0+ c #D8D3B7",
+"a+ c #E8DCBE",
+"b+ c #E6D8B9",
+"c+ c #CBBD9A",
+"d+ c #B7A787",
+"e+ c #A29677",
+"f+ c #79756C",
+"g+ c #5B574E",
+"h+ c #D2CDB2",
+"i+ c #E7DEBD",
+"j+ c #E5D7B6",
+"k+ c #C1B395",
+"l+ c #AEA183",
+"m+ c #A69A7B",
+"n+ c #C3BFA3",
+"o+ c #C2BB9F",
+"p+ c #C1B69B",
+"q+ c #ADA288",
+"r+ c #7E7761",
+"s+ c #7D7760",
+"t+ c #26241E",
+" . . . . . . . . . . . . . + @ # ",
+" . $ % & & & & & & & & * = = - ; > , ",
+" . % & & & & & & & & & & = ' ) ! & ~ { ",
+" . & & & & & & & ] & ] ^ = / ( _ & : < [ ",
+" . & & & & & ] ^ } ^ } | = 1 2 . . . . . ",
+" . & & & ] ^ } | 3 | 3 4 5 1 6 7 8 9 0 . ",
+" . & ] ^ } | = 5 a 4 b . . c d e f g h . ",
+" . & } | 3 i . . j k . l m . n . o p q . ",
+" . & 3 = 5 . r s . t . u v . w x y . z . ",
+" . & 4 A B . C D E F . G H . I J K L M . ",
+" . & N O P Q R S T . . U V . W X Y Z . . . ",
+" . & ` ...+.. @.#.$.. %.V . &.*.=.-.;.>.. ",
+" . & ` ...,.'.).!.~.. %.{.. ].^.. /.(._.. ",
+" . & ..:.. . <.. [.}.|.1.2.3.4.5.. 6.7.. . ",
+" . & 8.. 9.0.. a.b.c.d.e.f.g.h.i.j.k.l.. ",
+" . & m.. 9.n.o.. . p.q.r.s.t.u.v.w.x.. . ",
+" . & y.z.. n.A.B.Z C.D.E.F.G.H.I.J.K.. . ",
+" . * L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.. `.. ",
+" . +.+++@+. #+$+%+&+*+=+-+;+>+,+'+. )+. ",
+" . !+~+{+]+^+. /+(+&+}._+:+<+[+}+. |+1+. ",
+" . 2+3+4+5+6+7+8+9+0+c.a+b+c+d+e+. f+g+. ",
+" . . . . . . . . h+}.i+j+k+l+m+. . . . ",
+" . r n+o+p+q+r+s+t+ ",
+" . "};
diff --git a/src/stock/zoom_region.xpm b/src/stock/zoom_region.xpm
new file mode 100644
index 0000000..f5a7869
--- /dev/null
+++ b/src/stock/zoom_region.xpm
@@ -0,0 +1,229 @@
+/* XPM */
+static char * zoom_region_xpm[] = {
+"24 24 202 2",
+" c None",
+". c #343434",
+"+ c #2D2D2D",
+"@ c #292929",
+"# c #262626",
+"$ c #2E2E2E",
+"% c #303030",
+"& c #737373",
+"* c #A1A1A1",
+"= c #B4B4B4",
+"- c #B2B2B2",
+"; c #9D9D9D",
+"> c #676767",
+", c #202020",
+"' c #1C1C1C",
+") c #272727",
+"! c #616161",
+"~ c #CACACA",
+"{ c #CFCFCF",
+"] c #D0D0D0",
+"^ c #CECECE",
+"/ c #C9C9C9",
+"( c #C1C1C1",
+"_ c #A7A7A7",
+": c #4C4C4C",
+"< c #131313",
+"[ c #222222",
+"} c #757575",
+"| c #D3D3D3",
+"1 c #DBDBDB",
+"2 c #E7E7E7",
+"3 c #EFEFEF",
+"4 c #F3F3F3",
+"5 c #F1F1F1",
+"6 c #E5E5E5",
+"7 c #D2D2D2",
+"8 c #BCBCBC",
+"9 c #5E5E5E",
+"0 c #101010",
+"a c #212121",
+"b c #5B5B5B",
+"c c #CCCCCC",
+"d c #D7D7D7",
+"e c #F5F5F5",
+"f c #FAFAFA",
+"g c #FBFBFB",
+"h c #F8F8F8",
+"i c #F0F0F0",
+"j c #E1E1E1",
+"k c #C2C2C2",
+"l c #434343",
+"m c #0F0F0F",
+"n c #1D1D1D",
+"o c #3F3F3F",
+"p c #444444",
+"q c #4A4A4A",
+"r c #4B4B4B",
+"s c #FDFDFD",
+"t c #FCFCFC",
+"u c #F2F2F2",
+"v c #ECECEC",
+"w c #E4E4E4",
+"x c #ABABAB",
+"y c #0E0E0E",
+"z c #1B1B1B",
+"A c #6A6963",
+"B c #C8C1AB",
+"C c #D7CFB7",
+"D c #EAE2C8",
+"E c #EEE7CB",
+"F c #F1E0A4",
+"G c #FEFEFE",
+"H c #F9F9F9",
+"I c #EEEEEE",
+"J c #E6E6E6",
+"K c #575757",
+"L c #090909",
+"M c #141414",
+"N c #A2997B",
+"O c #CEBF8C",
+"P c #E2D39A",
+"Q c #EAD99F",
+"R c #EDDCA1",
+"S c #F0DFA3",
+"T c #F4F4F4",
+"U c #DCDCDC",
+"V c #9B9B9B",
+"W c #060606",
+"X c #111111",
+"Y c #BCAF84",
+"Z c #D4C591",
+"` c #E3D49B",
+" . c #E9D99F",
+".. c #ECDBA0",
+"+. c #EFDEA3",
+"@. c #6F6C5E",
+"#. c #818B94",
+"$. c #6D695C",
+"%. c #EDEDED",
+"&. c #BFBFBF",
+"*. c #BCAF80",
+"=. c #D8C993",
+"-. c #E0D199",
+";. c #E6D69D",
+">. c #A7B7C7",
+",. c #484848",
+"'. c #C8C8C8",
+"). c #EAEAEA",
+"!. c #E0E0E0",
+"~. c #BABABA",
+"{. c #050505",
+"]. c #0B0B0B",
+"^. c #9D936E",
+"/. c #D6C792",
+"(. c #DCCD96",
+"_. c #E1D29A",
+":. c #E5D59C",
+"<. c #E8D89E",
+"[. c #494949",
+"}. c #5C5C5C",
+"|. c #7F8892",
+"1. c #96A8BC",
+"2. c #818F9E",
+"3. c #7E7E7E",
+"4. c #D1D1D1",
+"5. c #939393",
+"6. c #020202",
+"7. c #0A0A0A",
+"8. c #5B5746",
+"9. c #CFC08D",
+"0. c #DBCC95",
+"a. c #DFD098",
+"b. c #474747",
+"c. c #A1B0C1",
+"d. c #93A4B8",
+"e. c #90A2B5",
+"f. c #B6B6B6",
+"g. c #D4D4D4",
+"h. c #000000",
+"i. c #090908",
+"j. c #AB9F74",
+"k. c #D1C38E",
+"l. c #D7C892",
+"m. c #DACB94",
+"n. c #778088",
+"o. c #8FA0B3",
+"p. c #8C9DB0",
+"q. c #8A9AAD",
+"r. c #768290",
+"s. c #CBCBCB",
+"t. c #040303",
+"u. c #383527",
+"v. c #C2B484",
+"w. c #C8BA88",
+"x. c #CABC89",
+"y. c #CFC18D",
+"z. c #414141",
+"A. c #93A1B0",
+"B. c #8798A9",
+"C. c #8596A7",
+"D. c #8493A5",
+"E. c #8090A1",
+"F. c #3B3B3B",
+"G. c #BDBDBD",
+"H. c #070706",
+"I. c #4D4734",
+"J. c #B8AC7D",
+"K. c #BDB081",
+"L. c #7B4E44",
+"M. c #8A97A5",
+"N. c #7F8E9F",
+"O. c #7D8D9E",
+"P. c #7C8A9B",
+"Q. c #788796",
+"R. c #717F8F",
+"S. c #AAA285",
+"T. c #030302",
+"U. c #373737",
+"V. c #7F8B99",
+"W. c #748291",
+"X. c #717E8D",
+"Y. c #59636F",
+"Z. c #1A1D21",
+"`. c #F5E9BF",
+" + c #BFAC66",
+".+ c #3B3520",
+"++ c #2D3135",
+"@+ c #4B545E",
+"#+ c #5F6A77",
+"$+ c #4A525C",
+"%+ c #25292E",
+"&+ c #EED680",
+"*+ c #464646",
+"=+ c #262A30",
+"-+ c #07080A",
+";+ c #C7D0D8",
+">+ c #7590AE",
+",+ c #617891",
+"'+ c #686868",
+")+ c #797979",
+"!+ c #3A3A3A",
+"~+ c #1F1F1F",
+" . + @ # # # ",
+" $ % & * = - ; > , ' ",
+" ) ! = ~ { ] ^ / ( _ : < ",
+" [ } ~ | 1 2 3 4 5 6 7 8 9 0 ",
+" a b c d 6 e f g f h e i j k l m ",
+" n o o p q q r s t g h u v w x y ",
+" z A B C D E F r G s 7 H e I J 1 K L ",
+" M N O P Q R S r G | q { T I J U V W ",
+" X Y Z ` ...+.r s @.#.$.u %.6 1 &.W ",
+" y *.=.-.;. ...q ~ q >.,.'.).!.d ~.{. ",
+" ].^./.(._.:.<.[.}.|.1.2.3.w 1 4.5.6. ",
+" 7.8.9./.0.a.P b.b.c.d.e.l f.g.~ : h. ",
+" i.j.O k.l.m.p n.o.p.q.r.q s.* 6. ",
+" t.u.v.w.x.y.z.A.B.C.D.E.F.G.$ h. ",
+" h.H.I.J.K.L.M.N.O.O.P.Q.R.+ h. ",
+" h.S.T.y + U.V.W.W.W.X.Y.Z.h.h.h. ",
+" h.`. +.+6.++@+#+#+$+%+h.h. h.h.h.h. ",
+" h.`.&+&+*+=+-+h.h.h.h. , , h.h. ",
+" h.h.h.h.;+>+>+>+,+h. h.'+. h.h. ",
+" h.h.h.h.h.h.h. h.)+!+h.h. ",
+" h.'+~+h. ",
+" h.h. ",
+" ",
+" "};
diff --git a/src/xml-compat.h b/src/xml-compat.h
new file mode 100644
index 0000000..86f209a
--- /dev/null
+++ b/src/xml-compat.h
@@ -0,0 +1,50 @@
+/*
+ * xml-compat.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 __XML_COMPAT_H
+#define __XML_COMPAT_H
+
+/*#include <xmlmemory.h> cl3: where is that file ? what is it used for ? */
+
+/*#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000 */
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+
+#define root children
+#define childs children
+
+/*#else
+# include <gnome-xml/parser.h>
+# include <gnome-xml/parserInternals.h>
+#endif*/
+
+#endif /* __XML_COMPAT_H */
+
diff --git a/src/xml-helper.c b/src/xml-helper.c
new file mode 100644
index 0000000..64934e7
--- /dev/null
+++ b/src/xml-helper.c
@@ -0,0 +1,254 @@
+/*
+ * xml-helper.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 "xml-compat.h"
+//#include <gnome-xml/parser.h>
+//#include <gnome-xml/parserInternals.h>
+
+#include "xml-helper.h"
+
+/**
+ * A modified version of XmlSAXParseFile in gnome-xml. This one lets us set
+ * the user_data that is passed to the various callbacks, to make it possible
+ * to avoid lots of global variables.
+ */
+int oreganoXmlSAXParseFile (xmlSAXHandlerPtr sax,
+ gpointer user_data, const gchar *filename)
+{
+ int ret = 0;
+ xmlParserCtxtPtr ctxt;
+
+ ctxt = xmlCreateFileParserCtxt (filename);
+ if (ctxt == NULL) return -1;
+ ctxt->sax = sax;
+ ctxt->userData = user_data;
+
+#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000
+ xmlKeepBlanksDefault(0);
+#endif
+ xmlParseDocument (ctxt);
+
+ if (ctxt->wellFormed)
+ ret = 0;
+ else
+ ret = -1;
+ if (sax != NULL)
+ ctxt->sax = NULL;
+ xmlFreeParserCtxt (ctxt);
+
+ return ret;
+}
+
+
+/**
+ * Set coodinate for a node, carried as the content of a child.
+ */
+void
+xmlSetCoordinate (xmlNodePtr node, const char *name, double x, double y)
+{
+ xmlNodePtr child;
+ gchar *str;
+ xmlChar *ret;
+
+ str = g_strdup_printf ("(%g %g)", x, y);
+ ret = xmlGetProp (node, BAD_CAST name);
+ if (ret != NULL){
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ g_free (str);
+ return;
+ }
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST str);
+ return;
+ }
+ child = child->next;
+ }
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ g_free (str);
+}
+
+
+
+/**
+ * Set coodinates for a node, carried as the content of a child.
+ */
+void
+xmlSetCoordinates (xmlNodePtr node, const char *name,
+ double x1, double y1, double x2, double y2)
+{
+ xmlNodePtr child;
+ gchar *str;
+ xmlChar *ret;
+
+ str = g_strdup_printf ("(%g %g)(%g %g)", x1, y1, x2, y2);
+ ret = xmlGetProp (node, BAD_CAST name);
+ if (ret != NULL){
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ g_free (str);
+ return;
+ }
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST str);
+ return;
+ }
+ child = child->next;
+ }
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ g_free (str);
+}
+
+
+/**
+ * Set a string value for a node either carried as an attibute or as
+ * the content of a child.
+ */
+void
+xmlSetGnomeCanvasPoints (xmlNodePtr node, const char *name,
+ GnomeCanvasPoints *val)
+{
+ xmlNodePtr child;
+ char *str, *base;
+ int i;
+
+ if (val == NULL)
+ return;
+ if ((val->num_points < 0) || (val->num_points > 5000))
+ return;
+ base = str = g_malloc (val->num_points * 30 * sizeof (char));
+ if (str == NULL)
+ return;
+ for (i = 0; i < val->num_points; i++){
+ str += sprintf (str, "(%g %g)", val->coords[2 * i],
+ val->coords[2 * i + 1]);
+ }
+
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST base);
+ free (base);
+ return;
+ }
+ child = child->next;
+ }
+ xmlNewChild (node, NULL, BAD_CAST name,
+ xmlEncodeEntitiesReentrant(node->doc, BAD_CAST base));
+ g_free (base);
+}
+
+
+/**
+ * Set a string value for a node either carried as an attibute or as
+ * the content of a child.
+ */
+void
+xmlSetValue (xmlNodePtr node, const char *name, const char *val)
+{
+ xmlChar *ret;
+ xmlNodePtr child;
+
+ ret = xmlGetProp (node, BAD_CAST name);
+ if (ret != NULL){
+ xmlSetProp (node, BAD_CAST name, BAD_CAST val);
+ return;
+ }
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST val);
+ return;
+ }
+ child = child->next;
+ }
+ xmlSetProp (node, BAD_CAST name, BAD_CAST val);
+}
+
+/**
+ * Set an integer value for a node either carried as an attibute or as
+ * the content of a child.
+ */
+void
+xmlSetIntValue (xmlNodePtr node, const char *name, int val)
+{
+ xmlChar *ret;
+ xmlNodePtr child;
+ char str[101];
+
+ snprintf (str, 100, "%d", val);
+ ret = xmlGetProp (node,BAD_CAST name);
+ if (ret != NULL){
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ return;
+ }
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST str);
+ return;
+ }
+ child = child->next;
+ }
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+}
+
+
+/**
+ * Set a double value for a node either carried as an attibute or as
+ * the content of a child.
+ */
+void
+xmlSetDoubleValue (xmlNodePtr node, const char *name, double val)
+{
+ xmlChar *ret;
+ xmlNodePtr child;
+ char str[101];
+
+ snprintf (str, 100, "%g", (float) val);
+ ret = xmlGetProp (node, BAD_CAST name);
+ if (ret != NULL){
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+ return;
+ }
+ child = node->childs;
+ while (child != NULL){
+ if (!xmlStrcmp (child->name, BAD_CAST name)){
+ xmlNodeSetContent (child, BAD_CAST str);
+ return;
+ }
+ child = child->next;
+ }
+ xmlSetProp (node, BAD_CAST name, BAD_CAST str);
+}
+
diff --git a/src/xml-helper.h b/src/xml-helper.h
new file mode 100644
index 0000000..aa35d2d
--- /dev/null
+++ b/src/xml-helper.h
@@ -0,0 +1,56 @@
+/*
+ * xml-helper.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 __XML_HELPER_H
+#define __XML_HELPER_H
+
+#include <gnome.h>
+#include "xml-compat.h"
+
+/*#include <gnome-xml/parser.h>*/
+/*#include <gnome-xml/parserInternals.h>*/
+
+int oreganoXmlSAXParseFile (xmlSAXHandlerPtr sax,
+ gpointer user_data, const gchar *filename);
+
+void xmlSetGnomeCanvasPoints (xmlNodePtr node,
+ const char *name, GnomeCanvasPoints *val);
+
+void xmlSetValue (xmlNodePtr node, const char *name, const char *val);
+
+void xmlSetIntValue (xmlNodePtr node, const char *name, int val);
+
+void xmlSetDoubleValue (xmlNodePtr node, const char *name, double val);
+
+void xmlSetCoordinate (xmlNodePtr node, const char *name, double x, double y);
+
+void xmlSetCoordinates (xmlNodePtr node, const char *name,
+ double x1, double y1, double x2, double y2);
+
+#endif