diff options
Diffstat (limited to 'src')
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 Binary files differnew file mode 100644 index 0000000..3ce93f6 --- /dev/null +++ b/src/pixmaps/logo.png 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 Binary files differnew file mode 100644 index 0000000..b47209b --- /dev/null +++ b/src/pixmaps/menu_zoom.xcf 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 Binary files differnew file mode 100644 index 0000000..6e37d85 --- /dev/null +++ b/src/pixmaps/mini_icon_plot.xcf 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, ¢er.x, ¢er.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, ¢er); + + 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, ¢er); + + /* + * 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 = >K_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 Binary files differnew file mode 100644 index 0000000..768f69d --- /dev/null +++ b/src/stock/grid.xcf 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 Binary files differnew file mode 100644 index 0000000..4aa45b3 --- /dev/null +++ b/src/stock/part-browser.xcf 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 Binary files differnew file mode 100644 index 0000000..04510f4 --- /dev/null +++ b/src/stock/plot.xcf 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 Binary files differnew file mode 100644 index 0000000..1071ccf --- /dev/null +++ b/src/stock/plot2.xcf 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 |