diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2018-07-13 06:17:58 +0000 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2018-07-13 06:17:58 +0000 |
commit | 9e0bd16a997e55d6b9c2e80734ea8e61794c7602 (patch) | |
tree | d73377e0368b0c730d5a14019b1eee53897505bf /src | |
parent | e1fffcb07ce0d8b0db9e0b4b5e1e0c1128197af5 (diff) |
New upstream version 0.84.22
Diffstat (limited to 'src')
136 files changed, 16083 insertions, 12652 deletions
diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index f4090a4..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,102 +0,0 @@ -SUBDIRS = \ - gplot \ - engines \ - model \ - sheet \ - stock \ - pixmaps - -AM_CFLAGS = -Wall -DG_DISABLE_DEPRECATED -DGSEAL_ENABLE \ - -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ - -DGTK_DISABLE_DEPRECATED \ - -DG_DISABLE_SINGLE_INCLUDES -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \ - -DGTK_DISABLE_SINGLE_INCLUDES - -oreganodir = $(datadir)/oregano - -INCLUDES = \ - -DVERSION="\"$(VERSION)\"" \ - -DPACKAGE=\""oregano\"" \ - -DGETTEXT_PACKAGE=\""oregano\"" \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -I$(includedir) $(GNOME_INCLUDEDIR) \ - -DOREGANO_UIDIR=\""$(oreganodir)/xml"\" \ - -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \ - -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" \ - -DOREGANO_LANGDIR=\""$(oreganodir)/language-specs"\" \ - -DDATADIR=\""$(datadir)"\" \ - -I./sheet \ - -I./gplot \ - -I./model \ - -I./engines \ - -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 \ - 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 \ - 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 \ - 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 \ - splash.h \ - splash.c \ - stock.c \ - stock.h \ - xml-compat.h \ - xml-helper.c \ - xml-helper.h \ - oregano.c \ - oregano.h - -oregano_LDADD = \ - $(OREGANO_LIBS) diff --git a/src/clipboard.c b/src/clipboard.c index 98c69fe..66c04b7 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -36,13 +36,13 @@ #include "sheet-item.h" #include "clipboard.h" -struct _ClipboardData { +struct _ClipboardData +{ ItemData *item_data; SheetItemClass *item_class; }; -void -clipboard_empty (void) +void clipboard_empty (void) { GSList *list; ClipboardData *cb_data; @@ -61,8 +61,7 @@ clipboard_empty (void) oregano.clipboard = NULL; } -gboolean -clipboard_is_empty (void) +gboolean clipboard_is_empty (void) { if (oregano.clipboard == NULL) return TRUE; @@ -70,8 +69,7 @@ clipboard_is_empty (void) return g_slist_length (oregano.clipboard) > 0 ? FALSE : TRUE; } -void -clipboard_foreach (ClipBoardFunction callback, gpointer user_data) +void clipboard_foreach (ClipBoardFunction callback, gpointer user_data) { GSList *list; ClipboardData *data; @@ -85,8 +83,7 @@ clipboard_foreach (ClipBoardFunction callback, gpointer user_data) } } -void -clipboard_add_object (GObject *item) +void clipboard_add_object (GObject *item) { ItemDataClass *id_class; ItemData *item_data, *clone; @@ -111,16 +108,14 @@ clipboard_add_object (GObject *item) oregano.clipboard = g_slist_prepend (oregano.clipboard, cb_data); } -GObject * -clipboard_data_get_item_data (ClipboardData *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) +GObjectClass *clipboard_data_get_item_class (ClipboardData *data) { g_return_val_if_fail (data != NULL, NULL); diff --git a/src/clipboard.h b/src/clipboard.h index 8f98871..05c1d47 100644 --- a/src/clipboard.h +++ b/src/clipboard.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __CLIPBOARD_H @@ -35,14 +35,13 @@ typedef struct _ClipboardData ClipboardData; -typedef void (*ClipBoardFunction) (ClipboardData *data, gpointer user_data); +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); +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/coords.c b/src/coords.c new file mode 100644 index 0000000..57eb66a --- /dev/null +++ b/src/coords.c @@ -0,0 +1,154 @@ +/* + * coords.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2012-2013 Bernhard Schuster + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "coords.h" + +inline Coords *coords_new (gdouble x, gdouble y) +{ + Coords *c = g_malloc (sizeof(Coords)); + g_assert (c != NULL); + c->x = x; + c->y = y; + return c; +} + +inline Coords *coords_new_copy (const Coords *src) { return coords_new (src->x, src->y); } + +inline void coords_destroy (Coords *c) +{ + g_free (c); +} + +inline Coords *coords_add (Coords *a, const Coords *b) +{ + if (!a || !b) + return NULL; + a->x += b->x; + a->y += b->y; + return a; +} + +inline Coords coords_sum (const Coords *a, const Coords *b) +{ + g_assert (a); + g_assert (b); + + Coords c; + c.x = a->x + b->x; + c.y = a->y + b->y; + return c; +} + +inline Coords coords_sub (const Coords *a, const Coords *b) +{ + g_assert (a); + g_assert (b); + + Coords c; + c.x = a->x - b->x; + c.y = a->y - b->y; + return c; +} + +inline Coords *coords_sum_new (const Coords *a, const Coords *b) +{ + if (G_UNLIKELY (!a || !b)) + return NULL; + Coords *c = coords_new (a->x, a->y); + c->x += b->x; + c->y += b->y; + return c; +} + +inline Coords *coords_set (Coords *a, const Coords *val) +{ + if (G_UNLIKELY (!a || !val)) + return NULL; + a->x = val->x; + a->y = val->y; + return a; +} + +inline Coords coords_average (const Coords *a, const Coords *b) +{ + Coords r = (Coords)(*a); + r.x += b->x; + r.x /= 2.0; + r.y += b->y; + r.y /= 2.0; + return r; +} + +inline gdouble coords_dot (const Coords *a, const Coords *b) +{ + return (a->x * b->x) + (a->y * b->y); +} + +inline gdouble coords_cross (const Coords *a, const Coords *b) +{ + return (a->x * b->y) - (a->y * b->x); +} + +inline gdouble coords_euclid (const Coords *a) { return sqrt (coords_dot (a, a)); } + +inline gdouble coords_euclid2 (const Coords *a) { return coords_dot (a, a); } + +inline gdouble coords_distance (const Coords *a, const Coords *b) +{ + return sqrt (coords_dot (a, b)); +} + +#define CIRCLE_R_SHIFT(x, r) ((x >> r) | (x << (sizeof(x) * 8 - r))) +#define CIRCLE_L_SHIFT(x, l) ((x << l) | (x >> (sizeof(x) * 8 - l))) +inline guint coords_hash (gconstpointer v) +{ + const Coords *c = v; + const guint x = (guint)(c->x); + const guint y = (guint)(c->y); + return CIRCLE_L_SHIFT (x, 7) ^ CIRCLE_R_SHIFT (y, 3); +} + +inline gboolean coords_equal (const Coords *a, const Coords *b) +{ + return G_UNLIKELY (fabs (a->x - b->x) < COORDS_DELTA && fabs (a->y - b->y) < COORDS_DELTA); +} + +inline gint coords_compare (const Coords *a, const Coords *b) +{ + if (coords_equal (a, b)) + return 0; + if ((a->x > b->x) || (fabs (a->x - b->x) < (COORDS_DELTA * COORDS_DELTA) && (a->y > b->y))) + return -1; + return +1; +} diff --git a/src/coords.h b/src/coords.h new file mode 100644 index 0000000..30e30fa --- /dev/null +++ b/src/coords.h @@ -0,0 +1,102 @@ +/* + * coords.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2012-2013 Bernhard Schuster + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __COORDS_H +#define __COORDS_H + +#define COORDS_DELTA (1e-5) + +#include <glib.h> +#include <math.h> + +typedef struct _Coords +{ + gdouble x; + gdouble y; +} Coords; + +Coords *coords_new (gdouble x, gdouble y); + +Coords *coords_new_copy (const Coords *c); + +void coords_destroy (Coords *c); + +/* + * Adds b to a and returns a pointer to a which holds now the result + */ +Coords *coords_add (Coords *a, const Coords *b); + +/* + * Adds b to a and returns a ptr to a Coord struct which has to be freed by + * either coords_destroy + */ +Coords *coords_sum_new (const Coords *a, const Coords *b); + +Coords *coords_set (Coords *a, const Coords *val); + +/* + * + */ +Coords coords_sum (const Coords *a, const Coords *b); + +Coords coords_sub (const Coords *a, const Coords *b); + +/* + * calculates the average of two points + */ +Coords coords_average (const Coords *a, const Coords *b); + +gdouble coords_cross (const Coords *a, const Coords *b); + +gdouble coords_dot (const Coords *a, const Coords *b); + +gdouble coords_euclid (const Coords *a); + +gdouble coords_euclid2 (const Coords *a); + +gdouble coords_distance (const Coords *a, const Coords *b); + +gboolean coords_equal (const Coords *a, const Coords *b); + +/* + * used for GHashTable key hashing + */ +guint coords_hash (gconstpointer v); + +/* + * used for comparsion in GHashTable + */ +gint coords_compare (const Coords *a, const Coords *b); + +#endif /* __COORDS_H */ diff --git a/src/cursors.c b/src/cursors.c index 80f9d04..78ac010 100644 --- a/src/cursors.c +++ b/src/cursors.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,46 +26,39 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "cursors.h" +#include "debug.h" OreganoCursor oregano_cursors[] = { - { NULL, GDK_LEFT_PTR }, - { NULL, GDK_TCROSS }, - { NULL, GDK_PENCIL }, - { NULL, GDK_XTERM }, - { NULL, -1 } -}; + {NULL, GDK_LEFT_PTR}, {NULL, GDK_TCROSS}, {NULL, GDK_PENCIL}, {NULL, GDK_XTERM}, {NULL, -1}}; -void -cursors_init (void) +void cursors_init (void) { int i; GdkDisplay *display; - display = gdk_display_get_default(); + display = gdk_display_get_default (); for (i = 0; oregano_cursors[i].type != -1; i++) { - oregano_cursors[i].cursor = - gdk_cursor_new_for_display (display, - oregano_cursors[i].type); + oregano_cursors[i].cursor = gdk_cursor_new_for_display (display, oregano_cursors[i].type); } } -void -cursors_shutdown (void) +void cursors_shutdown (void) { int i; - for (i = 0; oregano_cursors[i].type != -1; i++) - g_object_unref (oregano_cursors[i].cursor); + for (i = 0; oregano_cursors[i].type != -1; i++) { + if (oregano_cursors[i].cursor) + g_object_unref (oregano_cursors[i].cursor); + } } -void -cursor_set_widget (GtkWidget *w, int name) +void cursor_set_widget (GtkWidget *w, int name) { if (gtk_widget_get_window (w)) gdk_window_set_cursor (gtk_widget_get_window (w), oregano_cursors[name].cursor); diff --git a/src/cursors.h b/src/cursors.h index cd13c4b..0b840e1 100644 --- a/src/cursors.h +++ b/src/cursors.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,28 +26,29 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, 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 +#define OREGANO_CURSOR_LEFT_PTR 0 +#define OREGANO_CURSOR_CROSS 1 +#define OREGANO_CURSOR_PENCIL 2 +#define OREGANO_CURSOR_CARET 3 -typedef struct { +typedef struct +{ GdkCursor *cursor; GdkCursorType type; } OreganoCursor; extern OreganoCursor oregano_cursors[]; -void cursors_init (void); -void cursors_shutdown (void); +void cursors_init (void); +void cursors_shutdown (void); void cursor_set_widget (GtkWidget *w, int name); #endif diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..e9e4003 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,45 @@ +/* + * debug.h + * + * + * Authors: + * Bernhard Schuster <bernhard@ahoi.io> + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 2012-2013 Bernhard Schuster + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib/gprintf.h> + +#ifdef DEBUG_THIS +#undef DEBUG_THIS +#endif +#define DEBUG_THIS 0 + +#ifndef DEBUG_ALL +#define DEBUG_ALL 0 +#endif + +#define NG_DEBUG(msg, ...) \ + { \ + if (DEBUG_THIS || DEBUG_ALL) { \ + g_printf ("%s:%d @ %s +++ " msg "\n", __FILE__, __LINE__, __FUNCTION__, \ + ##__VA_ARGS__); \ + } \ + } diff --git a/src/dialogs.c b/src/dialogs.c index 1c6b90d..a18c2df 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -4,12 +4,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,10 +27,12 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ +#include <sys/syscall.h> + #include <glib/gi18n.h> #include <gtk/gtk.h> @@ -35,152 +41,216 @@ #include "pixmaps/logo.xpm" -static GtkWidget *about = NULL; +/* + * Schedule a call to the oregano_error() function so that it is + * called whenever there are no higher priority events pending. + * + * The caller should schedule this function call using the GLib + * function g_idle_add_full(). + */ +gboolean oregano_schedule_error (gchar *msg) { oregano_error_with_title (msg, NULL); return G_SOURCE_REMOVE; } -void -oregano_error (gchar *msg) +/* + * Schedule a call to the oregano_error_with_title() function + * so that it is called whenever there are no higher priority + * events pending. + * + * The caller should schedule this function call using the GLib + * function g_idle_add_full(). + */ +gboolean oregano_schedule_error_with_title (OreganoTitleMsg *tm) { - oregano_error_with_title (msg, NULL); + oregano_error_with_title (tm->title, tm->msg); + g_free (tm->title); + g_free (tm->msg); + g_free (tm); + + return G_SOURCE_REMOVE; } -void -oregano_error_with_title (gchar *title, gchar *desc) +void oregano_error (gchar *msg) { oregano_error_with_title (msg, NULL); } + +void oregano_error_with_title (gchar *title, gchar *msg) { GtkWidget *dialog; - GString* span_msg; + GString *span_msg; + pid_t tid = 0; + +#ifdef SYS_gettid + tid = syscall(SYS_gettid); +#endif + // make sure that this is running in the main thread + if (tid && (getpid() != tid)) { + OreganoTitleMsg *tm = g_malloc (sizeof (OreganoTitleMsg)); + g_assert (tm != NULL); + tm->title = g_strdup (title); + tm->msg = g_strdup (msg); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) oregano_schedule_error_with_title, tm, NULL); + return; + } 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>"); + 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); + if (msg && msg[0] != '\0') { + span_msg = g_string_append (span_msg, "\n\n"); + span_msg = g_string_append (span_msg, msg); } - dialog = gtk_message_dialog_new_with_markup ( - NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - span_msg->str); + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, NULL); + + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), span_msg->str); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); gtk_dialog_run (GTK_DIALOG (dialog)); g_string_free (span_msg, TRUE); + gtk_widget_destroy (dialog); } -void -oregano_warning (gchar *msg) +/* + * Schedule a call to the oregano_warning() function so that it is + * called whenever there are no higher priority events pending. + * + * The caller should schedule this function call using the GLib + * function g_idle_add_full(). + */ +gboolean oregano_schedule_warning (gchar *msg) { oregano_warning_with_title (msg, NULL); return G_SOURCE_REMOVE; } + +/* + * Schedule a call to the oregano_warning_with_title() function + * so that it is called whenever there are no higher priority + * events pending. + * + * The caller should schedule this function call using the GLib + * function g_idle_add_full(). + */ +gboolean oregano_schedule_warning_with_title (OreganoTitleMsg *tm) { - oregano_warning_with_title (msg, NULL); + oregano_warning_with_title (tm->title, tm->msg); + g_free (tm->title); + g_free (tm->msg); + g_free (tm); + + return G_SOURCE_REMOVE; } -void -oregano_warning_with_title (gchar *title, gchar *desc) +void oregano_warning (gchar *msg) { oregano_warning_with_title (msg, NULL); } + +void oregano_warning_with_title (gchar *title, gchar *msg) { GtkWidget *dialog; - GString* span_msg; + GString *span_msg; + pid_t tid = 0; + +#ifdef SYS_gettid + tid = syscall(SYS_gettid); +#endif + // make sure that this is running in the main thread + if (tid && (getpid() != tid)) { + OreganoTitleMsg *tm = g_malloc (sizeof (OreganoTitleMsg)); + g_assert (tm != NULL); + tm->title = g_strdup (title); + tm->msg = g_strdup (msg); + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) oregano_schedule_warning_with_title, tm, NULL); + return; + } 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>"); + 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); + if (msg && msg[0] != '\0') { + span_msg = g_string_append (span_msg, "\n\n"); + span_msg = g_string_append (span_msg, msg); } - dialog = gtk_message_dialog_new_with_markup ( - NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_OK, - span_msg->str); + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, + GTK_BUTTONS_CLOSE, NULL); + + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), span_msg->str); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); gtk_dialog_run (GTK_DIALOG (dialog)); g_string_free (span_msg, TRUE); + gtk_widget_destroy (dialog); } -gint -oregano_question (gchar *msg) +/* + * Schedule a call to the oregano_question() function so that it is + * called whenever there are no higher priority events pending. + * + * The caller should schedule this function call using the GLib + * function g_idle_add_full(). + */ +gboolean oregano_schedule_question (OreganoQuestionAnswer *qa) { + qa->ans = oregano_question (qa->msg); + g_free (qa->msg); + + return G_SOURCE_REMOVE; +} + +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); + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, NULL); + + gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), 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); + + return (ans == GTK_RESPONSE_YES); } -void -dialog_about (void) +void dialog_about (void) { + static GtkWidget *about = NULL; GdkPixbuf *logo; - const gchar *authors[] = { - "Richard Hult", - "Margarita Manterola", - "Andres de Barbara", - "Gustavo M. Pereyra", - "Maximiliano Curia", - "Ricardo Markiewicz", - "Marc Lorber", - NULL - }; - - const char *docs[] = { - "Ricardo Markiewicz <rmarkie@fi.uba.ar> (es)", - "Jordi Mallach <tradgnome@softcatala.net> (ca)", - "Marc Lorber <lorber.marc@wanadoo.fr> (en)", - NULL - }; - - const gchar *copy = _("(c) 2009-2012 Marc Lorber 2003-2006 LUGFi\n(c) 1999-2001 Richard Hult"); - - // Allow only one about box at a time. - if (about) { - gdk_window_raise (gtk_widget_get_window (about)); - return; - } + const gchar *authors[] = {"Richard Hult", "Margarita Manterola", "Andres de Barbara", + "Gustavo M. Pereyra", "Maximiliano Curia", "Ricardo Markiewicz", + "Marc Lorber", "Bernhard Schuster", "Guido Trentalancia", + NULL}; + + const char *docs[] = {"Ricardo Markiewicz <rmarkie@fi.uba.ar> (es)", + "Jordi Mallach <tradgnome@softcatala.net> (ca)", + "Marc Lorber <lorber.marc@wanadoo.fr> (en)", + "Bernhard Schuster <bernhard@ahoi.io> (de)", + NULL}; + + const gchar *copy = _ ("(c) 2017 Guido Trentalancia\n" + "(c) 2012-2017 Bernhard Schuster\n" + "(c) 2009-2012 Marc Lorber\n" + "(c) 2003-2006 LUGFi\n" + "(c) 1999-2001 Richard Hult"); - logo = gdk_pixbuf_new_from_xpm_data ((const char **) logo_xpm); + logo = gdk_pixbuf_new_from_xpm_data ((const char **)logo_xpm); about = gtk_about_dialog_new (); gtk_about_dialog_set_program_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), - "https://github.com/marc-lorber/oregano"); + 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), "https://ahoi.io/project/oregano"); 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); + about = NULL; } diff --git a/src/dialogs.h b/src/dialogs.h index 01d8e44..5556e56 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __DIALOGS_H @@ -35,10 +37,30 @@ #include "schematic.h" +typedef struct _OreganoTitleMsg OreganoTitleMsg; +typedef struct _OreganoQuestionAnswer OreganoQuestionAnswer; + +struct _OreganoTitleMsg +{ + gchar *title; + gchar *msg; +}; + +struct _OreganoQuestionAnswer +{ + gchar *msg; + gint ans; +}; + +gboolean oregano_schedule_error (gchar *msg); +gboolean oregano_schedule_error_with_title (OreganoTitleMsg *tm); void oregano_error (gchar *msg); -void oregano_error_with_title (gchar *title, gchar *desc); +void oregano_error_with_title (gchar *title, gchar *msg); +gboolean oregano_schedule_warning (gchar *msg); +gboolean oregano_schedule_warning_with_title (OreganoTitleMsg *tm); void oregano_warning (gchar *msg); -void oregano_warning_with_title (gchar *title, gchar *desc); +void oregano_warning_with_title (gchar *title, gchar *msg); +gboolean oregano_schedule_question (OreganoQuestionAnswer *qa); gint oregano_question (gchar *msg); void dialog_about (void); diff --git a/src/engines/Makefile.am b/src/engines/Makefile.am deleted file mode 100644 index 09af9f1..0000000 --- a/src/engines/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -oreganodir = $(datadir)/oregano - -AM_CFLAGS = -g -Wall -DG_DISABLE_DEPRECATED -DGSEAL_ENABLE \ - -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ - -DGTK_DISABLE_DEPRECATED \ - -DG_DISABLE_SINGLE_INCLUDES -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \ - -DGTK_DISABLE_SINGLE_INCLUDES - -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-internal.h \ - engine.h \ - gnucap.c \ - gnucap.h \ - netlist-helper.c \ - netlist-helper.h \ - ngspice-analysis.c \ - ngspice.c \ - ngspice-analysis.h \ - ngspice.h diff --git a/src/engines/engine-internal.h b/src/engines/engine-internal.h index 4f5bafe..661fcfd 100644 --- a/src/engines/engine-internal.h +++ b/src/engines/engine-internal.h @@ -5,7 +5,7 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __ENGINE_INTERNAL_H #define __ENGINE_INTERNAL_H 1 @@ -34,28 +34,34 @@ #include "schematic.h" #include "simulation.h" -#define OREGANO_TYPE_ENGINE (oregano_engine_get_type ()) -#define OREGANO_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass)) -#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)) +#define OREGANO_TYPE_ENGINE (oregano_engine_get_type ()) +#define OREGANO_ENGINE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), OREGANO_TYPE_ENGINE, OreganoEngineClass)) +#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 _OreganoEngineClass OreganoEngineClass; -struct _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); + void (*start)(OreganoEngine *engine); + void (*stop)(OreganoEngine *engine); + void (*progress_solver)(OreganoEngine *engine, double *p); + void (*progress_reader)(OreganoEngine *engine, double *p); + gboolean (*get_netlist)(OreganoEngine *engine, const gchar *sm, GError **error); + GList *(*get_results)(OreganoEngine *engine); + gchar *(*get_operation_solver)(OreganoEngine *engine); + gchar *(*get_operation_reader)(OreganoEngine *engine); + gboolean (*has_warnings)(OreganoEngine *engine); + gboolean (*is_available)(OreganoEngine *engine); // Signals - void (*done) (); - void (*abort) (); + void (*done)(); + void (*abort)(); }; #endif diff --git a/src/engines/engine.c b/src/engines/engine.c index 738a0b9..5984bd4 100644 --- a/src/engines/engine.c +++ b/src/engines/engine.c @@ -4,12 +4,14 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +25,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib/gi18n.h> @@ -35,155 +37,158 @@ #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_("Fourier Analysis"), - N_("Unknown Analysis"), - NULL -}; - -// Signals -enum { - DONE, - ABORTED, - LAST_SIGNAL -}; - -static guint engine_signals[LAST_SIGNAL] = { 0 }; + [ANALYSIS_TYPE_NONE] = N_ ("None"), + [ANALYSIS_TYPE_OP_POINT] = N_ ("Operating Point"), + [ANALYSIS_TYPE_TRANSIENT] = N_ ("Transient Analysis"), + [ANALYSIS_TYPE_DC_TRANSFER] = N_ ("DC transfer characteristic"), + [ANALYSIS_TYPE_AC] = N_ ("AC Analysis"), + [ANALYSIS_TYPE_TRANSFER] = N_ ("Transfer Function"), + [ANALYSIS_TYPE_DISTORTION] = N_ ("Distortion Analysis"), + [ANALYSIS_TYPE_NOISE] = N_ ("Noise Spectral Density Curves"), + [ANALYSIS_TYPE_POLE_ZERO] = N_ ("Pole-Zero Analysis"), + [ANALYSIS_TYPE_SENSITIVITY] = N_ ("Sensitivity Analysis"), + [ANALYSIS_TYPE_FOURIER] = N_ ("Fourier analysis"), + [ANALYSIS_TYPE_UNKNOWN] = N_ ("Unknown Analysis"), + NULL}; + +// Engines Types +static gchar *engine_names[] = { + [OREGANO_ENGINE_GNUCAP] = N_ ("gnucap"), + [OREGANO_ENGINE_SPICE3] = N_ ("spice3"), + [OREGANO_ENGINE_NGSPICE] = N_ ("ngspice"), + NULL}; + +// Signals +enum { DONE, ABORTED, LAST_SIGNAL }; + +static guint engine_signals[LAST_SIGNAL] = {0}; static void oregano_engine_base_init (gpointer g_class); -GType -oregano_engine_get_type (void) +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 + 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); + type = g_type_register_static (G_TYPE_INTERFACE, "OreganoEngine", &info, 0); } return type; } -static void -oregano_engine_base_init (gpointer g_class) +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); + 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; } } -void -oregano_engine_start (OreganoEngine *self) +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) { - OREGANO_ENGINE_GET_CLASS (self)->start (self); + return OREGANO_ENGINE_GET_CLASS (self)->has_warnings (self); } -void -oregano_engine_stop (OreganoEngine *self) +gboolean oregano_engine_is_available (OreganoEngine *self) { - OREGANO_ENGINE_GET_CLASS (self)->stop (self); + return OREGANO_ENGINE_GET_CLASS (self)->is_available (self); } -gboolean -oregano_engine_has_warnings (OreganoEngine *self) +void oregano_engine_get_progress_solver (OreganoEngine *self, double *p) { - return OREGANO_ENGINE_GET_CLASS (self)->has_warnings (self); + g_return_if_fail(OREGANO_ENGINE_GET_CLASS (self)->progress_solver != NULL); + OREGANO_ENGINE_GET_CLASS (self)->progress_solver (self, p); } -gboolean -oregano_engine_is_available (OreganoEngine *self) +void oregano_engine_get_progress_reader (OreganoEngine *self, double *p) { - return OREGANO_ENGINE_GET_CLASS (self)->is_available (self); + g_return_if_fail(OREGANO_ENGINE_GET_CLASS(self)->progress_reader != NULL); + OREGANO_ENGINE_GET_CLASS (self)->progress_reader (self, p); } -void -oregano_engine_get_progress (OreganoEngine *self, double *p) +gboolean oregano_engine_generate_netlist (OreganoEngine *self, const gchar *file, GError **error) { - OREGANO_ENGINE_GET_CLASS (self)->progress (self, p); + return OREGANO_ENGINE_GET_CLASS (self)->get_netlist (self, file, error); } -void -oregano_engine_generate_netlist (OreganoEngine *self, const gchar *file, GError **error) +GList *oregano_engine_get_results (OreganoEngine *self) { - OREGANO_ENGINE_GET_CLASS (self)->get_netlist (self, file, error); + return OREGANO_ENGINE_GET_CLASS (self)->get_results (self); } -GList* -oregano_engine_get_results (OreganoEngine *self) +gchar *oregano_engine_get_current_operation_solver (OreganoEngine *self) { - return OREGANO_ENGINE_GET_CLASS (self)->get_results (self); + g_return_val_if_fail(OREGANO_ENGINE_GET_CLASS (self)->get_operation_solver != NULL, NULL); + return OREGANO_ENGINE_GET_CLASS (self)->get_operation_solver (self); } -gchar* -oregano_engine_get_current_operation (OreganoEngine *self) +gchar *oregano_engine_get_current_operation_reader (OreganoEngine *self) { - return OREGANO_ENGINE_GET_CLASS (self)->get_operation (self); + g_return_val_if_fail(OREGANO_ENGINE_GET_CLASS (self)->get_operation_reader != NULL, NULL); + return OREGANO_ENGINE_GET_CLASS (self)->get_operation_reader (self); } -OreganoEngine* -oregano_engine_factory_create_engine (gint type, Schematic *sm) + +OreganoEngine *oregano_engine_factory_create_engine (gint type, Schematic *sm) { OreganoEngine *engine; switch (type) { - case OREGANO_ENGINE_GNUCAP: - engine = oregano_gnucap_new (sm); + case OREGANO_ENGINE_GNUCAP: + engine = oregano_gnucap_new (sm); break; - case OREGANO_ENGINE_NGSPICE: - engine = oregano_ngspice_new (sm); + case OREGANO_ENGINE_SPICE3: + engine = oregano_spice_new (sm, TRUE); break; - default: - engine = NULL; + case OREGANO_ENGINE_NGSPICE: + engine = oregano_spice_new (sm, FALSE); + break; + default: + engine = NULL; } return engine; } -gchar * -oregano_engine_get_analysis_name (SimulationData *sdat) +gchar *oregano_engine_get_analysis_name_by_type(AnalysisType type) { + return g_strdup(_(analysis_names[type])); +} + +gchar *oregano_engine_get_engine_name_by_index (const guint index) { + return g_strdup (_(engine_names[index])); +} + +/** + * @sdat: nullable + */ +gchar *oregano_engine_get_analysis_name (SimulationData *sdat) { if (sdat == NULL) { - return g_strdup (_(analysis_names[ANALYSIS_UNKNOWN])); + return g_strdup (_ (analysis_names[ANALYSIS_TYPE_UNKNOWN])); } - return g_strdup (_(analysis_names[sdat->type])); + return g_strdup (_ (analysis_names[sdat->type])); } diff --git a/src/engines/engine.h b/src/engines/engine.h index a7175cc..de6ef2e 100644 --- a/src/engines/engine.h +++ b/src/engines/engine.h @@ -5,7 +5,7 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __ENGINE_H @@ -35,31 +35,29 @@ #include "schematic.h" #include "simulation.h" - -#define OREGANO_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OREGANO_TYPE_ENGINE, OreganoEngine)) -#define OREGANO_IS_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), OREGANO_TYPE_ENGINE)) +#define OREGANO_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_ENGINE, OreganoEngine)) +#define OREGANO_IS_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_ENGINE)) typedef struct _OreganoEngine OreganoEngine; -// Engines IDs -enum { - OREGANO_ENGINE_GNUCAP=0, - OREGANO_ENGINE_NGSPICE, - OREGANO_ENGINE_COUNT -}; +// Engines IDs +enum { OREGANO_ENGINE_GNUCAP = 0, OREGANO_ENGINE_SPICE3, OREGANO_ENGINE_NGSPICE, OREGANO_ENGINE_COUNT }; 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); +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 *); +void oregano_engine_get_progress_solver (OreganoEngine *engine, double *p); +void oregano_engine_get_progress_reader (OreganoEngine *engine, double *p); +gboolean oregano_engine_generate_netlist (OreganoEngine *engine, const gchar *file, GError **error); +GList *oregano_engine_get_results (OreganoEngine *engine); +gchar *oregano_engine_get_current_operation_solver (OreganoEngine *); +gchar *oregano_engine_get_current_operation_reader (OreganoEngine *); gboolean oregano_engine_is_available (OreganoEngine *); -gchar *oregano_engine_get_analysis_name (SimulationData *id); +gchar *oregano_engine_get_analysis_name_by_type(AnalysisType type); +gchar *oregano_engine_get_engine_name_by_index (const guint index); +gchar *oregano_engine_get_analysis_name (SimulationData *id); #endif diff --git a/src/engines/gnucap.c b/src/engines/gnucap.c index e6297d0..f712153 100644 --- a/src/engines/gnucap.c +++ b/src/engines/gnucap.c @@ -5,7 +5,7 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -42,14 +42,8 @@ // TODO Move analysis data and result to another file #include "simulation.h" -typedef enum { - STATE_IDLE, - IN_VARIABLES, - IN_VALUES, - STATE_ABORT -} ParseDataState; - -struct analysis_tag { +struct analysis_tag +{ gchar *name; guint len; }; @@ -61,12 +55,13 @@ static struct analysis_tag analysis_tags[] = { {"#Freq", 5}, /* AC */ }; -#define IS_THIS_ITEM(str,item) (!strncmp(str,item.name,item.len)) +#define IS_THIS_ITEM(str, item) (!strncmp (str, item.name, item.len)) #define GNUCAP_TITLE '#' -#define TAGS_COUNT (sizeof (analysis_tags) / sizeof (struct analysis_tag)) +#define TAGS_COUNT (sizeof(analysis_tags) / sizeof(struct analysis_tag)) // Parser STATUS -struct _OreganoGnuCapPriv { +struct _OreganoGnuCapPriv +{ GPid child_pid; gint child_stdout; GIOChannel *child_iochannel; @@ -84,41 +79,39 @@ struct _OreganoGnuCapPriv { guint buf_count; }; -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" 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 gboolean gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, + OreganoGnuCap *gnucap); static void gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap); static gdouble strtofloat (char *s); static GObjectClass *parent_class = NULL; -GType -oregano_gnucap_get_type (void) +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 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 + (GInterfaceInitFunc)gnucap_interface_init, // interface_init + NULL, // interface_finalize + NULL // interface_data }; type = g_type_register_static (G_TYPE_OBJECT, "OreganoGnuCap", &info, 0); @@ -127,8 +120,7 @@ oregano_gnucap_get_type (void) return type; } -static void -gnucap_class_init (OreganoGnuCapClass *klass) +static void gnucap_class_init (OreganoGnuCapClass *klass) { GObjectClass *object_class; @@ -140,72 +132,60 @@ gnucap_class_init (OreganoGnuCapClass *klass) object_class->finalize = gnucap_finalize; } -static void -gnucap_finalize (GObject *object) +static void gnucap_finalize (GObject *object) { SimulationData *data; OreganoGnuCap *gnucap; - GList *list; + GList *iter; int i; gnucap = OREGANO_GNUCAP (object); - list = gnucap->priv->analysis; - while (list) { - data = SIM_DATA (list->data); - for (i=0; i<data->n_variables; i++) { + iter = gnucap->priv->analysis; + for (; iter; iter = iter->next) { + data = SIM_DATA (iter->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_variables; i++) + for (i = 0; i < data->n_variables; i++) g_array_free (data->data[i], TRUE); g_free (data->min_data); g_free (data->max_data); - g_free (list->data); - list = list->next; + g_free (iter->data); } g_list_free (gnucap->priv->analysis); gnucap->priv->analysis = NULL; - g_list_free_full (list, g_object_unref); parent_class->finalize (object); } -static void -gnucap_dispose (GObject *object) -{ - parent_class->dispose (object); -} +static void gnucap_dispose (GObject *object) { parent_class->dispose (object); } -static gboolean -gnucap_has_warnings (OreganoEngine *self) -{ - return FALSE; -} +static gboolean gnucap_has_warnings (OreganoEngine *self) { return FALSE; } -static gboolean -gnucap_is_available (OreganoEngine *self) +static gboolean gnucap_is_available (OreganoEngine *self) { gchar *exe; exe = g_find_program_in_path ("gnucap"); - if (!exe) return FALSE; // gnucap not found + if (!exe) + return FALSE; // gnucap not found g_free (exe); return TRUE; } -static void -gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, - GError **error) +static gboolean gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, + GError **error) { OreganoGnuCap *gnucap; Netlist output; SimOption *so; - GList *list; + GList *iter; FILE *file; GError *local_error = NULL; @@ -214,53 +194,50 @@ gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, netlist_helper_create (gnucap->priv->schematic, &output, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); - return; + return FALSE; } file = fopen (filename, "w"); if (!file) { - oregano_error (g_strdup_printf ("Creation of %s not possible\n", filename)); - return; + oregano_error (g_strdup_printf ("Creation of file %s not possible\n", filename)); + return FALSE; } - list = sim_settings_get_options (output.settings); - // Prints title fputs (output.title ? output.title : "Title: <unset>", file); fputs ("\n" - "*----------------------------------------------" - "\n" - "*\tGNUCAP - NETLIST" - "\n", file); + "*----------------------------------------------" + "\n" + "*\tGNUCAP - NETLIST" + "\n", + file); // Prints Options - fputs (".options OUT=120 ",file); + fputs (".options OUT=120 ", file); - while (list) { - so = list->data; + iter = sim_settings_get_options (output.settings); + for (; iter; iter = iter->next) { + so = iter->data; // Prevent send NULL text if (so->value) { - if (strlen(so->value) > 0) { - g_fprintf (file,"%s=%s ",so->name,so->value); + if (strlen (so->value) > 0) { + g_fprintf (file, "%s=%s ", so->name, so->value); } } - list = list->next; } - fputc ('\n',file); + fputc ('\n', file); // Include of subckt models - fputs ("*------------- Models -------------------------\n",file); - list = output.models; - while (list) { + fputs ("*------------- Models -------------------------\n", file); + for (iter = output.models; iter; iter = iter->next) { gchar *model; - model = (gchar *)list->data; - g_fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model); - list = list->next; + model = (gchar *)iter->data; + g_fprintf (file, ".include %s/%s.model\n", OREGANO_MODELDIR, model); } - // Prints template parts - fputs ("*------------- Circuit Description-------------\n",file); + // Prints template parts + fputs ("*------------- Circuit Description-------------\n", file); fputs (output.template->str, file); - fputs ("\n*----------------------------------------------\n",file); + fputs ("\n*----------------------------------------------\n", file); // Prints Transient Analysis if (sim_settings_get_trans (output.settings)) { @@ -268,71 +245,70 @@ gnucap_generate_netlist (OreganoEngine *engine, const gchar *filename, g_fprintf (file, ".print tran %s\n", tmp_str); g_free (tmp_str); - g_fprintf (file, ".tran %g %g ", - sim_settings_get_trans_start (output.settings), - sim_settings_get_trans_stop (output.settings)); + g_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)) - g_fprintf (file,"%g", - (sim_settings_get_trans_stop (output.settings)- - sim_settings_get_trans_start (output.settings))/100); + g_fprintf (file, "%g", (sim_settings_get_trans_stop (output.settings) - + sim_settings_get_trans_start (output.settings)) / + 100); else - g_fprintf (file,"%g", sim_settings_get_trans_step (output.settings)); + g_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); + fputs (" UIC\n", file); + } else { + fputs ("\n", file); } } - // Print DC Analysis + // Print DC Analysis if (sim_settings_get_dc (output.settings)) { - g_fprintf (file, ".print dc %s\n", netlist_helper_create_analysis_string (output.store, FALSE)); - fputs (".dc ",file); + g_fprintf (file, ".print dc %s\n", + netlist_helper_create_analysis_string (output.store, FALSE)); + fputs (".dc ", file); // GNUCAP don't support nesting so the first or the second // Maybe an error message must be show if both are on - g_fprintf (file, "%s %g %g %g", - sim_settings_get_dc_vsrc(output.settings), - sim_settings_get_dc_start (output.settings), - sim_settings_get_dc_stop (output.settings), - sim_settings_get_dc_step (output.settings)); - fputc ('\n',file); + g_fprintf (file, "%s %g %g %g", sim_settings_get_dc_vsrc (output.settings), + sim_settings_get_dc_start (output.settings), + sim_settings_get_dc_stop (output.settings), + sim_settings_get_dc_step (output.settings)); + 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 an error message - // must be shown if the netlist is set in that way. - ac_start = sim_settings_get_ac_start (output.settings) ; + // must be shown if the netlist 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); - g_fprintf(file, ".print ac %s\n", netlist_helper_create_analysis_string (output.store, TRUE)); + g_fprintf (file, ".print ac %s\n", + netlist_helper_create_analysis_string (output.store, TRUE)); // AC format : ac start stop step_size g_fprintf (file, ".ac %g %g %g\n", ac_start, ac_stop, ac_step); } - + // Prints analysis using a Fourier transform /* if (sim_settings_get_fourier (output.settings)) { - gchar *tmp_str = netlist_helper_create_analysis_string (output.store, FALSE); - g_fprintf (file, ".four %d %s\n", - sim_settings_get_fourier_frequency (output.settings), tmp_str); - g_free (tmp_str); + gchar *tmp_str = netlist_helper_create_analysis_string (output.store, + FALSE); + g_fprintf (file, ".four %d %s\n", + sim_settings_get_fourier_frequency (output.settings), + tmp_str); + g_free (tmp_str); }*/ - g_list_free_full (list, g_object_unref); - // Debug op analysis. fputs (".print op v(nodes)\n", file); fputs (".op\n", file); fputs (".end\n", file); fclose (file); + return TRUE; } -static void -gnucap_progress (OreganoEngine *self, double *d) +static void gnucap_progress (OreganoEngine *self, double *d) { OreganoGnuCap *gnucap = OREGANO_GNUCAP (self); @@ -340,8 +316,7 @@ gnucap_progress (OreganoEngine *self, double *d) (*d) = gnucap->priv->progress; } -static void -gnucap_stop (OreganoEngine *self) +static void gnucap_stop (OreganoEngine *self) { OreganoGnuCap *gnucap = OREGANO_GNUCAP (self); g_io_channel_shutdown (gnucap->priv->child_iochannel, TRUE, NULL); @@ -350,8 +325,7 @@ gnucap_stop (OreganoEngine *self) g_spawn_close_pid (gnucap->priv->child_stdout); } -static void -gnucap_watch_cb (GPid pid, gint status, OreganoGnuCap *gnucap) +static void gnucap_watch_cb (GPid pid, gint status, OreganoGnuCap *gnucap) { // check for status, see man waitpid(2) if (WIFEXITED (status)) { @@ -371,18 +345,17 @@ gnucap_watch_cb (GPid pid, gint status, OreganoGnuCap *gnucap) gnucap->priv->current = NULL; if (gnucap->priv->num_analysis == 0) { - schematic_log_append_error (gnucap->priv->schematic, - _("### Too few or none analysis found ###\n")); + 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 + } else g_signal_emit_by_name (G_OBJECT (gnucap), "done"); } } -static gboolean -gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCap *gnucap) +static gboolean gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, + OreganoGnuCap *gnucap) { gchar *line; gsize len, terminator; @@ -400,8 +373,7 @@ gnucap_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoGnuCa return TRUE; } -static void -gnucap_start (OreganoEngine *self) +static void gnucap_start (OreganoEngine *self) { OreganoGnuCap *gnucap; GError *error = NULL; @@ -418,67 +390,57 @@ gnucap_start (OreganoEngine *self) } 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 - )) { + 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->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.")); + 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) +static GList *gnucap_get_results (OreganoEngine *self) { return OREGANO_GNUCAP (self)->priv->analysis; } -static gchar * -gnucap_get_operation (OreganoEngine *self) +static gchar *gnucap_get_operation (OreganoEngine *self) { OreganoGnuCapPriv *priv = OREGANO_GNUCAP (self)->priv; if (priv->current == NULL) - return _("None"); + return g_strdup(_("None")); return oregano_engine_get_analysis_name (priv->current); } -static void -gnucap_interface_init (gpointer g_iface, gpointer iface_data) +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->progress_solver = 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->get_operation_solver = gnucap_get_operation; klass->is_available = gnucap_is_available; } -static void -gnucap_instance_init (GTypeInstance *instance, gpointer g_class) +static void gnucap_instance_init (GTypeInstance *instance, gpointer g_class) { OreganoGnuCap *self = OREGANO_GNUCAP (instance); @@ -493,8 +455,7 @@ gnucap_instance_init (GTypeInstance *instance, gpointer g_class) self->priv->aborted = FALSE; } -OreganoEngine* -oregano_gnucap_new (Schematic *sc) +OreganoEngine *oregano_gnucap_new (Schematic *sc) { OreganoGnuCap *gnucap; @@ -504,12 +465,12 @@ oregano_gnucap_new (Schematic *sc) return OREGANO_ENGINE (gnucap); } -typedef struct { +typedef struct +{ gchar *name; } GCap_Variable; -static GCap_Variable * -_get_variables (gchar *str, gint *count) +static GCap_Variable *_get_variables (gchar *str, gint *count) { GCap_Variable *out; gchar *tmp[100]; @@ -519,7 +480,8 @@ _get_variables (gchar *str, gint *count) i = 0; ini = str; // Put blank in advance - while (isspace (*ini)) ini++; + while (isspace (*ini)) + ini++; fin = ini; while (*fin != '\0') { if (isspace (*fin)) { @@ -528,10 +490,11 @@ _get_variables (gchar *str, gint *count) *fin = ' '; i++; ini = fin; - while (isspace (*ini)) ini++; + while (isspace (*ini)) + ini++; fin = ini; - } - else fin++; + } else + fin++; } if (i == 0) { @@ -541,23 +504,22 @@ _get_variables (gchar *str, gint *count) out = g_new0 (GCap_Variable, i); (*count) = i; - for ( i=0; i< (*count); i++ ) { + for (i = 0; i < (*count); i++) { out[i].name = tmp[i]; } return out; } -static void -_free_variables (GCap_Variable *v, gint count) +static void _free_variables (GCap_Variable *v, gint count) { int i; - for (i=0; i<count; i++) + for (i = 0; i < count; i++) g_free (v[i].name); g_free (v); } -gdouble -strtofloat (gchar *s) { +gdouble strtofloat (gchar *s) +{ gdouble val; gchar *error; @@ -565,25 +527,26 @@ strtofloat (gchar *s) { // 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; + case 'u': + val /= 1000000; break; - case 'n': - val /= 1000000; - val /= 1000; + case 'n': + val /= 1000000; + val /= 1000; break; - case 'p': - val /= 1000000; - val /= 1000000; + case 'p': + val /= 1000000; + val /= 1000000; break; - case 'f': - val /= 100; + case 'f': + val /= 100; break; - case 'K': - val *= 1000; + case 'K': + val *= 1000; break; - default: - if (strcmp (error, "Meg") == 0) val *= 1000000; + default: + if (strcmp (error, "Meg") == 0) + val *= 1000000; } return val; @@ -591,8 +554,7 @@ strtofloat (gchar *s) { // Main method. Here we'll transform GnuCap output // into SimulationResults! -static void -gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) +static void gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) { static SimulationData *sdata; static Analysis *data; @@ -603,28 +565,27 @@ gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) gdouble val; gchar *s; - for (j=0; j < len; j++) { + for (j = 0; j < len; j++) { if (raw[j] != '\n') { buf[priv->buf_count++] = raw[j]; continue; } buf[priv->buf_count] = '\0'; - //Got a complete line + // Got a complete line s = buf; - NG_DEBUG (g_strdup_printf ("%s", s)); + NG_DEBUG ("%s", s); if (s[0] == GNUCAP_TITLE) { SimSettings *sim_settings; gdouble np1; - sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); + sim_settings = 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->type = ANALYSIS_TYPE_UNKNOWN; sdata->functions = NULL; // Calculates the quantity of variables @@ -632,92 +593,90 @@ gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) for (i = 0; i < TAGS_COUNT; i++) if (IS_THIS_ITEM (variables[0].name, analysis_tags[i])) - sdata->type = i; + sdata->type = i + 1; - 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); + 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->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->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")); - } + case ANALYSIS_TYPE_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")); - } + case ANALYSIS_TYPE_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 (""); + 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 = 1.; - data->dc.start = sim_settings_get_dc_start (sim_settings); - data->dc.stop = sim_settings_get_dc_stop (sim_settings); - data->dc.step = sim_settings_get_dc_step (sim_settings); - np1 = (data->dc.stop - data->dc.start) / data->dc.step; - - data->dc.sim_length = np1; - break; - case TRANSFER: - case DISTORTION: - case NOISE: - case POLE_ZERO: - case SENSITIVITY: - case FOURIER: - break; - case ANALYSIS_UNKNOWN: - g_error (_("Unknown analysis")); - break; + case ANALYSIS_TYPE_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 ANALYSIS_TYPE_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 ANALYSIS_TYPE_OP_POINT: + break; + case ANALYSIS_TYPE_DC_TRANSFER: + np1 = 1.; + data->dc.start = sim_settings_get_dc_start (sim_settings); + data->dc.stop = sim_settings_get_dc_stop (sim_settings); + data->dc.step = sim_settings_get_dc_step (sim_settings); + np1 = (data->dc.stop - data->dc.start) / data->dc.step; + + data->dc.sim_length = np1; + break; + case ANALYSIS_TYPE_TRANSFER: + case ANALYSIS_TYPE_DISTORTION: + case ANALYSIS_TYPE_NOISE: + case ANALYSIS_TYPE_POLE_ZERO: + case ANALYSIS_TYPE_SENSITIVITY: + case ANALYSIS_TYPE_FOURIER: + break; + case ANALYSIS_TYPE_NONE: + g_error (_ ("No analysis")); + break; + case ANALYSIS_TYPE_UNKNOWN: + g_error (_ ("Unknown analysis")); + break; } - } - else { + } else { if ((priv->analysis == NULL) || (isalpha (s[0]))) { if (priv->buf_count > 1) { schematic_log_append (priv->schematic, s); @@ -727,49 +686,39 @@ gnucap_parse (gchar *raw, gint len, OreganoGnuCap *gnucap) 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; - default: - break; - } - 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; - } + val = strtofloat (s); + switch (sdata->type) { + case ANALYSIS_TYPE_TRANSIENT: + priv->progress = val / data->transient.sim_length; + break; + case ANALYSIS_TYPE_AC: + priv->progress = (val - data->ac.start) / data->ac.sim_length; + break; + case ANALYSIS_TYPE_DC_TRANSFER: + priv->progress = val / data->ac.sim_length; + break; + default: + break; + } + if (priv->progress > 1.0) + priv->progress = 1.0; - _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); - } + 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 minimum and maximum 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; } priv->buf_count = 0; } } - diff --git a/src/engines/gnucap.h b/src/engines/gnucap.h index e5f9709..bdd0483 100644 --- a/src/engines/gnucap.h +++ b/src/engines/gnucap.h @@ -5,7 +5,7 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __GNUCAP_ENGINE @@ -34,28 +34,32 @@ #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_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)) +#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 { +struct _OreganoGnuCap +{ GObject parent; OreganoGnuCapPriv *priv; }; -struct _OreganoGnuCapClass { +struct _OreganoGnuCapClass +{ GObjectClass parent; }; -GType oregano_gnucap_get_type (void); +GType oregano_gnucap_get_type (void); OreganoEngine *oregano_gnucap_new (Schematic *sm); #endif diff --git a/src/engines/netlist-helper.c b/src/engines/netlist-helper.c index 478d333..851a605 100644 --- a/src/engines/netlist-helper.c +++ b/src/engines/netlist-helper.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <Lorber.Marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <stdio.h> @@ -47,25 +51,23 @@ #include "errors.h" #include "dialogs.h" -#define NG_DEBUG(s) if (0) g_print ("NG: %s\n", s) - -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 netlist_helper_foreach_model_free (gpointer key, gpointer model, - gpointer user_data); -static gboolean netlist_helper_foreach_model_save (gpointer key, gpointer model, - gpointer user_data); -static char * netlist_helper_linebreak (char *str); -static void netlist_helper_nl_node_traverse (Node *node, GSList **lst); - -static void -netlist_helper_nl_wire_traverse (Wire *wire, GSList **lst) +#include "debug.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 netlist_helper_foreach_model_free (gpointer key, gpointer model, + gpointer user_data); +static gboolean netlist_helper_foreach_model_save (gpointer key, gpointer model, + gpointer user_data); +static char *netlist_helper_linebreak (char *str); +static void netlist_helper_nl_node_traverse (Node *node, GSList **lst); + +static void netlist_helper_nl_wire_traverse (Wire *wire, GSList **lst) { - GSList *nodes; + GSList *iter; + GSList *iter2; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); @@ -75,26 +77,27 @@ netlist_helper_nl_wire_traverse (Wire *wire, GSList **lst) wire_set_visited (wire, TRUE); - for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { - GSList *pins; - Part *part; - Node *node = nodes->data; + for (iter = wire_get_nodes (wire); iter; iter = iter->next) { + Node *node = iter->data; + + for (iter2 = node->pins; iter2; iter2 = iter2->next) { + Pin *pin = iter2->data; + Part *part = PART (pin->part); - 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; + 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])); + if (template_split[0] != NULL) + (*lst) = g_slist_prepend (*lst, g_strdup (template_split[0])); g_strfreev (template_split); g_free (tmp); @@ -103,13 +106,11 @@ netlist_helper_nl_wire_traverse (Wire *wire, GSList **lst) netlist_helper_nl_node_traverse (node, lst); } - g_slist_free_full (nodes, g_object_unref); } -static void -netlist_helper_nl_node_traverse (Node *node, GSList **lst) +static void netlist_helper_nl_node_traverse (Node *node, GSList **lst) { - GSList *wires; + GSList *iter; g_return_if_fail (node != NULL); g_return_if_fail (IS_NODE (node)); @@ -119,44 +120,46 @@ netlist_helper_nl_node_traverse (Node *node, GSList **lst) node_set_visited (node, TRUE); - for (wires = node->wires; wires; wires = wires->next) { - Wire *wire = wires->data; + for (iter = node->wires; iter; iter = iter->next) { + Wire *wire = iter->data; netlist_helper_nl_wire_traverse (wire, lst); } - g_slist_free_full (wires, g_object_unref); } -static GSList * -netlist_helper_get_clamp_parts (NodeStore *store, Node *node) +/** + * traverses the netlist and picks up all clamp type parts + * + * TODO this is part of the "logic" that determines which "nodes" (or V levels) + * TODO will be plotted (or: not discarded) + * + * @returns a list of parts whose type is clamp + */ +static GSList *netlist_helper_get_clamp_parts (NodeStore *store, Node *node) { - GList *wires; - GSList *list; + GList *iter; + GSList *iter2; Wire *wire; GSList *ret = NULL; - if (!node) return NULL; + if (!node) + return NULL; + + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL); - node_store_node_foreach (store, (GHFunc *) netlist_helper_node_foreach_reset, - NULL); - for (wires = store->wires; wires; wires = wires->next) { - wire = wires->data; + for (iter = store->wires; iter; iter = iter->next) { + wire = iter->data; wire_set_visited (wire, FALSE); } - list = node->wires; - while (list) { - netlist_helper_nl_wire_traverse (list->data, &ret); - list = list->next; + for (iter2 = node->wires; iter2; iter2 = iter2->next) { + wire = iter2->data; + netlist_helper_nl_wire_traverse (wire, &ret); } - g_list_free_full (wires, g_object_unref); - g_slist_free_full (list, g_object_unref); - return ret; } -void -netlist_helper_init_data (NetlistData *data) +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); @@ -167,18 +170,16 @@ netlist_helper_init_data (NetlistData *data) data->node_and_number_list = NULL; } -void -netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data) +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) +void netlist_helper_wire_traverse (Wire *wire, NetlistData *data) { - GSList *nodes; + GSList *iter; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); @@ -188,23 +189,22 @@ netlist_helper_wire_traverse (Wire *wire, NetlistData *data) wire_set_visited (wire, TRUE); - for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { - Node *node = nodes->data; + for (iter = wire_get_nodes (wire); iter; iter = iter->next) { + Node *node = iter->data; netlist_helper_node_traverse (node, data); } - g_slist_free_full (nodes, g_object_unref); } -void -netlist_helper_node_traverse (Node *node, NetlistData *data) +void netlist_helper_node_traverse (Node *node, NetlistData *data) { - GSList *wires, *pins; + GSList *iter; gchar *prop; NodeAndNumber *nan; g_return_if_fail (node != NULL); g_return_if_fail (IS_NODE (node)); + g_return_if_fail (data != NULL); if (node_is_visited (node)) return; @@ -215,12 +215,11 @@ netlist_helper_node_traverse (Node *node, NetlistData *data) 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); + 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; + for (iter = node->pins; iter; iter = iter->next) { + Pin *pin = iter->data; // First see if the pin belongs to an "internal", special part. prop = part_get_property (pin->part, "internal"); @@ -246,79 +245,13 @@ netlist_helper_node_traverse (Node *node, NetlistData *data) marker->node_nr = data->node_nr; marker->name = value; data->mark_list = g_slist_prepend (data->mark_list, marker); - } - else if (!g_ascii_strcasecmp (prop, "ground")) { - data->gnd_list = g_slist_prepend (data->gnd_list, GINT_TO_POINTER (data->node_nr)); - } - else if (!g_ascii_strcasecmp (prop, "point")) { - data->clamp_list = g_slist_prepend (data->clamp_list, GINT_TO_POINTER (data->node_nr)); - } - else if (!g_ascii_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; - } - g_list_free_full (list, g_object_unref); - } -#endif - netlist_helper_node_traverse (opposite_node, data); - } + } else if (g_ascii_strcasecmp (prop, "ground") == 0) { + data->gnd_list = g_slist_prepend (data->gnd_list, GINT_TO_POINTER (data->node_nr)); - if (g_ascii_strcasecmp (prop, "point")) { - g_free (prop); - continue; + } else if (g_ascii_strcasecmp (prop, "clamp") == 0) { + data->clamp_list = + g_slist_prepend (data->clamp_list, GINT_TO_POINTER (data->node_nr)); } g_free (prop); } @@ -335,19 +268,18 @@ netlist_helper_node_traverse (Node *node, NetlistData *data) } // Traverse the wires at this node. - for (wires = node->wires; wires; wires = wires->next) { - Wire *wire = wires->data; + for (iter = node->wires; iter; iter = iter->next) { + Wire *wire = iter->data; netlist_helper_wire_traverse (wire, data); } - g_slist_free_full (wires, g_object_unref); - g_slist_free_full (pins, g_object_unref); } -void -netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data) +void netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data) { Node *node = value; + g_return_if_fail (data != NULL); + // Only visit nodes that are not already visited. if (node_is_visited (node)) return; @@ -357,8 +289,7 @@ netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData data->node_nr++; } -gint -compare_marker (gconstpointer a, gconstpointer b) +gint compare_marker (gconstpointer a, gconstpointer b) { const Marker *ma; gint node_nr; @@ -372,28 +303,23 @@ compare_marker (gconstpointer a, gconstpointer b) return 1; } -gboolean -netlist_helper_foreach_model_save (gpointer key, gpointer model, - gpointer user_data) +gboolean netlist_helper_foreach_model_save (gpointer key, gpointer model, gpointer user_data) { - GList **l = (GList **)user_data; + GList **l = user_data; (*l) = g_list_prepend (*l, g_strdup ((gchar *)key)); return TRUE; } -gboolean -netlist_helper_foreach_model_free (gpointer key, gpointer model, - gpointer user_data) +gboolean netlist_helper_foreach_model_free (gpointer key, gpointer model, gpointer user_data) { g_free (key); return FALSE; } -char * -netlist_helper_linebreak (char *str) +char *netlist_helper_linebreak (char *str) { char **split, *tmp; GString *out; @@ -410,8 +336,7 @@ netlist_helper_linebreak (char *str) out = g_string_append_c (out, '\n'); out = g_string_append (out, split[i] + 1); } - } - else { + } else { out = g_string_append (out, split[i]); } @@ -424,28 +349,44 @@ netlist_helper_linebreak (char *str) return tmp; } -void -netlist_helper_create (Schematic *sm, Netlist *out, GError **error) +void update_schematic(Schematic *sm) { + char *version = schematic_get_version(sm); + if (version == NULL) { + NodeStore *store = schematic_get_store(sm); + + for (GList *iter = store->parts; iter; iter = iter->next) { + int node_ctr = 1; + Part *part = iter->data; + update_connection_designators(part, part_get_property_ref(part, "template"), &node_ctr); + } + } + schematic_set_version(sm, VERSION); + + return; +} + +// FIXME this one piece of ugly+bad code +void netlist_helper_create (Schematic *sm, Netlist *out, GError **error) { NetlistData data; - GList *parts, *wires, *list; + GList *iter; Part *part; - gint pin_nr, num_pins, num_nodes, num_gnd_nodes, i, j, num_clamps; + gint pin_nr, 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; + + for (iter = store->wires; iter; iter = iter->next) { + Wire *wire = iter->data; wire_set_visited (wire, FALSE); } @@ -458,82 +399,74 @@ netlist_helper_create (Schematic *sm, Netlist *out, GError **error) // 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.")); + g_set_error (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND, + _ ("At least one GND is required. Add at least one and try again.")); } else 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.")); + // FIXME put a V/I clamp on each and every subtree + // FIXME and let the user toggle visibility in the plot window + // FIXME see also TODO/FIXME above + g_set_error (error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP, + _ ("No test clamps found. Add at least one and try again.")); } else { 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); - + + // 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 + 1); + node2real[num_nodes + 1] = NULL; // so we can use g_strfreev + 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))) { + } 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 { + } 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) + // Fill in the netlist node names for all the used nodes. + for (iter = data.node_and_number_list; iter; iter = iter->next) { + NodeAndNumber *nan = iter->data; + if (nan->node_nr > 0) { 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) { + for (iter = store->parts; iter; iter = iter->next) { + part = iter->data; + gchar *tmp, *internal; GString *str; - - part = parts->data; + internal = part_get_property (part, "internal"); if (internal != NULL) { gint node_nr; Pin *pins; - if (g_ascii_strcasecmp (internal, "point") != 0) { + if (g_ascii_strcasecmp (internal, "clamp") != 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 { + g_warning ("Couldn't find part, pin_nr %d.", 0); + } else { // need to substrac 1, netlist starts in 0, and node_nr in 1 pins[0].node_nr = atoi (node2real[node_nr]); } @@ -547,144 +480,133 @@ netlist_helper_create (Schematic *sm, Netlist *out, GError **error) } template = part_property_expand_macros (part, tmp); - NG_DEBUG (g_strdup_printf ("Template: '%s'\n" "macro : '%s'\n", tmp, template)); - if (tmp != NULL) g_free (tmp); + NG_DEBUG ("Template: '%s'\n" + "macro : '%s'\n", + tmp, template); + g_free (tmp); tmp = netlist_helper_linebreak (template); - if (template != NULL) g_free (template); + 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; + + + + GRegex *regex; + GMatchInfo *match_info; + GError *error = NULL; + + regex = g_regex_new("%\\d*", 0, 0, NULL); + g_regex_match_full(regex, template, -1, 0, 0, &match_info, &error); + template_split = g_regex_split(regex, template, 0); 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 , ' '); - NG_DEBUG (g_strdup_printf ("str: %s\n", str->str)); - } + NG_DEBUG ("Reading pins.\n)"); + + int i; + for (i = 0; g_match_info_matches(match_info); i++) { + g_string_append (str, template_split[i]); - NG_DEBUG (g_strdup_printf ("Reading %d pins.\n)", num_pins)); + gchar *word = g_match_info_fetch(match_info, 0); - for (pin_nr = 0; pin_nr < num_pins; pin_nr++) { + + pin_nr = g_ascii_strtoll(word + 1, NULL, 10) - 1; gint node_nr = 0; node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[pin_nr])); if (!node_nr) { - gchar * tmp = g_strdup_printf (_("Couldn't find part, pin_nr %d."), pin_nr); - oregano_error_with_title (_("Problem of library:"), tmp); - return; - } - else { + g_set_error (&error, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_SUCH_PART, + _ ("Could not find part in library, pin #%d."), pin_nr); + return; // FIXME wtf?? this leaks like hell and did for ages! + } 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, ' '); - NG_DEBUG (g_strdup_printf ("str: %s\n", str->str)); - i++; + NG_DEBUG ("str: %s\n", str->str); } - while (template_split[i] != NULL) { - if (template_split[i][0] == '%') - break; - - g_string_append (str, template_split[i]); - g_string_append_c (str, ' '); - NG_DEBUG (g_strdup_printf ("str: %s\n", str->str)); - i++; - } + g_free(word); + g_match_info_next(match_info, NULL); } - - NG_DEBUG (g_strdup_printf ("Done with pins, i = %d\n", i)); - - while (template_split[i] != NULL) { - if (template_split[i][0] == '%') - break; + g_match_info_free(match_info); + g_free (template); + template = NULL; + g_regex_unref(regex); + if (template_split[i] != NULL) { g_string_append (str, template_split[i]); - g_string_append_c (str, ' '); - NG_DEBUG (g_strdup_printf ("str: %s\n", str->str)); - i++; } - g_strfreev (template_split); - NG_DEBUG (g_strdup_printf ("str: %s\n", str->str)); + if (error != NULL) { + g_printerr("Error while matching: %s\n", error->message); + g_error_free(error); + } + + NG_DEBUG ("Done with pins, i = %d\n", i); + + NG_DEBUG ("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_strfreev (node2real); - g_hash_table_foreach (data.models, (GHFunc)netlist_helper_foreach_model_save, - &out->models); + g_hash_table_foreach (data.models, (GHFunc)netlist_helper_foreach_model_save, &out->models); return; } g_hash_table_foreach (data.models, (GHFunc)netlist_helper_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); - - g_list_free_full (wires, g_object_unref); - g_list_free_full (list, g_object_unref); + g_list_free_full (data.node_and_number_list, (GDestroyNotify)g_free); } -char * -netlist_helper_create_analysis_string (NodeStore *store, gboolean do_ac) +char *netlist_helper_create_analysis_string (NodeStore *store, gboolean do_ac) { - GList *p; + GList *iter; + Part *part; gchar *prop; GString *out; gchar *ret; out = g_string_new (""); - for (p = node_store_get_parts (store); p; p = p->next) { - prop = part_get_property (p->data, "internal"); + for (iter = node_store_get_parts (store); iter; iter = iter->next) { + part = iter->data; + prop = part_get_property (part, "internal"); if (prop) { - if (!g_ascii_strcasecmp (prop, "point")) { - Pin *pins = part_get_pins (p->data); - g_free (prop); + if (!g_ascii_strcasecmp (prop, "clamp")) { + Pin *pins = part_get_pins (part); + + prop = part_get_property (part, "type"); - prop = part_get_property (p->data, "type"); - if (!g_ascii_strcasecmp (prop, "v")) { if (!do_ac) { g_string_append_printf (out, " %s(%d)", prop, pins[0].node_nr); - } - else { + } else { gchar *ac_type, *ac_db; - ac_type = part_get_property (p->data, "ac_type"); - ac_db = part_get_property (p->data, "ac_db"); - + ac_type = part_get_property (part, "ac_type"); + ac_db = part_get_property (part, "ac_db"); + if (!g_ascii_strcasecmp (ac_db, "true")) - g_string_append_printf (out, " %s%sdb(%d)", prop, ac_type, pins[0].node_nr); + 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); + g_string_append_printf (out, " %s%s(%d)", prop, ac_type, + pins[0].node_nr); } - } - else { + } else { Node *node; - SheetPos lookup_key; - SheetPos part_pos; - - item_data_get_pos (ITEM_DATA (p->data), &part_pos); + Coords lookup_key; + Coords part_pos; + + item_data_get_pos (ITEM_DATA (part), &part_pos); lookup_key.x = part_pos.x + pins[0].offset.x; lookup_key.y = part_pos.y + pins[0].offset.y; @@ -692,77 +614,143 @@ netlist_helper_create_analysis_string (NodeStore *store, gboolean do_ac) 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; + GSList *lst, *iter; + iter = lst = netlist_helper_get_clamp_parts (store, node); + + for (; iter; iter = iter->next) { + g_string_append_printf (out, " i(%s)", (char *)iter->data); } - g_slist_free (lst); + g_slist_free_full (lst, g_free); // need to free this, we are owning it! } } } + g_free (prop); } } ret = out->str; g_string_free (out, FALSE); - g_list_free_full (p, g_object_unref); return ret; } -GSList * -netlist_helper_get_voltmeters_list (Schematic *sm, GError **error) +GSList *netlist_helper_get_voltmeters_list (Schematic *sm, GError **error, gboolean with_type) { Netlist netlist_data; - GError *local_error = 0; + GError *e = NULL; GSList *clamp_list = NULL; NodeStore *node_store; - GList *p; + GList *iter; gchar *prop; + Part *part; + Pin *pins; + gint i; + gchar *ac_type, *ac_db, *tmp; - netlist_helper_create (sm, &netlist_data, &local_error); - error = &local_error; + netlist_helper_create (sm, &netlist_data, &e); + if (e) { + g_propagate_error (error, e); + return NULL; + } node_store = netlist_data.store; - for (p = node_store_get_parts (node_store); p; p = p->next) { - prop = part_get_property (p->data, "internal"); + for (iter = node_store_get_parts (node_store); iter; iter = iter->next) { + part = iter->data; + prop = part_get_property (part, "internal"); if (prop) { - if (!g_ascii_strcasecmp (prop, "point")) { - Pin *pins = part_get_pins (p->data); + if (!g_ascii_strcasecmp (prop, "clamp")) { g_free (prop); - prop = part_get_property (p->data, "type"); - + prop = part_get_property (part, "type"); + if (!g_ascii_strcasecmp (prop, "v")) { - gchar * tmp; - tmp = g_strdup_printf ("%d", pins[0].node_nr); + pins = part_get_pins (part); + if (with_type) { + ac_type = part_get_property(part, "ac_type"); + ac_db = part_get_property(part, "ac_db"); + i = 0; + while (ac_type[i]) { + ac_type[i] = g_ascii_toupper(ac_type[i]); + i++; + } + if (!g_strcmp0(ac_db, "true")) + tmp = g_strdup_printf ("VDB(%d)", pins[0].node_nr); + else + tmp = g_strdup_printf ("V%s(%d)", ac_type, pins[0].node_nr); + g_free(ac_type); + g_free(ac_db); + } else { + tmp = g_strdup_printf ("%d", pins[0].node_nr); + } clamp_list = g_slist_prepend (clamp_list, tmp); - if (0) printf ("clamp_list = %s\n", tmp); - } + if (0) + printf ("clamp_list = %s\n", tmp); + } } + g_free(prop); } } if (0) { - clamp_list = g_slist_prepend (clamp_list, "3"); - clamp_list = g_slist_prepend (clamp_list, "2"); - clamp_list = g_slist_prepend (clamp_list, "1"); + clamp_list = g_slist_prepend (clamp_list, "3"); + clamp_list = g_slist_prepend (clamp_list, "2"); + clamp_list = g_slist_prepend (clamp_list, "1"); } if (0) { GSList *slist = NULL; gchar *text = NULL; - + slist = g_slist_copy (clamp_list); text = g_strdup_printf ("V(%d)", atoi (slist->data)); slist = slist->next; - while (slist) - { + while (slist) { text = g_strdup_printf ("%s V(%d)", text, atoi (slist->data)); slist = slist->next; } - if (0) printf ("clamp_list = %s\n", text); + if (0) + printf ("clamp_list = %s\n", text); } - g_list_free_full (p, g_object_unref); - + return clamp_list; } + +GSList *netlist_helper_get_voltage_sources_list (Schematic *sm, GError **error, gboolean ac_only) +{ + Netlist netlist_data; + GError *e = NULL; + GSList *sources_list = NULL; + NodeStore *node_store; + GList *iter; + gchar *prop; + Part *part; + + netlist_helper_create (sm, &netlist_data, &e); + if (e) { + g_propagate_error (error, e); + return NULL; + } + + node_store = netlist_data.store; + + for (iter = node_store_get_parts (node_store); iter; iter = iter->next) { + part = iter->data; + prop = part_get_property (part, "Refdes"); + if (prop) { + if (prop[0] == 'V' && (prop[1] >= '0' && prop[1] <= '9')) { + gchar *tmp; + tmp = g_strdup (&prop[1]); + if (ac_only) { + g_free(prop); + prop = part_get_property (part, "Frequency"); + if (prop) + sources_list = g_slist_append (sources_list, tmp); + } else { + sources_list = g_slist_append (sources_list, tmp); + } + if (0) + printf ("sources_list = %s\n", tmp); + } + g_free(prop); + } + } + + return sources_list; +} diff --git a/src/engines/netlist-helper.h b/src/engines/netlist-helper.h index f0dbc33..5847dfa 100644 --- a/src/engines/netlist-helper.h +++ b/src/engines/netlist-helper.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2010 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NETLIST_HELPER_H #define __NETLIST_HELPER_H @@ -37,40 +39,45 @@ #include "schematic.h" #include "sim-settings.h" -typedef struct { - gint node_nr; ///< Node number +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; + 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; +typedef struct +{ + gchar *cmd; + gchar *title; + GString *template; SimSettings *settings; - NodeStore *store; - GList *models; + NodeStore *store; + GList *models; } Netlist; -typedef struct { - gint node_nr; +typedef struct +{ + gint node_nr; gchar *name; } Marker; -typedef struct { - gint node_nr; +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_analysis_string (NodeStore *store, - gboolean do_ac); -GSList * netlist_helper_get_voltmeters_list (Schematic *sm, GError **error); +void update_schematic(Schematic *sm); +void netlist_helper_init_data (NetlistData *data); +void netlist_helper_create (Schematic *sm, Netlist *out, GError **error); +char *netlist_helper_create_analysis_string (NodeStore *store, gboolean do_ac); +GSList *netlist_helper_get_voltmeters_list (Schematic *sm, GError **error, gboolean with_type); +GSList *netlist_helper_get_voltage_sources_list (Schematic *sm, GError **error, gboolean ac_only); #endif diff --git a/src/engines/ngspice-analysis.c b/src/engines/ngspice-analysis.c index 79c18c9..9936a80 100644 --- a/src/engines/ngspice-analysis.c +++ b/src/engines/ngspice-analysis.c @@ -3,10 +3,14 @@ * * Authors: * Marc Lorber <Lorber.Marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -20,8 +24,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -36,65 +40,63 @@ #include "dialogs.h" #include "engine-internal.h" #include "ngspice-analysis.h" +#include "../tools/thread-pipe.h" +#include "../tools/cancel-info.h" -typedef enum { - STATE_IDLE, - IN_VARIABLES, - IN_VALUES, - STATE_ABORT -} ParseDataState; - -static gchar *analysis_names[] = { - "Operating Point" , - "Transient Analysis" , - "DC transfer characteristic" , - "AC Analysis" , - "Transfer Function" , - "Distortion Analysis" , - "Noise Analysis" , - "Pole-Zero Analysis" , - "Sensitivity Analysis" , - "Fourier analysis" , - "Unknown Analysis" , - NULL -}; - -#define SP_TITLE "oregano\n" -#define CPU_TIME "CPU time since last call:" - -#define TAGS_COUNT (sizeof (analysis_tags) / sizeof (struct analysis_tag)) -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) -#define IS_THIS_ITEM(str,item) (!strncmp (str,item,strlen(item))) +#define SP_TITLE "oregano\n" +#define CPU_TIME "CPU time since last call:" -typedef struct { - gchar *name; -} NGSPICE_Variable; +#define TAGS_COUNT (sizeof(analysis_tags) / sizeof(struct analysis_tag)) +#include "debug.h" +#define IS_THIS_ITEM(str, item) (!strncmp (str, item, strlen (item))) +#ifdef DEBUG_THIS +#undef DEBUG_THIS +#endif +#define DEBUG_THIS 1 -static NGSPICE_Variable * -get_variables (gchar *str, gint *count) +/** + * \brief extract the resulting variables from ngspice output + * + * In ngspice a number can terminate only with a space, + * while in spice3 a number can also terminate with a + * comma. + * + * In the Fourier analysis the name of the output ends + * with a colon. + * + * Tested function. + * + * @returns a GArray filled up doubles + */ +gchar **get_variables (const gchar *str, gint *count) { - NGSPICE_Variable *out; + g_return_val_if_fail (str, NULL); + + gchar **out; static gchar *tmp[100]; - gchar *ini, *fin; + const gchar *start, *end; gint i = 0; - i = 0; - ini = str; - - /* Put blank in advance */ - while (isspace(*ini)) ini++; - fin = ini; - while (*fin != '\0') { - if (isspace(*fin)) { - *fin = '\0'; - tmp[i] = g_strdup (ini); - *fin = ' '; + start = str; + while (isspace (*start)) + start++; + end = start; + while (*end != '\0') { + if (isspace (*end) || *end == ',' || *end == ':') { + // number ended, designate as such and replace the string + tmp[i] = g_strndup (start, (gsize)(end - start)); i++; - ini = fin; - while (isspace(*ini)) ini++; - fin = ini; - } - else fin++; + start = end; + while (isspace (*start) || *start == ',' || *start == ':') + start++; + end = start; + } else { + end++; + } + } + if (end > start) { + tmp[i] = g_strndup (start, (gsize)(end - start)); + i++; } if (i == 0) { @@ -102,95 +104,96 @@ get_variables (gchar *str, gint *count) return NULL; } - out = g_new0 (NGSPICE_Variable, i); + // append an extra NUL slot to allow using g_strfreev + out = g_new0 (gchar *, i + 1); (*count) = i; - for ( i=0; i<(*count); i++ ) { - out[i].name = tmp[i]; - } + memcpy (out, tmp, sizeof(gchar *) * i); + out[i] = NULL; return out; } -void -parse_dc_analysis (OreganoNgSpice *ngspice, gchar * tmp) +/** + * @resources: caller frees + */ +static ThreadPipe *parse_dc_analysis (NgspiceAnalysisResources *resources) { + ThreadPipe *pipe = resources->pipe; + gchar **buf = &resources->buf; + const SimSettings* const sim_settings = resources->sim_settings; + GList **analysis = resources->analysis; + guint *num_analysis = resources->num_analysis; + static SimulationData *sdata; static Analysis *data; - OreganoNgSpicePriv *priv = ngspice->priv; - SimSettings *sim_settings; - static gchar buf[256]; - gboolean found = FALSE; - NGSPICE_Variable *variables; - gint i, n=0, index=0; + gsize size; + gboolean found = FALSE; + gchar **variables; + gint i, n = 0, index = 0; gdouble val[10]; gdouble np1; - sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); + NG_DEBUG ("DC: result str\n>>>\n%s\n<<<", *buf); + data = g_new0 (Analysis, 1); - priv->current = sdata = SIM_DATA (data); - priv->analysis = g_list_append (priv->analysis, sdata); - priv->num_analysis = 1; - sdata->state = IN_VALUES; - sdata->type = DC_TRANSFER; + sdata = SIM_DATA (data); + sdata->type = ANALYSIS_TYPE_DC_TRANSFER; sdata->functions = NULL; - np1 = 1.; - ANALYSIS (sdata)->dc.start = - sim_settings_get_dc_start (sim_settings); - ANALYSIS (sdata)->dc.stop = - sim_settings_get_dc_stop (sim_settings); - ANALYSIS (sdata)->dc.step = - sim_settings_get_dc_step (sim_settings); - - np1 = (ANALYSIS (sdata)->dc.stop - - ANALYSIS (sdata)->dc.start) / ANALYSIS (sdata)->dc.step; + ANALYSIS (sdata)->dc.start = sim_settings_get_dc_start (sim_settings); + ANALYSIS (sdata)->dc.stop = sim_settings_get_dc_stop (sim_settings); + ANALYSIS (sdata)->dc.step = sim_settings_get_dc_step (sim_settings); + + np1 = (ANALYSIS (sdata)->dc.stop - ANALYSIS (sdata)->dc.start) / ANALYSIS (sdata)->dc.step; ANALYSIS (sdata)->dc.sim_length = np1; - - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + // Calculates the number of variables - variables = get_variables (buf, &n); + variables = get_variables (*buf, &n); + if (!variables) + return pipe; - n=n-1; - sdata->var_names = (char**) g_new0 (gpointer, n); - sdata->var_units = (char**) g_new0 (gpointer, n); + n = n - 1; + sdata->var_names = (char **)g_new0 (gpointer, n); + sdata->var_units = (char **)g_new0 (gpointer, n); sdata->var_names[0] = g_strdup ("Voltage sweep"); - sdata->var_units[0] = g_strdup (_("voltage")); - sdata->var_names[1] = g_strdup (variables[2].name); - sdata->var_units[1] = g_strdup (_("voltage")); - - sdata->state = IN_VALUES; + sdata->var_units[0] = g_strdup (_ ("voltage")); + sdata->var_names[1] = g_strdup (variables[2]); + sdata->var_units[1] = g_strdup (_ ("voltage")); + sdata->n_variables = 2; - sdata->got_points = 0; - sdata->got_var = 0; - sdata->data = (GArray**) g_new0 (gpointer, 2); - - fgets (buf, 255, priv->inputfp); + sdata->got_points = 0; + sdata->got_var = 0; + sdata->data = (GArray **)g_new0 (gpointer, 2); + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); for (i = 0; i < 2; i++) - sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double)); - + sdata->data[i] = g_array_new (TRUE, TRUE, sizeof(double)); + sdata->min_data = g_new (double, n); sdata->max_data = g_new (double, n); // Read the data - for (i=0; i<2; i++) { + for (i = 0; i < 2; i++) { sdata->min_data[i] = G_MAXDOUBLE; sdata->max_data[i] = -G_MAXDOUBLE; } found = FALSE; - while ((fgets (buf, 255, priv->inputfp) != NULL) && !found) { - if (strlen (buf)<= 2) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); + while (((pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size)) != 0) && !found) { + if (strlen (*buf) <= 2) { + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); } - tmp=&buf[0]; - variables = get_variables (tmp, &i); - index = atoi (variables[0].name); - for (i=0; i<n; i++) { - val[i]=g_ascii_strtod (variables[i+1].name, NULL); + variables = get_variables (*buf, &i); + if (!variables) + return pipe; + index = atoi (variables[0]); + for (i = 0; i < n; i++) { + val[i] = g_ascii_strtod (variables[i + 1], NULL); sdata->data[i] = g_array_append_val (sdata->data[i], val[i]); if (val[i] < sdata->min_data[i]) sdata->min_data[i] = val[i]; @@ -199,436 +202,1000 @@ parse_dc_analysis (OreganoNgSpice *ngspice, gchar * tmp) } sdata->got_points++; sdata->got_var = 2; - if (index >= ANALYSIS (sdata)->dc.sim_length) found = TRUE; + if (index >= ANALYSIS (sdata)->dc.sim_length) + found = TRUE; } - return; - + + *analysis = g_list_append (*analysis, sdata); + (*num_analysis)++; + + return pipe; } -void -parse_transient_analysis (OreganoNgSpice *ngspice, gchar * tmp) +/** + * @resources: caller frees + */ +static ThreadPipe *parse_ac_analysis (NgspiceAnalysisResources *resources) { + ThreadPipe *pipe = resources->pipe; + gboolean is_vanilla = resources->is_vanilla; + gchar *scale, **variables, **buf = &resources->buf; + const SimSettings* const sim_settings = resources->sim_settings; + GList **analysis = resources->analysis; + guint *num_analysis = resources->num_analysis; static SimulationData *sdata; static Analysis *data; - OreganoNgSpicePriv *priv = ngspice->priv; - SimSettings *sim_settings; - static gchar buf[256]; - gboolean found = FALSE; - NGSPICE_Variable *variables; - gint i, n = 0, m = 0, p = 0; - gdouble val[10]; - GSList *nodes_list = NULL; - GError *error = NULL; - gint nodes_nb=0; - GArray **val_tmp1; - GArray **val_tmp2; - GArray **val_tmp3; - - sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); + gsize size; + gboolean found = FALSE; + gint i, n = 0, index = 0; + gdouble fstart, fstop, val[10]; + + NG_DEBUG ("AC: result str\n>>>\n%s\n<<<", *buf); + data = g_new0 (Analysis, 1); - priv->current = sdata = SIM_DATA (data); - priv->analysis = g_list_append (priv->analysis, sdata); - priv->num_analysis = 1; - sdata->state = IN_VALUES; - sdata->type = ANALYSIS_UNKNOWN; + sdata = SIM_DATA (data); + sdata->type = ANALYSIS_TYPE_AC; sdata->functions = NULL; - // Identify the number of analysis from the number of clamp - // ASCII format of ngspice only returns 3 columns at a time - nodes_list = netlist_helper_get_voltmeters_list (priv->schematic, &error); - for ( ; nodes_list; nodes_list = nodes_list->next ) { - nodes_nb++; - } + ANALYSIS (sdata)->ac.sim_length = 1.; + + ANALYSIS (sdata)->ac.start = fstart = sim_settings_get_ac_start (sim_settings); + ANALYSIS (sdata)->ac.stop = fstop = sim_settings_get_ac_stop (sim_settings); + + scale = sim_settings_get_ac_type (sim_settings); + if (!g_ascii_strcasecmp (scale, "LIN")) { + ANALYSIS (sdata)->ac.sim_length = (double) sim_settings_get_ac_npoints (sim_settings); + } else if (!g_ascii_strcasecmp (scale, "DEC")) { + ANALYSIS (sdata)->ac.sim_length = (double) sim_settings_get_ac_npoints (sim_settings) * log10 (fstop / fstart); + } else if (!g_ascii_strcasecmp (scale, "OCT")) { + ANALYSIS (sdata)->ac.sim_length = (double) sim_settings_get_ac_npoints (sim_settings) * log10 (fstop / fstart) / log10 (2); + } + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + // Calculates the number of variables + variables = get_variables (*buf, &n); + if (!variables) + return pipe; - if (nodes_nb > 9) { - oregano_warning (_("Only the 9 first nodes will be considered")); - nodes_nb = 9; - } - - tmp = g_strchug (tmp); - for (i = 0; analysis_names[i]; i++) { - if (g_str_has_prefix (g_strdup (tmp), analysis_names[i])) { - sdata->type = i; - } - } - - 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); - - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - - sdata->var_names = (char**) g_new0 (gpointer, nodes_nb + 1); - sdata->var_units = (char**) g_new0 (gpointer, nodes_nb + 1); - variables = get_variables (buf, &n); n = n - 1; - sdata->var_names[0] = g_strdup (_("Time")); - sdata->var_units[0] = g_strdup (_("time")); - for (i = 1; i < n; i++) { - sdata->var_names[i] = g_strdup (variables[i+1].name); - sdata->var_units[i] = g_strdup (_("voltage")); - } - sdata->state = IN_VALUES; - sdata->n_variables = nodes_nb+1; - sdata->got_points = 0; - sdata->got_var = 0; - sdata->data = (GArray**) g_new0 (gpointer, nodes_nb+1); - - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - - for (i = 0; i < nodes_nb+1; i++) - sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double)); - - val_tmp1 = (GArray**) g_new0 (gpointer, 4); - val_tmp2 = (GArray**) g_new0 (gpointer, 4); - val_tmp3 = (GArray**) g_new0 (gpointer, 4); - for (i = 0; i < 4; i++) { - val_tmp1[i] = g_array_new (TRUE, TRUE, sizeof (double)); - val_tmp2[i] = g_array_new (TRUE, TRUE, sizeof (double)); - val_tmp3[i] = g_array_new (TRUE, TRUE, sizeof (double)); - } - - sdata->min_data = g_new (double, nodes_nb+1); - sdata->max_data = g_new (double, nodes_nb+1); + sdata->var_names = (char **)g_new0 (gpointer, n); + sdata->var_units = (char **)g_new0 (gpointer, n); + sdata->var_names[0] = g_strdup ("Frequency"); + sdata->var_units[0] = g_strdup (_ ("frequency")); + sdata->var_names[1] = g_strdup (variables[2]); + sdata->var_units[1] = g_strdup (_ ("voltage")); + + sdata->n_variables = 2; + sdata->got_points = 0; + sdata->got_var = 0; + sdata->data = (GArray **)g_new0 (gpointer, 2); + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + for (i = 0; i < 2; 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); // Read the data - for (i=0; i < nodes_nb +1; i++) { + for (i = 0; i < 2; i++) { sdata->min_data[i] = G_MAXDOUBLE; sdata->max_data[i] = -G_MAXDOUBLE; } found = FALSE; - while ((fgets (buf, 255, priv->inputfp) != NULL) && !found) { - gint k=0; - if (strlen (buf)<= 2) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); + while (((pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size)) != 0) && !found) { + if (strlen (*buf) <= 2) { + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); } - tmp=&buf[0]; - variables = get_variables (tmp, &k); - for (i=0; i<n; i++) { - val[i]=g_ascii_strtod (variables[i+1].name, NULL); - val_tmp1[i] = g_array_append_val (val_tmp1[i], val[i]); + + variables = get_variables (*buf, &i); + if (!variables) + return pipe; + + index = atoi (variables[0]); + for (i = 0; i < n; i++) { + if (i == 0) + val[i] = g_ascii_strtod (variables[i + 1], NULL); + if (is_vanilla && i > 0) + val[i] = g_ascii_strtod (variables[i + 2], NULL); + else + val[i] = g_ascii_strtod (variables[i + 1], NULL); + sdata->data[i] = g_array_append_val (sdata->data[i], val[i]); if (val[i] < sdata->min_data[i]) sdata->min_data[i] = val[i]; if (val[i] > sdata->max_data[i]) sdata->max_data[i] = val[i]; } - if (val[0] >= ANALYSIS (sdata)->transient.sim_length) found = TRUE; + sdata->got_points++; + sdata->got_var = 2; + if (index >= ANALYSIS (sdata)->ac.sim_length - 1) + found = TRUE; } - if (n < nodes_nb + 1) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - variables = get_variables (buf, &m); - m=m-1; - - for (i=1; i<m; i++) { - sdata->var_names[i+n-1] = g_strdup (variables[i+1].name); - sdata->var_units[i+n-1] = g_strdup (_("voltage")); + *analysis = g_list_append (*analysis, sdata); + (*num_analysis)++; + + return pipe; +} + +/** + * Structure that helps parsing the output of ngspice. + * + * @name: The name (handled like an ID) of the column. + * @unit: The physical unit. it can have one of the following values: + * "none", "time", "voltage", "current", "unknown" + * @data: The data. + * @min: The min value of the data. + * @max: The max value of the data. + */ +typedef struct { + GString *name; + GString *unit; + GArray *data; + gdouble min; + gdouble max; +} NgspiceColumn; + +/** + * Structure that helps parsing the output of ngspice. + * + * @ngspice_columns: The columns. + * @current_variables: New columns are appended to the end + * of the columns array. Because the index- and time-column + * are displayed repeatedly with every 3 new columns, there + * is needed an information how to interpret the incoming + * data to append it to the right columns. The current_variables + * array maps the position of the input data in the file + * to the position of the related column position in the table. + */ +typedef struct { + GPtrArray *ngspice_columns; + GArray *current_variables; +} NgspiceTable; + +/** + * Allocates memory. + * + * @name: The name (ID) of the new column + * @predicted_size: The predicted end size of the new column. + */ +static NgspiceColumn *ngspice_column_new(const gchar *name, guint predicted_size) { + NgspiceColumn *ret_val = malloc(sizeof(NgspiceColumn)); + + ret_val->name = g_string_new(name); + + if (g_str_has_prefix(name, "Index")) + ret_val->unit = g_string_new("none"); + else if (g_str_has_prefix(name, "time")) + ret_val->unit = g_string_new("time"); + else if (g_str_has_prefix(name, "V") || g_str_has_prefix(name, "v")) + ret_val->unit = g_string_new("voltage"); + else if (g_str_has_suffix(name, "#branch")) + ret_val->unit = g_string_new("current"); + else + ret_val->unit = g_string_new("unknown"); + + ret_val->data = g_array_sized_new(TRUE, TRUE, sizeof(gdouble), predicted_size); + ret_val->min = G_MAXDOUBLE; + ret_val->max = -G_MAXDOUBLE; + + return ret_val; +} + +/** + * Frees memory. + */ +static void ngspice_column_destroy(gpointer ptr) { + NgspiceColumn *column = (NgspiceColumn *)ptr; + if (column == NULL) + return; + if (column->name != NULL) + g_string_free(column->name, TRUE); + if (column->unit != NULL) + g_string_free(column->unit, TRUE); + g_free(column); +} + +/** + * Allocates memory. + */ +static NgspiceTable *ngspice_table_new() { + NgspiceTable *ret_val = malloc(sizeof(NgspiceTable)); + ret_val->ngspice_columns = g_ptr_array_new_with_free_func(ngspice_column_destroy); + ret_val->current_variables = g_array_new(TRUE, TRUE, sizeof(guint)); + return ret_val; +} + +/** + * Frees the memory. + */ +static void ngspice_table_destroy(NgspiceTable *ngspice_table) { + + if (ngspice_table->ngspice_columns != NULL) { + guint len = ngspice_table->ngspice_columns->len; + + for (int i = 0; i < len; i++) { + NgspiceColumn *column = ngspice_table->ngspice_columns->pdata[i]; + if (column != NULL) + g_array_free(column->data, TRUE); } - fgets (buf, 255, priv->inputfp); - found = FALSE; - while ((fgets (buf, 255, priv->inputfp) != NULL) && !found) { - gint k=0; - if (strlen (buf)<= 2) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - } - tmp=&buf[0]; - variables = get_variables (tmp, &k); - for (i = 0; i < m; i++) { - val[i]=g_ascii_strtod (variables[i+1].name, NULL); - val_tmp2[i] = g_array_append_val (val_tmp2[i], val[i]); - if (val[i] < sdata->min_data[i+n]) - sdata->min_data[i+n-1] = val[i]; - if (val[i] > sdata->max_data[i+n]) - sdata->max_data[i+n-1] = val[i]; + + g_ptr_array_free(ngspice_table->ngspice_columns, TRUE); + } + + if (ngspice_table->current_variables != NULL) + g_array_free(ngspice_table->current_variables, TRUE); + + g_free(ngspice_table); +} + +/** + * Converts "x...x#branch" to "I(x...x)". + * + * Let "x...x" be the name of a part in the ngspice netlist, + * then the current variable (current, that flows through + * that part) of that part is named + * "x...x#branch" in ngspice. To shorten the name and make + * it prettier, the name will be displayed as "I(x...x)" + * in Oregano (analogous to V(node_nr)). + */ +static void convert_variable_name(gchar **variable) { + gchar **splitted = g_regex_split_simple("\\#branch", *variable, 0, 0); + g_free(*variable); + *variable = g_strdup_printf("I(%s)", *splitted); + g_strfreev(splitted); +} + +/** + * Creates and appends new columns to the table, if there are new variables. + * The referenced columns are now the newly added columns. + * + * @predicted_size: The predicted end size of the new columns. It is assumed + * that the new columns will have the same size. + */ +static void ngspice_table_new_columns(NgspiceTable *ngspice_table, gchar **variables, guint predicted_size) { + if (ngspice_table->ngspice_columns->len > 0) { + NgspiceColumn *column = (NgspiceColumn *)ngspice_table->ngspice_columns->pdata[0]; + predicted_size = column->data->len; + } + + g_array_free(ngspice_table->current_variables, TRUE); + ngspice_table->current_variables = g_array_new(TRUE, TRUE, sizeof(guint)); + + for (gchar **variable = variables; *variable != NULL && **variable != '\n' && **variable != 0; variable++) { + if (g_str_has_suffix(*variable, "#branch")) + convert_variable_name(variable); + int i; + for (i = 0; i < ngspice_table->ngspice_columns->len; i++) { + if (!strcmp(*variable, ((NgspiceColumn *)ngspice_table->ngspice_columns->pdata[i])->name->str)) + break; + } + if (i == ngspice_table->ngspice_columns->len) { + NgspiceColumn *new_column = ngspice_column_new(*variable, predicted_size); + g_ptr_array_add(ngspice_table->ngspice_columns, new_column); + } + g_array_append_val(ngspice_table->current_variables, i); + } +} + +/** + * Appends a split line to the end of the current referenced columns. + */ +static void ngspice_table_add_data(NgspiceTable *ngspice_table, gchar **data) { + g_return_if_fail(data != NULL); + g_return_if_fail(*data != NULL); + if (**data == 0) + return; + g_return_if_fail(ngspice_table != NULL); + + guint64 index = g_ascii_strtoull(*data, NULL, 10); + for (int i = 0; data[i] != NULL && data[i][0] != 0 && data[i][0] != '\n' && i < ngspice_table->current_variables->len; i++) { + guint column_index = g_array_index(ngspice_table->current_variables, guint, i); + NgspiceColumn *column = (NgspiceColumn*)(ngspice_table->ngspice_columns->pdata[column_index]); + if (column->data->len > index) { + continue;//assert equal + } + gdouble new_content = g_ascii_strtod(data[i], NULL); + g_array_append_val(column->data, new_content); + if (new_content < column->min) + column->min = new_content; + if (new_content > column->max) + column->max = new_content; + } +} + +/** + * Counts the number of different guints. If a certain + * guint occurs more than once, it is counted as one. + */ +static guint get_real_len(GArray *array_guint) { + guint ret_val = 0; + for (guint i = 0; i < array_guint->len; i++) { + ret_val++; + for (guint j = 0; j < i; j++) { + if (g_array_index(array_guint, guint, i) == g_array_index(array_guint, guint, j)) { + ret_val--; + break; } - if (val[0] >= ANALYSIS (sdata)->transient.sim_length) found = TRUE; } - if (n + m -1 < nodes_nb + 1) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - variables = get_variables (buf, &p); - p=p-1; - - for (i=1; i<p; i++) { - sdata->var_names[i+n+m-2] = g_strdup (variables[i+1].name); - sdata->var_units[i+n+m-2] = g_strdup (_("voltage")); + } + + return ret_val; +} + +/** + * Returns the index that is currently parsed. + * + * The first referenced column is the string-index column, + * the second referenced column is the time. The index and + * the time column can already be filled up by earlier + * iterations, so the length of the third referenced column + * represents the index that is currently parsed. + */ +static guint get_current_index(NgspiceTable *ngspice_table) { + guint column_nr = g_array_index(ngspice_table->current_variables, guint, 2); + NgspiceColumn *column = g_ptr_array_index(ngspice_table->ngspice_columns, column_nr); + return column->data->len; +} + +typedef struct { + NgspiceTable *table; + ThreadPipe *pipe; + gboolean is_cancel; +} ParseTransientAnalysisReturnResources; + +/** + * @resources: caller frees + */ +static ParseTransientAnalysisReturnResources parse_transient_analysis_resources (NgspiceAnalysisResources *resources) +{ + gchar **buf = &resources->buf; + const SimSettings *sim_settings = resources->sim_settings; + guint *num_analysis = resources->num_analysis; + ProgressResources *progress_reader = resources->progress_reader; + guint64 no_of_data_rows = resources->no_of_data_rows_transient; + guint no_of_variables = resources->no_of_variables; + + + enum STATE { + NGSPICE_ANALYSIS_STATE_READ_DATA, + NGSPICE_ANALYSIS_STATE_DATA_SMALL_BLOCK_END, + NGSPICE_ANALYSIS_STATE_DATA_LARGE_BLOCK_END, + NGSPICE_ANALYSIS_STATE_DATA_END, + NGSPICE_ANALYSIS_STATE_READ_VARIABLES_NEW, + NGSPICE_ANALYSIS_STATE_READ_VARIABLES_OLD + }; + + g_mutex_lock(&progress_reader->progress_mutex); + progress_reader->progress = 0; + progress_reader->time = g_get_monotonic_time(); + g_mutex_unlock(&progress_reader->progress_mutex); + + gsize size; + + NgspiceTable *ngspice_table = ngspice_table_new(); + + ParseTransientAnalysisReturnResources ret_val; + ret_val.table = ngspice_table; + ret_val.pipe = resources->pipe; + ret_val.is_cancel = TRUE; + + enum STATE state = NGSPICE_ANALYSIS_STATE_DATA_LARGE_BLOCK_END; + + guint i = 0; + + do { + if (i % 50 == 0 && cancel_info_is_cancel(resources->cancel_info)) + return ret_val; + + switch (state) { + case NGSPICE_ANALYSIS_STATE_READ_VARIABLES_NEW: + { + gchar **splitted_line = g_regex_split_simple(" +", *buf, 0, 0); + + ngspice_table_new_columns(ngspice_table, splitted_line, no_of_data_rows); + + g_strfreev(splitted_line); + + state = NGSPICE_ANALYSIS_STATE_READ_VARIABLES_OLD; + break; } - fgets (buf, 255, priv->inputfp); - found = FALSE; - while ((fgets (buf, 255, priv->inputfp) != NULL) && !found) { - gint k=0; - if (strlen (buf)<= 2) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - } - tmp=&buf[0]; - variables = get_variables (tmp, &k); - for (i = 0; i < p; i++) { - val[i]=g_ascii_strtod (variables[i+1].name, NULL); - val_tmp3[i] = g_array_append_val (val_tmp3[i], val[i]); - if (val[i] < sdata->min_data[i+n+m-3]) - sdata->min_data[i+n+m-3] = val[i]; - if (val[i] > sdata->max_data[i+n+m-3]) - sdata->max_data[i+n+m-3] = val[i]; + case NGSPICE_ANALYSIS_STATE_READ_DATA: + { + gchar **splitted_line = g_regex_split_simple("\\t+|-{2,}", *buf, 0, 0); + + ngspice_table_add_data(ngspice_table, splitted_line); + + g_strfreev(splitted_line); + + if ((ret_val.pipe = thread_pipe_pop(ret_val.pipe, (gpointer *)buf, &size)) == NULL) + return ret_val; + + switch (*buf[0]) { + case '\f':{ + // estimate progress begin + guint len_of_current_variables = get_real_len(ngspice_table->current_variables) - 2; + guint len_of_current_columns = ngspice_table->ngspice_columns->len - 2; + guint count_of_variables_already_finished = len_of_current_columns - len_of_current_variables; + g_mutex_lock(&progress_reader->progress_mutex); + progress_reader->progress = (double)count_of_variables_already_finished / (double)no_of_variables + + (double)len_of_current_variables / (double)no_of_variables * + (double)get_current_index(ngspice_table) / (double)no_of_data_rows; + progress_reader->time = g_get_monotonic_time(); + g_mutex_unlock(&progress_reader->progress_mutex); + // estimate progress end + + state = NGSPICE_ANALYSIS_STATE_DATA_SMALL_BLOCK_END; + break;} + case '\n': + state = NGSPICE_ANALYSIS_STATE_DATA_END; + break; } - if (val[0] >= ANALYSIS (sdata)->transient.sim_length) found = TRUE; + break; } - } - } - found = FALSE; - while (!found) { - gdouble val0 = 0.; - for (i=0; i<nodes_nb+1; i++) { - // 0 = time - // From node 1 to node 3 - if (i < n) { - gdouble val=0; - val = g_array_index (val_tmp1[i], gdouble, sdata->got_points); - if (i == 0) val0 = val; - sdata->data[i] = g_array_append_val (sdata->data[i], val); + case NGSPICE_ANALYSIS_STATE_READ_VARIABLES_OLD: + { + if ((ret_val.pipe = thread_pipe_pop(ret_val.pipe, (gpointer *)buf, &size)) == NULL) + return ret_val; + + if (*buf[0] != '-') + state = NGSPICE_ANALYSIS_STATE_READ_DATA; + break; } - // From node 4 to node 6 - else if (i < n+m-1) { - gdouble val=0; - val = g_array_index (val_tmp2[i-n+1], gdouble, sdata->got_points); - sdata->data[i] = g_array_append_val (sdata->data[i], val); + case NGSPICE_ANALYSIS_STATE_DATA_SMALL_BLOCK_END: + { + if ((ret_val.pipe = thread_pipe_pop(ret_val.pipe, (gpointer *)buf, &size)) == NULL) + return ret_val; + + switch (*buf[0]) { + case 'I': + state = NGSPICE_ANALYSIS_STATE_READ_VARIABLES_OLD; + break; + case ' ': + state = NGSPICE_ANALYSIS_STATE_DATA_LARGE_BLOCK_END; + break; + } + break; } - // From node 7 to node 9 - else { - gdouble val=0; - val = g_array_index (val_tmp3[i-n-m+2], gdouble, sdata->got_points); - sdata->data[i] = g_array_append_val (sdata->data[i], val); + case NGSPICE_ANALYSIS_STATE_DATA_LARGE_BLOCK_END: + { + if ((ret_val.pipe = thread_pipe_pop(ret_val.pipe, (gpointer *)buf, &size)) == NULL) + return ret_val; + + if (*buf[0] == 'I') + state = NGSPICE_ANALYSIS_STATE_READ_VARIABLES_NEW; + break; } + case NGSPICE_ANALYSIS_STATE_DATA_END: + break; } - sdata->got_points++; - sdata->got_var = nodes_nb+1; - if (val0 >= ANALYSIS (sdata)->transient.sim_length) found = TRUE; + } while (state != NGSPICE_ANALYSIS_STATE_DATA_END); + + ret_val.is_cancel = FALSE; + + SimulationData *sdata = SIM_DATA (g_new0 (Analysis, 1)); + sdata->type = ANALYSIS_TYPE_TRANSIENT; + sdata->functions = NULL; + + gint nodes_nb = ngspice_table->ngspice_columns->len - 2; + + 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); + + sdata->var_names = g_new0 (gchar *, nodes_nb + 1); + sdata->var_units = g_new0 (gchar *, nodes_nb + 1); + sdata->data = g_new0 (GArray *, nodes_nb + 1); + sdata->min_data = g_new (gdouble, nodes_nb + 1); + sdata->max_data = g_new (gdouble, nodes_nb + 1); + + for (int i = 0; i < nodes_nb + 1; i++) { + NgspiceColumn *column = ngspice_table->ngspice_columns->pdata[i+1]; + sdata->var_names[i] = g_strdup(column->name->str); + sdata->var_units[i] = g_strdup(column->unit->str); + sdata->data[i] = column->data; + sdata->min_data[i] = column->min; + sdata->max_data[i] = column->max; + + ngspice_table->ngspice_columns->pdata[i+1] = NULL; + } + sdata->n_variables = nodes_nb + 1; + NgspiceColumn *column = ngspice_table->ngspice_columns->pdata[0]; + sdata->got_points = column->data->len; + sdata->got_var = nodes_nb + 1; + + (*num_analysis)++; + *resources->analysis = g_list_append (*resources->analysis, sdata); + + return ret_val; +} + +static ThreadPipe *parse_transient_analysis (NgspiceAnalysisResources *resources) { + ParseTransientAnalysisReturnResources ret_res = parse_transient_analysis_resources(resources); + + ngspice_table_destroy(ret_res.table); + + if (ret_res.is_cancel && ret_res.pipe) { + thread_pipe_set_read_eof(ret_res.pipe); + ret_res.pipe = NULL; } + + return ret_res.pipe; } -void -parse_fourier_analysis (OreganoNgSpice *ngspice, gchar * tmp) +/** + * @resources: caller frees + */ +static ThreadPipe *parse_fourier_analysis (NgspiceAnalysisResources *resources) { + ThreadPipe *pipe = resources->pipe; + gchar **buf = &resources->buf; + const SimSettings *sim_settings = resources->sim_settings; + GList **analysis = resources->analysis; + guint *num_analysis = resources->num_analysis; + static SimulationData *sdata; static Analysis *data; - OreganoNgSpicePriv *priv = ngspice->priv; - SimSettings *sim_settings; - static gchar buf[256]; - NGSPICE_Variable *variables; - gint i, n=0, freq, j, k; - gdouble val[10], mag[10][10]; + gsize size; + gchar **variables; + gint i, n = 0, j, k; + gdouble val[3]; gchar **node_ids; gchar *vout; - - sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); - + NG_DEBUG ("FOURIER: result str\n>>>\n%s\n<<<", *buf); - // New analysis data = g_new0 (Analysis, 1); sdata = SIM_DATA (data); - priv->current = sdata; - priv->analysis = g_list_append (priv->analysis, sdata); - sdata->state = IN_VALUES; + sdata->type = ANALYSIS_TYPE_FOURIER; sdata->functions = NULL; - priv->num_analysis++; - sdata->type = FOURIER; - tmp = g_strchug (tmp); - ANALYSIS (sdata)->fourier.freq = - sim_settings_get_fourier_frequency (sim_settings); + g_strchug (*buf); + ANALYSIS (sdata)->fourier.freq = sim_settings_get_fourier_frequency (sim_settings); + vout = sim_settings_get_fourier_vout (sim_settings); - node_ids = g_strsplit (g_strdup (vout), " ", 0); - for (i=0; node_ids[i]!= NULL; i++) {} - ANALYSIS (sdata)->fourier.nb_var = i+1; + node_ids = g_strsplit (vout, " ", 0); + for (i = 0; node_ids[i] != NULL; i++) { + } + g_strfreev (node_ids); g_free (vout); - + ANALYSIS (sdata)->fourier.nb_var = i + 1; n = ANALYSIS (sdata)->fourier.nb_var; sdata->n_variables = n; - sdata->var_names = (char**) g_new0 (gpointer, n); - sdata->var_units = (char**) g_new0 (gpointer, n); - variables = get_variables (tmp, &k); - sdata->var_names[0] = _("Frequency"); - sdata->var_names[1] = g_strdup (variables[3].name); - for (i = 0; i < n; i++) { - if (i == 0) - sdata->var_units[i] = g_strdup (_("frequency")); - else - sdata->var_units[i] = g_strdup (_("voltage")); - } - sdata->state = IN_VALUES; - sdata->got_points = 0; - sdata->got_var = 0; - sdata->data = (GArray**) g_new0 (gpointer, n); - - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); + sdata->var_names = (char **)g_new0 (gpointer, n); + sdata->var_units = (char **)g_new0 (gpointer, n); + sdata->var_names[0] = g_strdup ("Frequency"); + sdata->var_units[0] = g_strdup (_ ("frequency")); + + sdata->got_points = 0; + sdata->got_var = 0; + + sdata->data = (GArray **)g_new0 (gpointer, n); for (i = 0; i < n; i++) - sdata->data[i] = g_array_new (TRUE, TRUE, sizeof (double)); - + sdata->data[i] = g_array_new (TRUE, TRUE, sizeof(double)); + sdata->min_data = g_new (double, n); sdata->max_data = g_new (double, n); - // Read the data - for (i=0; i<n; i++) { - sdata->min_data[i] = G_MAXDOUBLE;; + for (i = 0; i < n; i++) { + sdata->min_data[i] = G_MAXDOUBLE; sdata->max_data[i] = -G_MAXDOUBLE; } - - for (j=0; j<10; j++) { - fgets (buf, 255, priv->inputfp); - sscanf (buf, "\t%d\t%d\t%lf\t%lf", &i, &freq, &val[0], &val[1]); - mag[j][0] = (gdouble) freq; - mag[j][1] = val[0]; - } - - for (j=2; j<n; j++) { - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - tmp = &buf[0]; - variables = get_variables (tmp, &k); - sdata->var_names[j] = g_strdup (variables[3].name); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - - for (k=0; k<10; k++) { - fgets (buf, 255, priv->inputfp); - sscanf (buf, "\t%d\t%d\t%lf\t%lf", &i, &freq, &val[0], &val[1]); - mag[k][j]=val[0]; + + // For each output voltage (plus the frequency for the x-axis), + // scan its data set + for (k = 1; k < n; k++) { + variables = get_variables (*buf, &i); + if (!variables) + return pipe; + + // Skip data set header (4 lines) + for (i = 0; i < 4; i++) + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + sdata->var_names[k] = g_strdup_printf ("mag(%s)", variables[3]); + sdata->var_units[k] = g_strdup (_ ("voltage")); + + // Scan data set for 10 harmonics + for (j = 0; j < 10; j++) { + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + if (!pipe) + return pipe; + + sscanf (*buf, "\t%d\t%lf\t%lf\t%lf", &i, &val[0], &val[1], &val[2]); + if (k == 1) { + sdata->data[0] = g_array_append_val (sdata->data[0], val[0]); + if (val[0] < sdata->min_data[0]) + sdata->min_data[0] = val[0]; + if (val[0] > sdata->max_data[0]) + sdata->max_data[0] = val[0]; + sdata->data[1] = g_array_append_val (sdata->data[1], val[1]); + if (val[1] < sdata->min_data[1]) + sdata->min_data[1] = val[1]; + if (val[1] > sdata->max_data[1]) + sdata->max_data[1] = val[1]; + sdata->got_points = sdata->got_points + 2; + } else { + sdata->data[k] = g_array_append_val (sdata->data[k], val[1]); + if (val[1] < sdata->min_data[k]) + sdata->min_data[k] = val[1]; + if (val[1] > sdata->max_data[k]) + sdata->max_data[k] = val[1]; + sdata->got_points++; + } } + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + if (!pipe) + return pipe; + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + if (!pipe) + return pipe; + + } + + sdata->got_var = n; + + *analysis = g_list_append (*analysis, sdata); + (*num_analysis)++; + + return pipe; +} + +/** + * @resources: caller frees + */ +static ThreadPipe *parse_noise_analysis (NgspiceAnalysisResources *resources) +{ + ThreadPipe *pipe = resources->pipe; + gboolean is_vanilla = resources->is_vanilla; + gchar *scale, **variables, **buf = &resources->buf; + const SimSettings* const sim_settings = resources->sim_settings; + GList **analysis = resources->analysis; + guint *num_analysis = resources->num_analysis; + static SimulationData *sdata; + static Analysis *data; + gsize size; + gboolean found = FALSE; + gint i, n = 0, index = 0; + gdouble fstart, fstop, val[10]; + + NG_DEBUG ("NOISE: result str\n>>>\n%s\n<<<", *buf); + + data = g_new0 (Analysis, 1); + sdata = SIM_DATA (data); + sdata->type = ANALYSIS_TYPE_NOISE; + sdata->functions = NULL; + + ANALYSIS (sdata)->noise.sim_length = 1.; + + ANALYSIS (sdata)->noise.start = fstart = sim_settings_get_noise_start (sim_settings); + ANALYSIS (sdata)->noise.stop = fstop = sim_settings_get_noise_stop (sim_settings); + + scale = sim_settings_get_noise_type (sim_settings); + if (!g_ascii_strcasecmp (scale, "LIN")) { + ANALYSIS (sdata)->noise.sim_length = (double) sim_settings_get_noise_npoints (sim_settings); + } else if (!g_ascii_strcasecmp (scale, "DEC")) { + ANALYSIS (sdata)->noise.sim_length = (double) sim_settings_get_noise_npoints (sim_settings) * log10 (fstop / fstart); + } else if (!g_ascii_strcasecmp (scale, "OCT")) { + ANALYSIS (sdata)->noise.sim_length = (double) sim_settings_get_noise_npoints (sim_settings) * log10 (fstop / fstart) / log10 (2); } - - for (i=0; i<n; i++) { + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + // Calculates the number of variables + variables = get_variables (*buf, &n); + if (!variables) + return pipe; + + n = n - 1; + sdata->var_names = (char **)g_new0 (gpointer, 3); + sdata->var_units = (char **)g_new0 (gpointer, 3); + sdata->var_names[0] = g_strdup ("Frequency"); + sdata->var_units[0] = g_strdup (_ ("frequency")); + sdata->var_names[1] = g_strdup ("Input Noise Spectrum"); + sdata->var_units[1] = g_strdup (_ ("psd")); + sdata->var_names[2] = g_strdup ("Output Noise Spectrum"); + sdata->var_units[2] = g_strdup (_ ("psd")); + + sdata->n_variables = 3; + sdata->got_points = 0; + sdata->got_var = 0; + sdata->data = (GArray **)g_new0 (gpointer, 3); + + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + for (i = 0; i < 3; i++) + sdata->data[i] = g_array_new (TRUE, TRUE, sizeof(double)); + + sdata->min_data = g_new (double, 3); + sdata->max_data = g_new (double, 3); + + // Read the data + for (i = 0; i < 3; i++) { sdata->min_data[i] = G_MAXDOUBLE; sdata->max_data[i] = -G_MAXDOUBLE; } - - for (j=0; j<10; j++) { - for (i=0; i<n; i++) { - sdata->data[i] = g_array_append_val (sdata->data[i], mag[j][i]); - if (mag[j][i] < sdata->min_data[i]) - sdata->min_data[i] = mag[j][i]; - if (mag[j][i] > sdata->max_data[i]) - sdata->max_data[i] = mag[j][i]; - sdata->got_points++; - sdata->got_var = n; + found = FALSE; + while (!found && ((pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size)) != 0)) { + if (strlen (*buf) <= 2) { + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + } + + variables = get_variables (*buf, &i); + if (!variables) + return pipe; + + index = atoi (variables[0]); + for (i = 0; i < 3; i++) { + val[i] = g_ascii_strtod (variables[i + 1], NULL); + sdata->data[i] = g_array_append_val (sdata->data[i], val[i]); + if (val[i] < sdata->min_data[i]) + sdata->min_data[i] = val[i]; + if (val[i] > sdata->max_data[i]) + sdata->max_data[i] = val[i]; + } + sdata->got_points++; + + if (index >= ANALYSIS (sdata)->noise.sim_length - 1) + found = TRUE; + } + + // Spice 3f5 is affected by a sort of bug and prints extra data + if (is_vanilla) { + while (((pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size)) != 0)) { + if (strlen (*buf) < 2) + break; } - NG_DEBUG (g_strdup_printf ("ngspice-analysis: mag[%d][0]=%lf\tmag[%d][1]=%lf\n", j, mag[j][0], j, mag[j][1])); } - return; + + sdata->got_var = 3; + + *analysis = g_list_append (*analysis, sdata); + (*num_analysis)++; + + return pipe; } -void -ngspice_parse (OreganoNgSpice *ngspice) +/** + * Stores all the data coming through the given pipe to the given file. + * This is needed because in case of failure, ngspice prints additional + * error information to stdout. + * + * Maybe the user wants to analyze the ngspice output by external software. + */ +void ngspice_save (const gchar *path_to_file, ThreadPipe *pipe, CancelInfo *cancel_info) { - OreganoNgSpicePriv *priv = ngspice->priv; - SimSettings *sim_settings; - static gchar buf[256]; - gchar * tmp = NULL; - gboolean found = FALSE; - gint i, analysis_type = 11; - gboolean transient_enabled = FALSE; - gboolean fourier_enabled = FALSE; - gboolean dc_enabled = FALSE; - //gboolean ac_enabled = FALSE; - - sim_settings = (SimSettings *)schematic_get_sim_settings (priv->schematic); - - transient_enabled = sim_settings_get_trans (sim_settings); - fourier_enabled = sim_settings_get_fourier (sim_settings); - //ac_enabled = sim_settings_get_ac (sim_settings); - dc_enabled = sim_settings_get_dc (sim_settings); - - priv->inputfp = fopen ("/tmp/netlist.lst", "r"); - - fgets (buf, 255, priv->inputfp); - fgets (buf, 255, priv->inputfp); - while ((fgets (buf, 255, priv->inputfp) != NULL) && !found) { - if (g_str_has_suffix (g_strdup (buf), SP_TITLE)) { - found = TRUE; - } + FILE *file = fopen(path_to_file, "w"); + gpointer buf = NULL; + gsize size; + + for (int i = 0; (pipe = thread_pipe_pop(pipe, &buf, &size)) != NULL; i++) { + if (size != 0) + fwrite(buf, 1, size, file); + /** + * cancel_info uses mutex operations, so it shouldn't be + * called to often. + */ + if (i % 50 == 0 && cancel_info_is_cancel(cancel_info)) + break; } - tmp = &buf[0]; - tmp = g_strchug (tmp); - NG_DEBUG (g_strdup_printf ("0 buf = %s\n", buf)); + fclose(file); + if (pipe != NULL) + thread_pipe_set_read_eof(pipe); +} - for (i = 0; analysis_names[i]; i++) { - if (g_str_has_prefix (g_strdup (tmp), analysis_names[i])) { - analysis_type = i; +static gboolean get_analysis_type(gchar *buf_in, AnalysisType *type_out) { + + int i = 0; + gchar *analysis_name = oregano_engine_get_analysis_name_by_type(i); + while (analysis_name) { + if (g_str_has_prefix (buf_in, analysis_name)) { + *type_out = i; + g_free(analysis_name); + return TRUE; } + g_free(analysis_name); + + i++; + analysis_name = oregano_engine_get_analysis_name_by_type(i); } - if (!analysis_names[analysis_type]) { - oregano_warning (_("No analysis found")); - return; + return FALSE; +} + +static guint64 parse_no_of_data_rows(gchar *line) { + gchar **splitted = g_regex_split_simple("No\\. of Data Rows \\: \\D*(\\d+)\\n", line, 0, 0); + guint64 no_of_data_rows = g_ascii_strtoull(splitted[1], NULL, 10); + g_strfreev(splitted); + + return no_of_data_rows; +} + +static guint parse_no_of_variables(ThreadPipe *pipe, gchar **buf) { + gsize size; + + for (int i = 0; i < 5; i++) + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + + guint no_of_variables = 0; + while (**buf != '\n') { + no_of_variables++; + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); } - if (transient_enabled) { - if (g_str_has_prefix (analysis_names[analysis_type], - "Transient Analysis")) { - parse_transient_analysis (ngspice, buf); + return no_of_variables; +} + +/** + * @resources: caller frees + */ +void ngspice_analysis (NgspiceAnalysisResources *resources) +{ + ThreadPipe *pipe = resources->pipe; + const SimSettings *sim_settings = resources->sim_settings; + AnalysisTypeShared *current = resources->current; + gboolean is_vanilla = resources->is_vanilla; + gchar **buf = &resources->buf; + gsize size; + gboolean end_of_output; + gboolean transient_enabled = sim_settings_get_trans (sim_settings); + gboolean fourier_enabled = sim_settings_get_fourier (sim_settings); + gboolean dc_enabled = sim_settings_get_dc (sim_settings); + gboolean ac_enabled = sim_settings_get_ac (sim_settings); + gboolean noise_enabled = sim_settings_get_noise (sim_settings); + + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + if (!is_vanilla) { + // Get the number of AC Analysis data rows + while (ac_enabled && !g_str_has_prefix (*buf, "No. of Data Rows : ")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + } + if (ac_enabled && g_str_has_prefix(*buf, "No. of Data Rows : ")) + resources->no_of_data_rows_ac = parse_no_of_data_rows(*buf); + + if (ac_enabled && thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + // Get the number of DC Analysis data rows + while (dc_enabled && !g_str_has_prefix (*buf, "No. of Data Rows : ")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } - else { - oregano_warning (_("Transient analysis expected not found")); + if (dc_enabled && g_str_has_prefix(*buf, "No. of Data Rows : ")) + resources->no_of_data_rows_dc = parse_no_of_data_rows(*buf); + + if (dc_enabled && thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + // Get the number of Operating Point Analysis data rows + while (!g_str_has_prefix (*buf, "No. of Data Rows : ")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } - } - - fgets (buf, 255, priv->inputfp); - NG_DEBUG (g_strdup_printf ("1 buf = %s\n", buf)); - tmp = &buf[0]; - tmp = g_strchug (tmp); - - analysis_type = ANALYSIS_UNKNOWN; - for (i = 0; analysis_names[i]; i++) { - if (g_str_has_prefix (g_strdup (tmp), analysis_names[i])) { - analysis_type = i; + if (g_str_has_prefix(*buf, "No. of Data Rows : ")) + resources->no_of_data_rows_op = parse_no_of_data_rows(*buf); + + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + // Get the number of Transient Analysis variables + while (transient_enabled && !g_str_has_prefix (*buf, "Initial Transient Solution")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } - } - if (fourier_enabled) { - if (g_str_has_prefix(analysis_names[analysis_type], "Fourier analysis")) { - parse_fourier_analysis (ngspice, buf); + if (transient_enabled && g_str_has_prefix(*buf, "Initial Transient Solution")) + resources->no_of_variables = parse_no_of_variables(pipe, buf); + + if (transient_enabled && thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + // Get the number of Transient Analysis data rows + while (transient_enabled && !g_str_has_prefix (*buf, "No. of Data Rows : ")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } - else { - oregano_warning (_("Fourier analysis expected not found")); + if (transient_enabled && g_str_has_prefix(*buf, "No. of Data Rows : ")) + resources->no_of_data_rows_transient = parse_no_of_data_rows(*buf); + + if (transient_enabled && thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + + // Get the number of Noise Analysis data rows + while (noise_enabled && !g_str_has_prefix (*buf, "No. of Data Rows : ")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } - } - fgets (buf, 255, priv->inputfp); - NG_DEBUG (g_strdup_printf ("2 buf = %s\n", buf)); - tmp = &buf[0]; - tmp = g_strchug (tmp); - - analysis_type = ANALYSIS_UNKNOWN; - for (i = 0; analysis_names[i]; i++) { - if (g_str_has_prefix (g_strdup (tmp), analysis_names[i])) { - analysis_type = i; + if (noise_enabled && g_str_has_prefix(*buf, "No. of Data Rows : ")) + resources->no_of_data_rows_noise = parse_no_of_data_rows(*buf); + } else { + while (!g_str_has_prefix (*buf, "Operating point information:")) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; } } - if (dc_enabled) { - if (g_str_has_prefix(analysis_names[analysis_type], "DC transfer characteristic")) { - parse_dc_analysis (ngspice, buf); + + while (!g_str_has_suffix (*buf, SP_TITLE)) { + if (thread_pipe_pop(pipe, (gpointer *)buf, &size) == NULL) + return; + } + + end_of_output = FALSE; + for (int i = 0; transient_enabled || fourier_enabled || dc_enabled || ac_enabled || noise_enabled; i++) { + + AnalysisType analysis_type = ANALYSIS_TYPE_NONE; + + do { + pipe = thread_pipe_pop(pipe, (gpointer *)buf, &size); + g_strstrip (*buf); + if (!g_ascii_strncasecmp (*buf, "CPU time", 8)) + end_of_output = TRUE; + NG_DEBUG ("%d buf = %s", i, *buf); + } while (pipe != NULL && !end_of_output && (*buf[0] == '*' || *buf[0] == '\0')); + + // The simulation has finished: no more analysis to parse + if (end_of_output) + break; + + if (!get_analysis_type(*buf, &analysis_type) && i == 0) { + oregano_warning ("No analysis found"); + break; } - else { - oregano_warning (_("DC Sweep expected but not found")); + + gboolean unexpected_analysis_found = FALSE; + + g_mutex_lock(¤t->mutex); + current->type = analysis_type; + g_mutex_unlock(¤t->mutex); + + switch (analysis_type) { + case ANALYSIS_TYPE_TRANSIENT: + pipe = parse_transient_analysis (resources); + transient_enabled = FALSE; + break; + case ANALYSIS_TYPE_FOURIER: + pipe = parse_fourier_analysis (resources); + fourier_enabled = FALSE; + break; + case ANALYSIS_TYPE_DC_TRANSFER: + pipe = parse_dc_analysis (resources); + dc_enabled = FALSE; + break; + case ANALYSIS_TYPE_AC: + pipe = parse_ac_analysis (resources); + ac_enabled = FALSE; + break; + case ANALYSIS_TYPE_NOISE: + pipe = parse_noise_analysis (resources); + noise_enabled = FALSE; + break; + default: + oregano_warning ("Unexpected analysis found"); + unexpected_analysis_found = TRUE; + break; } + + g_mutex_lock(¤t->mutex); + current->type = ANALYSIS_TYPE_NONE; + g_mutex_unlock(¤t->mutex); + + if (unexpected_analysis_found || pipe == NULL) + break; } - - fclose (priv->inputfp); + + if (pipe != NULL) + thread_pipe_set_read_eof(pipe); + + resources->pipe = NULL; } diff --git a/src/engines/ngspice-analysis.h b/src/engines/ngspice-analysis.h index 658be41..171b894 100644 --- a/src/engines/ngspice-analysis.h +++ b/src/engines/ngspice-analysis.h @@ -4,12 +4,14 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +25,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NGSPICE_ANALYSIS_H @@ -35,37 +37,80 @@ #include <sys/wait.h> #include <ctype.h> #include <glib/gi18n.h> +#include "../tools/thread-pipe.h" +#include "../tools/cancel-info.h" #include "ngspice.h" #include "netlist-helper.h" #include "dialogs.h" #include "engine-internal.h" #include "ngspice.h" +/* + * The file buffer size (recommended value + * is 512 bytes). + */ +#define BSIZE_SP 512 + +/** + * Progress is a shared variable between GUI thread + * that displays the progress bar and working thread + * which executes the heavy work. + */ +typedef struct { + gdouble progress; + // time (from g_get_monotonic_time) of the last writing access + gint64 time; + GMutex progress_mutex; +} ProgressResources; + +/** + * AnalysisType is a shared variable between progress + * bar displaying GUI thread and the working thread. + */ +typedef struct { + AnalysisType type; + GMutex mutex; +} AnalysisTypeShared; + +typedef struct { + ThreadPipe *pipe; + gchar *buf; + gboolean is_vanilla; + const SimSettings* sim_settings; + AnalysisTypeShared *current; + GList **analysis; + guint *num_analysis; + ProgressResources *progress_reader; + guint64 no_of_data_rows_ac; + guint64 no_of_data_rows_dc; + guint64 no_of_data_rows_op; + guint64 no_of_data_rows_transient; + guint64 no_of_data_rows_noise; + guint no_of_variables; + CancelInfo *cancel_info; +} NgspiceAnalysisResources; + // Parser STATUS -struct _OreganoNgSpicePriv { - GPid child_pid; - gint child_stdout; - gint child_error; - GIOChannel *child_iochannel; - GIOChannel *child_ioerror; - gint child_iochannel_watch; - gint child_ioerror_watch; - Schematic *schematic; +struct _OreganoNgSpicePriv +{ + gboolean is_vanilla; + + GPid child_pid; + + Schematic *schematic; + + gboolean aborted; + CancelInfo *cancel_info; - gboolean aborted; + GList *analysis; + guint num_analysis; + AnalysisTypeShared current; - GList *analysis; - gint num_analysis; - SimulationData *current; - double progress; - gboolean char_last_newline; - guint status; - guint buf_count; - // Added to store ngspice output into a file - // input for oregano... - FILE *inputfp; + ProgressResources progress_ngspice; + ProgressResources progress_reader; }; -void ngspice_parse (OreganoNgSpice *ngspice); +void ngspice_analysis (NgspiceAnalysisResources *resources); +void ngspice_save (const gchar *path_to_file, ThreadPipe *pipe, CancelInfo *cancel_info); #endif diff --git a/src/engines/ngspice-watcher.c b/src/engines/ngspice-watcher.c new file mode 100644 index 0000000..417ab74 --- /dev/null +++ b/src/engines/ngspice-watcher.c @@ -0,0 +1,715 @@ +/* + * ngspice-watcher.c + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <glib/gprintf.h> +#include <math.h> + +#include "../tools/thread-pipe.h" +#include "ngspice.h" +#include "ngspice-analysis.h" +#include "../log-interface.h" +#include "ngspice-watcher.h" + +enum ERROR_STATE { + ERROR_STATE_NO_ERROR, + ERROR_STATE_NO_SUCH_FILE_OR_DIRECTORY, + ERROR_STATE_ERROR_IN_NETLIST +}; + +//data wrapper +typedef struct { + GMutex mutex; + GCond cond; + gboolean boolean; +} IsNgspiceStderrDestroyed; + +//data wrapper +typedef struct { + gchar *path_to_file; + ThreadPipe *pipe; + CancelInfo *cancel_info; +} NgSpiceSaverResources; + +//data wrapper +typedef struct { + ThreadPipe *thread_pipe_worker; + ThreadPipe *thread_pipe_saver; +} NgSpiceWatchForkResources; + +//data wrapper +typedef struct { + NgSpiceWatchForkResources ngspice_watch_fork_resources; + guint cancel_info_count; + CancelInfo *cancel_info; +} NgSpiceWatchSTDOUTResources; + +//data wrapper +typedef struct { + ProgressResources *progress_ngspice; + LogInterface log; + const SimSettings *sim_settings; + enum ERROR_STATE *error_state; + IsNgspiceStderrDestroyed *is_ngspice_stderr_destroyed; +} NgSpiceWatchSTDERRResources; + +//data wrapper +typedef struct { + GThread *worker; + GThread *saver; + LogInterface log; + const void* emit_instance; + GPid *child_pid; + gboolean *aborted; + guint *num_analysis; + GMainLoop *main_loop; + gchar *netlist_file; + gchar *ngspice_result_file; + enum ERROR_STATE *error_state; + IsNgspiceStderrDestroyed *is_ngspice_stderr_destroyed; + CancelInfo *cancel_info; +} NgSpiceWatcherWatchNgSpiceResources; + +/** + * Wraps the heavy work of a function into a thread. + */ +static gpointer ngspice_worker (NgspiceAnalysisResources *resources) { + + ngspice_analysis(resources); + + cancel_info_unsubscribe(resources->cancel_info); + g_free(resources); + + return NULL; +} + +/** + * Wraps the heavy work of a function into a thread. + */ +static gpointer ngspice_saver (NgSpiceSaverResources *resources) +{ + ngspice_save(resources->path_to_file, resources->pipe, resources->cancel_info); + + cancel_info_unsubscribe(resources->cancel_info); + g_free(resources->path_to_file); + g_free(resources); + + return NULL; +} + +/** + * returns the number of strings in a NULL terminated array of strings + */ +static int get_count(gchar** array) { + int i = 0; + while (array[i] != NULL) + i++; + return i; +} + +/** + * adds the line number followed by a colon and a space at the beginning of each line + */ +static void add_line_numbers(gchar **string) { + gchar **splitted = g_regex_split_simple("\\n", *string, 0, 0); + GString *new_string = g_string_new(""); + //why -1? Because g_regex_split_simple adds one empty string too much at the end + //of the array. + int count = get_count(splitted) - 1; + int max_length = floor(log10((double) count)) + 1; + //splitted[i+1] != NULL (why not only i but i+1?) because g_regex_split_simple + //adds one empty string too much at the end of the array + for (int i = 0; splitted[i+1] != NULL; i++) + g_string_append_printf(new_string, "%0*d: %s\n", max_length, i+1, splitted[i]); + //remove the last newline, which was added additionally + new_string = g_string_truncate(new_string, new_string->len - 1); + g_free(*string); + *string = new_string->str; + g_string_free(new_string, FALSE); +} + +//data wrapper +typedef struct { + const void* emit_instance; + gchar *signal_name; +} NgspiceEmitData; + +/** + * Use this function to return the program main control flow to the + * main thread (which is the gui thread). + */ +static gboolean g_signal_emit_by_name_main_thread(NgspiceEmitData *data) { + const void* emit_instance = data->emit_instance; + gchar *signal_name = data->signal_name; + g_free(data); + g_signal_emit_by_name (G_OBJECT (emit_instance), signal_name); + g_free(signal_name); + return G_SOURCE_REMOVE; +} + +/** + * main function of the ngspice watcher + */ +static gpointer ngspice_watcher_main(GMainLoop *main_loop) { + g_main_loop_run(main_loop); + + // unrefs its GMainContext by 1 + g_main_loop_unref(main_loop); + + return NULL; +} + +/** + * forks data to file and heap + */ +static void ngspice_watcher_fork_data(NgSpiceWatchForkResources *resources, gpointer data, gsize size) { + thread_pipe_push(resources->thread_pipe_worker, data, size); + /** + * size_in = size - 1, because the trailing 0 of the string should not + * be written to file. + */ + thread_pipe_push(resources->thread_pipe_saver, data, size - 1); +} + +/** + * forks eof to file-pipe and heap-pipe + */ +static void ngspice_watcher_fork_eof(NgSpiceWatchForkResources *resources) { + thread_pipe_set_write_eof(resources->thread_pipe_worker); + thread_pipe_set_write_eof(resources->thread_pipe_saver); +} + +/** + * Does not handle input resources. + */ +static gboolean ngspice_watcher_watch_stdout_resources(GIOChannel *channel, GIOCondition condition, NgSpiceWatchSTDOUTResources *resources) { + gchar *str_return = NULL; + gsize length; + gsize terminator_pos; + GError *error = NULL; + + resources->cancel_info_count++; + if (resources->cancel_info_count % 50 == 0 && cancel_info_is_cancel(resources->cancel_info)) { + return G_SOURCE_REMOVE; + } + + GIOStatus status = g_io_channel_read_line(channel, &str_return, &length, &terminator_pos, &error); + if (error) { + gchar *message = g_strdup_printf ("spice pipe stdout: %s - %i", error->message, error->code); + g_printf("%s", message); + g_free(message); + g_clear_error (&error); + } else if (status == G_IO_STATUS_NORMAL && length > 0) { + ngspice_watcher_fork_data(&resources->ngspice_watch_fork_resources, str_return, length + 1); + } else if (status == G_IO_STATUS_EOF) { + return G_SOURCE_REMOVE; + } + if (str_return) + g_free(str_return); + return G_SOURCE_CONTINUE; +} + +/** + * ngspice reading source function + * + * reads the pipe (stdout) of ngspice + */ +static gboolean ngspice_watcher_watch_stdout(GIOChannel *channel, GIOCondition condition, NgSpiceWatchSTDOUTResources *resources) { + + gboolean g_source_continue = ngspice_watcher_watch_stdout_resources(channel, condition, resources); + + if (g_source_continue == G_SOURCE_CONTINUE) + return G_SOURCE_CONTINUE; + + ngspice_watcher_fork_eof(&resources->ngspice_watch_fork_resources); + g_source_destroy(g_main_current_source()); + cancel_info_unsubscribe(resources->cancel_info); + g_free(resources); + return G_SOURCE_REMOVE; +} + +static void ngspice_watch_ngspice_resources_finalize(NgSpiceWatcherWatchNgSpiceResources *resources) { + g_source_destroy(g_main_current_source()); + g_main_loop_quit(resources->main_loop); + g_spawn_close_pid (*resources->child_pid); + *resources->child_pid = 0; + + g_free(resources->ngspice_result_file); + g_free(resources->netlist_file); + g_free(resources->error_state); + g_mutex_clear(&resources->is_ngspice_stderr_destroyed->mutex); + g_cond_clear(&resources->is_ngspice_stderr_destroyed->cond); + g_free(resources); +} + +static void print_additional_info(LogInterface log, const gchar *ngspice_result_file, const gchar *netlist_file) { + log.log_append_error(log.log, "\n### spice output: ###\n\n"); + gchar *ngspice_error_contents = NULL; + gsize ngspice_error_length; + GError *ngspice_error_read_error = NULL; + + g_file_get_contents(ngspice_result_file, &ngspice_error_contents, &ngspice_error_length, &ngspice_error_read_error); + add_line_numbers(&ngspice_error_contents); + log.log_append_error(log.log, ngspice_error_contents); + g_free(ngspice_error_contents); + if (ngspice_error_read_error != NULL) + g_error_free(ngspice_error_read_error); + + gchar *netlist_contents = NULL; + gsize netlist_lentgh; + GError *netlist_read_error = NULL; + g_file_get_contents(netlist_file, &netlist_contents, &netlist_lentgh, &netlist_read_error); + add_line_numbers(&netlist_contents); + log.log_append_error(log.log, "\n\n### netlist: ###\n\n"); + log.log_append_error(log.log, netlist_contents); + g_free(netlist_contents); + if (netlist_read_error != NULL) + g_error_free(netlist_read_error); +} + +enum NGSPICE_WATCHER_RETURN_VALUE { + NGSPICE_WATCHER_RETURN_VALUE_DONE, + NGSPICE_WATCHER_RETURN_VALUE_ABORTED, + NGSPICE_WATCHER_RETURN_VALUE_CANCELED +}; + +/** + * Does not care about input resource handling. + */ +static enum NGSPICE_WATCHER_RETURN_VALUE +ngspice_watcher_watch_ngspice_resources (GPid pid, gint status, NgSpiceWatcherWatchNgSpiceResources *resources) { + GThread *worker = resources->worker; + GThread *saver = resources->saver; + LogInterface log = resources->log; + guint *num_analysis = resources->num_analysis; + enum ERROR_STATE *error_state = resources->error_state; + IsNgspiceStderrDestroyed *is_ngspice_stderr_destroyed = resources->is_ngspice_stderr_destroyed; + + // wait for stderr to finish reading + g_mutex_lock(&is_ngspice_stderr_destroyed->mutex); + while (!is_ngspice_stderr_destroyed->boolean) + g_cond_wait(&is_ngspice_stderr_destroyed->cond, &is_ngspice_stderr_destroyed->mutex); + g_mutex_unlock(&is_ngspice_stderr_destroyed->mutex); + + GError *exit_error = NULL; + gboolean exited_normal = g_spawn_check_exit_status(status, &exit_error); + if (exit_error != NULL) + g_error_free(exit_error); + + g_thread_join(worker); + + if (cancel_info_is_cancel(resources->cancel_info)) + return NGSPICE_WATCHER_RETURN_VALUE_CANCELED; + + + if (!exited_normal) { + // check for exit via return in main, exit() or _exit() of the child, see man + // waitpid(2) + // WIFEXITED(wstatus) + // returns true if the child terminated normally, that is, by call‐ + // ing exit(3) or _exit(2), or by returning from main(). + if (!(WIFEXITED (status))) + log.log_append_error(log.log, "### spice exited with exception ###\n"); + else + log.log_append_error(log.log, "### spice exited abnormally ###\n"); + + g_thread_join(saver); + + switch (*error_state) { + case ERROR_STATE_NO_ERROR: + log.log_append_error(log.log, "### unknown error detected ###\n"); + log.log_append_error(log.log, "The following information might help you to analyze the error.\n"); + + print_additional_info(log, resources->ngspice_result_file, resources->netlist_file); + break; + case ERROR_STATE_NO_SUCH_FILE_OR_DIRECTORY: + log.log_append_error(log.log, "spice could not simulate because netlist generation failed.\n"); + break; + case ERROR_STATE_ERROR_IN_NETLIST: + log.log_append_error(log.log, "### netlist error detected ###\n"); + log.log_append_error(log.log, "You made a mistake in the simulation settings or part properties.\n"); + log.log_append_error(log.log, "The following information will help you to analyze the error.\n"); + + print_additional_info(log, resources->ngspice_result_file, resources->netlist_file); + break; + } + + return NGSPICE_WATCHER_RETURN_VALUE_ABORTED; + } + // saver not needed any more. It could have been needed by error handling. + g_thread_unref(saver); + + if (*num_analysis == 0) { + log.log_append_error(log.log, _("### Too few or none analysis found ###\n")); + return NGSPICE_WATCHER_RETURN_VALUE_ABORTED; + } + + return NGSPICE_WATCHER_RETURN_VALUE_DONE; + +} + +/** + * function is called after ngspice process has died and the ngspice reading + * source function is finished with reading to + * - clean up, + * - check if all went good or fail, + * - wait for data conversion thread, + * - return the main program flow to the gui thread. + */ +static void ngspice_watcher_watch_ngspice (GPid pid, gint status, NgSpiceWatcherWatchNgSpiceResources *resources) { + enum NGSPICE_WATCHER_RETURN_VALUE ret_val = ngspice_watcher_watch_ngspice_resources (pid, status, resources); + + NgspiceEmitData *emitData = g_malloc(sizeof(NgspiceEmitData)); + emitData->emit_instance = resources->emit_instance; + + switch(ret_val) { + case NGSPICE_WATCHER_RETURN_VALUE_ABORTED: + case NGSPICE_WATCHER_RETURN_VALUE_CANCELED: + emitData->signal_name = g_strdup("aborted"); + *resources->aborted = TRUE; + break; + case NGSPICE_WATCHER_RETURN_VALUE_DONE: + emitData->signal_name = g_strdup("done"); + break; + } + + cancel_info_unsubscribe(resources->cancel_info); + ngspice_watch_ngspice_resources_finalize(resources); + + /* + * return to main thread + * + * Don't return too early, because if you do, the ngspice + * object could be finalized but some resources depend on it. + */ + g_main_context_invoke(NULL, (GSourceFunc)g_signal_emit_by_name_main_thread, emitData); +} + +/** + * Extracts a progress number (time of transient analysis) + * out of a string (if existing) and saves it to the thread-shared + * progress variable. + */ +static void read_progress_ngspice(ProgressResources *progress_ngspice, gdouble progress_end, const gchar *line) { + if (!g_regex_match_simple("Reference value.*\\r", line, 0, 0)) + return; + gchar **splitted = g_regex_split_simple(".* (.+)\\r", line, 0, 0); + gchar **ptr; + for (ptr = splitted; *ptr != NULL; ptr++) + if (**ptr != 0) + break; + if (*ptr != NULL) { + gdouble progress_absolute = g_ascii_strtod(*ptr, NULL); + + g_mutex_lock(&progress_ngspice->progress_mutex); + progress_ngspice->progress = progress_absolute / progress_end; + if (g_str_has_suffix(line, "\r\n")) + progress_ngspice->progress = 1; + progress_ngspice->time = g_get_monotonic_time(); + g_mutex_unlock(&progress_ngspice->progress_mutex); + } + g_strfreev(splitted); +} + +/** + * Reads stderr of ngspice. + * + * stderr of ngspice might contain progress information. + */ +static gboolean ngspice_child_stderr_cb (GIOChannel *channel, GIOCondition condition, + NgSpiceWatchSTDERRResources *resources) +{ + LogInterface log = resources->log; + const SimSettings* const sim_settings = resources->sim_settings; + ProgressResources *progress_ngspice = resources->progress_ngspice; + enum ERROR_STATE *error_state = resources->error_state; + IsNgspiceStderrDestroyed *is_ngspice_stderr_destroyed = resources->is_ngspice_stderr_destroyed; + + gchar *line = NULL; + gsize len, terminator; + GError *e = NULL; + + GIOStatus status = g_io_channel_read_line (channel, &line, &len, &terminator, &e); + if (e) { + gchar *message = g_strdup_printf("spice pipe stderr: %s - %i", e->message, e->code); + log.log_append_error(log.log, message); + g_free(message); + g_clear_error (&e); + } else if (status == G_IO_STATUS_NORMAL && len > 0) { + log.log_append_error(log.log, line); + + if (g_str_has_suffix(line, ": No such file or directory\n")) + *error_state = ERROR_STATE_NO_SUCH_FILE_OR_DIRECTORY; + if (g_str_equal(line, "spice stopped due to error, no simulation run!\n")) + *error_state = ERROR_STATE_ERROR_IN_NETLIST; + + gdouble progress_end = sim_settings_get_trans_stop(sim_settings); + read_progress_ngspice(progress_ngspice, progress_end, line); + } else if (status == G_IO_STATUS_EOF) { + g_source_destroy(g_main_current_source()); + g_free(resources); + + // emit signal, that stderr reading has finished + g_mutex_lock(&is_ngspice_stderr_destroyed->mutex); + is_ngspice_stderr_destroyed->boolean = TRUE; + g_cond_signal(&is_ngspice_stderr_destroyed->cond); + g_mutex_unlock(&is_ngspice_stderr_destroyed->mutex); + + return G_SOURCE_REMOVE; + } + if (line) + g_free (line); + + return G_SOURCE_CONTINUE; +} + +/** + * @resources: caller frees + * + * Prepares data structures to launch some threads and finally launches them. + * + * The launched threads are: + * - process ngspice + * - thread watcher + * - thread saver + * - thread worker + * + * As you should know ngspice is the program that actually simulates the simulation. + * + * The watcher thread handles stdout- and death-events of the ngspice process. + * stdout data is forked to the threads "saver" and "worker". + * As response to the death-event of ngspice, the watcher + * - cleans the field of war, + * - checks if all went good and creates error messages if not all went good, + * - waits for the worker to finish work, + * - finally returns the main program flow to the gui thread. + * + * The stderr-events are handled by the main (gui) thread, because it is not heavy work. + * Furthermore additionally shared variables can be avoided. + * + * The saver saves the data to SSD/HDD (temporary folder). It is needed to create + * good error messages in case of failure. Besides of that the user can analyze + * the data with external/other programs. + * + * The worker parses the stream of data, interprets and converts it to structured data + * so it can be plotted by gui. + */ +void ngspice_watcher_build_and_launch(const NgspiceWatcherBuildAndLaunchResources *resources) { + LogInterface log = resources->log; + const SimSettings* const sim_settings = resources->sim_settings; + gboolean is_vanilla = resources->is_vanilla; + GPid *child_pid = resources->child_pid; + gboolean *aborted = resources->aborted; + const void* emit_instance = resources->emit_instance; + guint *num_analysis = resources->num_analysis; + ProgressResources *progress_ngspice = resources->progress_ngspice; + ProgressResources *progress_reader = resources->progress_reader; + GList **analysis = resources->analysis; + AnalysisTypeShared *current = resources->current; + GError *e = NULL; + + char *argv[] = {NULL, "-b", resources->netlist_file, NULL}; + + if (is_vanilla) + argv[0] = SPICE_EXE; + else + argv[0] = NGSPICE_EXE; + + gint ngspice_stdout_fd; + gint ngspice_stderr_fd; + // Launch ngspice + if (!g_spawn_async_with_pipes (NULL, // Working directory + argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, + NULL, child_pid, + NULL, // STDIN + &ngspice_stdout_fd, // STDOUT + &ngspice_stderr_fd, // STDERR + &e)) { + + *aborted = TRUE; + log.log_append_error(log.log, _("Unable to execute NgSpice.")); + g_signal_emit_by_name (G_OBJECT (emit_instance), "aborted"); + g_clear_error (&e); + return; + + } + + // synchronizes stderr listener with is_ngspice_finished listener (needed for error handling) + IsNgspiceStderrDestroyed *is_ngspice_stderr_destroyed = g_new0(IsNgspiceStderrDestroyed, 1); + g_mutex_init(&is_ngspice_stderr_destroyed->mutex); + g_cond_init(&is_ngspice_stderr_destroyed->cond); + is_ngspice_stderr_destroyed->boolean = FALSE; + + // variable needed for error handling + enum ERROR_STATE *error_state = g_new0(enum ERROR_STATE, 1); + + GMainContext *forker_context = g_main_context_new(); + GMainLoop *forker_main_loop = g_main_loop_new(forker_context, FALSE); + g_main_context_unref(forker_context); + + // Create pipes to fork the stdout data of ngspice + ThreadPipe *thread_pipe_worker = thread_pipe_new(20, 2048); + ThreadPipe *thread_pipe_saver = thread_pipe_new(20, 2048); + + /** + * Launch analyzer + */ + NgspiceAnalysisResources *ngspice_worker_resources = g_new0(NgspiceAnalysisResources, 1); + ngspice_worker_resources->analysis = analysis; + ngspice_worker_resources->buf = NULL; + ngspice_worker_resources->is_vanilla = is_vanilla; + ngspice_worker_resources->current = current; + ngspice_worker_resources->no_of_data_rows_ac = 0; + ngspice_worker_resources->no_of_data_rows_dc = 0; + ngspice_worker_resources->no_of_data_rows_op = 0; + ngspice_worker_resources->no_of_data_rows_transient = 0; + ngspice_worker_resources->no_of_data_rows_noise = 0; + ngspice_worker_resources->no_of_variables = 0; + ngspice_worker_resources->num_analysis = num_analysis; + ngspice_worker_resources->pipe = thread_pipe_worker; + ngspice_worker_resources->progress_reader = progress_reader; + ngspice_worker_resources->sim_settings = sim_settings; + ngspice_worker_resources->cancel_info = resources->cancel_info; + cancel_info_subscribe(ngspice_worker_resources->cancel_info); + + GThread *worker = g_thread_new("spice worker", (GThreadFunc)ngspice_worker, ngspice_worker_resources); + + /** + * Launch output saver + */ + NgSpiceSaverResources *ngspice_saver_resources = g_new0(NgSpiceSaverResources, 1); + ngspice_saver_resources->path_to_file = g_strdup(resources->ngspice_result_file); + ngspice_saver_resources->pipe = thread_pipe_saver; + ngspice_saver_resources->cancel_info = resources->cancel_info; + cancel_info_subscribe(ngspice_saver_resources->cancel_info); + + GThread *saver = g_thread_new("spice saver", (GThreadFunc)ngspice_saver, ngspice_saver_resources); + + /** + * Add an ngspice-is-finished watcher + */ + NgSpiceWatcherWatchNgSpiceResources *ngspice_watcher_watch_ngspice_resources = g_new0(NgSpiceWatcherWatchNgSpiceResources, 1); + ngspice_watcher_watch_ngspice_resources->emit_instance = emit_instance; + ngspice_watcher_watch_ngspice_resources->aborted = aborted; + ngspice_watcher_watch_ngspice_resources->child_pid = child_pid; + ngspice_watcher_watch_ngspice_resources->log = log; + ngspice_watcher_watch_ngspice_resources->num_analysis = num_analysis; + ngspice_watcher_watch_ngspice_resources->worker = worker; + ngspice_watcher_watch_ngspice_resources->saver = saver; + ngspice_watcher_watch_ngspice_resources->main_loop = forker_main_loop; + ngspice_watcher_watch_ngspice_resources->ngspice_result_file = g_strdup(resources->ngspice_result_file); + ngspice_watcher_watch_ngspice_resources->netlist_file = g_strdup(resources->netlist_file); + ngspice_watcher_watch_ngspice_resources->error_state = error_state; + ngspice_watcher_watch_ngspice_resources->is_ngspice_stderr_destroyed = is_ngspice_stderr_destroyed; + ngspice_watcher_watch_ngspice_resources->cancel_info = resources->cancel_info; + cancel_info_subscribe(ngspice_watcher_watch_ngspice_resources->cancel_info); + + GSource *child_watch_source = g_child_watch_source_new (*child_pid); + g_source_set_priority (child_watch_source, G_PRIORITY_LOW); + g_source_set_callback (child_watch_source, (GSourceFunc)ngspice_watcher_watch_ngspice, ngspice_watcher_watch_ngspice_resources, NULL); + g_source_attach (child_watch_source, forker_context); + g_source_unref (child_watch_source); + + /** + * Add a GIOChannel to read from process stdout + */ + NgSpiceWatchSTDOUTResources *ngspice_watch_stdout_resources = g_new0(NgSpiceWatchSTDOUTResources, 1); + ngspice_watch_stdout_resources->ngspice_watch_fork_resources.thread_pipe_worker = thread_pipe_worker; + ngspice_watch_stdout_resources->ngspice_watch_fork_resources.thread_pipe_saver = thread_pipe_saver; + ngspice_watch_stdout_resources->cancel_info = resources->cancel_info; + cancel_info_subscribe(ngspice_watch_stdout_resources->cancel_info); + + GIOChannel *ngspice_stdout_channel = g_io_channel_unix_new(ngspice_stdout_fd); + g_io_channel_set_close_on_unref(ngspice_stdout_channel, TRUE); + GSource *ngspice_stdout_source = g_io_create_watch (ngspice_stdout_channel, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_NVAL); + g_io_channel_unref(ngspice_stdout_channel); + g_source_set_priority (ngspice_stdout_source, G_PRIORITY_HIGH); + g_source_set_callback (ngspice_stdout_source, (GSourceFunc)ngspice_watcher_watch_stdout, ngspice_watch_stdout_resources, NULL); + g_source_attach (ngspice_stdout_source, forker_context); + g_source_unref (ngspice_stdout_source); + + /** + * Add a GIOChannel to read from process stderr (attach to gui thread because it prints to log). + * I hope that ngspice does not print too much errors so that it is a minor work + * that does not hold the gui back from paint and user events + */ + NgSpiceWatchSTDERRResources *ngspice_watch_stderr_resources = g_new0(NgSpiceWatchSTDERRResources, 1); + ngspice_watch_stderr_resources->log = log; + ngspice_watch_stderr_resources->sim_settings = sim_settings; + ngspice_watch_stderr_resources->progress_ngspice = progress_ngspice; + ngspice_watch_stderr_resources->error_state = error_state; + ngspice_watch_stderr_resources->is_ngspice_stderr_destroyed = is_ngspice_stderr_destroyed; + + GIOChannel *ngspice_stderr_channel = g_io_channel_unix_new (ngspice_stderr_fd); + g_io_channel_set_close_on_unref(ngspice_stderr_channel, TRUE); + // sometimes there is no data and then the GUI will hang up if NONBLOCK not set + g_io_channel_set_flags(ngspice_stderr_channel, g_io_channel_get_flags(ngspice_stderr_channel) | G_IO_FLAG_NONBLOCK, NULL); + GSource *channel_stderr_watch_source = g_io_create_watch(ngspice_stderr_channel, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_NVAL); + g_io_channel_unref(ngspice_stderr_channel); + g_source_set_priority (channel_stderr_watch_source, G_PRIORITY_LOW); + g_source_set_callback (channel_stderr_watch_source, (GSourceFunc)ngspice_child_stderr_cb, ngspice_watch_stderr_resources, NULL); + g_source_attach (channel_stderr_watch_source, NULL); + g_source_unref (channel_stderr_watch_source); + + // Launch watcher + g_thread_unref(g_thread_new("spice forker", (GThreadFunc)ngspice_watcher_main, forker_main_loop)); +} + +NgspiceWatcherBuildAndLaunchResources *ngspice_watcher_build_and_launch_resources_new(OreganoNgSpice *ngspice) { + + NgspiceWatcherBuildAndLaunchResources *resources = g_new0(NgspiceWatcherBuildAndLaunchResources, 1); + + resources->is_vanilla = ngspice->priv->is_vanilla; + resources->aborted = &ngspice->priv->aborted; + resources->analysis = &ngspice->priv->analysis; + resources->child_pid = &ngspice->priv->child_pid; + resources->current = &ngspice->priv->current; + resources->emit_instance = ngspice; + + resources->log.log = ngspice->priv->schematic; + resources->log.log_append = (LogFunction)schematic_log_append; + resources->log.log_append_error = (LogFunction)schematic_log_append_error; + + resources->num_analysis = &ngspice->priv->num_analysis; + resources->progress_ngspice = &ngspice->priv->progress_ngspice; + resources->progress_reader = &ngspice->priv->progress_reader; + resources->sim_settings = schematic_get_sim_settings(ngspice->priv->schematic); + + resources->netlist_file = g_strdup("/tmp/netlist.tmp"); + resources->ngspice_result_file = g_strdup("/tmp/netlist.lst"); + + resources->cancel_info = ngspice->priv->cancel_info; + cancel_info_subscribe(resources->cancel_info); + + return resources; +} + +void ngspice_watcher_build_and_launch_resources_finalize(NgspiceWatcherBuildAndLaunchResources *resources) { + cancel_info_unsubscribe(resources->cancel_info); + g_free(resources->netlist_file); + g_free(resources->ngspice_result_file); + g_free(resources); +} diff --git a/src/engines/ngspice-watcher.h b/src/engines/ngspice-watcher.h new file mode 100644 index 0000000..f54ff4a --- /dev/null +++ b/src/engines/ngspice-watcher.h @@ -0,0 +1,62 @@ +/* + * ngspice-watcher.h + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ENGINES_NGSPICE_WATCHER_H_ +#define ENGINES_NGSPICE_WATCHER_H_ + +#include "../log-interface.h" +#include "../sim-settings.h" +#include "ngspice-analysis.h" + +typedef struct _NgspiceWatcherBuildAndLaunchResources NgspiceWatcherBuildAndLaunchResources; + +/** + * This struct has to be public for testing purposes. + */ +struct _NgspiceWatcherBuildAndLaunchResources { + LogInterface log;//in + const SimSettings* sim_settings;//in + gboolean is_vanilla;//in + GPid* child_pid;//out + gboolean* aborted;//out + //OreganoNgSpice object + const void* emit_instance;//in + guint* num_analysis;//out + ProgressResources* progress_ngspice;//out + ProgressResources* progress_reader;//out + GList** analysis;//out + AnalysisTypeShared* current;//out + gchar* ngspice_result_file;//in + gchar* netlist_file;//in + CancelInfo *cancel_info;//in +}; + +NgspiceWatcherBuildAndLaunchResources *ngspice_watcher_build_and_launch_resources_new(OreganoNgSpice *ngspice); +void ngspice_watcher_build_and_launch_resources_finalize(NgspiceWatcherBuildAndLaunchResources *resources); + +void ngspice_watcher_build_and_launch(const NgspiceWatcherBuildAndLaunchResources *resources); + +#endif /* ENGINES_NGSPICE_WATCHER_H_ */ diff --git a/src/engines/ngspice.c b/src/engines/ngspice.c index cd013ba..ee482e6 100644 --- a/src/engines/ngspice.c +++ b/src/engines/ngspice.c @@ -4,12 +4,16 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +27,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -39,43 +43,37 @@ #include "dialogs.h" #include "engine-internal.h" #include "ngspice-analysis.h" +#include "errors.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "ngspice-watcher.h" static void ngspice_class_init (OreganoNgSpiceClass *klass); static void ngspice_finalize (GObject *object); static void ngspice_dispose (GObject *object); 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 gboolean ngspice_child_stderr_cb (GIOChannel *source, - GIOCondition condition, OreganoNgSpice *ngspice); static GObjectClass *parent_class = NULL; -GType -oregano_ngspice_get_type (void) +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 - (GClassInitFunc) ngspice_class_init, // class_init - NULL, // class_finalize - NULL, // class_data - sizeof (OreganoNgSpice), - 0, // n_preallocs - (GInstanceInitFunc) ngspice_instance_init, // instance_init - NULL - }; + static const GTypeInfo info = {sizeof(OreganoNgSpiceClass), + NULL, // base_init + NULL, // base_finalize + (GClassInitFunc)ngspice_class_init, // class_init + NULL, // class_finalize + NULL, // class_data + sizeof(OreganoNgSpice), + 0, // n_preallocs + (GInstanceInitFunc)ngspice_instance_init, // instance_init + NULL}; static const GInterfaceInfo ngspice_info = { - (GInterfaceInitFunc) ngspice_interface_init,// interface_init - NULL, // interface_finalize - NULL // interface_data + (GInterfaceInitFunc)ngspice_interface_init, // interface_init + NULL, // interface_finalize + NULL // interface_data }; type = g_type_register_static (G_TYPE_OBJECT, "OreganoNgSpice", &info, 0); @@ -84,8 +82,7 @@ oregano_ngspice_get_type (void) return type; } -static void -ngspice_class_init (OreganoNgSpiceClass *klass) +static void ngspice_class_init (OreganoNgSpiceClass *klass) { GObjectClass *object_class; @@ -97,395 +94,385 @@ ngspice_class_init (OreganoNgSpiceClass *klass) object_class->finalize = ngspice_finalize; } -static void -ngspice_finalize (GObject *object) +static void ngspice_finalize (GObject *object) { - SimulationData *data; OreganoNgSpice *ngspice; - GList *list; + GList *iter; int i; ngspice = OREGANO_NGSPICE (object); - list = ngspice->priv->analysis; - while (list) { - data = SIM_DATA (list->data); + iter = ngspice->priv->analysis; + for (; iter; iter = iter->next) { + SimulationData *data = SIM_DATA (iter->data); + for (i = 0; i < data->n_variables; i++) { + g_free(data->var_names[i]); + g_free(data->var_units[i]); + g_array_free (data->data[i], TRUE); + } g_free (data->var_names); g_free (data->var_units); - for (i=0; i<data->n_variables; i++) - g_array_free (data->data[i], TRUE); - + g_free (data->data); g_free (data->min_data); g_free (data->max_data); - - g_free (list->data); - list = list->next; + + g_free (data); } g_list_free (ngspice->priv->analysis); - ngspice->priv->analysis = NULL; - g_list_free_full (list, g_object_unref); + g_mutex_clear(&ngspice->priv->progress_ngspice.progress_mutex); + g_mutex_clear(&ngspice->priv->progress_reader.progress_mutex); + g_mutex_clear(&ngspice->priv->current.mutex); + cancel_info_unsubscribe(ngspice->priv->cancel_info); + g_free(ngspice->priv); parent_class->finalize (object); } -static void -ngspice_dispose (GObject *object) -{ - parent_class->dispose (object); -} +static void ngspice_dispose (GObject *object) { parent_class->dispose (object); } -static gboolean -ngspice_has_warnings (OreganoEngine *self) -{ - return FALSE; -} +static gboolean ngspice_has_warnings (OreganoEngine *self) { return FALSE; } -static gboolean -ngspice_is_available (OreganoEngine *self) +static gboolean ngspice_is_available (OreganoEngine *self) { + gboolean is_vanilla; gchar *exe; - exe = g_find_program_in_path ("ngspice"); + OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + + is_vanilla = ngspice->priv->is_vanilla; - if (!exe) return FALSE; // ngspice not found + if (is_vanilla) + exe = g_find_program_in_path (SPICE_EXE); + else + exe = g_find_program_in_path (NGSPICE_EXE); + + if (!exe) + return FALSE; // ngspice not found g_free (exe); return TRUE; } -static void -ngspice_generate_netlist (OreganoEngine *engine, const gchar *filename, - GError **error) +/** + * \brief create a netlist buffer from the engine inernals + * + * @engine + * @error [allow-none] + */ +static GString *ngspice_generate_netlist_buffer (OreganoEngine *engine, GError **error) { OreganoNgSpice *ngspice; Netlist output; - SimOption *so; - GList *list; - FILE *file; - GError *local_error = NULL; + GList *iter; + GError *e = NULL; + GString *buffer = NULL; ngspice = OREGANO_NGSPICE (engine); - netlist_helper_create (ngspice->priv->schematic, &output, &local_error); - if (local_error != NULL) { - g_propagate_error (error, local_error); - return; + netlist_helper_create (ngspice->priv->schematic, &output, &e); + if (e) { + g_propagate_error (error, e); + return NULL; } - file = fopen (filename, "w"); - if (!file) { - oregano_error (g_strdup_printf ("Creation of %s not possible\n", filename)); - return; + buffer = g_string_sized_new (500); + if (!buffer) { + g_set_error_literal (&e, OREGANO_ERROR, OREGANO_OOM, + "Failed to allocate intermediate buffer."); + g_propagate_error (error, e); + return NULL; } - - list = sim_settings_get_options (output.settings); - - // Prints title - fputs ("* ",file); - fputs (output.title ? output.title : "Title: <unset>", file); - fputs ("\n" - "*----------------------------------------------" - "\n" - "*\tngspice - NETLIST" - "\n", file); + // Prints title + g_string_append (buffer, "* "); + g_string_append (buffer, output.title ? output.title : "Title: <unset>"); + g_string_append (buffer, "\n" + "*----------------------------------------------" + "\n" + "*\tngspice - NETLIST" + "\n"); // Prints Options - fputs (".options OUT=120 ",file); + g_string_append (buffer, ".options OUT=120 "); - while (list) { - so = list->data; + iter = sim_settings_get_options (output.settings); + for (; iter; iter = iter->next) { + const SimOption *so = iter->data; // Prevent send NULL text if (so->value) { - if (strlen(so->value) > 0) { - g_fprintf (file,"%s=%s ",so->name,so->value); + if (strlen (so->value) > 0) { + g_string_append_printf (buffer, "%s=%s ", so->name, so->value); } } - list = list->next; } - fputc ('\n',file); + g_string_append_c (buffer, '\n'); // Include of subckt models - fputs ("*------------- Models -------------------------\n",file); - list = output.models; - while (list) { - gchar *model; - model = (gchar *)list->data; - g_fprintf (file,".include %s/%s.model\n", OREGANO_MODELDIR, model); - list = list->next; + g_string_append (buffer, "*------------- Models -------------------------\n"); + for (iter = output.models; iter; iter = iter->next) { + const gchar *model = iter->data; + gchar *model_with_ext = g_strdup_printf ("%s.model", model); + gchar *model_path = g_build_filename (OREGANO_MODELDIR, model_with_ext, NULL); + g_string_append_printf (buffer, ".include %s\n", model_path); + g_free (model_path); + g_free (model_with_ext); } - // Prints template parts - fputs ("*------------- Circuit Description-------------\n",file); - fputs (output.template->str, file); - fputs ("\n*----------------------------------------------\n",file); + // Prints template parts + g_string_append (buffer, "*------------- Circuit Description-------------\n"); + g_string_append (buffer, output.template->str); + g_string_append (buffer, "\n*----------------------------------------------\n"); // Prints Transient Analysis if (sim_settings_get_trans (output.settings)) { gdouble st = 0; gdouble start = sim_settings_get_trans_start (output.settings); - gdouble stop = sim_settings_get_trans_stop (output.settings); + gdouble stop = sim_settings_get_trans_stop (output.settings); if (sim_settings_get_trans_step_enable (output.settings)) st = sim_settings_get_trans_step (output.settings); else - st = (stop-start) / 50; + st = (stop - start) / 50; + + if ((stop - start) <= 0) { + // FIXME ask for swapping or cancel simulation + oregano_error (_ ("Transient: Start time is after Stop time - fix this." + "stop figure\n")); + g_string_free (buffer, TRUE); + return NULL; + } - if ((stop-start) <= 0) { - oregano_error (_("Your transient analysis settings present a " - "weakness... the start figure is greater than " - "stop figure\n")); - return; + g_string_append_printf (buffer, ".tran %e %e %e", st, stop, start); + if (sim_settings_get_trans_init_cond (output.settings)) { + g_string_append_printf (buffer, " uic"); } + g_string_append_printf (buffer, "\n"); - g_fprintf (file, ".tran %lf %lf %lf\n", st, stop, start); - { + if (sim_settings_get_trans_analyze_all(output.settings)) { + g_string_append_printf (buffer, ".print tran all\n"); + } else { gchar *tmp_str = netlist_helper_create_analysis_string (output.store, FALSE); - g_fprintf (file, ".print tran %s\n", tmp_str); + g_string_append_printf (buffer, ".print tran %s\n", tmp_str); g_free (tmp_str); - } - fputs ("\n", file); + g_string_append_c (buffer, '\n'); } - // Print DC Analysis + // Prints DC Analysis if (sim_settings_get_dc (output.settings)) { - fputs (".dc ",file); + g_string_append (buffer, ".dc "); if (sim_settings_get_dc_vsrc (output.settings)) { - g_fprintf (file, "V_V%s %g %g %g\n", - sim_settings_get_dc_vsrc (output.settings), - sim_settings_get_dc_start (output.settings), - sim_settings_get_dc_stop (output.settings), - sim_settings_get_dc_step (output.settings)); - g_fprintf (file, ".print dc V(%s)\n", sim_settings_get_dc_vsrc (output.settings)); + g_string_append_printf (buffer, "V_%s %g %g %g\n", + sim_settings_get_dc_vsrc (output.settings), + sim_settings_get_dc_start (output.settings), + sim_settings_get_dc_stop (output.settings), + sim_settings_get_dc_step (output.settings)); + g_string_append_printf (buffer, ".print dc V(%s)\n", + sim_settings_get_dc_vout (output.settings)); } } // Prints AC Analysis if (sim_settings_get_ac (output.settings)) { - g_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)); + if (sim_settings_get_ac_vout (output.settings)) { + g_string_append_printf (buffer, ".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)); + g_string_append_printf (buffer, ".print ac %s\n", + sim_settings_get_ac_vout (output.settings)); + } } - + // Prints analysis using a Fourier transform - if (sim_settings_get_fourier (output.settings)) { - g_fprintf (file, ".four %d %s\n", - sim_settings_get_fourier_frequency (output.settings), - sim_settings_get_fourier_nodes (output.settings)); + if (sim_settings_get_fourier (output.settings)) { + if (sim_settings_get_fourier_frequency (output.settings) && + sim_settings_get_fourier_nodes (output.settings)) { + g_string_append_printf (buffer, ".four %.3f %s\n", + sim_settings_get_fourier_frequency (output.settings), + sim_settings_get_fourier_nodes (output.settings)); + } } - g_list_free_full (list, g_object_unref); - - // Debug op analysis. - fputs (".op\n", file); - fputs ("\n.END\n", file); - fclose (file); + // Prints Noise Analysis + if (sim_settings_get_noise (output.settings)) { + if (sim_settings_get_noise_vout (output.settings)) { + g_string_append_printf (buffer, ".noise V(%s) V_%s %s %d %g %g\n", + sim_settings_get_noise_vout (output.settings), + sim_settings_get_noise_vsrc (output.settings), + sim_settings_get_noise_type (output.settings), + sim_settings_get_noise_npoints (output.settings), + sim_settings_get_noise_start (output.settings), + sim_settings_get_noise_stop (output.settings)); + g_string_append (buffer, ".print noise inoise_spectrum onoise_spectrum\n"); + } + } + + g_string_append (buffer, ".op\n\n.END\n"); + + return buffer; } -static void -ngspice_progress (OreganoEngine *self, double *d) +/** + * \brief generate a netlist and write to file + * + * @engine engine to extract schematic and settings from + * @filename target netlist file, user selected + * @error [allow-none] + */ +static gboolean ngspice_generate_netlist (OreganoEngine *engine, const gchar *filename, + GError **error) { - OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + GError *e = NULL; + GString *buffer; + gboolean success = FALSE; + + buffer = ngspice_generate_netlist_buffer (engine, &e); + if (!buffer) { + oregano_error (g_strdup_printf ("Failed generate netlist buffer\n")); + g_propagate_error (error, e); + return FALSE; + } + + success = g_file_set_contents (filename, buffer->str, buffer->len, &e); + g_string_free (buffer, TRUE); - ngspice->priv->progress += 0.1; - (*d) = ngspice->priv->progress; + if (!success) { + g_propagate_error (error, e); + oregano_error (g_strdup_printf ("Failed to open file \"%s\" in 'w' mode.\n", filename)); + return FALSE; + } + return TRUE; } -static void -ngspice_stop (OreganoEngine *self) +static void ngspice_progress (OreganoEngine *self, double *d) { OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); - g_io_channel_shutdown (ngspice->priv->child_iochannel, TRUE, NULL); - g_source_remove (ngspice->priv->child_iochannel_watch); - g_spawn_close_pid (ngspice->priv->child_pid); - g_spawn_close_pid (ngspice->priv->child_stdout); -} -static void -ngspice_watch_cb (GPid pid, gint status, OreganoNgSpice *ngspice) -{ - // check for status, see man waitpid(2) - if (WIFEXITED (status)) { - gchar *line; - gsize len; - g_io_channel_read_to_end (ngspice->priv->child_iochannel, &line, &len, NULL); - if (len > 0) { - fprintf (ngspice->priv->inputfp, "%s", line); - } - g_free (line); - - // Free stuff - g_io_channel_shutdown (ngspice->priv->child_iochannel, TRUE, NULL); - g_source_remove (ngspice->priv->child_iochannel_watch); - g_spawn_close_pid (ngspice->priv->child_pid); - g_spawn_close_pid (ngspice->priv->child_stdout); - g_io_channel_shutdown (ngspice->priv->child_ioerror, TRUE, NULL); - g_source_remove (ngspice->priv->child_ioerror_watch); - g_spawn_close_pid (ngspice->priv->child_error); - - ngspice->priv->current = NULL; - fclose (ngspice->priv->inputfp); - ngspice_parse (ngspice); - - 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"); - } - } + g_mutex_lock(&ngspice->priv->progress_ngspice.progress_mutex); + *d = ngspice->priv->progress_ngspice.progress; + g_mutex_unlock(&ngspice->priv->progress_ngspice.progress_mutex); } -static gboolean -ngspice_child_stdout_cb (GIOChannel *source, GIOCondition condition, OreganoNgSpice *ngspice) +static void reader_progress (OreganoEngine *self, double *d) { - 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)) { - fprintf (ngspice->priv->inputfp, "%s", line); - } - g_free (line); - - // Let UI update - return g_main_context_iteration (NULL, FALSE); + OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + + g_mutex_lock(&ngspice->priv->progress_reader.progress_mutex); + *d = ngspice->priv->progress_reader.progress; + g_mutex_unlock(&ngspice->priv->progress_reader.progress_mutex); } -static gboolean -ngspice_child_stderr_cb (GIOChannel *source, GIOCondition condition, OreganoNgSpice *ngspice) +static void ngspice_stop (OreganoEngine *self) { - 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)) { - schematic_log_append_error (ngspice->priv->schematic, line); + OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + cancel_info_set_cancel(ngspice->priv->cancel_info); + GPid child_pid = ngspice->priv->child_pid; + if (child_pid != 0) { + // CTRL+C (Terminal quit signal.) + kill(child_pid, SIGINT); + ngspice->priv->child_pid = 0; } - g_free (line); - - // Let UI update - g_main_context_iteration (NULL, FALSE); - return TRUE; } -static void -ngspice_start (OreganoEngine *self) +static void ngspice_start (OreganoEngine *self) { - OreganoNgSpice *ngspice; - GError *error = NULL; - char *argv[] = {"ngspice", "-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); + OreganoNgSpice *ngspice = OREGANO_NGSPICE (self); + OreganoNgSpicePriv *priv = ngspice->priv; + + GError *e = NULL; + if (!oregano_engine_generate_netlist (self, "/tmp/netlist.tmp", &e)) { + priv->aborted = TRUE; + if (e) + schematic_log_append_error (priv->schematic, e->message); + else + schematic_log_append_error(priv->schematic, "Error at netlist generation."); g_signal_emit_by_name (G_OBJECT (ngspice), "aborted"); - g_error_free (error); + g_clear_error (&e); return; } - - // Open the file storing the output of ngspice - ngspice->priv->inputfp = fopen ("/tmp/netlist.lst", "w"); - - 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 - &ngspice->priv->child_stdout, // STDOUT - &ngspice->priv->child_error, // STDERR - &error)) { - // Add a watch for process status - g_child_watch_add (ngspice->priv->child_pid, (GChildWatchFunc)ngspice_watch_cb, ngspice); - // Add a GIOChannel to read from process stdout - ngspice->priv->child_iochannel = g_io_channel_unix_new (ngspice->priv->child_stdout); - // Watch the I/O Channel to read child strout - ngspice->priv->child_iochannel_watch = g_io_add_watch (ngspice->priv->child_iochannel, - G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_NVAL, (GIOFunc)ngspice_child_stdout_cb, ngspice); - // Add a GIOChannel to read from process stderr - ngspice->priv->child_ioerror = g_io_channel_unix_new (ngspice->priv->child_error); - // Watch the I/O error channel to read the child sterr - ngspice->priv->child_ioerror_watch = g_io_add_watch (ngspice->priv->child_ioerror, - G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_NVAL, (GIOFunc)ngspice_child_stderr_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"); - } + + NgspiceWatcherBuildAndLaunchResources *resources = ngspice_watcher_build_and_launch_resources_new(ngspice); + ngspice_watcher_build_and_launch(resources); + ngspice_watcher_build_and_launch_resources_finalize(resources); } -static GList* -ngspice_get_results (OreganoEngine *self) +static GList *ngspice_get_results (OreganoEngine *self) { if (OREGANO_NGSPICE (self)->priv->analysis == NULL) printf ("pas d'analyse\n"); return OREGANO_NGSPICE (self)->priv->analysis; } -static gchar * -ngspice_get_operation (OreganoEngine *self) +static gchar *ngspice_get_operation_ngspice (OreganoEngine *self) +{ + OreganoNgSpicePriv *priv = OREGANO_NGSPICE (self)->priv; + + g_mutex_lock(&priv->progress_ngspice.progress_mutex); + gint64 old_time = priv->progress_ngspice.time; + g_mutex_unlock(&priv->progress_ngspice.progress_mutex); + + gint64 new_time = g_get_monotonic_time(); + if (new_time - old_time >= 1000000) + return g_strdup("ngspice not responding"); + return g_strdup(_("ngspice solving")); +} + +static gchar *ngspice_get_operation_reader (OreganoEngine *self) { OreganoNgSpicePriv *priv = OREGANO_NGSPICE (self)->priv; - if (priv->current == NULL) - return _("None"); + g_mutex_lock(&priv->current.mutex); + AnalysisType type = priv->current.type; + g_mutex_unlock(&priv->current.mutex); - return oregano_engine_get_analysis_name (priv->current); + return oregano_engine_get_analysis_name_by_type(type); } -static void -ngspice_interface_init (gpointer g_iface, gpointer iface_data) +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->progress_solver = ngspice_progress; + klass->progress_reader = reader_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->get_operation_solver = ngspice_get_operation_ngspice; + klass->get_operation_reader = ngspice_get_operation_reader; klass->is_available = ngspice_is_available; } -static void -ngspice_instance_init (GTypeInstance *instance, gpointer g_class) +static void ngspice_instance_init (GTypeInstance *instance, gpointer g_class) { OreganoNgSpice *self = OREGANO_NGSPICE (instance); self->priv = g_new0 (OreganoNgSpicePriv, 1); - self->priv->progress = 0.0; - self->priv->char_last_newline = TRUE; - self->priv->status = 0; - self->priv->buf_count = 0; + self->priv->progress_ngspice.progress = 0.0; + self->priv->progress_ngspice.time = g_get_monotonic_time(); + g_mutex_init(&self->priv->progress_ngspice.progress_mutex); + self->priv->progress_reader.progress = 0.0; + self->priv->progress_reader.time = g_get_monotonic_time(); + g_mutex_init(&self->priv->progress_reader.progress_mutex); + self->priv->current.type = ANALYSIS_TYPE_NONE; + g_mutex_init(&self->priv->current.mutex); self->priv->num_analysis = 0; self->priv->analysis = NULL; - self->priv->current = NULL; self->priv->aborted = FALSE; + + self->priv->cancel_info = cancel_info_new(); } -OreganoEngine* -oregano_ngspice_new (Schematic *sc) +/* + * Set "is_vanilla" to TRUE if using the original spice3 from + * UC Berkeley. + */ +OreganoEngine *oregano_spice_new (Schematic *sc, gboolean is_vanilla) { OreganoNgSpice *ngspice; ngspice = OREGANO_NGSPICE (g_object_new (OREGANO_TYPE_NGSPICE, NULL)); ngspice->priv->schematic = sc; + ngspice->priv->is_vanilla = is_vanilla; return OREGANO_ENGINE (ngspice); } diff --git a/src/engines/ngspice.h b/src/engines/ngspice.h index bb46dc6..26e00f8 100644 --- a/src/engines/ngspice.h +++ b/src/engines/ngspice.h @@ -4,12 +4,14 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,39 +25,60 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NGSPICE_H #define __NGSPICE_H +/* + * The name of the vanilla spice3 executable. + */ +#define SPICE_EXE "spice3" + +/* + * The name of the ngspice executable. + */ +#define NGSPICE_EXE "ngspice" + +/* + * The filename used for the temporary noise + * analysis file. + */ +#define NOISE_ANALYSIS_FILENAME "oregano-noise.txt" + #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_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)) +#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 { +struct _OreganoNgSpice +{ GObject parent; OreganoNgSpicePriv *priv; }; -struct _OreganoNgSpiceClass { +struct _OreganoNgSpiceClass +{ GObjectClass parent; }; -GType oregano_ngspice_get_type (void); -OreganoEngine *oregano_ngspice_new (Schematic *sm); +GType oregano_ngspice_get_type (void); +OreganoEngine *oregano_spice_new (Schematic *sm, gboolean is_vanilla); #endif diff --git a/src/errors.c b/src/errors.c index b03dcc2..af3cd44 100644 --- a/src/errors.c +++ b/src/errors.c @@ -5,9 +5,9 @@ * Author: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano - * + * + * Web page: https://ahoi.io/project/oregano + * * Copyright (C) 2003-2008 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * @@ -23,14 +23,13 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "errors.h" -GQuark -oregano_error_quark (void) +GQuark oregano_error_quark (void) { static GQuark err = 0; if (!err) { @@ -38,4 +37,3 @@ oregano_error_quark (void) } return err; } - diff --git a/src/errors.h b/src/errors.h index 937bb6f..89d7c59 100644 --- a/src/errors.h +++ b/src/errors.h @@ -2,14 +2,16 @@ * errors.h * * - * Author: + * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * + * Web page: https://ahoi.io/project/oregano * - * Web page: https://github.com/marc-lorber/oregano - * * Copyright (C) 2003-2008 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +25,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __ERRORS_H @@ -32,16 +34,19 @@ #include <glib.h> -#define OREGANO_ERROR (oregano_error_quark()) +#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_NO_SUCH_PART, OREGANO_SIMULATE_ERROR_IO_ERROR, OREGANO_SCHEMATIC_BAD_FILE_FORMAT, OREGANO_SCHEMATIC_FILE_NOT_FOUND, + OREGANO_UI_ERROR_NO_BUILDER, + OREGANO_OOM } OREGANO_ERRORS; #endif diff --git a/src/file-manager.c b/src/file-manager.c index 8be0460..0548d76 100644 --- a/src/file-manager.c +++ b/src/file-manager.c @@ -7,7 +7,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber @@ -24,26 +24,22 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "file-manager.h" FileType file_types[] = { - FILE_TYPE ("oregano", "Oregano Schematic File", schematic_parse_xml_file, - schematic_write_xml) -}; + FILE_TYPE ("oregano", "Oregano Schematic File", schematic_parse_xml_file, schematic_write_xml)}; -#define FILE_TYPES_COUNT (sizeof(file_types)/sizeof(FileType)) +#define FILE_TYPES_COUNT (sizeof(file_types) / sizeof(FileType)) - -FileType * -file_manager_get_handler (const gchar *fname) +FileType *file_manager_get_handler (const gchar *fname) { int i; gchar *ext, *ptr; - FileType *ft = NULL; + FileType *ft = NULL; g_return_val_if_fail (fname != NULL, NULL); @@ -57,7 +53,7 @@ file_manager_get_handler (const gchar *fname) ptr++; } - for (i=0; i<FILE_TYPES_COUNT; i++) + for (i = 0; i < FILE_TYPES_COUNT; i++) if (!strcmp (file_types[i].extension, ext)) { ft = &file_types[i]; break; diff --git a/src/file-manager.h b/src/file-manager.h index 73888d4..66a9869 100644 --- a/src/file-manager.h +++ b/src/file-manager.h @@ -7,7 +7,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber @@ -24,8 +24,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _FILE_MANAGER_H_ @@ -38,9 +38,13 @@ #include "load-schematic.h" #include "save-schematic.h" -#define FILE_TYPE(a,b,c,d) {a, b, c, d} +#define FILE_TYPE(a, b, c, d) \ + { \ + a, b, c, d \ + } -typedef struct _file_manager_ext_ { +typedef struct _file_manager_ext_ +{ gchar *extension; gchar *description; int (*load_func)(Schematic *schematic, const gchar *filename, GError **error); @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> @@ -39,9 +39,7 @@ #include "dialogs.h" #include "save-schematic.h" - -char * -dialog_open_file (SchematicView *sv) +char *dialog_open_file (SchematicView *sv) { GtkWidget *dialog; GtkFileFilter *allfilter, *orefilter; @@ -49,39 +47,38 @@ dialog_open_file (SchematicView *sv) allfilter = gtk_file_filter_new (); orefilter = gtk_file_filter_new (); - - gtk_file_filter_set_name (orefilter, _("Oregano Files")); + + 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_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); - + + dialog = gtk_file_chooser_dialog_new (_ ("Open File"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + _("_Cancel"), + GTK_RESPONSE_CANCEL, + _("_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 = 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 + } + } else name = NULL; gtk_widget_destroy (dialog); return name; } -void -dialog_save_as (SchematicView *sv) +void dialog_save_as (SchematicView *sv) { GtkWidget *dialog; GtkFileFilter *orefilter, *allfilter; @@ -91,33 +88,33 @@ dialog_save_as (SchematicView *sv) orefilter = gtk_file_filter_new (); allfilter = gtk_file_filter_new (); - gtk_file_filter_set_name (orefilter, _("Oregano Files")); + 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_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); + + dialog = gtk_file_chooser_dialog_new (_ ("Save File"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), + GTK_RESPONSE_CANCEL, + _("_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] != '/') { + 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 { + } else { tmp = g_strdup (name); } @@ -126,9 +123,7 @@ dialog_save_as (SchematicView *sv) schematic_set_filename (sm, tmp); if (!schematic_save_file (sm, &error)) { - char *msg = g_strdup_printf ( - "Could not save Schematic file %s\n", - tmp); + char *msg = g_strdup_printf ("Could not save Schematic file %s\n", tmp); oregano_error (msg); g_free (msg); g_free (name); @@ -140,52 +135,50 @@ dialog_save_as (SchematicView *sv) gtk_widget_destroy (dialog); } -char * -dialog_netlist_file (SchematicView *sv) +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); + dialog = gtk_file_chooser_dialog_new (_ ("Netlist File"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), + GTK_RESPONSE_CANCEL, + _("_Save"), + GTK_RESPONSE_ACCEPT, + NULL); gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); - gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), - TRUE); - + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); 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 { + } else { name = g_strdup (name); } - } - else + } else name = NULL; gtk_widget_destroy (dialog); return name; } -char * -dialog_file_open (const gchar *title) +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); + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), + GTK_RESPONSE_CANCEL, + _("_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)); @@ -193,11 +186,9 @@ dialog_file_open (const gchar *title) if (name[strlen (name) - 1] == '/') { g_free (name); name = NULL; - } - else + } else name = g_strdup (name); - } - else + } else name = NULL; gtk_widget_destroy (dialog); @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __FILE_H @@ -38,7 +38,7 @@ #include "schematic-view.h" char *dialog_open_file (SchematicView *sv); -void dialog_save_as (SchematicView *sv); +void dialog_save_as (SchematicView *sv); char *dialog_netlist_file (SchematicView *sv); char *dialog_file_open (const gchar *file); diff --git a/src/gplot/Makefile.am b/src/gplot/Makefile.am deleted file mode 100644 index a7dd460..0000000 --- a/src/gplot/Makefile.am +++ /dev/null @@ -1,19 +0,0 @@ -oreganodir = $(datadir)/oregano - -AM_CFLAGS = -Wall -DG_DISABLE_DEPRECATED -DGSEAL_ENABLE \ - -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ - -DGTK_DISABLE_DEPRECATED -DGTK_DISABLE_SINGLE_INCLUDES \ - -DG_DISABLE_SINGLE_INCLUDES -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES - -INCLUDES = \ - $(OREGANO_CFLAGS) - -noinst_LIBRARIES = libgplot.a -libgplot_a_SOURCES = \ - gplot.c \ - gplotfunction.c \ - gplotfunction.h \ - gplot-internal.h \ - gplot.h \ - gplotlines.c \ - gplotlines.h diff --git a/src/gplot/gplot-internal.h b/src/gplot/gplot-internal.h index 90a1aee..2e3aecf 100644 --- a/src/gplot/gplot-internal.h +++ b/src/gplot/gplot-internal.h @@ -3,8 +3,8 @@ * * Authors: * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -22,8 +22,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _GPLOT_INTERNAL_H_ @@ -41,30 +41,33 @@ typedef struct _GPlotClass GPlotClass; typedef struct _GPlotPriv GPlotPriv; - -struct _GPlot { +struct _GPlot +{ GtkLayout parent; GPlotPriv *priv; }; -struct _GPlotClass { +struct _GPlotClass +{ GtkLayoutClass parent_class; }; - // Internal definitions associated to gplotfunction.h -#define TYPE_GPLOT_FUNCTION (g_plot_function_get_type ()) -#define GPLOT_FUNCTION_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, TYPE_GPLOT_FUNCTION, GPlotFunctionClass) -#define GPLOT_FUNCTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TYPE_GPLOT_FUNCTION, GPlotFunctionClass)) +#define TYPE_GPLOT_FUNCTION (g_plot_function_get_type ()) +#define GPLOT_FUNCTION_CLASS(klass) \ + G_TYPE_CHECK_CLASS_CAST (klass, TYPE_GPLOT_FUNCTION, GPlotFunctionClass) +#define GPLOT_FUNCTION_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), TYPE_GPLOT_FUNCTION, GPlotFunctionClass)) typedef struct _GPlotFunctionClass GPlotFunctionClass; -struct _GPlotFunctionClass { +struct _GPlotFunctionClass +{ GTypeInterface parent; - void (*draw) (GPlotFunction *, cairo_t *, GPlotFunctionBBox *); - void (*get_bbox) (GPlotFunction *, GPlotFunctionBBox *); + void (*draw)(GPlotFunction *, cairo_t *, GPlotFunctionBBox *); + void (*get_bbox)(GPlotFunction *, GPlotFunctionBBox *); }; #endif diff --git a/src/gplot/gplot.c b/src/gplot/gplot.c index 9f50432..185f6cc 100644 --- a/src/gplot/gplot.c +++ b/src/gplot/gplot.c @@ -4,12 +4,14 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,8 +25,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> @@ -34,10 +36,10 @@ #define BORDER_SIZE 50 -static void g_plot_class_init (GPlotClass* class); -static void g_plot_init (GPlot* plot); -static gboolean g_plot_draw (GtkWidget* widget, cairo_t *cr); -static cairo_t* g_plot_create_cairo (GPlot *); +static void g_plot_class_init (GPlotClass *class); +static void g_plot_init (GPlot *plot); +static gboolean g_plot_draw (GtkWidget *widget, cairo_t *cr); +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 *); @@ -47,17 +49,12 @@ 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 -}; +enum { ACTION_NONE, ACTION_STARTING_PAN, ACTION_PAN, ACTION_STARTING_REGION, ACTION_REGION }; + +static GtkLayoutClass *parent_class = NULL; -static GtkLayoutClass* parent_class = NULL; - -struct _GPlotPriv { +struct _GPlotPriv +{ GList *functions; gchar *xlabel; @@ -75,8 +72,8 @@ struct _GPlotPriv { gdouble zoom; gdouble offset_x; gdouble offset_y; - gdouble last_x; - gdouble last_y; + gdouble press_x; + gdouble press_y; // Window->Viewport * Transformation Matrix cairo_matrix_t matrix; @@ -86,36 +83,31 @@ struct _GPlotPriv { GPlotFunctionBBox viewport_bbox; GPlotFunctionBBox rubberband; + +#if GTK_CHECK_VERSION(3,22,0) + GdkDrawingContext *gdk_ctx; + + cairo_region_t *region; +#endif }; -GType -g_plot_get_type () +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 - }; + 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) +static void g_plot_class_init (GPlotClass *class) { - GObjectClass* object_class; - GtkWidgetClass* widget_class; + GObjectClass *object_class; + GtkWidgetClass *widget_class; object_class = G_OBJECT_CLASS (class); widget_class = GTK_WIDGET_CLASS (class); @@ -127,32 +119,35 @@ g_plot_class_init (GPlotClass* class) object_class->finalize = g_plot_finalize; } -static cairo_t* -g_plot_create_cairo (GPlot *p) +static cairo_t *g_plot_create_cairo (GPlot *p) { + GdkWindow *window; cairo_t *cr; - cr = gdk_cairo_create (gtk_layout_get_bin_window (GTK_LAYOUT (p))); + window = gtk_layout_get_bin_window (GTK_LAYOUT (p)); + +#if GTK_CHECK_VERSION(3,22,0) + p->priv->region = gdk_window_get_clip_region (window); + p->priv->gdk_ctx = gdk_window_begin_draw_frame (window, p->priv->region); + cr = gdk_drawing_context_get_cairo_context (p->priv->gdk_ctx); +#else + cr = gdk_cairo_create (window); +#endif return cr; } -static void -g_plot_finalize (GObject *object) +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_free (p->priv->xlabel); + g_free (p->priv->ylabel); G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -g_plot_dispose (GObject *object) +static void g_plot_dispose (GObject *object) { GList *lst; GPlot *plot; @@ -172,40 +167,39 @@ g_plot_dispose (GObject *object) G_OBJECT_CLASS (parent_class)->dispose (object); } -static int -get_best_exponent (int div) +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; + 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) + 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) +void draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, gboolean vertical, + gint *div) { gchar *label; cairo_text_extents_t extents; @@ -218,7 +212,7 @@ draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, get_order_of_magnitude (min, &man1, &pw1); get_order_of_magnitude (max, &man2, &pw2); - (*div) = get_best_exponent ((pw2+pw1) / 2.0 + 0.5); + (*div) = get_best_exponent ((pw2 + pw1) / 2.0 + 0.5); if ((*div) == 0) divisor = 1; else @@ -229,9 +223,9 @@ draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, 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); + 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 ("%.*f", MAX(0, (int)(2 - floor(log10(fabs((max - min)/divisor))))), j / divisor); cairo_text_extents (cr, label, &extents); if (vertical) { @@ -239,10 +233,9 @@ draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, y1 = i; x2 = bbox->xmin + 4; y2 = i; - x3 = bbox->xmin - extents.width * 1.5; + x3 = bbox->xmin - extents.width - 6; y3 = i + extents.height / 2.0; - } - else { + } else { x1 = i; y1 = bbox->ymax - 4; x2 = i; @@ -254,48 +247,69 @@ draw_axis (cairo_t *cr, GPlotFunctionBBox *bbox, gdouble min, gdouble max, cairo_move_to (cr, x1, y1); cairo_line_to (cr, x2, y2); cairo_move_to (cr, x3, y3); + if (!vertical && extents.width > s - 5) { + cairo_save(cr); + cairo_rotate(cr, M_PI/10); + } cairo_show_text (cr, label); + if (!vertical && extents.width > s - 5) { + cairo_restore(cr); + } g_free (label); } cairo_stroke (cr); } -static gchar* -get_unit_text (int div) +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"); + 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 gboolean -g_plot_draw (GtkWidget* widget, cairo_t *cr) +static gboolean g_plot_draw (GtkWidget *widget, cairo_t *cr) { - static double dashes[] = - {3, // ink - 3, // skip - 3, // ink - 3}; // skip - static int ndash = sizeof (dashes) / sizeof (dashes[0]); + 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; @@ -325,19 +339,19 @@ g_plot_draw (GtkWidget* widget, cairo_t *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_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_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); - + priv->viewport_bbox.xmax = width - priv->right_border; priv->viewport_bbox.xmin = priv->left_border; priv->viewport_bbox.ymax = height - priv->bottom_border; @@ -345,75 +359,92 @@ g_plot_draw (GtkWidget* widget, cairo_t *cr) // Calculating Window to Viewport matrix aX = (priv->viewport_bbox.xmax - priv->viewport_bbox.xmin) / - (priv->window_bbox.xmax - priv->window_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); + (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); + //plot functions cairo_save (cr); - cairo_rectangle (cr, priv->left_border, priv->right_border, graph_width, graph_height); - cairo_clip (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_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_transform(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); + //plot red axis cairo_save (cr); + { cairo_rectangle (cr, priv->left_border, priv->right_border, graph_width, graph_height); cairo_clip (cr); + //plot x axis cairo_save (cr); - cairo_set_matrix (cr, &priv->matrix); + { + cairo_transform(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); + //plot y axis cairo_save (cr); - cairo_set_matrix (cr, &priv->matrix); + { + cairo_transform(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); + //plot axis ticks cairo_save (cr); - 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_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 + // Axis x Label if (priv->xlabel) { char *txt; if (priv->xlabel_unit == NULL) @@ -422,16 +453,17 @@ g_plot_draw (GtkWidget* widget, cairo_t *cr) 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_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); } + //axis y label if (priv->ylabel) { char *txt; if (priv->ylabel_unit == NULL) @@ -440,16 +472,17 @@ g_plot_draw (GtkWidget* widget, cairo_t *cr) 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_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); } + //plot rubberband (zoom-in-rectangle) if (priv->action == ACTION_REGION) { gdouble x, y, w, h; x = priv->rubberband.xmin; @@ -457,13 +490,12 @@ g_plot_draw (GtkWidget* widget, cairo_t *cr) w = priv->rubberband.xmax; h = priv->rubberband.ymax; cairo_save (cr); - cairo_identity_matrix (cr); - cairo_set_dash (cr, dashes, ndash, offset); + 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_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); } g_list_free_full (lst, g_object_unref); @@ -471,13 +503,12 @@ g_plot_draw (GtkWidget* widget, cairo_t *cr) return FALSE; } -static void -g_plot_init (GPlot* plot) +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->functions = NULL; plot->priv->action = ACTION_NONE; plot->priv->zoom = 1.0; plot->priv->offset_x = 0.0; @@ -492,31 +523,28 @@ g_plot_init (GPlot* plot) plot->priv->ylabel_unit = NULL; } -GtkWidget* -g_plot_new () +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", + 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) +int g_plot_add_function (GPlot *plot, GPlotFunction *func) { g_return_val_if_fail (IS_GPLOT (plot), -1); @@ -525,182 +553,177 @@ g_plot_add_function (GPlot *plot, GPlotFunction *func) return 0; } -static gboolean -g_plot_motion_cb (GtkWidget *w, GdkEventMotion *e, GPlot *p) +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 (gtk_widget_get_window (w), 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; +#if GTK_CHECK_VERSION (3,16,0) + GdkDisplay *display; + display = gtk_widget_get_display (w); +#endif - p->priv->last_x = e->x; - p->priv->last_y = e->y; - p->priv->action = ACTION_PAN; - gtk_widget_queue_draw (w); - } + 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; +#if GTK_CHECK_VERSION (3,16,0) + GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_FLEUR); +#else + GdkCursor *cursor = gdk_cursor_new (GDK_FLEUR); +#endif + gdk_window_set_cursor (gtk_widget_get_window (w), cursor); + gdk_flush (); + + dx = p->priv->press_x - e->x; + dy = p->priv->press_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->press_x = e->x; + p->priv->press_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 (gtk_widget_get_window (w), 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); - } + case GPLOT_ZOOM_REGION: + if ((p->priv->action == ACTION_STARTING_REGION) || (p->priv->action == ACTION_REGION)) { +#if GTK_CHECK_VERSION (3,16,0) + GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_CROSS); +#else + GdkCursor *cursor = gdk_cursor_new (GDK_CROSS); +#endif + gdk_window_set_cursor (gtk_widget_get_window (w), cursor); + gdk_flush (); + + p->priv->rubberband.xmin = MIN(e->x, p->priv->press_x); + p->priv->rubberband.xmax = MAX(e->x, p->priv->press_x); + + p->priv->rubberband.ymin = MIN(e->y, p->priv->press_y); + p->priv->rubberband.ymax = MAX(e->y, p->priv->press_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) +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 : Check function below cursor and open a property dialog :) */ - } - else { + } 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; - } + case GPLOT_ZOOM_INOUT: + if (e->button == 1) { + p->priv->action = ACTION_STARTING_PAN; + p->priv->press_x = e->x; + p->priv->press_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; - } + 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->press_x = e->x; + p->priv->press_y = e->y; + } } } return TRUE; } -static gboolean -g_plot_button_release_cb (GtkWidget *w, GdkEventButton *e, GPlot *p) +static gboolean g_plot_button_release_cb (GtkWidget *w, GdkEventButton *e, GPlot *p) { gdouble zoom = 0.0; 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 (gtk_widget_get_window (w), NULL); - gdk_flush (); + 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 (gtk_widget_get_window (w), NULL); + gdk_flush (); + } break; - case GPLOT_ZOOM_REGION: - if ((e->button == 1) && (p->priv->action == ACTION_REGION)) { - gdk_window_set_cursor (gtk_widget_get_window (w), 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 (gtk_widget_get_window (w), NULL); - gdk_flush (); + case GPLOT_ZOOM_REGION: + if ((e->button == 1) && (p->priv->action == ACTION_REGION)) { + gdk_window_set_cursor (gtk_widget_get_window (w), NULL); + gdk_flush (); + + if (e->x < p->priv->press_x || e->y < p->priv->press_y) { + g_plot_reset_zoom(p); + } else { + 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; } - gtk_widget_queue_draw (w); + } else if (e->button == 3) { + gdk_window_set_cursor (gtk_widget_get_window (w), 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) +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) +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) +static void g_plot_update_bbox (GPlot *p) { GPlotFunction *f; GPlotFunctionBBox bbox; @@ -738,8 +761,7 @@ g_plot_update_bbox (GPlot *p) priv->window_valid = TRUE; } -void -g_plot_reset_zoom (GPlot *p) +void g_plot_reset_zoom (GPlot *p) { g_return_if_fail (IS_GPLOT (p)); @@ -748,8 +770,7 @@ g_plot_reset_zoom (GPlot *p) gtk_widget_queue_draw (GTK_WIDGET (p)); } -void -g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y) +void g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y) { cairo_text_extents_t extents; @@ -760,12 +781,16 @@ g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y) cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_set_font_size (cr, 14); cairo_text_extents (cr, x, &extents); +#if GTK_CHECK_VERSION(3,22,0) + gdk_window_end_draw_frame (gtk_layout_get_bin_window (GTK_LAYOUT (p)) , p->priv->gdk_ctx); + cairo_region_destroy (p->priv->region); +#else cairo_destroy (cr); +#endif p->priv->xlabel = g_strdup (x); p->priv->bottom_border = BORDER_SIZE + extents.height; - } - else { + } else { p->priv->bottom_border = BORDER_SIZE; if (p->priv->xlabel) { g_free (p->priv->xlabel); @@ -778,14 +803,18 @@ g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y) cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_set_font_size (cr, 14); cairo_text_extents (cr, y, &extents); +#if GTK_CHECK_VERSION(3,22,0) + gdk_window_end_draw_frame (gtk_layout_get_bin_window (GTK_LAYOUT (p)) , p->priv->gdk_ctx); + cairo_region_destroy (p->priv->region); +#else cairo_destroy (cr); +#endif p->priv->xlabel = g_strdup (x); p->priv->left_border = BORDER_SIZE + extents.height; p->priv->ylabel = g_strdup (y); - } - else { + } else { p->priv->left_border = BORDER_SIZE; if (p->priv->ylabel) { g_free (p->priv->ylabel); @@ -794,8 +823,7 @@ g_plot_set_axis_labels (GPlot *p, gchar *x, gchar *y) } } -void -g_plot_clear (GPlot *plot) +void g_plot_clear (GPlot *plot) { GList *lst; @@ -814,8 +842,7 @@ g_plot_clear (GPlot *plot) g_list_free (lst); } -void -g_plot_window_to_device (GPlot *plot, double *x, double *y) +void g_plot_window_to_device (GPlot *plot, double *x, double *y) { cairo_t *cr; @@ -825,18 +852,23 @@ g_plot_window_to_device (GPlot *plot, double *x, double *y) cr = g_plot_create_cairo (plot); cairo_set_matrix (cr, &plot->priv->matrix); cairo_device_to_user (cr, x, y); + +#if GTK_CHECK_VERSION(3,22,0) + gdk_window_end_draw_frame (gtk_layout_get_bin_window (GTK_LAYOUT (plot)) , plot->priv->gdk_ctx); + cairo_region_destroy (plot->priv->region); +#else cairo_destroy (cr); +#endif } -static void -get_order_of_magnitude (gdouble val, gdouble *man, gdouble *pw) +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); + b = 3.0 * rint (b); sx = (b != 0.0 ? pow (10.0, b) : 1.0); *man = val / sx; - *pw = b; + *pw = b; } diff --git a/src/gplot/gplot.h b/src/gplot/gplot.h index a03c31c..8b269ff 100644 --- a/src/gplot/gplot.h +++ b/src/gplot/gplot.h @@ -4,8 +4,8 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _GPLOT_H_ @@ -34,26 +34,25 @@ #include <gtk/gtk.h> #include <glib.h> -#define GPLOT(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT, GPlot) -#define IS_GPLOT(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT) -#define TYPE_GPLOT (g_plot_get_type()) +#define GPLOT(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT, GPlot) +#define IS_GPLOT(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT) +#define TYPE_GPLOT (g_plot_get_type ()) typedef struct _GPlot GPlot; -typedef struct _GPlotFunction {} GPlotFunction; +typedef struct _GPlotFunction +{ +} GPlotFunction; -enum { - GPLOT_ZOOM_INOUT, - GPLOT_ZOOM_REGION -}; +enum { GPLOT_ZOOM_INOUT, GPLOT_ZOOM_REGION }; -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); +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 index d88de06..1702116 100644 --- a/src/gplot/gplotfunction.c +++ b/src/gplot/gplotfunction.c @@ -4,8 +4,8 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -23,63 +23,55 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "gplot-internal.h" #include "gplotfunction.h" - -static void -g_plot_function_base_init (gpointer g_class) +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)); + 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) +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; + 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) +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); + 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) +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); + 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 index bbd0e2f..6575317 100644 --- a/src/gplot/gplotfunction.h +++ b/src/gplot/gplotfunction.h @@ -5,8 +5,8 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano - * + * Web page: https://ahoi.io/project/oregano + * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2010 Marc Lorber @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _GPLOT_FUNCTION_H_ @@ -32,19 +32,20 @@ #include <gtk/gtk.h> -#define GPLOT_FUNCTION(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT_FUNCTION, GPlotFunction) -#define IS_GPLOT_FUNCTION(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT_FUNCTION) +#define GPLOT_FUNCTION(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT_FUNCTION, GPlotFunction) +#define IS_GPLOT_FUNCTION(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT_FUNCTION) -typedef struct _GPlotFunctionBBox { +typedef struct _GPlotFunctionBBox +{ gdouble xmin; gdouble ymin; gdouble xmax; gdouble ymax; } GPlotFunctionBBox; -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); +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 index 9f1bf3e..5b7ab66 100644 --- a/src/gplot/gplotlines.c +++ b/src/gplot/gplotlines.c @@ -5,7 +5,7 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> @@ -36,41 +36,36 @@ typedef struct _GPlotLines GPlotLines; typedef struct _GPlotLinesPriv GPlotLinesPriv; typedef struct _GPlotLinesClass GPlotLinesClass; -struct _GPlotLines { +struct _GPlotLines +{ GObject parent; GPlotLinesPriv *priv; }; -struct _GPlotLinesClass { +struct _GPlotLinesClass +{ GObjectClass parent; }; -GType g_plot_function_get_type (); +GType g_plot_function_get_type (); -static void g_plot_lines_class_init (GPlotLinesClass* class); -static void g_plot_lines_init (GPlotLines* plot); +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, - ARG_GRAPH_TYPE, - ARG_SHIFT -}; +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); -struct _GPlotLinesPriv { +static GObjectClass *parent_class = NULL; + +enum { ARG_0, ARG_WIDTH, ARG_COLOR, ARG_COLOR_GDKRGBA, ARG_VISIBLE, ARG_GRAPH_TYPE, ARG_SHIFT }; + +struct _GPlotLinesPriv +{ gboolean bbox_valid; GPlotFunctionBBox bbox; gdouble *x; @@ -83,7 +78,7 @@ struct _GPlotLinesPriv { // Line Color gchar *color_string; - GdkColor color; + GdkRGBA color; // Graphic type GraphicType graphic_type; @@ -92,44 +87,37 @@ struct _GPlotLinesPriv { gdouble shift; }; -#define TYPE_GPLOT_LINES (g_plot_lines_get_type ()) -#define TYPE_GPLOT_GRAPHIC_TYPE (g_plot_lines_graphic_get_type ()) -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#define TYPE_GPLOT_LINES (g_plot_lines_get_type ()) +#define TYPE_GPLOT_GRAPHIC_TYPE (g_plot_lines_graphic_get_type ()) +#include "debug.h" G_DEFINE_TYPE_WITH_CODE (GPlotLines, g_plot_lines, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TYPE_GPLOT_FUNCTION, - g_plot_lines_function_init)); + G_IMPLEMENT_INTERFACE (TYPE_GPLOT_FUNCTION, g_plot_lines_function_init)); -GType -g_plot_lines_graphic_get_type (void) +GType g_plot_lines_graphic_get_type (void) { static GType etype = 0; - if (etype == 0) { - static const GEnumValue values[] = { - { FUNCTIONAL_CURVE, "FUNCTIONAL_CURVE", "funct-curve" }, - { FREQUENCY_PULSE, "FREQUENCY_PULSE", "freq-pulse" }, - { 0, NULL, NULL } - }; - etype = g_enum_register_static ("GraphicType", values); - } - return etype; + if (etype == 0) { + static const GEnumValue values[] = {{FUNCTIONAL_CURVE, "FUNCTIONAL_CURVE", "funct-curve"}, + {FREQUENCY_PULSE, "FREQUENCY_PULSE", "freq-pulse"}, + {0, NULL, NULL}}; + etype = g_enum_register_static ("GraphicType", values); + } + return etype; } -static void -g_plot_lines_function_init (GPlotFunctionClass *iface) +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) +static void g_plot_lines_dispose (GObject *object) { - G_OBJECT_CLASS (parent_class)->dispose(object); + G_OBJECT_CLASS (parent_class)->dispose (object); } -static void -g_plot_lines_finalize (GObject *object) +static void g_plot_lines_finalize (GObject *object) { GPlotLines *lines; @@ -145,14 +133,13 @@ g_plot_lines_finalize (GObject *object) G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -g_plot_lines_class_init (GPlotLinesClass* class) +static void g_plot_lines_class_init (GPlotLinesClass *class) { - GObjectClass* object_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; @@ -161,31 +148,34 @@ g_plot_lines_class_init (GPlotLinesClass* class) 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_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_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)); + g_object_class_install_property (object_class, ARG_COLOR_GDKRGBA, + g_param_spec_pointer ("color-rgb", "GPlotLines::color-rgb", + "the GdkRGBA of the line", + G_PARAM_READWRITE)); - g_object_class_install_property (object_class, ARG_GRAPH_TYPE, - g_param_spec_enum ("graph-type", "GPlotLines::graph-type", - "the type of graphic", TYPE_GPLOT_GRAPHIC_TYPE, - FUNCTIONAL_CURVE, G_PARAM_READWRITE)); + g_object_class_install_property ( + object_class, ARG_GRAPH_TYPE, + g_param_spec_enum ("graph-type", "GPlotLines::graph-type", "the type of graphic", + TYPE_GPLOT_GRAPHIC_TYPE, FUNCTIONAL_CURVE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, ARG_SHIFT, - g_param_spec_double ("shift", "GPlotLines::shift", - "the shift for multiple pulses", 0.0, 500.0, 50.0, G_PARAM_READWRITE)); + g_param_spec_double ("shift", "GPlotLines::shift", + "the shift for multiple pulses", 0.0, + 15e+10, 50.0, G_PARAM_READWRITE)); } -static void -g_plot_lines_init (GPlotLines* plot) +static void g_plot_lines_init (GPlotLines *plot) { - GPlotLinesPriv* priv = g_new0 (GPlotLinesPriv, 1); + GPlotLinesPriv *priv = g_new0 (GPlotLinesPriv, 1); priv->bbox_valid = FALSE; @@ -194,41 +184,39 @@ g_plot_lines_init (GPlotLines* plot) priv->width = 1.0; priv->visible = TRUE; priv->color_string = g_strdup ("white"); - memset (&priv->color, 0xFF, sizeof (GdkColor)); + memset (&priv->color, 0xFF, sizeof(GdkRGBA)); } -static void -g_plot_lines_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *spec) +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_GRAPH_TYPE: - plot->priv->graphic_type = g_value_get_enum (value); - break; - case ARG_SHIFT: - plot->priv->shift = g_value_get_double (value); - break; - default: - break; + 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_rgba_parse (&plot->priv->color, plot->priv->color_string); + break; + case ARG_GRAPH_TYPE: + plot->priv->graphic_type = g_value_get_enum (value); + break; + case ARG_SHIFT: + plot->priv->shift = g_value_get_double (value); + break; + default: + break; } } -static void -g_plot_lines_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +static void g_plot_lines_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec) { GPlotLines *plot = GPLOT_LINES (object); @@ -253,8 +241,7 @@ g_plot_lines_get_property (GObject *object, guint prop_id, GValue *value, } } -GPlotFunction* -g_plot_lines_new (gdouble *x, gdouble *y, guint points) +GPlotFunction *g_plot_lines_new (gdouble *x, gdouble *y, guint points) { GPlotLines *plot; @@ -266,8 +253,7 @@ g_plot_lines_new (gdouble *x, gdouble *y, guint points) return GPLOT_FUNCTION (plot); } -static void -g_plot_lines_get_bbox (GPlotFunction *f, GPlotFunctionBBox *bbox) +static void g_plot_lines_get_bbox (GPlotFunction *f, GPlotFunctionBBox *bbox) { GPlotLines *plot; @@ -300,11 +286,9 @@ g_plot_lines_get_bbox (GPlotFunction *f, GPlotFunctionBBox *bbox) (*bbox) = plot->priv->bbox; } - // This procedure is in charge to link points with ligne. // Modified to only draw spectral ray for Fourier analysis. -static void -g_plot_lines_draw (GPlotFunction *f, cairo_t *cr, GPlotFunctionBBox *bbox) +static void g_plot_lines_draw (GPlotFunction *f, cairo_t *cr, GPlotFunctionBBox *bbox) { gboolean first_point = TRUE; guint point; @@ -317,53 +301,49 @@ g_plot_lines_draw (GPlotFunction *f, cairo_t *cr, GPlotFunctionBBox *bbox) plot = GPLOT_LINES (f); - if (!plot->priv->visible) return; + if (!plot->priv->visible) + return; points = plot->priv->points; x = plot->priv->x; y = plot->priv->y; - if (plot->priv->graphic_type == FUNCTIONAL_CURVE) { + if (plot->priv->graphic_type == FUNCTIONAL_CURVE) { for (point = 1; point < points; point++) { - if ((x[point] >= bbox->xmin) && (x[point] <= bbox->xmax) && - (y[point] >= bbox->ymin) && (y[point] <= bbox->ymax)) { + 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]); + cairo_move_to (cr, x[point - 1], y[point - 1]); first_point = FALSE; } cairo_line_to (cr, x[point], y[point]); - } - else { + } else { if (!first_point) { cairo_line_to (cr, x[point], y[point]); first_point = TRUE; } } } - } - else { + } else { for (point = 1; point < points; point++) { - x1 = x[point-1]+plot->priv->shift; + x1 = x[point - 1] + plot->priv->shift; y1 = 0; - NG_DEBUG (g_strdup_printf ("gplotlines: x= %lf\ty= %lf\n", x1, y1)); + NG_DEBUG ("gplotlines: x= %lf\ty= %lf\n", x1, y1); cairo_line_to (cr, x1, y1); - cairo_move_to (cr, x[point]+ plot->priv->shift, y[point]); + cairo_move_to (cr, x[point] + plot->priv->shift, y[point]); } - } 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_set_source_rgb (cr, plot->priv->color.red, plot->priv->color.green, + plot->priv->color.blue); + 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 index 56450af..3dc252f 100644 --- a/src/gplot/gplotlines.h +++ b/src/gplot/gplotlines.h @@ -4,8 +4,8 @@ * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -23,24 +23,20 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _GPLOT_LINES_H_ #define _GPLOT_LINES_H_ -#include "gplotfunction.h" - +#include "gplotfunction.h" -typedef enum { - FUNCTIONAL_CURVE=0, - FREQUENCY_PULSE -} GraphicType; +typedef enum { FUNCTIONAL_CURVE = 0, FREQUENCY_PULSE } GraphicType; -#define GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT_LINES, GPlotLines) -#define IS_GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT_LINES) +#define GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_GPLOT_LINES, GPlotLines) +#define IS_GPLOT_LINES(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_GPLOT_LINES) -GPlotFunction* g_plot_lines_new (gdouble *x, gdouble *y, guint points); +GPlotFunction *g_plot_lines_new (gdouble *x, gdouble *y, guint points); #endif diff --git a/src/load-common.h b/src/load-common.h index 675a7ab..4313282 100644 --- a/src/load-common.h +++ b/src/load-common.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __LOAD_COMMON_H @@ -35,42 +35,44 @@ #include <glib.h> -#include "sheet-pos.h" +#include "coords.h" // Note: this must be synced with Pin in part.h for now. -typedef struct { - SheetPos pos; +typedef struct +{ + Coords pos; } Connection; -typedef struct { +typedef struct +{ gchar *name; gchar *value; } Property; -typedef struct { +typedef struct +{ gchar *name; gchar *author; gchar *version; - GSList *parts_list; - - GHashTable *part_hash; - GHashTable *symbol_hash; + GHashTable *part_hash; + GHashTable *symbol_hash; } Library; -typedef struct { - gchar *name; +typedef struct +{ + gchar *name; - gchar *description; + gchar *description; - Library* library; + Library *library; - gchar *symbol_name; - int symbol_rotation; + gchar *symbol_name; + int symbol_rotation; - gchar *refdes; - gchar *template; - gchar *model; + gchar *refdes; + gchar *template; + gchar *model; GSList *labels; GSList *properties; } LibraryPart; diff --git a/src/load-library.c b/src/load-library.c index 6ef337c..827e27d 100644 --- a/src/load-library.c +++ b/src/load-library.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <goocanvas.h> @@ -46,6 +46,7 @@ typedef enum { PARSE_LIBRARY, PARSE_NAME, PARSE_AUTHOR, + PARSE_VERSION, PARSE_SYMBOLS, PARSE_SYMBOL, PARSE_SYMBOL_NAME, @@ -74,7 +75,8 @@ typedef enum { PARSE_ERROR } State; -typedef struct { +typedef struct +{ Library *library; State state; @@ -82,12 +84,12 @@ typedef struct { gint unknown_depth; GString *content; - // Temporary placeholder for part + // Temporary placeholder for part LibraryPart *part; PartLabel *label; Property *property; - // Temporary placeholder for symbol + // Temporary placeholder for symbol LibrarySymbol *symbol; Connection *connection; SymbolObject *object; @@ -96,8 +98,7 @@ typedef struct { 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 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); @@ -106,58 +107,56 @@ 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 + 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 *library_get_symbol (const gchar *symbol_name) { LibrarySymbol *symbol; Library *library; - GList *libraries; + GList *iter; 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; + for (iter = oregano.libraries; iter; iter = iter->next) { + library = iter->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); + 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 *library_get_part (Library *library, const gchar *part_name) { LibraryPart *part; @@ -166,33 +165,30 @@ library_get_part (Library *library, const gchar *part_name) 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); + g_message (_ ("Could not find the requested part: %s\n"), part_name); } return part; } -Library * -library_parse_xml_file (const gchar *filename) +Library *library_parse_xml_file (const gchar *filename) { Library *library; ParseState state; - if (oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename) < 0) { + if (!oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename)) { g_warning ("Library '%s' not well formed!", filename); } if (state.state == PARSE_ERROR) { library = NULL; - } - else { + } else { library = state.library; } return library; } -static void -start_document (ParseState *state) +static void start_document (ParseState *state) { state->state = PARSE_START; state->unknown_depth = 0; @@ -214,15 +210,13 @@ start_document (ParseState *state) state->library->symbol_hash = g_hash_table_new (g_str_hash, g_str_equal); } -static void -end_document (ParseState *state) +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) +static void start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs) { const char *name = (const char *)xml_name; @@ -231,8 +225,7 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (strcmp (name, "ogo:library")) { g_warning ("Expecting 'ogo:library'. Got '%s'", name); state->state = PARSE_ERROR; - } - else + } else state->state = PARSE_LIBRARY; break; @@ -240,18 +233,17 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:author")) { state->state = PARSE_AUTHOR; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:name")) { + } else if (!strcmp (name, "ogo:name")) { state->state = PARSE_NAME; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:symbols")) { + } else if (!strcmp (name, "ogo:version")) { + state->state = PARSE_VERSION; + g_string_truncate (state->content, 0); + } else if (!strcmp (name, "ogo:symbols")) { state->state = PARSE_SYMBOLS; - } - else if (!strcmp (name, "ogo:parts")) { + } else if (!strcmp (name, "ogo:parts")) { state->state = PARSE_PARTS; - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -262,8 +254,7 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:symbol")) { state->state = PARSE_SYMBOL; state->symbol = g_new0 (LibrarySymbol, 1); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -274,14 +265,11 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:name")) { state->state = PARSE_SYMBOL_NAME; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:objects")) { + } else if (!strcmp (name, "ogo:objects")) { state->state = PARSE_SYMBOL_OBJECTS; - } - else if (!strcmp (name, "ogo:connections")) { + } else if (!strcmp (name, "ogo:connections")) { state->state = PARSE_SYMBOL_CONNECTIONS; - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -293,20 +281,17 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs state->object->type = SYMBOL_OBJECT_LINE; state->state = PARSE_SYMBOL_LINE; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:arc")) { + } 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")) { + } 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 { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -318,22 +303,19 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs state->state = PARSE_SYMBOL_CONNECTION; state->connection = g_new0 (Connection, 1); g_string_truncate (state->content, 0); - } - else { + } 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 { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -343,22 +325,17 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:name")) { state->state = PARSE_PART_NAME; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:description")) { + } else if (!strcmp (name, "ogo:description")) { state->state = PARSE_PART_DESCRIPTION; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:symbol")) { + } else if (!strcmp (name, "ogo:symbol")) { state->state = PARSE_PART_USESYMBOL; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:labels")) { + } else if (!strcmp (name, "ogo:labels")) { state->state = PARSE_PART_LABELS; - } - else if (!strcmp (name, "ogo:properties")) { + } else if (!strcmp (name, "ogo:properties")) { state->state = PARSE_PART_PROPERTIES; - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -368,8 +345,7 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:label")) { state->state = PARSE_PART_LABEL; state->label = g_new0 (PartLabel, 1); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -379,16 +355,13 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:name")) { state->state = PARSE_PART_LABEL_NAME; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:text")) { + } else if (!strcmp (name, "ogo:text")) { state->state = PARSE_PART_LABEL_TEXT; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:position")) { + } else if (!strcmp (name, "ogo:position")) { state->state = PARSE_PART_LABEL_POS; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -399,8 +372,7 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:property")) { state->state = PARSE_PART_PROPERTY; state->property = g_new0 (Property, 1); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -410,12 +382,10 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs if (!strcmp (name, "ogo:name")) { state->state = PARSE_PART_PROPERTY_NAME; g_string_truncate (state->content, 0); - } - else if (!strcmp (name, "ogo:value")) { + } else if (!strcmp (name, "ogo:value")) { state->state = PARSE_PART_PROPERTY_VALUE; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -437,7 +407,8 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs case PARSE_PART_PROPERTY_VALUE: case PARSE_NAME: case PARSE_AUTHOR: - // there should be no tags inside these types of tags + case PARSE_VERSION: + // 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; @@ -450,15 +421,14 @@ start_element (ParseState *state, const xmlChar *xml_name, const xmlChar **attrs state->unknown_depth++; break; case PARSE_FINISH: - // should not start new elements in this state + // should not start new elements in this state g_assert_not_reached (); break; } - //g_message("Start element %s (state %s)", name, states[state->state]); + // g_message("Start element %s (state %s)", name, states[state->state]); } -static void -end_element (ParseState *state, const xmlChar *name) +static void end_element (ParseState *state, const xmlChar *name) { switch (state->state) { case PARSE_UNKNOWN: @@ -474,12 +444,15 @@ end_element (ParseState *state, const xmlChar *name) state->library->name = g_strdup (state->content->str); state->state = PARSE_LIBRARY; break; + case PARSE_VERSION: + state->library->version = 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); + g_hash_table_insert (state->library->symbol_hash, state->symbol->name, state->symbol); state->state = PARSE_SYMBOLS; break; case PARSE_SYMBOL_NAME: @@ -489,16 +462,14 @@ end_element (ParseState *state, const xmlChar *name) case PARSE_SYMBOL_OBJECTS: state->state = PARSE_SYMBOL; break; - case PARSE_SYMBOL_LINE: - { + 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; + 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; @@ -507,45 +478,43 @@ end_element (ParseState *state, const xmlChar *name) points = g_strsplit (ptr, "(", 0); i = 0; - // Count the points. + // Count the points. while (points[i] != NULL) { i++; } - // Do not count the first string, which simply is a (. + // Do not count the first string, which simply is a (. i--; - // Construct goo canvas points. + // Construct goo canvas points. state->object->u.uline.line = goo_canvas_points_new (i); for (j = 0; j < i; j++) { double x, y; - sscanf (points[j+1], "%lf %lf)", &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->symbol->symbol_objects = + g_slist_prepend (state->symbol->symbol_objects, state->object); state->state = PARSE_SYMBOL_OBJECTS; - } - break; + } 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); + 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); + 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; @@ -555,8 +524,9 @@ end_element (ParseState *state, const xmlChar *name) 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->connection->pos.y); + state->symbol->connections = + g_slist_prepend (state->symbol->connections, state->connection); state->state = PARSE_SYMBOL_CONNECTIONS; break; @@ -564,8 +534,7 @@ end_element (ParseState *state, const xmlChar *name) state->state = PARSE_LIBRARY; break; case PARSE_PART: - g_hash_table_insert (state->library->part_hash, state->part->name, - state->part); + g_hash_table_insert (state->library->part_hash, state->part->name, state->part); state->state = PARSE_PARTS; break; case PARSE_PART_NAME: @@ -585,8 +554,7 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_PART_LABEL: state->state = PARSE_PART_LABELS; - state->part->labels = g_slist_prepend (state->part->labels, - state->label); + 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); @@ -597,8 +565,7 @@ end_element (ParseState *state, const xmlChar *name) 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); + sscanf (state->content->str, "(%lf %lf)", &state->label->pos.x, &state->label->pos.y); state->state = PARSE_PART_LABEL; break; case PARSE_PART_PROPERTIES: @@ -606,8 +573,7 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_PART_PROPERTY: state->state = PARSE_PART_PROPERTIES; - state->part->properties = g_slist_prepend (state->part->properties, - state->property); + 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); @@ -619,43 +585,38 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_LIBRARY: - // The end of the file. + // 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. + // 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]); + // g_message("End element %s (state %s)", name, states[state->state]); } -static void -my_characters (ParseState *state, const xmlChar *chars, int len) +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) + 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) +static xmlEntityPtr get_entity (void *user_data, const xmlChar *name) { return xmlGetPredefinedEntity (name); } -static void -my_warning (void *user_data, const char *msg, ...) +static void my_warning (void *user_data, const char *msg, ...) { va_list args; @@ -664,8 +625,7 @@ my_warning (void *user_data, const char *msg, ...) va_end (args); } -static void -my_error (void *user_data, const char *msg, ...) +static void my_error (void *user_data, const char *msg, ...) { va_list args; @@ -674,8 +634,7 @@ my_error (void *user_data, const char *msg, ...) va_end (args); } -static void -my_fatal_error (void *user_data, const char *msg, ...) +static void my_fatal_error (void *user_data, const char *msg, ...) { va_list args; @@ -683,4 +642,3 @@ my_fatal_error (void *user_data, const char *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 index f913988..03b836d 100644 --- a/src/load-library.h +++ b/src/load-library.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __LOAD_LIBRARY_H @@ -41,41 +41,43 @@ typedef struct _LibrarySymbol LibrarySymbol; #include "load-common.h" -struct _LibrarySymbol { - char *name; +struct _LibrarySymbol +{ + char *name; GSList *connections; GSList *symbol_objects; }; -typedef enum { - SYMBOL_OBJECT_LINE, - SYMBOL_OBJECT_ARC, - SYMBOL_OBJECT_TEXT -} SymbolObjectType; +typedef enum { SYMBOL_OBJECT_LINE, SYMBOL_OBJECT_ARC, SYMBOL_OBJECT_TEXT } SymbolObjectType; -struct _SymbolObject { +struct _SymbolObject +{ SymbolObjectType type; - union { - struct { + union + { + struct + { gboolean spline; GooCanvasPoints *line; } uline; - struct { + struct + { double x1; double y1; double x2; double y2; } arc; - struct { - gint x,y; + 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); +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 index 710e31f..8c19bd1 100644 --- a/src/load-schematic.c +++ b/src/load-schematic.c @@ -2,17 +2,21 @@ * load-schematic.c * * - * Author: + * Authors: * Richard Hult <rhult@hem.passagen.se> * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> @@ -38,20 +42,22 @@ #include "xml-helper.h" #include "load-common.h" #include "load-schematic.h" -#include "sheet-pos.h" +#include "coords.h" #include "part-label.h" +#include "schematic.h" #include "sim-settings.h" #include "textbox.h" #include "errors.h" +#include "engines/netlist-helper.h" - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" typedef enum { PARSE_START, PARSE_SCHEMATIC, PARSE_TITLE, PARSE_AUTHOR, + PARSE_VERSION, PARSE_COMMENTS, PARSE_SIMULATION_SETTINGS, @@ -62,9 +68,12 @@ typedef enum { PARSE_TRANSIENT_STEP, PARSE_TRANSIENT_STEP_ENABLE, PARSE_TRANSIENT_INIT_COND, + PARSE_TRANSIENT_ANALYZE_ALL, PARSE_AC_SETTINGS, PARSE_AC_ENABLED, + PARSE_AC_VOUT, + PARSE_AC_TYPE, PARSE_AC_NPOINTS, PARSE_AC_START, PARSE_AC_STOP, @@ -72,6 +81,7 @@ typedef enum { PARSE_DC_SETTINGS, PARSE_DC_ENABLED, PARSE_DC_VSRC, + PARSE_DC_VOUT, PARSE_DC_START, PARSE_DC_STOP, PARSE_DC_STEP, @@ -81,12 +91,20 @@ typedef enum { PARSE_FOURIER_FREQ, PARSE_FOURIER_VOUT, + PARSE_NOISE_SETTINGS, + PARSE_NOISE_ENABLED, + PARSE_NOISE_VSRC, + PARSE_NOISE_VOUT, + PARSE_NOISE_TYPE, + PARSE_NOISE_NPOINTS, + PARSE_NOISE_START, + PARSE_NOISE_STOP, + PARSE_OPTION_LIST, PARSE_OPTION, PARSE_OPTION_NAME, PARSE_OPTION_VALUE, - PARSE_ZOOM, PARSE_PARTS, @@ -112,7 +130,7 @@ typedef enum { PARSE_WIRES, PARSE_WIRE, PARSE_WIRE_POINTS, -// PARSE_WIRE_LABEL, + // PARSE_WIRE_LABEL, PARSE_TEXTBOXES, PARSE_TEXTBOX, PARSE_TEXTBOX_TEXT, @@ -122,7 +140,8 @@ typedef enum { PARSE_ERROR } State; -typedef struct { +typedef struct +{ State state; State prev_state; int unknown_depth; @@ -130,21 +149,22 @@ typedef struct { Schematic *schematic; SimSettings *sim_settings; - char *title; char *author; + char *title; + char *oregano_version; char *comments; char *textbox_text; // Temporary place holder for a wire - SheetPos wire_start; - SheetPos wire_end; + Coords wire_start; + Coords wire_end; // Temporary place holder for a part LibraryPart *part; PartLabel *label; Property *property; - SheetPos pos; + Coords pos; int rotation; IDFlip flip; @@ -155,8 +175,7 @@ typedef struct { 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 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); @@ -168,47 +187,45 @@ 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 + 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) +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); + item_data_set_pos (ITEM_DATA (textbox), &state->pos); schematic_add_item (state->schematic, ITEM_DATA (textbox)); } -static void -create_wire (ParseState *state) +static void create_wire (ParseState *state) { - SheetPos start_pos, length; + Coords start_pos, length; Wire *wire; start_pos.x = state->wire_start.x; @@ -217,74 +234,79 @@ create_wire (ParseState *state) 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); + item_data_set_pos (ITEM_DATA (wire), &state->wire_start); schematic_add_item (state->schematic, ITEM_DATA (wire)); } -static void -create_part (ParseState *state) +static void create_part (ParseState *state) { Part *part; LibraryPart *library_part = state->part; part = part_new_from_library_part (library_part); - if (!part) + if (!part) { + g_warning ("Failed to create Part from LibraryPart"); 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); + item_data_flip (ITEM_DATA (part), ID_FLIP_HORIZ, NULL); if (state->flip & ID_FLIP_VERT) - item_data_flip (ITEM_DATA (part), FALSE, NULL); + item_data_flip (ITEM_DATA (part), ID_FLIP_VERT, NULL); schematic_add_item (state->schematic, ITEM_DATA (part)); } -int -schematic_parse_xml_file (Schematic *sm, const char *filename, GError **error) +int schematic_parse_xml_file (Schematic *sm, const char *filename, GError **error) { ParseState state; - int retval = 0; + int retval = 0; state.schematic = sm; state.sim_settings = schematic_get_sim_settings (sm); - state.title = NULL; state.author = NULL; + state.title = NULL; + state.oregano_version = NULL; state.comments = NULL; - if (oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename) < 0) { + if (!oreganoXmlSAXParseFile (&oreganoSAXParser, &state, filename)) { g_warning ("Document not well formed!"); if (error != NULL) { - g_set_error (error, - OREGANO_ERROR, - OREGANO_SCHEMATIC_BAD_FILE_FORMAT, - _("Bad file format.")); + 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.")); + g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_BAD_FILE_FORMAT, + _ ("Unknown parser error.")); } retval = -2; } + schematic_set_filename(sm, filename); schematic_set_author (sm, state.author); schematic_set_title (sm, state.title); + schematic_set_version(sm, state.oregano_version); schematic_set_comments (sm, state.comments); + g_free(state.author); + g_free(state.title); + g_free(state.oregano_version); + g_free(state.comments); + + update_schematic(sm); + return retval; } -static void -start_document (ParseState *state) +static void start_document (ParseState *state) { state->state = PARSE_START; state->unknown_depth = 0; @@ -294,24 +316,21 @@ start_document (ParseState *state) state->part = NULL; } -static void -end_document (ParseState *state) +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) +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 + } else state->state = PARSE_SCHEMATIC; break; @@ -319,32 +338,27 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:version")) { + state->state = PARSE_VERSION; + 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")) { + } 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:parts")) { state->state = PARSE_PARTS; - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wires")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wires")) { state->state = PARSE_WIRES; - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textboxes")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textboxes")) { state->state = PARSE_TEXTBOXES; - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -352,22 +366,19 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_SIMULATION_SETTINGS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:transient")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:transient")) { state->state = PARSE_TRANSIENT_SETTINGS; - } - else if ( !xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:ac")) { + } 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")) { + } 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:fourier")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:fourier")) { state->state = PARSE_FOURIER_SETTINGS; - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:options")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:noise")) { + state->state = PARSE_NOISE_SETTINGS; + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:options")) { state->state = PARSE_OPTION_LIST; - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -375,31 +386,28 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_TRANSIENT_SETTINGS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) { + 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:init-conditions")) { state->state = PARSE_TRANSIENT_INIT_COND; g_string_truncate (state->content, 0); - } - else { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:analyze-all")) { + state->state = PARSE_TRANSIENT_ANALYZE_ALL; + g_string_truncate (state->content, 0); + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -407,23 +415,25 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_AC_SETTINGS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) { + 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) { + state->state = PARSE_AC_VOUT; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:type")) { + state->state = PARSE_AC_TYPE; + 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop")) { state->state = PARSE_AC_STOP; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -431,27 +441,25 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_DC_SETTINGS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) { + 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vsrc1")) { state->state = PARSE_DC_VSRC; g_string_truncate (state->content, 0); - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:start1")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) { + state->state = PARSE_DC_VOUT; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start1")) { state->state = PARSE_DC_START; g_string_truncate (state->content, 0); - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:stop1")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop1")) { state->state = PARSE_DC_STOP; g_string_truncate (state->content, 0); - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:step1")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:step1")) { state->state = PARSE_DC_STEP; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -459,47 +467,70 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_FOURIER_SETTINGS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:enabled")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) { state->state = PARSE_FOURIER_ENABLED; g_string_truncate (state->content, 0); - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:freq")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:freq")) { state->state = PARSE_FOURIER_FREQ; g_string_truncate (state->content, 0); - } - else if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:vout")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout")) { state->state = PARSE_FOURIER_VOUT; g_string_truncate (state->content, 0); - } - else { + } else { + state->prev_state = state->state; + state->state = PARSE_UNKNOWN; + state->unknown_depth++; + } + break; + + case PARSE_NOISE_SETTINGS: + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:enabled")) { + state->state = PARSE_NOISE_ENABLED; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vsrc1")) { + state->state = PARSE_NOISE_VSRC; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:vout1")) { + state->state = PARSE_NOISE_VOUT; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:type")) { + state->state = PARSE_NOISE_TYPE; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:npoints")) { + state->state = PARSE_NOISE_NPOINTS; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:start")) { + state->state = PARSE_NOISE_START; + g_string_truncate (state->content, 0); + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:stop")) { + state->state = PARSE_NOISE_STOP; + g_string_truncate (state->content, 0); + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; } - break; + break; case PARSE_OPTION_LIST: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:option")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:option")) { state->state = PARSE_OPTION; - state->option= g_new0 (SimOption,1); + state->option = g_new0 (SimOption, 1); g_string_truncate (state->content, 0); - } - else { + } 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")) { + 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:value")) { state->state = PARSE_OPTION_VALUE; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -507,94 +538,78 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_PARTS: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:part")) { + 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 { + } 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")) { + 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:properties")) { state->state = PARSE_PART_PROPERTIES; - } - else { + } 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")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:label")) { state->state = PARSE_PART_LABEL; state->label = g_new0 (PartLabel, 1); - } - else { + } 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")) { + 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")) { + } 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:position")) { state->state = PARSE_PART_LABEL_POS; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -602,49 +617,43 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_PART_PROPERTIES: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:property")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:property")) { state->state = PARSE_PART_PROPERTY; state->property = g_new0 (Property, 1); - } - else { + } 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")) { + 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:value")) { state->state = PARSE_PART_PROPERTY_VALUE; g_string_truncate (state->content, 0); - } - else { + } 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")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:wire")) { state->state = PARSE_WIRE; - } - else { + } 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")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:points")) { state->state = PARSE_WIRE_POINTS; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -652,25 +661,22 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) break; case PARSE_TEXTBOXES: - if (!xmlStrcmp (BAD_CAST name, BAD_CAST"ogo:textbox")) { + if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:textbox")) { state->state = PARSE_TEXTBOX; - } - else { + } 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")) { + 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")) { + } else if (!xmlStrcmp (BAD_CAST name, BAD_CAST "ogo:text")) { state->state = PARSE_TEXTBOX_TEXT; g_string_truncate (state->content, 0); - } - else { + } else { state->prev_state = state->state; state->state = PARSE_UNKNOWN; state->unknown_depth++; @@ -684,12 +690,15 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) case PARSE_TRANSIENT_STEP_ENABLE: case PARSE_AC_ENABLED: + case PARSE_AC_VOUT: + case PARSE_AC_TYPE: case PARSE_AC_NPOINTS: case PARSE_AC_START: case PARSE_AC_STOP: case PARSE_DC_ENABLED: case PARSE_DC_VSRC: + case PARSE_DC_VOUT: case PARSE_DC_START: case PARSE_DC_STOP: case PARSE_DC_STEP: @@ -698,6 +707,14 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) case PARSE_FOURIER_FREQ: case PARSE_FOURIER_VOUT: + case PARSE_NOISE_ENABLED: + case PARSE_NOISE_VSRC: + case PARSE_NOISE_VOUT: + case PARSE_NOISE_TYPE: + case PARSE_NOISE_NPOINTS: + case PARSE_NOISE_START: + case PARSE_NOISE_STOP: + case PARSE_WIRE_POINTS: case PARSE_OPTION_NAME: case PARSE_OPTION_VALUE: @@ -720,8 +737,11 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) case PARSE_ZOOM: case PARSE_TITLE: case PARSE_AUTHOR: + case PARSE_VERSION: case PARSE_COMMENTS: - // there should be no tags inside these types of tags + case PARSE_TRANSIENT_INIT_COND: + case PARSE_TRANSIENT_ANALYZE_ALL: + // 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; @@ -734,19 +754,17 @@ start_element (ParseState *state, const xmlChar *name, const xmlChar **attrs) state->unknown_depth++; break; case PARSE_FINISH: - // should not start new elements in this state + // 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]); + // g_message("Start element %s (state %s)", name, states[state->state]); } -static void -end_element (ParseState *state, const xmlChar *name) +static void end_element (ParseState *state, const xmlChar *name) { GList *libs; + Schematic *schematic = state->schematic; switch (state->state) { case PARSE_UNKNOWN: @@ -762,12 +780,15 @@ end_element (ParseState *state, const xmlChar *name) state->title = g_strdup (state->content->str); state->state = PARSE_SCHEMATIC; break; + case PARSE_VERSION: + state->oregano_version = 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: - { + case PARSE_ZOOM: { double zoom; zoom = g_strtod (state->content->str, NULL); @@ -780,18 +801,17 @@ end_element (ParseState *state, const xmlChar *name) 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_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_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); + sim_settings_set_trans_start (state->sim_settings, state->content->str); state->state = PARSE_TRANSIENT_SETTINGS; break; case PARSE_TRANSIENT_STOP: @@ -804,12 +824,17 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_TRANSIENT_STEP_ENABLE: sim_settings_set_trans_step_enable (state->sim_settings, - !g_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_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_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_strcasecmp (state->content->str, "true")); + state->state = PARSE_TRANSIENT_SETTINGS; + break; + case PARSE_TRANSIENT_ANALYZE_ALL: + sim_settings_set_trans_analyze_all(state->sim_settings, + !g_ascii_strcasecmp (state->content->str, "true")); state->state = PARSE_TRANSIENT_SETTINGS; break; @@ -818,11 +843,21 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_AC_ENABLED: sim_settings_set_ac (state->sim_settings, - !g_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_strcasecmp (state->content->str, "true")); state->state = PARSE_AC_SETTINGS; + break; + case PARSE_AC_VOUT: + sim_settings_set_ac_vout (state->sim_settings, state->content->str); + state->state = PARSE_AC_SETTINGS; + break; + case PARSE_AC_TYPE: + sim_settings_set_ac_type (state->sim_settings, state->content->str); + state->state = PARSE_AC_SETTINGS; + break; case PARSE_AC_NPOINTS: sim_settings_set_ac_npoints (state->sim_settings, state->content->str); state->state = PARSE_AC_SETTINGS; + break; case PARSE_AC_START: sim_settings_set_ac_start (state->sim_settings, state->content->str); state->state = PARSE_AC_SETTINGS; @@ -837,11 +872,17 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_DC_ENABLED: sim_settings_set_dc (state->sim_settings, - !g_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_strcasecmp (state->content->str, "true")); state->state = PARSE_DC_SETTINGS; + break; case PARSE_DC_VSRC: sim_settings_set_dc_vsrc (state->sim_settings, state->content->str); state->state = PARSE_DC_SETTINGS; + break; + case PARSE_DC_VOUT: + sim_settings_set_dc_vout (state->sim_settings, state->content->str); + state->state = PARSE_DC_SETTINGS; + break; case PARSE_DC_START: sim_settings_set_dc_start (state->sim_settings, state->content->str); state->state = PARSE_DC_SETTINGS; @@ -854,13 +895,13 @@ end_element (ParseState *state, const xmlChar *name) sim_settings_set_dc_step (state->sim_settings, state->content->str); state->state = PARSE_DC_SETTINGS; break; - + case PARSE_FOURIER_SETTINGS: state->state = PARSE_SIMULATION_SETTINGS; break; case PARSE_FOURIER_ENABLED: sim_settings_set_fourier (state->sim_settings, - !g_ascii_strcasecmp (state->content->str, "true")); + !g_ascii_strcasecmp (state->content->str, "true")); state->state = PARSE_FOURIER_SETTINGS; break; case PARSE_FOURIER_FREQ: @@ -872,22 +913,55 @@ end_element (ParseState *state, const xmlChar *name) state->state = PARSE_FOURIER_SETTINGS; break; + case PARSE_NOISE_SETTINGS: + state->state = PARSE_SIMULATION_SETTINGS; + break; + case PARSE_NOISE_ENABLED: + sim_settings_set_noise (state->sim_settings, + !g_ascii_strcasecmp (state->content->str, "true")); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_VSRC: + sim_settings_set_noise_vsrc (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_VOUT: + sim_settings_set_noise_vout (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_TYPE: + sim_settings_set_noise_type (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_NPOINTS: + sim_settings_set_noise_npoints (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_START: + sim_settings_set_noise_start (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_SETTINGS; + break; + case PARSE_NOISE_STOP: + sim_settings_set_noise_stop (state->sim_settings, state->content->str); + state->state = PARSE_NOISE_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); + 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; + state->state = PARSE_OPTION; break; case PARSE_OPTION_VALUE: state->option->value = g_strdup (state->content->str); - state->state=PARSE_OPTION; + state->state = PARSE_OPTION; break; case PARSE_PARTS: @@ -906,7 +980,7 @@ end_element (ParseState *state, const xmlChar *name) state->part->library = NULL; libs = oregano.libraries; while (libs) { - Library *lib = (Library *) libs->data; + Library *lib = (Library *)libs->data; if (g_ascii_strcasecmp (state->content->str, lib->name) == 0) { state->part->library = lib; break; @@ -916,6 +990,16 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_PART_POSITION: sscanf (state->content->str, "(%lf %lf)", &state->pos.x, &state->pos.y); + // Try to fix invalid positions + if (state->pos.x < 0) + state->pos.x = -state->pos.x; + if (state->pos.y < 0) + state->pos.y = -state->pos.y; + // Determine the maximum parts' coordinates to be used during sheet creation + if (state->pos.x > schematic_get_width(schematic)) + schematic_set_width(schematic, (guint) state->pos.x); + if (state->pos.y > schematic_get_height(schematic)) + schematic_set_height(schematic, (guint) state->pos.y); state->state = PARSE_PART; break; case PARSE_PART_ROTATION: @@ -923,9 +1007,9 @@ end_element (ParseState *state, const xmlChar *name) state->state = PARSE_PART; break; case PARSE_PART_FLIP: - if (g_ascii_strcasecmp ( state->content->str, "horizontal") == 0) + if (g_ascii_strcasecmp (state->content->str, "horizontal") == 0) state->flip = state->flip | ID_FLIP_HORIZ; - else if (g_ascii_strcasecmp ( state->content->str, "vertical") == 0) + else if (g_ascii_strcasecmp (state->content->str, "vertical") == 0) state->flip = state->flip | ID_FLIP_VERT; state->state = PARSE_PART; break; @@ -950,8 +1034,7 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_PART_LABEL: state->state = PARSE_PART_LABELS; - state->part->labels = g_slist_prepend (state->part->labels, - state->label); + 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); @@ -962,8 +1045,7 @@ end_element (ParseState *state, const xmlChar *name) 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); + sscanf (state->content->str, "(%lf %lf)", &state->label->pos.x, &state->label->pos.y); state->state = PARSE_PART_LABEL; break; case PARSE_PART_PROPERTIES: @@ -971,8 +1053,7 @@ end_element (ParseState *state, const xmlChar *name) break; case PARSE_PART_PROPERTY: state->state = PARSE_PART_PROPERTIES; - state->part->properties = g_slist_prepend (state->part->properties, - state->property); + 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); @@ -987,13 +1068,12 @@ end_element (ParseState *state, const xmlChar *name) 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); + sscanf (state->content->str, "(%lf %lf)(%lf %lf)", &state->wire_start.x, + &state->wire_start.y, &state->wire_end.x, &state->wire_end.y); + create_wire (state); state->state = PARSE_WIRE; break; @@ -1005,8 +1085,7 @@ end_element (ParseState *state, const xmlChar *name) state->state = PARSE_TEXTBOXES; break; case PARSE_TEXTBOX_POSITION: - sscanf (state->content->str, "(%lf %lf)", - &state->pos.x, &state->pos.y); + sscanf (state->content->str, "(%lf %lf)", &state->pos.x, &state->pos.y); state->state = PARSE_TEXTBOX; break; case PARSE_TEXTBOX_TEXT: @@ -1014,42 +1093,37 @@ end_element (ParseState *state, const xmlChar *name) state->state = PARSE_TEXTBOX; break; case PARSE_SCHEMATIC: - // The end of the file. + // 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. + // There should not be a closing tag in this state. g_assert_not_reached (); break; } } -static void -my_characters (ParseState *state, const xmlChar *chars, int len) +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) + 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) +static xmlEntityPtr get_entity (void *user_data, const xmlChar *name) { return xmlGetPredefinedEntity (name); } -static void -my_warning (void *user_data, const char *msg, ...) +static void my_warning (void *user_data, const char *msg, ...) { va_list args; @@ -1058,8 +1132,8 @@ my_warning (void *user_data, const char *msg, ...) va_end (args); } -static void -my_error (void *user_data, const char *msg, ...) +// FIXME this should not be critical but forward to the oregano log buffer +static void my_error (void *user_data, const char *msg, ...) { va_list args; @@ -1068,8 +1142,7 @@ my_error (void *user_data, const char *msg, ...) va_end (args); } -static void -my_fatal_error (void *user_data, const char *msg, ...) +static void my_fatal_error (void *user_data, const char *msg, ...) { va_list args; diff --git a/src/load-schematic.h b/src/load-schematic.h index 6243de9..ce3926d 100644 --- a/src/load-schematic.h +++ b/src/load-schematic.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __LOAD_SCHEMATIC_H @@ -39,5 +39,4 @@ gint schematic_parse_xml_file (Schematic *sm, const gchar *filename, GError **); - #endif diff --git a/src/log-interface.h b/src/log-interface.h new file mode 100644 index 0000000..d51eab3 --- /dev/null +++ b/src/log-interface.h @@ -0,0 +1,44 @@ +/* + * log-interface.h + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * Use this interface to get rid of GUI dependencies. For example + * if you want to write a unit test in which the test function + * needs a log to not throw an exception. + */ + +#ifndef LOG_INTERFACE_H_ +#define LOG_INTERFACE_H_ + +typedef void (*LogFunction)(gpointer log, const char *message); + +typedef struct { + LogFunction log_append; + LogFunction log_append_error; + gpointer log; +} LogInterface; + +#endif /* LOG_INTERFACE_H_ */ diff --git a/src/log-view.c b/src/log-view.c new file mode 100644 index 0000000..fa5d5bd --- /dev/null +++ b/src/log-view.c @@ -0,0 +1,52 @@ +#include "log-view.h" + +#define LOG_VIEW_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), TYPE_LOG_VIEW, LogViewPrivate)) + +struct _LogViewPrivate +{ + char x; +}; + +G_DEFINE_TYPE (LogView, log_view, GTK_TYPE_TREE_VIEW); + +static void log_view_finalize (GObject *object) +{ + G_OBJECT_CLASS (log_view_parent_class)->finalize (object); +} + +static void log_view_class_init (LogViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = log_view_finalize; + + g_type_class_add_private (object_class, sizeof(LogViewPrivate)); +} + +static void log_view_init (LogView *self) { self->priv = LOG_VIEW_GET_PRIVATE (self); } + +LogView *log_view_new () +{ + LogView *self = g_object_new (TYPE_LOG_VIEW, NULL); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self), TRUE); + + GtkTreeViewColumn *col1, *col2; + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + + g_object_ref (renderer); + + col1 = gtk_tree_view_column_new_with_attributes ("Source", renderer, "text", 0, NULL); + + col2 = gtk_tree_view_column_new_with_attributes ("Message", renderer, "text", 1, NULL); + + gtk_tree_view_insert_column (GTK_TREE_VIEW (self), col1, 0); + gtk_tree_view_insert_column (GTK_TREE_VIEW (self), col2, 1); + + return self; +} + +void log_view_set_store (LogView *lv, Log *ls) +{ + gtk_tree_view_set_model (GTK_TREE_VIEW (lv), GTK_TREE_MODEL (ls)); +} diff --git a/src/log-view.h b/src/log-view.h new file mode 100644 index 0000000..8124711 --- /dev/null +++ b/src/log-view.h @@ -0,0 +1,42 @@ +#ifndef __LOG_VIEW_H__ +#define __LOG_VIEW_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#include "log.h" + +G_BEGIN_DECLS + +#define TYPE_LOG_VIEW (log_view_get_type ()) +#define LOG_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_LOG_VIEW, LogView)) +#define LOG_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_LOG_VIEW, LogView const)) +#define LOG_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_LOG_VIEW, LogViewClass)) +#define LOG_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_LOG_VIEW)) +#define LOG_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_LOG_VIEW)) +#define LOG_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_LOG_VIEW, LogViewClass)) + +typedef struct _LogView LogView; +typedef struct _LogViewClass LogViewClass; +typedef struct _LogViewPrivate LogViewPrivate; + +struct _LogView +{ + GtkTreeView parent; + + LogViewPrivate *priv; +}; + +struct _LogViewClass +{ + GtkTreeViewClass parent_class; +}; + +GType log_view_get_type (void) G_GNUC_CONST; +LogView *log_view_new (void); + +void log_view_set_store (LogView *lv, Log *ls); + +G_END_DECLS + +#endif /* __LOG_VIEW_H__ */ diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..a48486f --- /dev/null +++ b/src/log.c @@ -0,0 +1,107 @@ +#include <string.h> +#include "log.h" + +#define LOG_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), LOG_TYPE, LogPrivate)) + +struct _LogPrivate +{ + char x; +}; + +G_DEFINE_TYPE (Log, log, GTK_TYPE_LIST_STORE); + +static void log_finalize (GObject *object) { G_OBJECT_CLASS (log_parent_class)->finalize (object); } + +static void log_class_init (LogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = log_finalize; + + g_type_class_add_private (object_class, sizeof(LogPrivate)); +} + +static void log_init (Log *self) +{ + static GType v[] = {G_TYPE_STRING, G_TYPE_STRING}; + self->priv = LOG_GET_PRIVATE (self); + gtk_list_store_set_column_types (GTK_LIST_STORE (self), 2, v); +} + +Log *log_new () { return g_object_new (LOG_TYPE, NULL); } + +/** + * trim only newlines + */ +gchar *log_append_trim_message(const gchar *string) { + if (string == NULL) + return NULL; + gchar **array = g_regex_split_simple("[\\r\\n]*(.*?)[\\n$]+", string, 0, 0); + gchar *ret_val = g_strdup(array[0]); + g_strfreev(array); + if (*ret_val == 0) { + g_free(ret_val); + ret_val = NULL; + } + return ret_val; +} + +/** + * Show on top of the items in the logview. + * + * If the previous message had an '\r' at the end, the previous message will be + * replaced by the new message instead of prepending the new message. + * + * Leading and trailing '\n's will be truncated. + */ +void log_append (Log *log, const gchar *prefix, const gchar *message) +{ + //trim message (only newlines), because newlines do not make sense in the log view + gchar *message_trimed = log_append_trim_message(message); + if (message_trimed == NULL) + return; + + GtkTreeIter iter; + //if the previous message had an '\r' at the end, the previous message will be + //replaced by the new message instead of prepending the new message + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(log), &iter)) { + GValue previous_value; + previous_value.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(log), &iter, 1, &previous_value); + const gchar *previous_message = g_value_get_string(&previous_value); + + if (previous_message != NULL && previous_message[strlen(previous_message) - 1] != '\r') + gtk_list_store_prepend (GTK_LIST_STORE (log), &iter); + g_value_unset(&previous_value); + } else { + gtk_list_store_prepend (GTK_LIST_STORE (log), &iter); + } + gtk_list_store_set (GTK_LIST_STORE (log), &iter, 0, g_strdup (prefix), 1, g_strdup (message_trimed), + -1); + g_free(message_trimed); +} + +void log_append_error (Log *log, const gchar *prefix, const gchar *message, GError *error) +{ + if (error == NULL) { + return log_append (log, prefix, message); + } + gchar *concat = NULL; + + if (message) { + concat = g_strdup_printf ("%s\n%s - %i", message, error->message, error->code); + } else { + concat = g_strdup_printf ("\n%s - %i", error->message, error->code); + } + + GtkTreeIter item; + gtk_list_store_insert (GTK_LIST_STORE (log), &item, 0); + gtk_list_store_set (GTK_LIST_STORE (log), &item, 0, g_strdup (prefix), 1, concat, -1); +} + +void log_clear (Log *log) { gtk_list_store_clear (GTK_LIST_STORE (log)); } + +void log_export (Log *log, const gchar *path) +{ + // FIXME TODO +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..12b009f --- /dev/null +++ b/src/log.h @@ -0,0 +1,41 @@ +#ifndef __LOG_H__ +#define __LOG_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define LOG_TYPE (log_get_type ()) +#define LOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LOG_TYPE, Log)) +#define LOG_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LOG_TYPE, Log const)) +#define LOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LOG_TYPE, LogClass)) +#define LOG_IS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LOG_TYPE)) +#define LOG_IS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LOG_TYPE)) +#define LOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LOG_TYPE, LogClass)) + +typedef struct _Log Log; +typedef struct _LogClass LogClass; +typedef struct _LogPrivate LogPrivate; + +struct _Log +{ + GtkListStore parent; + + LogPrivate *priv; +}; + +struct _LogClass +{ + GtkListStoreClass parent_class; +}; + +GType log_get_type (void) G_GNUC_CONST; +Log *log_new (void); + +void log_append (Log *log, const gchar *prefix, const gchar *message); + +void log_append_error (Log *log, const gchar *prefix, const gchar *message, GError *error); + +G_END_DECLS + +#endif /* __LOG_H__ */ @@ -1,13 +1,14 @@ /* * main.c * - * Author: + * + * Authors: * Richard Hult <rhult@hem.passagen.se> * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -25,22 +26,31 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#include <config.h> +#include <glib.h> +#include <glib/gprintf.h> + #include <gtk/gtk.h> #include <glib/gi18n.h> #include "oregano.h" +#include "options.h" +#include "schematic.h" -int -main (int argc, char *argv[]) +int main (int argc, char *argv[]) { + // keep in mind the app is a subclass of GtkApplication + // which is a subclass of GApplication + // so casts from g_application_get_default to + // GtkApplication as well as GApplication or Oregano + // are explicitly allowed Oregano *app; + GError *error = NULL; int status; - + gpointer class = NULL; #ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); @@ -48,10 +58,35 @@ main (int argc, char *argv[]) textdomain (GETTEXT_PACKAGE); #endif - gtk_init (&argc, &argv); - app = oregano_new (); - status = g_application_run (G_APPLICATION (app), argc, argv); - g_object_unref (app); + oregano_options_parse (&argc, &argv, &error); + if (error) { + g_warning ("Failed to parse commandline arguments: %i - %s", error->code, error->message); + g_clear_error (&error); + return 1; + } + if (oregano_options_version()) { + g_printf("Oregano "VERSION"\n" + " Website: https://ahoi.io/projects/oregano\n" + " License: GPLv2/GPLv3\n" + " Main Developer: Bernhard Schuster\n"); + return 0; + } + + // required? + gtk_init (&argc, &argv); + + // required, as we possibly need signal + // information within oregano.c _before_ the + // first Schematic instance exists + class = g_type_class_ref (TYPE_SCHEMATIC); + app = oregano_new (); + + status = g_application_run (G_APPLICATION (app), argc, argv); + + oregano_deallocate_memory (); + + g_object_unref (app); + g_type_class_unref (class); - return status; + return status; } diff --git a/src/model/Makefile.am b/src/model/Makefile.am deleted file mode 100644 index 6abbb8b..0000000 --- a/src/model/Makefile.am +++ /dev/null @@ -1,40 +0,0 @@ -oreganodir = $(datadir)/oregano - -AM_CFLAGS = -Wall -DG_DISABLE_DEPRECATED -DGSEAL_ENABLE \ - -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ - -DGTK_DISABLE_DEPRECATED \ - -DG_DISABLE_SINGLE_INCLUDES -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES \ - -DGTK_DISABLE_SINGLE_INCLUDES - -INCLUDES = \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -I$(includedir) $(GNOME_INCLUDEDIR) \ - -DOREGANO_UIDIR=\""$(oreganodir)/xml"\" \ - -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 diff --git a/src/model/item-data.c b/src/model/item-data.c index 382a012..c9391d4 100644 --- a/src/model/item-data.c +++ b/src/model/item-data.c @@ -8,12 +8,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -27,80 +29,87 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#include "goocanvas.h" +#include <glib.h> +#include <goocanvas.h> #include "item-data.h" #include "node-store.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.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_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 gboolean emit_moved_signal_when_handler_connected (gpointer data); -enum { - ARG_0, - ARG_STORE, - ARG_POS -}; +enum { ARG_0, ARG_STORE, ARG_POS }; enum { MOVED, ROTATED, FLIPPED, + CHANGED, // used to notify the view to reset and recalc the transformation HIGHLIGHT, LAST_SIGNAL }; -struct _ItemDataPriv { +struct _ItemDataPriv +{ NodeStore *store; - SheetPos pos; - // Bounding box. + + // modificator matrices + cairo_matrix_t translate; + cairo_matrix_t rotate; + cairo_matrix_t flip; + + // Bounding box GooCanvasBounds bounds; }; -// Structure defined to cover exchange between g_timeout_add and the -// function in charge to emit the moved signal only once the handler -// has been connected. -typedef struct { - ItemData *item_data; - SheetPos delta; -} SignalStruct; +G_DEFINE_TYPE (ItemData, item_data, G_TYPE_OBJECT); -G_DEFINE_TYPE (ItemData, item_data, G_TYPE_OBJECT) +static guint item_data_signals[LAST_SIGNAL] = {0}; -static guint item_data_signals [LAST_SIGNAL] = { 0 }; +static void item_data_init (ItemData *item_data) +{ + ItemDataPriv *priv; + + priv = g_slice_new0 (ItemDataPriv); + + priv->bounds.x1 = priv->bounds.x2 = priv->bounds.y1 = priv->bounds.y2 = 0; -static void -item_data_dispose (GObject *object) + cairo_matrix_init_identity (&(priv->translate)); + cairo_matrix_init_identity (&(priv->rotate)); + cairo_matrix_init_identity (&(priv->flip)); + + item_data->priv = priv; +} + +static void item_data_dispose (GObject *object) { + ItemDataPriv *priv = ITEM_DATA (object)->priv; // Remove the item from the sheet node store if there. - if (ITEM_DATA (object)->priv->store) { + if (priv->store) { item_data_unregister (ITEM_DATA (object)); } - + g_slice_free (ItemDataPriv, priv); G_OBJECT_CLASS (item_data_parent_class)->dispose (object); } - -static void -item_data_finalize (GObject *object) +static void item_data_finalize (GObject *object) { g_return_if_fail (object != NULL); G_OBJECT_CLASS (item_data_parent_class)->finalize (object); } -static void -item_data_class_init (ItemDataClass *klass) +static void item_data_class_init (ItemDataClass *klass) { GObjectClass *object_class; @@ -108,63 +117,41 @@ item_data_class_init (ItemDataClass *klass) object_class = G_OBJECT_CLASS (klass); - // This assignment must be performed before the call + // This assignment must be performed before the call // to 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_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)); + 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); + 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[CHANGED] = + g_signal_new ("changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, + NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + 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; @@ -173,38 +160,27 @@ item_data_class_init (ItemDataClass *klass) klass->flip = NULL; klass->reg = NULL; klass->unreg = NULL; + klass->changed = NULL; // Signals. klass->moved = NULL; } -static void -item_data_init (ItemData *item_data) -{ - ItemDataPriv *priv; - - priv = g_new0 (ItemDataPriv, 1); - - priv->pos.x = 0; - priv->pos.y = 0; - priv->bounds.x1 = priv->bounds.x2 = priv->bounds.y1 = priv->bounds.y2 = 0; +//////////////////////////////////////////////////////////////////////////////// +// END BOILER PLATE +//////////////////////////////////////////////////////////////////////////////// - item_data->priv = priv; -} - -ItemData * -item_data_new (void) +ItemData *item_data_new (void) { ItemData *item_data; - item_data = ITEM_DATA (g_object_new (item_data_get_type(), NULL)); + 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) +static void item_data_set_gproperty (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) { ItemData *item_data = ITEM_DATA (object); @@ -213,13 +189,12 @@ item_data_set_gproperty (GObject *object, guint prop_id, const GValue *value, item_data->priv->store = g_value_get_pointer (value); break; default: - break; + G_OBJECT_WARN_INVALID_PROPERTY_ID (item_data, prop_id, spec); } } -static void -item_data_get_gproperty (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +static void item_data_get_gproperty (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec) { ItemData *item_data = ITEM_DATA (object); @@ -232,72 +207,48 @@ item_data_get_gproperty (GObject *object, guint prop_id, GValue *value, } } -void -item_data_get_pos (ItemData *item_data, SheetPos *pos) +/** + * returns the top left corner of the item + */ +void item_data_get_pos (ItemData *item_data, Coords *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; + ItemDataPriv *priv; + priv = item_data->priv; + pos->x = priv->translate.x0; + pos->y = priv->translate.y0; } -void -item_data_set_pos (ItemData *item_data, SheetPos *pos) +void item_data_set_pos (ItemData *item_data, Coords *pos) { ItemDataPriv *priv; - SheetPos delta; - SignalStruct *signal_struct; + gboolean handler_connected; - g_return_if_fail (pos != NULL); - g_return_if_fail (item_data != NULL); + g_return_if_fail (pos); + g_return_if_fail (item_data); g_return_if_fail (IS_ITEM_DATA (item_data)); - if (pos == NULL) - return; - priv = item_data->priv; - priv->pos.x = pos->x; - priv->pos.y = pos->y; - - delta.x = pos->x; - delta.y = pos->y; - - signal_struct = g_new0 (SignalStruct, 1); - signal_struct->item_data = item_data; - signal_struct->delta = delta; - g_timeout_add (10, - (gpointer) emit_moved_signal_when_handler_connected, - (gpointer) signal_struct); -} -static gboolean -emit_moved_signal_when_handler_connected (gpointer data) -{ - gboolean handler_connected; - SignalStruct *signal_struct = (SignalStruct *) data; - SheetPos delta; - ItemData *item_data; + cairo_matrix_init_translate (&(priv->translate), pos->x, pos->y); - item_data = signal_struct->item_data; - delta.x = signal_struct->delta.x; - delta.y = signal_struct->delta.y; - - handler_connected = g_signal_handler_is_connected (G_OBJECT (item_data), - item_data->moved_handler_id); + handler_connected = + g_signal_handler_is_connected (G_OBJECT (item_data), item_data->moved_handler_id); if (handler_connected) { - g_signal_emit_by_name (G_OBJECT (item_data), - "moved", &delta); + g_signal_emit_by_name (G_OBJECT (item_data), "moved", pos); + } + handler_connected = + g_signal_handler_is_connected (G_OBJECT (item_data), item_data->changed_handler_id); + if (handler_connected) { + g_signal_emit_by_name (G_OBJECT (item_data), "changed"); } - - return !handler_connected; } -void -item_data_move (ItemData *item_data, SheetPos *delta) +void item_data_move (ItemData *item_data, const Coords *delta) { ItemDataPriv *priv; - SignalStruct *signal_struct; g_return_if_fail (item_data != NULL); g_return_if_fail (IS_ITEM_DATA (item_data)); @@ -305,22 +256,17 @@ item_data_move (ItemData *item_data, SheetPos *delta) if (delta == NULL) return; + if (fabs (delta->x) < 1e-2 && fabs (delta->y) < 1e-2) + return; + priv = item_data->priv; - priv->pos.x += delta->x; - priv->pos.y += delta->y; - delta->x = priv->pos.x; - delta->y = priv->pos.y; - - signal_struct = g_new0 (SignalStruct, 1); - signal_struct->item_data = item_data; - signal_struct->delta = (* delta); - g_timeout_add (10, - (gpointer) emit_moved_signal_when_handler_connected, - (gpointer) signal_struct); + cairo_matrix_translate (&(priv->translate), delta->x, delta->y); + + g_signal_emit_by_name (G_OBJECT (item_data), "changed"); } -gpointer // NodeStore * -item_data_get_store (ItemData *item_data) +// NodeStore * +gpointer 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); @@ -328,8 +274,7 @@ item_data_get_store (ItemData *item_data) return item_data->priv->store; } -ItemData * -item_data_clone (ItemData *src) +ItemData *item_data_clone (ItemData *src) { ItemDataClass *id_class; @@ -343,20 +288,20 @@ item_data_clone (ItemData *src) return id_class->clone (src); } -static void -item_data_copy (ItemData *dest, ItemData *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->translate = src->priv->translate; + dest->priv->rotate = src->priv->rotate; + dest->priv->flip = src->priv->flip; dest->priv->store = NULL; } -void -item_data_get_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2) +void item_data_get_relative_bbox (ItemData *data, Coords *p1, Coords *p2) { g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); @@ -372,27 +317,28 @@ item_data_get_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2) } } -void -item_data_get_absolute_bbox (ItemData *data, SheetPos *p1, SheetPos *p2) +void item_data_get_absolute_bbox (ItemData *data, Coords *p1, Coords *p2) { g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); + ItemDataPriv *priv; + item_data_get_relative_bbox (data, p1, p2); + priv = data->priv; if (p1) { - p1->x += data->priv->pos.x; - p1->y += data->priv->pos.y; + p1->x += priv->translate.x0; + p1->y += priv->translate.y0; } if (p2) { - p2->x += data->priv->pos.x; - p2->y += data->priv->pos.y; + p2->x += priv->translate.x0; + p2->y += priv->translate.y0; } } -void -item_data_set_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2) +void item_data_set_relative_bbox (ItemData *data, Coords *p1, Coords *p2) { g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); @@ -408,20 +354,20 @@ item_data_set_relative_bbox (ItemData *data, SheetPos *p1, SheetPos *p2) } } -void -item_data_list_get_absolute_bbox (GList *item_data_list, SheetPos *p1, - SheetPos *p2) +void item_data_list_get_absolute_bbox (GList *item_data_list, Coords *p1, Coords *p2) { - GList *list; - SheetPos b1, b2; + GList *iter; + Coords 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); + for (iter = item_data_list; iter; iter = iter->next) { + if (G_UNLIKELY (iter->data == NULL)) + continue; + item_data_get_absolute_bbox (iter->data, &b1, &b2); if (p1) { p1->x = MIN (p1->x, b1.x); @@ -433,11 +379,9 @@ item_data_list_get_absolute_bbox (GList *item_data_list, SheetPos *p1, p2->y = MAX (p2->y, b2.y); } } - g_list_free_full (list, g_object_unref); } -void -item_data_rotate (ItemData *data, int angle, SheetPos *center) +void item_data_rotate (ItemData *data, int angle, Coords *center) { ItemDataClass *id_class; @@ -450,8 +394,7 @@ item_data_rotate (ItemData *data, int angle, SheetPos *center) } } -void -item_data_flip (ItemData *data, gboolean horizontal, SheetPos *center) +void item_data_flip (ItemData *data, IDFlip direction, Coords *center) { ItemDataClass *id_class; @@ -460,12 +403,11 @@ item_data_flip (ItemData *data, gboolean horizontal, SheetPos *center) id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data)); if (id_class->flip) { - id_class->flip (data, horizontal, center); + id_class->flip (data, direction, center); } } -void -item_data_unregister (ItemData *data) +void item_data_unregister (ItemData *data) { ItemDataClass *id_class; @@ -478,23 +420,21 @@ item_data_unregister (ItemData *data) } } -int -item_data_register (ItemData *data) +gboolean 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); + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (IS_ITEM_DATA (data), FALSE); id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data)); if (id_class->reg) { return id_class->reg (data); } - return -1; + return FALSE; } -char * -item_data_get_refdes_prefix (ItemData *data) +char *item_data_get_refdes_prefix (ItemData *data) { ItemDataClass *id_class; @@ -509,8 +449,7 @@ item_data_get_refdes_prefix (ItemData *data) return NULL; } -void -item_data_set_property (ItemData *data, char *property, char *value) +void item_data_set_property (ItemData *data, char *property, char *value) { ItemDataClass *id_class; @@ -524,8 +463,7 @@ item_data_set_property (ItemData *data, char *property, char *value) } } -gboolean -item_data_has_properties (ItemData *data) +gboolean item_data_has_properties (ItemData *data) { ItemDataClass *id_class; @@ -539,8 +477,7 @@ item_data_has_properties (ItemData *data) return FALSE; } -void -item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) +void item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) { ItemDataClass *id_class; @@ -553,3 +490,49 @@ item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) id_class->print (data, cr, ctx); } } + +/** + * \brief changed, forcefully emits a changed signal to recalculate the morph + *matrix + * + * @param data determines which item to refresh + * + * \note this function does _not_ request a redraw + */ +void item_data_changed (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->changed == NULL) + return; + + return id_class->changed (data); +} + +/** + * @param data + * @returns [transfer-none] pointer to cairo matrix which only includes the + * translation + */ +cairo_matrix_t *item_data_get_translate (ItemData *data) +{ + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (IS_ITEM_DATA (data), NULL); + return &(data->priv->translate); +} + +/** + * @param data + * @returns [transfer-none] pointer to cairo matrix which only includes the + * rotation + */ +cairo_matrix_t *item_data_get_rotate (ItemData *data) +{ + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (IS_ITEM_DATA (data), NULL); + return &(data->priv->rotate); +} diff --git a/src/model/item-data.h b/src/model/item-data.h index fb1b4b6..4ebf177 100644 --- a/src/model/item-data.h +++ b/src/model/item-data.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __ITEM_DATA_H #define __ITEM_DATA_H @@ -36,31 +38,36 @@ #include <cairo/cairo.h> -#include "sheet-pos.h" +#include "coords.h" +#include "grid.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)) +#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 + ID_FLIP_NONE = 0, + ID_FLIP_HORIZ = 1 << 0, + ID_FLIP_VERT = 1 << 1, + ID_FLIP_MASK = 3 } IDFlip; -struct _ItemData { - GObject parent; - gulong moved_handler_id; - gulong rotated_handler_id; - gulong flipped_handler_id; - ItemDataPriv * priv; +struct _ItemData +{ + GObject parent; + gulong moved_handler_id; + gulong rotated_handler_id; + gulong flipped_handler_id; + gulong changed_handler_id; + ItemDataPriv *priv; }; struct _ItemDataClass @@ -68,95 +75,92 @@ struct _ItemDataClass GObjectClass parent_class; // Signals. - void (*moved) (ItemData *data, SheetPos *delta); + void (*moved)(ItemData *data, Coords *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); + ItemData *(*clone)(ItemData *src); + void (*copy)(ItemData *dest, ItemData *src); + void (*rotate)(ItemData *data, int angle, Coords *center); + void (*flip)(ItemData *data, IDFlip direction, Coords *center); + void (*unreg)(ItemData *data); + int (*reg)(ItemData *data); + void (*changed)(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); +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); +ItemData *item_data_clone (ItemData *src); // Get Item position -void item_data_get_pos (ItemData *item_data, SheetPos *pos); +void item_data_get_pos (ItemData *item_data, Coords *pos); // Set Item position -void item_data_set_pos (ItemData *item_data, SheetPos *pos); +void item_data_set_pos (ItemData *item_data, Coords *pos); // Move an ItemData // \param delta offset to move the item -void item_data_move (ItemData *item_data, SheetPos *delta); +void item_data_move (ItemData *item_data, const Coords *delta); -// Get the bounding box of an item data -//Retrieve the bounding box of the item relative to position +// 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); +void item_data_get_relative_bbox (ItemData *data, Coords *p1, Coords *p2); // Set the relative bounding box -void item_data_set_relative_bbox (ItemData *data, SheetPos *p1, - SheetPos *p2); +void item_data_set_relative_bbox (ItemData *data, Coords *p1, Coords *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); +void item_data_get_absolute_bbox (ItemData *data, Coords *p1, Coords *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); +void item_data_list_get_absolute_bbox (GList *item_data_list, Coords *p1, Coords *p2); // Rotate an item -void item_data_rotate (ItemData *data, int angle, SheetPos *center); +void item_data_rotate (ItemData *data, int angle, Coords *center); // Flip an item -void item_data_flip (ItemData *data, gboolean horizontal, - SheetPos *center); +void item_data_flip (ItemData *data, IDFlip direction, Coords *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); +gpointer item_data_get_store (ItemData *item_data); // Unregister item in its Store -void item_data_unregister (ItemData *data); +void item_data_unregister (ItemData *data); // Register item in its Store -int item_data_register (ItemData *data); +int item_data_register (ItemData *data); // Get the prefix of a part reference -char * item_data_get_refdes_prefix (ItemData *data); +char *item_data_get_refdes_prefix (ItemData *data); -gboolean item_data_has_properties (ItemData *date); +gboolean item_data_has_properties (ItemData *date); // Set property -void item_data_set_property (ItemData *data, char *property, - char *value); +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); +void item_data_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx); + +// Refresh the canvas representation of the item based on the itemdata's +// properties (or its subclasses) +void item_data_changed (ItemData *data); +// +cairo_matrix_t *item_data_get_translate (ItemData *data); +cairo_matrix_t *item_data_get_rotate (ItemData *data); #endif diff --git a/src/model/node-store-private.h b/src/model/node-store-private.h new file mode 100644 index 0000000..a41a067 --- /dev/null +++ b/src/model/node-store-private.h @@ -0,0 +1,353 @@ +#ifndef NODE_STORE_PRIV_H__ +#define NODE_STORE_PRIV_H__ + +#include <glib.h> + +#include "coords.h" +#include "node.h" +#include "wire.h" + +#include "debug.h" +static gboolean is_point_on_wire (Wire *w, Coords *where); + +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); +} + +/** + * check if 2 wires intersect + * @param a wire + * @param b wire + * @param where [out] [NULL allowed] the position of intersection, if + * @returns TRUE when @a and @b intersect, else FALSE + */ +gboolean do_wires_intersect (Wire *a, Wire *b, Coords *where) +{ + g_assert (a); + g_assert (b); + Coords delta1, start1; + Coords delta2, start2; + wire_get_pos_and_length (a, &start1, &delta1); + wire_get_pos_and_length (b, &start2, &delta2); + + // parallel check + const gdouble d = coords_cross (&delta1, &delta2); + if (fabs (d) < NODE_EPSILON) { + NG_DEBUG ("do_wires_intersect(%p,%p): NO! d=%lf\n", a, b, d); + return FALSE; + } + + // implemented according to + // http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect + const Coords qminusp = coords_sub (&start2, &start1); + + // p = start1, q = start2, r = delta1, s = delta2 + const gdouble t = coords_cross (&qminusp, &delta1) / d; + const gdouble u = coords_cross (&qminusp, &delta2) / d; + + if (t >= -NODE_EPSILON && t - NODE_EPSILON <= 1.f && u >= -NODE_EPSILON && + u - NODE_EPSILON <= 1.f) { + NG_DEBUG ("do_wires_intersect(%p,%p): YES! t,u = %lf,%lf\n", a, b, t, u); + if (where) { + where->x = start1.x + u * delta1.x; + where->y = start1.y + u * delta1.y; + } + return TRUE; + } + NG_DEBUG ("do_wires_intersect(%p,%p): NO! t,u = %lf,%lf\n", a, b, t, u); + return FALSE; +} + +/** + * @param store which NodeStore to use + * @param pos where to look for wires + * @returns list of wire at position p (including endpoints) + */ +static GSList *get_wires_at_pos (NodeStore *store, Coords pos) +{ + GList *iter; + GSList *wire_list; + + g_return_val_if_fail (store, FALSE); + g_return_val_if_fail (IS_NODE_STORE (store), FALSE); + + wire_list = NULL; + + for (iter = store->wires; iter; iter = iter->next) { + Wire *wire = iter->data; + + if (is_point_on_wire (wire, &pos)) + wire_list = g_slist_prepend (wire_list, wire); + } + + return wire_list; +} + +/** + * projects the point @p onto wire @w + * @param w the wire + * @param p the point + * @param projected [out][NULL-allowed] the point p is projected onto - this is + * always filled no matter of return value + * @param d [out][NULL-allowed] the distance between @p and @projected - this is + * always filled no matter of return value + * @returns TRUE if the point was within the line segement bounds, else FALSE + */ +static gboolean project_point_on_wire (Wire *w, Coords *p, Coords *projected, gdouble *d) +{ + Coords cstart, clen; + wire_get_pos_and_length (w, &cstart, &clen); + + const Coords delta = coords_sub (p, &cstart); + const gdouble l2 = coords_euclid2 (&clen); + const gdouble lambda = coords_dot (&delta, &clen) / l2; + Coords pro = {cstart.x + lambda * clen.x, cstart.y + lambda * clen.y}; + if (projected) { + *projected = pro; + } + if (d) { + pro.x -= p->x; + pro.y -= p->y; + *d = coords_euclid2 (&pro); + } + + return (lambda >= 0. - NODE_EPSILON && lambda <= 1. + NODE_EPSILON); +} + +/** + * test if point is within a wire (including endpoints) + * @param w the wire + * @param p the point to test + */ +static gboolean is_point_on_wire (Wire *w, Coords *p) +{ + g_assert (w); + g_assert (IS_WIRE (w)); + g_assert (p); + gdouble d = -7777.7777; + const gboolean ret = project_point_on_wire (w, p, NULL, &d); + return (ret && fabs (d) < NODE_EPSILON); +} + +/** + * check if the two given wires have any incomon endpoints + * @param w the wire + * @param p the point to test + */ +static gboolean is_point_at_end_of_wire (Wire *w, Coords *p) +{ + g_assert (w); + g_assert (IS_WIRE (w)); + g_assert (p); + + Coords start1, end1; + wire_get_start_and_end_pos (w, &start1, &end1); + + return coords_equal (&start1, p) || coords_equal (&end1, p); +} + +/** + * check if 2 wires are colinear/parallel + */ +static gboolean check_colinear (Wire *a, Wire *b) +{ + Coords la, sa; + Coords lb, sb; + wire_get_pos_and_length (a, &sa, &la); + wire_get_pos_and_length (b, &sb, &lb); + +// below implementations are idential +#if 0 + Coords nb; + n1.x = +lb.y; + n1.y = -lb.x; + return fabs(coords_dot (&la,&nb)) < NODE_EPSILON; +#else + return fabs (coords_cross (&la, &lb)) < NODE_EPSILON; +#endif +} + +/** + * check if the two given wires overlap + * @param so start overlap + * @param eo end overlap + */ +static gboolean do_wires_overlap (Wire *a, Wire *b, Coords *so, Coords *eo) +{ + g_assert (a); + g_assert (IS_WIRE (a)); + g_assert (b); + g_assert (IS_WIRE (b)); + g_assert (so); + g_assert (eo); + + Coords sa, la, ea; + Coords sb, lb, eb; + + wire_get_pos_and_length (a, &sa, &la); + wire_get_pos_and_length (b, &sb, &lb); + ea = coords_sum (&sa, &la); + eb = coords_sum (&sb, &lb); + + // parallel check + if (!check_colinear (a, b)) + return FALSE; + + const gboolean sb_on_a = is_point_on_wire (a, &sb); + const gboolean eb_on_a = is_point_on_wire (a, &eb); + const gboolean sa_on_b = is_point_on_wire (b, &sa); + const gboolean ea_on_b = is_point_on_wire (b, &ea); + + // a-----b++++++b---a + if (sb_on_a && eb_on_a) { + *so = sb; + *eo = eb; + return TRUE; + } + if (sa_on_b && ea_on_b) { + *so = sa; + *eo = ea; + return TRUE; + } + + // a----b~~~~a++++++b + // this also covers "touching" of colinear wires + if (sb_on_a && sa_on_b) { + *so = sb; + *eo = sa; + return TRUE; + } + if (eb_on_a && sa_on_b) { + *so = eb; + *eo = ea; + return TRUE; + } + if (sb_on_a && ea_on_b) { + *so = sb; + *eo = ea; + return TRUE; + } + if (eb_on_a && ea_on_b) { + *so = eb; + *eo = sa; + return TRUE; + } + return FALSE; +} + +/** + * check if wires a and b form a t crossing + * @param a + * @param b + * @param t [out][NULL-allowed] which endpoint is the T point (where both wires + * "intersect") + * @returns which of the inputs + * @attention wire @a and @b are taken as granted to be intersecting + * @attention L-like crossings are a special form of T-crossings + */ +gboolean is_t_crossing (Wire *a, Wire *b, Coords *t) +{ + g_assert (a); + g_assert (IS_WIRE (a)); + g_assert (b); + g_assert (IS_WIRE (b)); + + Coords sa, ea, sb, eb; + + wire_get_start_and_end_pos (a, &sa, &ea); + wire_get_start_and_end_pos (b, &sb, &eb); + + if (is_point_on_wire (a, &sb) /* && !is_point_on_wire (a, &sb)*/) { + if (t) // a + *t = sb; // sbbb + return TRUE; // a + } + if (is_point_on_wire (a, &eb) /* && !is_point_at_end_of_wire (a, &eb)*/) { + if (t) // a + *t = eb; // bbe + return TRUE; // a + } + if (is_point_on_wire (b, &sa) /* && !is_point_at_end_of_wire (b, &sa)*/) { + if (t) + *t = sa; + return TRUE; + } + if (is_point_on_wire (b, &ea) /* && !is_point_at_end_of_wire (b, &ea)*/) { + if (t) + *t = ea; + return TRUE; + } + return FALSE; +} + +/** + * merge wire @b into wire @a, where @so and @eo are the overlapping wire part + * + * @param a wire + * @param b wire to merge into @a + * @param so [out] coords of the overlapping wire part - start + * @param eo [out] coords of the overlapping wire part - end + * @returns the pointer to a, or NUL if something went wrong + * @attention onlycall this for two parallel and overlapping wires, ever! + */ +static Wire *vulcanize_wire (NodeStore *store, Wire *a, Wire *b) +{ + g_assert (store); + g_assert (IS_NODE_STORE (store)); + g_assert (a); + g_assert (IS_WIRE (a)); + g_assert (b); + g_assert (IS_WIRE (b)); + + Coords starta, enda; + Coords startb, endb; + GSList *list; + + wire_get_start_pos (a, &starta); + wire_get_end_pos (a, &enda); + wire_get_start_pos (b, &startb); + wire_get_end_pos (b, &endb); + + Coords start, end, len; + start.x = MIN (MIN (starta.x, startb.x), MIN (enda.x, endb.x)); + start.y = MIN (MIN (starta.y, startb.y), MIN (enda.y, endb.y)); + end.x = MAX (MAX (starta.x, startb.x), MAX (enda.x, endb.x)); + end.y = MAX (MAX (starta.y, startb.y), MAX (enda.y, endb.y)); + len.x = end.x - start.x; + len.y = end.y - start.y; + + g_assert ((fabs (len.x) < NODE_EPSILON) ^ (fabs (len.y) < NODE_EPSILON)); + +// FIXME register and unregister to new position +#define CREATE_NEW_WIRE 0 +// always null, or schematic_add_item in create_wire +// will return pure bogus (and potentially crash!) +#if CREATE_NEW_WIRE + Wire *w = wire_new (item_data_get_grid (ITEM_DATA (a))); + g_return_val_if_fail (w, NULL); + g_return_val_if_fail (IS_WIRE (w), NULL); +#else + Wire *w = a; +#endif + item_data_set_pos (ITEM_DATA (w), &start); + wire_set_length (w, &len); + + for (list = wire_get_nodes (b); list;) { + Node *n = list->data; + list = list->next; // needs to be done here, as wire_add_node mods the list + if (!IS_NODE (n)) + g_warning ("Found bogus node entry in wire %p, ignored.", b); + wire_add_node (w, n); + node_add_wire (n, w); + } + return w; +} + +#endif /* NODE_STORE_PRIVATE_H__ */ diff --git a/src/model/node-store.c b/src/model/node-store.c index 2342c5c..6b09df5 100644 --- a/src/model/node-store.c +++ b/src/model/node-store.c @@ -1,19 +1,27 @@ /* * node-store.c * - * This is where the circuit elements (pins/wires) are stored. + * + * Store nodes, wires to hashtables. + * Generate new wires, merge new wire create requests where possible with + *already existing ones + * * * Authors: * Richard Hult <rhult@hem.passagen.se> * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2012-2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -27,8 +35,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -36,57 +44,47 @@ #include <gtk/gtk.h> #include <math.h> +#define NODE_EPSILON 1e-10 +#define HASH_EPSILON 1e-3 #include "node-store.h" +#include "node-store-private.h" #include "node.h" #include "part.h" #include "wire.h" #include "wire-private.h" -// NODE_EPSILON is used to check for intersection. -#define NODE_EPSILON 1e-10 -// HASH_EPSILON is used in the hash equality check function. -#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 ) - -#define NG_DEBUG(s) if (0) g_print ("NG: %s\n", s) - -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 { - NODE_DOT_ADDED, - NODE_DOT_REMOVED, - LAST_SIGNAL -}; - -G_DEFINE_TYPE (NodeStore, node_store, G_TYPE_OBJECT) - -static guint node_store_signals [LAST_SIGNAL] = { 0 }; - -static void -node_store_finalize (GObject *object) +#include "debug.h" + +/* NODE_EPSILON is used to check for intersection. */ +/* HASH_EPSILON is used in the hash equality check function. */ + +/* 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 void node_store_finalize (GObject *self); +static void node_store_dispose (GObject *self); + +enum { NODE_DOT_ADDED, NODE_DOT_REMOVED, LAST_SIGNAL }; + +G_DEFINE_TYPE (NodeStore, node_store, G_TYPE_OBJECT); + +static guint node_store_signals[LAST_SIGNAL] = {0}; + +static void node_store_dispose (GObject *self) +{ + G_OBJECT_CLASS (node_store_parent_class)->dispose (self); +} + +static void node_store_finalize (GObject *object) { NodeStore *self = NODE_STORE (object); @@ -107,18 +105,15 @@ node_store_finalize (GObject *object) g_list_free (self->items); self->items = NULL; } + if (self->textbox) { + g_list_free (self->textbox); + self->textbox = NULL; + } G_OBJECT_CLASS (node_store_parent_class)->finalize (object); } -static void -node_store_dispose (GObject *self) -{ - G_OBJECT_CLASS (node_store_parent_class)->dispose (self); -} - -static void -node_store_class_init (NodeStoreClass *klass) +static void node_store_class_init (NodeStoreClass *klass) { GObjectClass *gobject_class; @@ -128,31 +123,18 @@ node_store_class_init (NodeStoreClass *klass) gobject_class->finalize = node_store_finalize; gobject_class->dispose = node_store_dispose; - node_store_signals [NODE_DOT_ADDED] = - g_signal_new ("node_dot_added", - G_TYPE_FROM_CLASS (gobject_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NodeStoreClass, node_dot_added), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, G_TYPE_POINTER); - - node_store_signals [NODE_DOT_REMOVED] = - g_signal_new ("node_dot_removed", - TYPE_NODE_STORE, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NodeStoreClass, node_dot_removed), - NULL, - NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, - 1, G_TYPE_POINTER); + node_store_signals[NODE_DOT_ADDED] = + g_signal_new ("node_dot_added", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NodeStoreClass, node_dot_added), NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + + node_store_signals[NODE_DOT_REMOVED] = + g_signal_new ("node_dot_removed", TYPE_NODE_STORE, G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NodeStoreClass, node_dot_removed), NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } -static void -node_store_init (NodeStore *self) +static void node_store_init (NodeStore *self) { self->nodes = g_hash_table_new (node_hash, node_equal); self->wires = NULL; @@ -161,14 +143,13 @@ node_store_init (NodeStore *self) self->textbox = NULL; } -NodeStore * -node_store_new (void) -{ - return NODE_STORE(g_object_new (TYPE_NODE_STORE, NULL)); -} +//////////////////////////////////////////////////////////////////////////////// +// END BOILERPLATE +//////////////////////////////////////////////////////////////////////////////// -static void -node_dot_added_callback (Node *node, SheetPos *pos, NodeStore *store) +NodeStore *node_store_new (void) { return NODE_STORE (g_object_new (TYPE_NODE_STORE, NULL)); } + +static void node_dot_added_callback (Node *node, Coords *pos, NodeStore *store) { g_return_if_fail (store != NULL); g_return_if_fail (IS_NODE_STORE (store)); @@ -176,21 +157,23 @@ node_dot_added_callback (Node *node, SheetPos *pos, NodeStore *store) g_signal_emit_by_name (G_OBJECT (store), "node_dot_added", pos); } -static void -node_dot_removed_callback (Node *node, SheetPos *pos, NodeStore *store) +static void node_dot_removed_callback (Node *node, Coords *pos, NodeStore *store) { - g_return_if_fail (store != NULL); + g_return_if_fail (store); g_return_if_fail (IS_NODE_STORE (store)); g_signal_emit_by_name (G_OBJECT (store), "node_dot_removed", pos); } -Node * -node_store_get_or_create_node (NodeStore *self, SheetPos pos) +/** + * lookup if a node at @pos exists, if so return it, otherwise + * create one, add it to the nodestore and return it + */ +Node *node_store_get_or_create_node (NodeStore *self, Coords pos) { Node *node; - g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (self, NULL); g_return_val_if_fail (IS_NODE_STORE (self), NULL); node = g_hash_table_lookup (self->nodes, &pos); @@ -200,65 +183,58 @@ node_store_get_or_create_node (NodeStore *self, SheetPos pos) node = node_new (pos); g_signal_connect_object (G_OBJECT (node), "node_dot_added", - G_CALLBACK (node_dot_added_callback), G_OBJECT (self), 0); + G_CALLBACK (node_dot_added_callback), G_OBJECT (self), 0); g_signal_connect_object (G_OBJECT (node), "node_dot_removed", - G_CALLBACK (node_dot_removed_callback), G_OBJECT (self), 0); + G_CALLBACK (node_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) +/** + * register a part to the nodestore + */ +gboolean 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); + NG_DEBUG ("-0-"); + g_return_val_if_fail (self, FALSE); g_return_val_if_fail (IS_NODE_STORE (self), FALSE); - g_return_val_if_fail (part != NULL, FALSE); + g_return_val_if_fail (part, FALSE); g_return_val_if_fail (IS_PART (part), FALSE); + GSList *iter, *copy; + Node *node; + Coords pin_pos; + Coords part_pos; + int i, num_pins; + Pin *pins; + num_pins = part_get_num_pins (part); + pins = part_get_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; + // Use the position of the pin as hash key. + pin_pos.x = part_pos.x + pins[i].offset.x; + pin_pos.y = part_pos.y + pins[i].offset.y; // Retrieve a node for this position. - node = node_store_get_or_create_node (self, lookup_key); + node = node_store_get_or_create_node (self, pin_pos); // 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; - - NG_DEBUG ("Add pin to wire.\n"); + copy = get_wires_at_pos (self, pin_pos); + for (iter = copy; iter; iter = iter->next) { + Wire *wire = copy->data; node_add_wire (node, wire); wire_add_node (wire, node); } - g_slist_free (wire_list); + g_slist_free (copy); node_add_pin (node, &pins[i]); } @@ -270,51 +246,48 @@ node_store_add_part (NodeStore *self, Part *part) return TRUE; } -int -node_store_remove_part (NodeStore *self, Part *part) +/** + * remove/unregister a part from the nodestore + * this does _not_ free the part! + */ +gboolean node_store_remove_part (NodeStore *self, Part *part) { Node *node; - SheetPos lookup_key; - SheetPos pos; - gdouble x, y; + Coords pin_pos; + Coords part_pos; int i, num_pins; Pin *pins; - g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (self, FALSE); g_return_val_if_fail (IS_NODE_STORE (self), FALSE); - g_return_val_if_fail (part != NULL, FALSE); + g_return_val_if_fail (part, 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); + item_data_get_pos (ITEM_DATA (part), &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; + pin_pos.x = part_pos.x + pins[i].offset.x; + pin_pos.y = part_pos.y + pins[i].offset.y; - node = g_hash_table_lookup (self->nodes, &lookup_key); + node = g_hash_table_lookup (self->nodes, &pin_pos); if (node) { if (!node_remove_pin (node, &pins[i])) { - g_warning ("Couldn't remove pin."); + g_warning ("Could not remove pin[%i] from node %p.", i, node); 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_hash_table_remove (self->nodes, &pin_pos); g_object_unref (G_OBJECT (node)); } - } - else { + } else { return FALSE; } } @@ -322,8 +295,7 @@ node_store_remove_part (NodeStore *self, Part *part) return TRUE; } -int -node_store_add_textbox (NodeStore *self, Textbox *text) +gboolean 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); @@ -331,178 +303,202 @@ node_store_add_textbox (NodeStore *self, Textbox *text) return TRUE; } -int -node_store_remove_textbox (NodeStore *self, Textbox *text) +gboolean 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) +/** + * Remove all wires in the nodestore that overlap with a given + * wire (preserve the longest of the two overlapping wires). + * This is basically an optimization of the nodestore. + */ +void node_store_remove_overlapping_wires (NodeStore *store, Wire *wire) { - gdouble x1, y1, x2, y2; - GSList *ip_list, *list; - IntersectionPoint *ipoint; + gdouble a, b; + Coords start_pos, end_pos, start_pos_other, end_pos_other; + Coords length, length_other; + Coords so, eo; + GList *list, *to_be_removed = NULL; + Node *sn, *en; + Wire *other; + gboolean overlap; + + g_return_if_fail (store != NULL); + g_return_if_fail (IS_NODE_STORE (store)); + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + // Check for overlapping with other wires. + for (list = store->wires; list && list->data != wire; list = list->next) { + g_assert (list->data != NULL); + g_assert (IS_WIRE (list->data)); + other = list->data; + overlap = do_wires_overlap (wire, other, &so, &eo); + NG_DEBUG ("overlap [ %p] and [ %p ] -- %s", wire, other, + overlap == TRUE ? "YES" : "NO"); + if (overlap) { + sn = node_store_get_node (store, eo); + en = node_store_get_node (store, so); + if (!sn && !en) + wire = vulcanize_wire (store, wire, other); + wire_get_pos_and_length (wire, &start_pos, &length); + wire_get_pos_and_length (other, &start_pos_other, &length_other); + wire_get_start_and_end_pos (wire, &start_pos, &end_pos); + wire_get_start_and_end_pos (other, &start_pos_other, &end_pos_other); + if (length.x < 0) { + a = start_pos.x; + b = end_pos.x; + start_pos.x = b; + end_pos.x = a; + length.x = -length.x; + } + if (length.y < 0) { + a = start_pos.y; + b = end_pos.y; + start_pos.y = b; + end_pos.y = a; + length.y = -length.y; + } + if (length_other.x < 0) { + a = start_pos_other.x; + b = end_pos_other.x; + start_pos_other.x = b; + end_pos_other.x = a; + length_other.x = -length_other.x; + } + if (length_other.y < 0) { + a = start_pos_other.y; + b = end_pos_other.y; + start_pos_other.y = b; + end_pos_other.y = a; + length_other.y = -length_other.y; + } + if (length.x > 0 && length_other.x > 0 && length.y == 0 && length_other.y == 0) { + if (start_pos.x >= start_pos_other.x && end_pos.x <= end_pos_other.x) + to_be_removed = g_list_append (to_be_removed, wire); + if (start_pos_other.x >= start_pos.x && end_pos_other.x <= end_pos.x) + to_be_removed = g_list_append (to_be_removed, other); + } else if (length.y > 0 && length_other.y > 0 && length.x == 0 && length_other.x == 0) { + if (start_pos.y >= start_pos_other.y && end_pos.y <= end_pos_other.y) + to_be_removed = g_list_append (to_be_removed, wire); + if (start_pos_other.y >= start_pos.y && end_pos_other.y <= end_pos.y) + to_be_removed = g_list_append (to_be_removed, other); + } + } + } + + // Remove all the wires that overlap with a given wire + for (list = to_be_removed; list; list = list->next) { + if (node_store_remove_wire (store, list->data)) + wire_delete (list->data); + } + g_list_free (to_be_removed); +} + +/** + * add/register the wire to the nodestore + * + * @param store + * @param wire + * @returns TRUE if the wire was added or merged, else FALSE + */ +gboolean node_store_add_wire (NodeStore *store, Wire *wire) +{ + GList *list; Node *node; - SheetPos pos, length; + Coords where; + int i = 0; - g_return_val_if_fail (store != NULL, FALSE); + g_return_val_if_fail (store, FALSE); g_return_val_if_fail (IS_NODE_STORE (store), FALSE); - g_return_val_if_fail (wire != NULL, FALSE); + g_return_val_if_fail (wire, FALSE); g_return_val_if_fail (IS_WIRE (wire), FALSE); - 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; - - 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 ((fabs (node->key.x - p1.x) < 1e-3) && - (fabs (node->key.y - p1.y) < 1e-3)){ - can_join = FALSE; - break; - } - p1.x = _x2; - p1.y = _y2; - if ((fabs (node->key.x - p1.x) < 1e-3) && - (fabs (node->key.y - p1.y) < 1e-3)){ - 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 is 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 ((fabs (node->key.x - p.x) < 1e-3) && - (fabs (node->key.y - p.y) < 1e-3)){ - can_join = FALSE; - break; - } - p.x = _x2; - p.y = _y2; - if ((fabs (node->key.x - p.x) < 1e-3) && - (fabs (node->key.y - p.y) < 1e-3)){ - can_join = FALSE; - break; + for (list = store->wires; list; list = list->next) { + g_assert (list->data != NULL); + g_assert (IS_WIRE (list->data)); + + Wire *other = list->data; + if (do_wires_intersect (wire, other, &where)) { + if (is_t_crossing (wire, other, &where) || is_t_crossing (other, wire, &where)) { + + node = node_store_get_or_create_node (store, where); + + node_add_wire (node, wire); + node_add_wire (node, other); + + wire_add_node (wire, node); + wire_add_node (other, node); + + NG_DEBUG ("Add wire %p to wire %p @ %lf,%lf.\n", wire, other, where.x, where.y); + } else { + // magic node removal if a x crossing is overlapped with another wire + node = node_store_get_node (store, where); + NG_DEBUG ("Nuke that node [ %p ] at coords inbetween", node); + if (node) { + Coords c[4]; + wire_get_start_and_end_pos (other, c + 0, c + 1); + wire_get_start_and_end_pos (wire, c + 2, c + 3); + if (!coords_equal (&where, c + 0) && !coords_equal (&where, c + 1) && + !coords_equal (&where, c + 2) && !coords_equal (&where, c + 3)) { + + wire_remove_node (wire, node); + wire_remove_node (other, node); + node_remove_wire (node, wire); + node_remove_wire (node, other); + } } } - - 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); + node_store_remove_overlapping_wires (store, wire); - wire_add_node (wire, node); - wire_add_node (ipoint->wire, node); + // Check for intersection with parts (pins). + for (list = store->parts; list; list = list->next) { + g_assert (list->data != NULL); + g_assert (IS_PART (list->data)); - NG_DEBUG ("Add wire to wire.\n"); + Coords part_pos; + gint num_pins = -1; + Part *part = list->data; - g_free (ipoint); - } - g_slist_free (ip_list); + num_pins = part_get_num_pins (part); + item_data_get_pos (ITEM_DATA (part), &part_pos); - // Check for intersection with parts (pins). - ip_list = wire_intersect_parts (store, wire); + // Go through all the parts and see which of their + // pins that intersect the wire. + for (i = 0; i < num_pins; i++) { + Pin *pins; + Coords lookup_pos; - for (list = ip_list; list; list = list->next) { - node = list->data; + pins = part_get_pins (part); + lookup_pos.x = part_pos.x + pins[i].offset.x; + lookup_pos.y = part_pos.y + pins[i].offset.y; - // Add the wire to the node (pin) that it intersected. - node_add_wire (node, wire); - wire_add_node (wire, node); + // If there is a wire at this pin's position, + // add it to the return list. + if (is_point_on_wire (wire, &lookup_pos)) { + Node *node; + node = node_store_get_node (store, lookup_pos); - NG_DEBUG ("Add wire to pin.\n"); + if (node != NULL) { + // Add the wire to the node (pin) that it intersected. + node_add_wire (node, wire); + wire_add_node (wire, node); + NG_DEBUG ("Add wire %p to pin (node) %p.\n", wire, node); + } else { + g_warning ("Bug: Found no node at pin at (%g %g).\n", lookup_pos.x, + lookup_pos.y); + } + } + } } - 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); @@ -510,214 +506,81 @@ node_store_add_wire (NodeStore *store, Wire *wire) return TRUE; } -int -node_store_remove_wire (NodeStore *store, Wire *wire) +/** + * removes/unregisters a wire from the nodestore + * this does _not_ free the wire itself! + */ +gboolean node_store_remove_wire (NodeStore *store, Wire *wire) { - GSList *list; - SheetPos lookup_key, pos, length; + GSList *copy, *iter; + Coords lookup_key; - g_return_val_if_fail (store != NULL, FALSE); + g_return_val_if_fail (store, FALSE); g_return_val_if_fail (IS_NODE_STORE (store), FALSE); - g_return_val_if_fail (wire != NULL, FALSE); + g_return_val_if_fail (wire, FALSE); g_return_val_if_fail (IS_WIRE (wire), FALSE); if (item_data_get_store (ITEM_DATA (wire)) == NULL) { - g_warning ("Trying to remove non-stored wire."); + g_warning ("Trying to remove not-stored wire %p.", wire); return FALSE; } - wire_get_pos_and_length (wire, &pos, &length); - 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; + // FIXME if done properly, a list copy is _not_ necessary + copy = g_slist_copy (wire_get_nodes (wire)); + for (iter = copy; iter; iter = iter->next) { + Node *node = iter->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); + g_slist_free (copy); 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) - ip_list = g_slist_prepend (ip_list, node); - } - } - } - g_list_free_full (list, g_object_unref); - - 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); - } - g_list_free_full (list, g_object_unref); - - return wire_list; -} - -int -node_store_is_wire_at_pos (NodeStore *store, SheetPos pos) +/** + * check if there is at least one wire at the specified position + * + * @param store which NodeStore to check + * @param pos the position + * @returns TRUE or FALSE + */ +gboolean node_store_is_wire_at_pos (NodeStore *store, Coords pos) { - GList *list; - Wire *wire; - SheetPos wire_pos, wire_length; - double x1, y1, x2, y2; + GList *iter; - g_return_val_if_fail (store != NULL, FALSE); + g_return_val_if_fail (store, FALSE); g_return_val_if_fail (IS_NODE_STORE (store), FALSE); - for (list = store->wires; list; list = list->next) { - wire = list->data; + for (iter = store->wires; iter; iter = iter->next) { + Wire *wire = iter->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)) + if (is_point_on_wire (wire, &pos)) return TRUE; } - g_list_free_full (list, g_object_unref); - return FALSE; } -static GSList * -wires_intersect (NodeStore *store, double x1, double y1, double x2, double y2) -{ - GList *list; - GSList *ip_list; - Wire *wire; - SheetPos pos, wire_pos, wire_length; - double wire_x1, wire_y1, wire_x2, wire_y2; - - g_return_val_if_fail (store != NULL, FALSE); - g_return_val_if_fail (IS_NODE_STORE (store), FALSE); - - // Search through all the wires. Is there a better way? - ip_list = NULL; - for (list = store->wires; list; list = list->next) { - wire = list->data; - - wire_get_pos_and_length (wire, &wire_pos, &wire_length); - wire_x1 = wire_pos.x; - wire_y1 = wire_pos.y; - wire_x2 = wire_x1 + wire_length.x; - wire_y2 = wire_y1 + wire_length.y; - - if (do_wires_intersect (x1, y1, x2, y2, wire_x1, wire_y1, - wire_x2, wire_y2, &pos)) { - IntersectionPoint *ip; - - ip = g_new0 (IntersectionPoint, 1); - - ip->wire = wire; - ip->pos = pos; - ip_list = g_slist_prepend (ip_list, ip); - } - } - g_list_free_full (list, g_object_unref); - - 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) +/** + * lookup node at specified position + * + * @param store which store to check + * @param pos where to check in that store + * @returns the node pointer if there is a node, else NULL + */ +Node *node_store_get_node (NodeStore *store, Coords pos) { Node *node; @@ -727,53 +590,17 @@ node_store_get_node (NodeStore *store, SheetPos pos) node = g_hash_table_lookup (store->nodes, &pos); if (!node) { - NG_DEBUG (g_strdup_printf ("No node at (%g, %g)\n", pos.x, pos.y));} - else { - NG_DEBUG (g_strdup_printf ("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; - - // Hash on any other node? - - 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) - NG_DEBUG ("A neighbour of B in Y\n"); - - return 0; + NG_DEBUG ("No node at (%g, %g)", pos.x, pos.y); + } else { + NG_DEBUG ("Found node at (%g, %g)", pos.x, pos.y); } - - if (fabs (spa->x - spb->x) > HASH_EPSILON) { - if (fabs (spa->x - spb->x) < 5.0) - NG_DEBUG ("A neighbour of B in X\n\n"); - - return 0; - } - - return 1; + return node; } -void -node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data) +/** + * Call GHFunc for each node in the nodestore + */ +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)); @@ -781,106 +608,10 @@ node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data) 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; - } - } - - NG_DEBUG (g_strdup_printf ("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)); - 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) +/** + * [transfer-none] + */ +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); @@ -888,8 +619,10 @@ node_store_get_parts (NodeStore *store) return store->parts; } -GList * -node_store_get_wires (NodeStore *store) +/** + * [transfer-none] + */ +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); @@ -897,8 +630,10 @@ node_store_get_wires (NodeStore *store) return store->wires; } -GList * -node_store_get_items (NodeStore *store) +/** + * [transfer-none] + */ +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); @@ -906,21 +641,10 @@ node_store_get_items (NodeStore *store) return store->items; } -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) +/** + * the caller has to free the list himself, but not the actual data items! + */ +GList *node_store_get_node_positions (NodeStore *store) { GList *result; @@ -928,13 +652,15 @@ node_store_get_node_positions (NodeStore *store) g_return_val_if_fail (IS_NODE_STORE (store), NULL); result = NULL; - g_hash_table_foreach (store->nodes, (GHFunc) add_node_position, &result); + g_hash_table_foreach (store->nodes, (GHFunc)add_node_position, &result); return result; } -GList * -node_store_get_nodes (NodeStore *store) +/** + * the caller has to free the list himself, but not the actual data items! + */ +GList *node_store_get_nodes (NodeStore *store) { GList *result; @@ -942,16 +668,15 @@ node_store_get_nodes (NodeStore *store) g_return_val_if_fail (IS_NODE_STORE (store), NULL); result = NULL; - g_hash_table_foreach (store->nodes, (GHFunc) add_node, &result); + g_hash_table_foreach (store->nodes, (GHFunc)add_node, &result); return result; } -void -node_store_get_bounds (NodeStore *store, NodeRect *rect) +void node_store_get_bounds (NodeStore *store, NodeRect *rect) { GList *list; - SheetPos p1, p2; + Coords p1, p2; g_return_if_fail (store != NULL); g_return_if_fail (IS_NODE_STORE (store)); @@ -970,14 +695,12 @@ node_store_get_bounds (NodeStore *store, NodeRect *rect) rect->x1 = MAX (rect->x1, p2.x); rect->y1 = MAX (rect->y1, p2.y); } - g_list_free_full (list, g_object_unref); } -gint -node_store_count_items (NodeStore *store, NodeRect *rect) +gint node_store_count_items (NodeStore *store, NodeRect *rect) { GList *list; - SheetPos p1, p2; + Coords p1, p2; ItemData *data; gint n; @@ -990,23 +713,20 @@ node_store_count_items (NodeStore *store, NodeRect *rect) 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) { + if (p1.x <= rect->x1 && p1.y <= rect->y1 && p2.x >= rect->x0 && p2.y >= rect->y0) { n++; } } - g_list_free_full (list, g_object_unref); return n; } -static void -draw_dot (SheetPos *key, Node *value, cairo_t *cr) +static void draw_dot (Coords *pos, 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_translate (cr, pos->x, pos->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); @@ -1014,8 +734,7 @@ draw_dot (SheetPos *key, Node *value, cairo_t *cr) } } -void -node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ctx) +void node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ctx) { GList *list; ItemData *data; @@ -1028,16 +747,14 @@ node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ct data = ITEM_DATA (list->data); item_data_print (data, cr, ctx); } - g_list_free_full (list, g_object_unref); g_hash_table_foreach (store->nodes, (GHFunc)draw_dot, cr); } -int -node_store_is_pin_at_pos (NodeStore *store, SheetPos pos) +gboolean node_store_is_pin_at_pos (NodeStore *store, Coords pos) { int num_pins; - SheetPos part_pos; + Coords part_pos; GList *p; Part *part; Pin *pins; @@ -1056,11 +773,9 @@ node_store_is_pin_at_pos (NodeStore *store, SheetPos pos) 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; - } + if (fabs (x - pos.x) < NODE_EPSILON && fabs (y - pos.y) < NODE_EPSILON) + return TRUE; } } - g_list_free_full (p, g_object_unref); - return 0; + return FALSE; } diff --git a/src/model/node-store.h b/src/model/node-store.h index 3e42630..9d6aba5 100644 --- a/src/model/node-store.h +++ b/src/model/node-store.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NODE_STORE_H #define __NODE_STORE_H @@ -35,14 +37,14 @@ #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)) +#include "coords.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; @@ -54,7 +56,8 @@ typedef struct _NodeRect NodeRect; #include "part.h" #include "textbox.h" -struct _NodeStore { +struct _NodeStore +{ GObject parent; GHashTable *nodes; @@ -70,8 +73,8 @@ struct _NodeStoreClass // signals - void (*node_dot_added) (NodeStore*); - void (*node_dot_removed) (NodeStore*); + void (*node_dot_added)(NodeStore *); + void (*node_dot_removed)(NodeStore *); }; struct _NodeRect @@ -79,29 +82,28 @@ struct _NodeRect double x0, y0, x1, y1; }; -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, NodeRect *rect); -gint node_store_count_items (NodeStore *store, NodeRect *rect); -void node_store_print_items (NodeStore *store, cairo_t *opc, - SchematicPrintContext *ctx); -Node *node_store_get_or_create_node (NodeStore *store, SheetPos pos); +GType node_store_get_type (void); +NodeStore *node_store_new (void); +Node *node_store_get_node (NodeStore *store, Coords pos); +gboolean node_store_add_part (NodeStore *store, Part *part); +gboolean node_store_remove_part (NodeStore *store, Part *part); +void node_store_remove_overlapping_wires (NodeStore *store, Wire *wire); +gboolean node_store_add_wire (NodeStore *store, Wire *wire); +gboolean node_store_remove_wire (NodeStore *store, Wire *wire); +gboolean node_store_add_textbox (NodeStore *self, Textbox *text); +gboolean node_store_remove_textbox (NodeStore *self, Textbox *text); +void node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data); +gboolean node_store_is_wire_at_pos (NodeStore *store, Coords pos); +gboolean node_store_is_pin_at_pos (NodeStore *store, Coords 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, NodeRect *rect); +gint node_store_count_items (NodeStore *store, NodeRect *rect); +void node_store_print_items (NodeStore *store, cairo_t *opc, SchematicPrintContext *ctx); +Node *node_store_get_or_create_node (NodeStore *store, Coords pos); #endif diff --git a/src/model/node.c b/src/model/node.c index a8f5af8..dc04c89 100644 --- a/src/model/node.c +++ b/src/model/node.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> @@ -35,47 +35,35 @@ #include "node.h" #include "part.h" -#define NG_DEBUG(s) if (0) g_print ("NG: %s\n", s) +#include "debug.h" static void node_class_init (NodeClass *klass); static void node_init (Node *node); -enum { - NODE_DOT_ADDED, - NODE_DOT_REMOVED, - VOLTAGE_CHANGED, - LAST_SIGNAL -}; +enum { NODE_DOT_ADDED, NODE_DOT_REMOVED, VOLTAGE_CHANGED, LAST_SIGNAL }; G_DEFINE_TYPE (Node, node, G_TYPE_OBJECT) -static guint node_signals [LAST_SIGNAL] = { 0 }; +static guint node_signals[LAST_SIGNAL] = {0}; -static void -node_dispose (GObject *object) +static void node_dispose (GObject *object) { G_OBJECT_CLASS (node_parent_class)->dispose (object); } + +static void node_finalize (GObject *object) { + g_return_if_fail (object != NULL); + // Remove the pins and wires encountered by the node. if (NODE (object)->pins) { g_slist_free (NODE (object)->pins); } - + if (NODE (object)->wires) { g_slist_free (NODE (object)->wires); } - - G_OBJECT_CLASS (node_parent_class)->dispose (object); -} - - -static void -node_finalize (GObject *object) -{ - g_return_if_fail (object != NULL); G_OBJECT_CLASS (node_parent_class)->finalize (object); } -static void -node_class_init (NodeClass *klass) +static void node_class_init (NodeClass *klass) { GObjectClass *object_class; @@ -83,38 +71,21 @@ node_class_init (NodeClass *klass) object_class->dispose = node_dispose; object_class->finalize = node_finalize; node_parent_class = g_type_class_peek_parent (klass); - - - node_signals [NODE_DOT_ADDED] = g_signal_new ("node_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 [NODE_DOT_REMOVED] = g_signal_new ("node_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); + + node_signals[NODE_DOT_ADDED] = + g_signal_new ("node_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[NODE_DOT_REMOVED] = + g_signal_new ("node_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) +static void node_init (Node *node) { node->pin_count = 0; node->wire_count = 0; @@ -123,32 +94,36 @@ node_init (Node *node) node->visited = FALSE; } -Node * -node_new (SheetPos pos) +Node *node_new (Coords pos) { Node *node; - node = NODE (g_object_new (node_get_type(), NULL)); + node = NODE (g_object_new (node_get_type (), NULL)); node->key = pos; return node; } -#define SEP(p1,p2) ((fabs(p1.x - p2.x) < 1e-3) && (fabs(p1.y - p2.y) < 1e-3)) -#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) +#define SEP(p1, p2) ((fabs (p1.x - p2.x) < 1e-3) && (fabs (p1.y - p2.y) < 1e-3)) +#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; + Coords start_pos1, length1, end_pos1; + Coords start_pos2, length2, end_pos2; - if ((node->pin_count > 2) || (node->wire_count > 2)) + NG_DEBUG ("node: %p --- pins: %i --- wires: %i", node, node->pin_count, node->wire_count); + + // always display a black dot if a part hits a wire + if (node->pin_count > 0 && node->wire_count > 0) { + NG_DEBUG (" TRUE (pins>0 && wires>0)"); return TRUE; - else if ((node->pin_count + node->wire_count) > 2) + } else if (node->pin_count > 1 || node->wire_count > 2) { + NG_DEBUG (" TRUE (pins>1 || wires>2)"); return TRUE; - else if (node->wire_count == 2) { + } else if (node->wire_count == 2) { // Check that we don't have two wire endpoints. wire1 = node->wires->data; wire2 = node->wires->next->data; @@ -161,42 +136,17 @@ node_needs_dot (Node *node) 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; + if (!(SEP (start_pos1, start_pos2) || SEP (start_pos1, end_pos2) || + SEP (end_pos1, end_pos2) || SEP (end_pos1, start_pos2))) { + return TRUE; } - 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; } - + NG_DEBUG (" FALSE (else)"); return FALSE; } -gint -node_add_pin (Node *node, Pin *pin) +gboolean node_add_pin (Node *node, Pin *pin) { gboolean dot; @@ -213,15 +163,13 @@ node_add_pin (Node *node, Pin *pin) 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), "node_dot_added", &node->key); return TRUE; } -gint -node_remove_pin (Node *node, Pin *pin) +gboolean node_remove_pin (Node *node, Pin *pin) { gboolean dot; @@ -243,8 +191,7 @@ node_remove_pin (Node *node, Pin *pin) return TRUE; } -gint -node_add_wire (Node *node, Wire *wire) +gboolean node_add_wire (Node *node, Wire *wire) { gboolean dot; @@ -269,8 +216,7 @@ node_add_wire (Node *node, Wire *wire) return TRUE; } -gint -node_remove_wire (Node *node, Wire *wire) +gboolean node_remove_wire (Node *node, Wire *wire) { gboolean dot; @@ -298,8 +244,7 @@ node_remove_wire (Node *node, Wire *wire) return TRUE; } -gint -node_is_empty (Node *node) +gboolean node_is_empty (Node *node) { g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (IS_NODE (node), FALSE); @@ -310,8 +255,7 @@ node_is_empty (Node *node) return FALSE; } -gint -node_is_visited (Node *node) +gboolean node_is_visited (Node *node) { g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (IS_NODE (node), FALSE); @@ -319,11 +263,46 @@ node_is_visited (Node *node) return node->visited; } -void -node_set_visited (Node *node, gboolean is_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; } + +#define HASH_EPSILON 1e-3 + +/** + * Hash function to be used with a GHashTable (and others) + */ +guint node_hash (gconstpointer key) +{ + Coords *sp = (Coords *)key; + register int x, y; + const int shift = sizeof(int) * 8 / 2; + + // Hash on any other node? + + x = (int)rint (sp->x) % 1 << shift; + y = (int)rint (sp->y) % 1 << shift; + + return (y << shift) | x; +} + +/** + * Comparsion function to be used with a GHashTable (and others) + */ +gboolean node_equal (gconstpointer a, gconstpointer b) +{ + const Coords *ca = a; + const Coords *cb = b; + + if (fabs (ca->y - cb->y) > HASH_EPSILON) + return 0; + + if (fabs (ca->x - cb->x) > HASH_EPSILON) + return 0; + + return 1; +} diff --git a/src/model/node.h b/src/model/node.h index a5983ce..c170f31 100644 --- a/src/model/node.h +++ b/src/model/node.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,21 +26,21 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NODE_H #define __NODE_H #include <gtk/gtk.h> -#include "sheet-pos.h" +#include "coords.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 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; @@ -48,7 +48,8 @@ typedef struct _NodeClass NodeClass; #include "wire.h" -struct _Node { +struct _Node +{ GObject parent; // Used for traversing all nodes in the netlist generation. @@ -63,7 +64,7 @@ struct _Node { GSList *pins; GSList *wires; - SheetPos key; + Coords key; }; struct _NodeClass @@ -72,18 +73,21 @@ struct _NodeClass }; GType node_get_type (void); -Node *node_new (SheetPos pos); +Node *node_new (Coords pos); gint node_is_empty (Node *node); -gint node_add_pin (Node *node, Pin *pin); -gint node_remove_pin (Node *node, Pin *pin); +gboolean node_add_pin (Node *node, Pin *pin); +gboolean node_remove_pin (Node *node, Pin *pin); -gint node_add_wire (Node *node, Wire *wire); -gint node_remove_wire (Node *node, Wire *wire); +gboolean node_add_wire (Node *node, Wire *wire); +gboolean node_remove_wire (Node *node, Wire *wire); -gint node_is_visited (Node *node); +gboolean node_is_visited (Node *node); void node_set_visited (Node *node, gboolean is_visited); gboolean node_needs_dot (Node *node); +guint node_hash (gconstpointer key); +gboolean node_equal (gconstpointer a, gconstpointer b); + #endif diff --git a/src/model/part-label.h b/src/model/part-label.h index cd9daff..5a60b72 100644 --- a/src/model/part-label.h +++ b/src/model/part-label.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,17 +26,18 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_LABEL_H #define __PART_LABEL_H -typedef struct { +typedef struct +{ gchar *name; gchar *text; - SheetPos pos; + Coords pos; } PartLabel; #endif diff --git a/src/model/part-private.h b/src/model/part-private.h index 6f705a0..771db33 100644 --- a/src/model/part-private.h +++ b/src/model/part-private.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,26 +28,30 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_PRIVATE_H #define __PART_PRIVATE_H -struct _PartPriv { - guint16 num_pins : 16; - guint16 rotation : 16; - IDFlip flip : 8; +#include <glib.h> - gchar *name; - GSList *properties; - GSList *labels; +struct _PartPriv +{ + guint16 num_pins : 16; + // guint16 rotation : 16; + IDFlip flip : 8; - gchar *symbol_name; + gchar *name; + GSList *properties; + GSList *labels; + + gchar *symbol_name; Library *library; - Pin *pins; // Array of pins. + Pin *pins; // Array of pins, without any transformations applied. + Pin *pins_orig; }; #endif diff --git a/src/model/part-property.c b/src/model/part-property.c index f4b1733..1553075 100644 --- a/src/model/part-property.c +++ b/src/model/part-property.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib.h> @@ -43,10 +43,9 @@ // @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) +static char *get_macro_name (char macro, const char *str, char **cls1, char **cls2, size_t *sz) { - char separators[] = { ",.;/|()" }; + char separators[] = {",.;/|()"}; GString *out; const char *q, *qend; char *csep = NULL; @@ -74,11 +73,11 @@ static char *get_macro_name (const char *str, char **cls1, if (rc) goto error; - // Look for conditional clauses - if (csep) { - // get the first one + // Look for conditional clauses + if (csep && macro != '@' && macro != '&') { + // get the first one GString *aux; - q++; // skip the separator and store the clause in tmp + 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); @@ -89,10 +88,10 @@ static char *get_macro_name (const char *str, char **cls1, } *cls1 = aux->str; - q++; // skip the end-of-clause separator + q++; // skip the end-of-clause separator g_string_free (aux, FALSE); - // Check for the second one + // Check for the second one if ((*q) && (csep = strchr (separators, *q))) { q++; // skip the separator and store in tmp aux = g_string_new (""); @@ -106,12 +105,12 @@ static char *get_macro_name (const char *str, char **cls1, } *cls2 = aux->str; - q++; // skip the end-of-clause separator + q++; // skip the end-of-clause separator g_string_free (aux, FALSE); } } - *sz = out->len; + *sz = out->len + (*cls1 != NULL ? strlen(*cls1) + 2 : 0) + (*cls2 != NULL ? strlen(*cls2) + 2 : 0); ret = NULL; if (out->len > 0) { out = g_string_append_c (out, '\0'); @@ -121,13 +120,32 @@ static char *get_macro_name (const char *str, char **cls1, return ret; - error: +error: g_string_free (out, TRUE); return NULL; } -char * -part_property_expand_macros (Part *part, char *string) +// 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 template if <id> undefined + +// Separators can be any of {',', '.', ';', '/', '|', '(', ')'}. +// For an opening-closing pair of +// separators the same character has to be used. + +// Examples: +// R^@refdes %1 %2 @value +// V^@refdes %+ %- SIN(@offset @ampl @freq 0 0) +// ?DC|DC @DC| +char *part_property_expand_macros (Part *part, char *string) { static char mcode[] = {"@?~#&"}; char *value; @@ -142,25 +160,7 @@ part_property_expand_macros (Part *part, char *string) 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 - - // Separators 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); out = g_string_new (""); @@ -168,87 +168,76 @@ part_property_expand_macros (Part *part, char *string) 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); + qn = get_macro_name (*temp, 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); + } else if (*temp == '&' && !value) { + g_warning ("expand macro error: macro %s undefined", qn); g_free (qn); return NULL; - } - else if (*temp == '?' || *temp == '~') { + } 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) - ); + 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_warning ("error in template: %s", temp); g_free (qn); - } - else { + } else { out = g_string_append (out, t0); g_free (t0); } } - } - else if (*temp=='#') { + } else if (*temp == '#') { if (value) { t0 = part_property_expand_macros (part, value); if (!t0) { - g_warning ( "error in template: %s", temp); + g_warning ("error in template: %s", temp); g_free (qn); - } - else { + } else { out = g_string_append (out, t0); g_free (t0); } - } - else + } 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== '\\' ) { + g_free (qn); + g_free (cls1); + g_free (cls2); + } else { + if (*temp == '\\') { temp++; switch (*temp) { case 'n': out = g_string_append_c (out, '\n'); - break; + break; case 't': out = g_string_append_c (out, '\t'); - break; + break; case 'r': out = g_string_append_c (out, '\r'); - break; + break; case 'f': out = g_string_append_c (out, '\f'); } temp++; - } - else { + } else { out = g_string_append_c (out, *temp); temp++; } } } - if (tmp0) g_free (tmp0); + g_free (tmp0); out = g_string_append_c (out, '\0'); ret = g_strdup (out->str); @@ -256,3 +245,136 @@ part_property_expand_macros (Part *part, char *string) return ret; } + +/** + * see #168 + */ +void update_connection_designators(Part *part, char **prop, int *node_ctr) +{ + if (prop == NULL || *prop == NULL) + return; + if (node_ctr == NULL) + return; + if (part == NULL || !IS_PART(part)) + return; + + char *temp = *prop; + GString *out = g_string_new (""); + + int breakout = FALSE; + while (!breakout) { + char **prop_split = g_regex_split_simple("[@?~#&%].*", temp, 0, 0); + if (prop_split[0] == NULL) { + g_strfreev(prop_split); + break; + } + temp += strlen(prop_split[0]); + g_string_append_printf(out, "%s", prop_split[0]); + g_strfreev(prop_split); + char macro = *temp; + temp++; + switch (macro) { + case '%': + { + char **prop_split = g_regex_split_simple(" .*", temp, 0, 0); + temp += strlen(prop_split[0]); + g_string_append_printf(out, "%%%d", (*node_ctr)++); + g_strfreev(prop_split); + break; + } + case '@': + { + char **prop_split = g_regex_split_simple("[,.;/|() ].*", temp, 0, 0); + temp += strlen(prop_split[0]); + char *prop_ref_name = prop_split[0]; + char **prop_ref_value = part_get_property_ref(part, prop_ref_name); + g_string_append_printf(out, "@%s", prop_ref_name); + update_connection_designators(part, prop_ref_value, node_ctr); + g_strfreev(prop_split); + break; + } + case '&': + { + char **prop_split = g_regex_split_simple("[,.;/|() ].*", temp, 0, 0); + temp += strlen(prop_split[0]); + char *prop_ref_name = prop_split[0]; + char **prop_ref_value = part_get_property_ref(part, prop_ref_name); + g_string_append_printf(out, "&%s", prop_ref_name); + if (prop_ref_value != NULL && *prop_ref_value != NULL) + update_connection_designators(part, prop_ref_value, node_ctr); + g_strfreev(prop_split); + break; + } + case '?': + case '~': + { + char **prop_split = g_regex_split_simple("([,.;/|()])(.*?)(\\g{-3})(?(?=[,.;/|()])([,.;/|()])(.*?)(\\g{-3})).*", temp, 0, 0); + char *prop_ref_name = g_strdup(prop_split[0]); + char separator1 = *prop_split[1]; + char *cls1 = g_strdup(prop_split[2]); + //separator1 == *prop_split_name[3] + char separator2 = prop_split[4] != NULL ? *prop_split[4] : 0; + char *cls2 = NULL; + if (separator2 != 0) { + cls2 = g_strdup(prop_split[5]); + } + char **prop_ref_value = part_get_property_ref(part, prop_ref_name); + for (int i = 0; prop_split[i] != NULL; i++) + temp += strlen(prop_split[i]); + g_strfreev(prop_split); + + if ( + (macro == '?' && prop_ref_value != NULL && *prop_ref_value != NULL) + || + (macro == '~' && (prop_ref_value == NULL || *prop_ref_value == NULL)) + ) + update_connection_designators(part, &cls1, node_ctr); + else if (cls2 != NULL) + update_connection_designators(part, &cls2, node_ctr); + + g_string_append_printf(out, "%c%s%c%s%c", macro, prop_ref_name, separator1, cls1, separator1); + if (cls2 != NULL) { + g_string_append_printf(out, "%c%s%c", separator2, cls2, separator2); + g_free(cls2); + } + + g_free(cls1); + g_free(prop_ref_name); + break; + } + case '#': + { + char **prop_split = g_regex_split_simple("([,.;/|()])(.*?)(\\g{-3}).*", temp, 0, 0); + char *prop_ref_name = g_strdup(prop_split[0]); + char separator = *prop_split[1]; + char *cls = g_strdup(prop_split[2]); + //separator == *prop_split_name[3] + char **prop_ref_value = part_get_property_ref(part, prop_ref_name); + for (int i = 0; prop_split[i] != NULL; i++) + temp += strlen(prop_split[i]); + g_strfreev(prop_split); + + if (prop_ref_value != NULL && *prop_ref_value != NULL) { + update_connection_designators(part, &cls, node_ctr); + g_string_append_printf(out, "#%s%c%s%c", prop_ref_name, separator, cls, separator); + } else { + g_string_append_printf(out, "#%s%c%s%c%s", prop_ref_name, separator, cls, separator, temp); + breakout = TRUE; + } + + g_free(cls); + g_free(prop_ref_name); + break; + } + default: + { + breakout = TRUE; + break; + } + } + } + g_free(*prop); + *prop = out->str; + g_string_free (out, FALSE); + return; +} diff --git a/src/model/part-property.h b/src/model/part-property.h index 3bd3b5c..698774f 100644 --- a/src/model/part-property.h +++ b/src/model/part-property.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_PROPERTY_H @@ -35,11 +35,13 @@ #include "part.h" -typedef struct { +typedef struct +{ gchar *name; gchar *value; } PartProperty; +void update_connection_designators (Part *part, char **prop, int *node_ctr); gchar *part_property_expand_macros (Part *part, gchar *string); #endif diff --git a/src/model/part.c b/src/model/part.c index aad128d..0ef7336 100644 --- a/src/model/part.c +++ b/src/model/part.c @@ -8,12 +8,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://beerbach.me/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * * This program is free software; you can redistribute it and/or @@ -28,14 +32,15 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <ctype.h> #include <math.h> #include <string.h> #include <glib/gi18n.h> +#include <stdlib.h> #include "part.h" #include "part-property.h" @@ -47,17 +52,16 @@ #include "schematic-print-context.h" #include "dialogs.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.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_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 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); @@ -68,9 +72,9 @@ 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_rotate (ItemData *data, int angle, Coords *center); -static void part_flip (ItemData *data, gboolean horizontal, SheetPos *center); +static void part_flip (ItemData *data, IDFlip direction, Coords *center); static void part_update_bbox (Part *part); @@ -78,12 +82,12 @@ static void part_unregister (ItemData *data); static int part_register (ItemData *data); +static void part_changed (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); -static gboolean emit_rotated_signal_when_handler_connected (gpointer data); -static gboolean emit_flipped_signal_when_handler_connected (gpointer data); enum { ARG_0, @@ -91,41 +95,20 @@ enum { ARG_LABELS, }; -enum { - CHANGED, - LAST_SIGNAL -}; - -// Structure defined to cover exchange between g_timeout_add and the -// function in charge to emit the rotated signal only once the handler -// has been connected. -typedef struct { - Part *part; -} SignalRotatedStruct; +G_DEFINE_TYPE (Part, part, TYPE_ITEM_DATA); -// Structure defined to cover exchange between g_timeout_add and the -// function in charge to emit the flipped signal only once the handler -// has been connected. -typedef struct { - Part * part; - gboolean horizontal; - SheetPos *center; -} SignalFlippedStruct; +static ItemDataClass *parent_class = NULL; -G_DEFINE_TYPE (Part, part, TYPE_ITEM_DATA) +static void part_init (Part *part) { part->priv = g_slice_new0 (PartPriv); } -static guint part_signals [LAST_SIGNAL] = { 0 }; -static ItemDataClass *parent_class = NULL; +static void part_dispose (GObject *object) { G_OBJECT_CLASS (parent_class)->dispose (object); } -static void -part_finalize (GObject *object) +static void part_finalize (GObject *object) { - Part *part; PartPriv *priv; GSList *list; - part = PART (object); - priv = part->priv; + priv = PART (object)->priv; if (priv) { g_free (priv->name); @@ -149,21 +132,15 @@ part_finalize (GObject *object) g_slist_free (priv->labels); g_free (priv->pins); + g_free (priv->pins_orig); g_free (priv->symbol_name); - g_free (priv); - part->priv = NULL; + + g_slice_free (PartPriv, priv); } 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) +static void part_class_init (PartClass *klass) { GObjectClass *object_class; ItemDataClass *item_data_class; @@ -178,12 +155,12 @@ part_class_init (PartClass *klass) 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)); + 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; @@ -191,35 +168,19 @@ part_class_init (PartClass *klass) item_data_class->flip = part_flip; item_data_class->unreg = part_unregister; item_data_class->reg = part_register; + item_data_class->changed = part_changed; 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); } -static void -part_init (Part *part) -{ - PartPriv *priv; +//////////////////////////////////////////////////////////////////////////////// +// END BOILER PLATE +//////////////////////////////////////////////////////////////////////////////// - priv = g_new0 (PartPriv, 1); - - part->priv = priv; -} - -Part * -part_new (void) +Part *part_new () { Part *part; @@ -228,8 +189,7 @@ part_new (void) return part; } -Part * -part_new_from_library_part (LibraryPart *library_part) +Part *part_new_from_library_part (LibraryPart *library_part) { Part *part; GSList *pins; @@ -245,11 +205,10 @@ part_new_from_library_part (LibraryPart *library_part) priv = part->priv; symbol = library_get_symbol (library_part->symbol_name); - if (symbol == NULL) { - oregano_warning (g_strdup_printf (_("Couldn't find the requested symbol" - "%s for part %s in library.\n"), - library_part->symbol_name, - library_part->name)); + if (symbol == NULL) { + oregano_warning (g_strdup_printf (_ ("Couldn't find the requested symbol" + "%s for part %s in library.\n"), + library_part->symbol_name, library_part->name)); return NULL; } @@ -258,10 +217,8 @@ part_new_from_library_part (LibraryPart *library_part) if (pins) part_set_pins (part, pins); - g_object_set (G_OBJECT (part), - "Part::properties", library_part->properties, - "Part::labels", library_part->labels, - NULL); + g_object_set (G_OBJECT (part), "Part::properties", library_part->properties, "Part::labels", + library_part->labels, NULL); priv->name = g_strdup (library_part->name); priv->symbol_name = g_strdup (library_part->symbol_name); @@ -272,9 +229,8 @@ part_new_from_library_part (LibraryPart *library_part) return part; } -static void -part_set_gproperty (GObject *object, guint prop_id, const GValue *value, - GParamSpec *spec) +static void part_set_gproperty (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) { GSList *list; Part *part = PART (object); @@ -291,9 +247,7 @@ part_set_gproperty (GObject *object, guint prop_id, const GValue *value, } } -static void -part_get_gproperty (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +static void part_get_gproperty (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) { Part *part = PART (object); PartPriv *priv = part->priv; @@ -307,8 +261,7 @@ part_get_gproperty (GObject *object, guint prop_id, GValue *value, } } -int -part_get_num_pins (Part *part) +gint part_get_num_pins (Part *part) { PartPriv *priv; @@ -319,20 +272,48 @@ part_get_num_pins (Part *part) return priv->num_pins; } -int -part_get_rotation (Part *part) +/** + * @returns the rotation in degrees + * @attention steps of 90 degrees only! + */ +gint part_get_rotation (Part *part) { - PartPriv *priv; + ItemData *item; + gdouble register a, b, c, d; + cairo_matrix_t *t; g_return_val_if_fail (part != NULL, 0); g_return_val_if_fail (IS_PART (part), 0); - priv = part->priv; - return priv->rotation; + item = ITEM_DATA (part); + + t = item_data_get_rotate (item); + a = t->xx; + b = t->xy; + c = t->yx; + d = t->yy; + + //check whether matrix is rotation matrix + //Let Q be the matrix. Q is a rotation matrix if and only if Q^T Q = I and det Q = 1. + //Then it is existing a real value alpha so that + // ( a b ) ( cos(alpha) -sin(alpha) ) + //Q = ( ) = ( ) + // ( c d ) ( sin(alpha) cos(alpha) ) + if (G_UNLIKELY (fabs (1 - (a * a + c * c)) > 1e-10 || fabs (1 - (b * b + d * d)) > 1e-10 || fabs (a*b + c*d) > 1e-10 || fabs (1 - (a*d - b*c)) > 1e-10)) { + g_warning ("Unabled to calculate rotation from matrix. Assuming 0°."); + return 0; + } + + //Now we want to extract alpha. + //this case differentiation is only for numerical stability at the edges of the domains of definition of acos and atan + if (-0.5 <= a && a <= 0.5) { + return 180. / M_PI * (acos(a) + (c < 0 ? M_PI : 0)); + } else { + return 180. / M_PI * (atan(c/a) + (a < 0 ? M_PI : 0)); + } } -IDFlip -part_get_flip (Part *part) +IDFlip part_get_flip (Part *part) { PartPriv *priv; @@ -343,8 +324,7 @@ part_get_flip (Part *part) return priv->flip; } -Pin * -part_get_pins (Part *part) +Pin *part_get_pins (Part *part) { PartPriv *priv; @@ -355,16 +335,14 @@ part_get_pins (Part *part) return priv->pins; } -static gboolean -part_has_properties (ItemData *item) +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) +static gboolean part_set_properties (Part *part, GSList *properties) { PartPriv *priv; GSList *list; @@ -392,8 +370,7 @@ part_set_properties (Part *part, GSList *properties) return TRUE; } -GSList * -part_get_properties (Part *part) +GSList *part_get_properties (Part *part) { PartPriv *priv; @@ -405,8 +382,10 @@ part_get_properties (Part *part) return priv->properties; } -char * -part_get_property (Part *part, char *name) +/** + * @return no free() pls + */ +char **part_get_property_ref (Part *part, char *name) { PartPriv *priv; GSList *props; @@ -421,14 +400,25 @@ part_get_property (Part *part, char *name) for (props = priv->properties; props; props = props->next) { prop = props->data; if (g_ascii_strcasecmp (prop->name, name) == 0) { - return g_strdup (prop->value); + return &(prop->value); } } + return ((char **)0); +} + +/** + * @returns [transfer-full] + */ +char *part_get_property (Part *part, char *name) +{ + char **prop = part_get_property_ref(part, name); + if (prop != NULL && *prop != NULL) { + return g_strdup(*prop); + } return NULL; } -static int -part_set_labels (Part *part, GSList *labels) +static gboolean part_set_labels (Part *part, GSList *labels) { PartPriv *priv; GSList *list; @@ -466,8 +456,10 @@ part_set_labels (Part *part, GSList *labels) return TRUE; } -int -part_set_pins (Part *part, GSList *pins) +/** + * overwrite the pins with those given in the list + */ +gboolean part_set_pins (Part *part, GSList *pins) { PartPriv *priv; GSList *list; @@ -481,29 +473,32 @@ part_set_pins (Part *part, GSList *pins) num_pins = g_slist_length (pins); - if (priv->pins) - g_free (priv->pins); + g_free (priv->pins); + g_free (priv->pins_orig); priv->pins = g_new0 (Pin, num_pins); + priv->pins_orig = 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. + // Connections which only have the Coords 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; + priv->pins_orig[i].pin_nr = i; + priv->pins_orig[i].node_nr = 0; + priv->pins_orig[i].offset.x = pin->offset.x; + priv->pins_orig[i].offset.y = pin->offset.y; + priv->pins_orig[i].part = part; + + memcpy (priv->pins, priv->pins_orig, sizeof(Pin) * num_pins); } return TRUE; } -GSList * -part_get_labels (Part *part) +GSList *part_get_labels (Part *part) { PartPriv *priv; @@ -515,156 +510,183 @@ part_get_labels (Part *part) return priv->labels; } -static void -part_rotate (ItemData *data, int angle, SheetPos *center) +/** + * \brief rotate an item by an @angle increment (may be negative) + * + * @angle the increment the item will be rotated (usually 90° steps) + * @center_pos if rotated as part of a group, this is the center to rotate + *around + */ +static void part_rotate (ItemData *data, int angle, Coords *center_pos) { - cairo_matrix_t affine; - double dx, dy, x, y; - Part *part; - PartPriv *priv; - int i, tot_rotation; - SheetPos b1, b2, part_center_before, part_center_after, delta; - SignalRotatedStruct *signal_struct; - - g_return_if_fail (data != NULL); + g_return_if_fail (data); g_return_if_fail (IS_PART (data)); - if (angle == 0) - return; + cairo_matrix_t morph, morph_rot, local_rot; + gint i; + gdouble x, y; + Part *part; + PartPriv *priv; + gboolean handler_connected; part = PART (data); priv = part->priv; - tot_rotation = (priv->rotation + angle) % 360; - - priv->rotation = tot_rotation; - angle = tot_rotation; + // FIXME store vanilla coords, apply the morph + // FIXME to these and store the result in the + // FIXME instance then everything will be fine + // XXX also prevents rounding yiggle up downs - cairo_matrix_init_rotate (&affine, (double) (angle* M_PI / 180)); + angle /= 90; + angle *= 90; - // Rotate the pins. - for (i = 0; i < priv->num_pins; i++) { - x = priv->pins[i].offset.x; - y = priv->pins[i].offset.y; - cairo_matrix_transform_point (&affine, &x, &y); + // normalize it + angle = angle % 360; - if (fabs (x) < 1e-2) - x = 0.0; - if (fabs (y) < 1e-2) - y = 0.0; + if (angle == 0) + return; - priv->pins[i].offset.x = x; - priv->pins[i].offset.y = y; - } + cairo_matrix_init_rotate (&local_rot, (double)angle * M_PI / 180.); - // Rotate the bounding box. - item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); - part_center_before.x = (b1.x + b2.x) / 2; - part_center_before.y = (b1.y + b2.y) / 2; + cairo_matrix_multiply (item_data_get_rotate (data), item_data_get_rotate (data), &local_rot); - x = b1.x; - y = b1.y; - cairo_matrix_transform_point (&affine, &x, &y); - b1.x = x; - b1.y = y; + morph_rot = *(item_data_get_rotate (data)); - x = b2.x; - y = b2.y; - cairo_matrix_transform_point (&affine, &x, &y); - b2.x = x; - b2.y = y; + cairo_matrix_multiply (&morph, &morph_rot, item_data_get_translate (data)); - item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); + Coords delta_to_center, delta_to_center_transformed; + Coords delta_to_apply; + Coords item_pos; - if (center) { - SheetPos part_pos; - gfloat tmp_x, tmp_y; + item_data_get_pos (ITEM_DATA (part), &item_pos); - part_center_after.x = (b1.x + b2.x) / 2; - part_center_after.y = (b1.y + b2.y) / 2; + Coords rotation_center; - dx = part_center_before.x - part_center_after.x; - dy = part_center_before.y - part_center_after.y; + if (center_pos == NULL) { + rotation_center = item_pos; + } else { + rotation_center = *center_pos; + } - item_data_get_pos (ITEM_DATA (part), &part_pos); + delta_to_center_transformed = delta_to_center = coords_sub (&rotation_center, &item_pos); + cairo_matrix_transform_point (&local_rot, &(delta_to_center_transformed.x), + &(delta_to_center_transformed.y)); - tmp_x = x = part_center_before.x - center->x + part_pos.x; - tmp_y = y = part_center_before.y - center->y + part_pos.y; - cairo_matrix_transform_point (&affine, &x, &y); + delta_to_apply = coords_sub (&delta_to_center, &delta_to_center_transformed); - delta.x = dx + x - tmp_x; - delta.y = dy + y - tmp_y; +#define DEBUG_THIS 0 + // use the cairo matrix funcs to transform the pin + // positions relative to the item center + // this is only indirectly related to displayin + // HINT: we need to modify the actual pins to make the + // pin tests work being used to detect connections - item_data_move (ITEM_DATA (part), &delta); + // Rotate the pins. + for (i = 0; i < priv->num_pins; i++) { + if (priv->pins_orig && priv->pins) { + x = priv->pins_orig[i].offset.x; + y = priv->pins_orig[i].offset.y; + cairo_matrix_transform_point (&morph_rot, &x, &y); + + if (fabs (x) < 1e-2) + x = 0.0; + if (fabs (y) < 1e-2) + y = 0.0; + + priv->pins[i].offset.x = x; + priv->pins[i].offset.y = y; + } } - - // Let the views (canvas items) know about the rotation. - signal_struct = g_new0 (SignalRotatedStruct, 1); - signal_struct->part = part; - g_timeout_add (10, - (gpointer) emit_rotated_signal_when_handler_connected, - (gpointer) signal_struct); -} -static gboolean -emit_rotated_signal_when_handler_connected (gpointer data) -{ - gboolean handler_connected; - SignalRotatedStruct *signal_struct = (SignalRotatedStruct *) data; - int angle = 0; - Part *part; - - - part = signal_struct->part; - angle = part->priv->rotation; + item_data_move (data, &delta_to_apply); - handler_connected = g_signal_handler_is_connected (G_OBJECT (part), - ITEM_DATA (part)->rotated_handler_id); + handler_connected = g_signal_handler_is_connected (G_OBJECT (data), data->changed_handler_id); if (handler_connected) { - g_signal_emit_by_name (G_OBJECT (part), - "rotated", angle); + g_signal_emit_by_name (G_OBJECT (data), "changed"); + } else { + NG_DEBUG ("handler not yet registerd."); } - - return !handler_connected; + NG_DEBUG ("\n\n"); } -static void -part_flip (ItemData *data, gboolean horizontal, SheetPos *center) +/** + * flip a part in a given direction + * @direction gives the direction the item will be flipped, end users pov! + * @center the center to flip over - currently ignored FIXME + */ +static void part_flip (ItemData *data, IDFlip direction, Coords *center) { +#if 0 Part *part; PartPriv *priv; int i; cairo_matrix_t affine; double x, y; - SignalFlippedStruct *signal_struct; - - g_return_if_fail (data != NULL); + double scale_v, scale_h; + gboolean handler_connected; + Coords delta; + Coords pos, trans; + Coords b1, b2; + Coords pos_new, pos_old; + //FIXME properly recenter after flipping + //Coords part_center_before, part_center_after, delta; + + g_return_if_fail (data); 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) - cairo_matrix_init_scale (&affine, 1.0, -1.0); - else - cairo_matrix_init_scale (&affine, -1.0, 1.0); - - //Flip the pins. + item_data_get_pos (data, &trans); + + // mask, just for the sake of cleanness + direction &= ID_FLIP_MASK; + + // TODO evaluate if we really want to be able to do double flips (180* rots via flipping) + g_assert (direction != ID_FLIP_MASK); + + + // create a transformation _relativ_ to the current _state_ + // reverse axis and fix the created offset by adding 2*pos.x or .y + + // convert the flip direction to binary, used in the matrix setup + // keep in mind that we do relativ manipulations within the model + // which in turn makes this valid for all rotations! + scale_h = ((direction & ID_FLIP_HORIZ) != 0) ? -1. : 1.; + scale_v = ((direction & ID_FLIP_VERT) != 0) ? -1. : 1.; + + // magic, if we are in either 270 or 90 state, we need to rotate the flip state by 90° to draw it properly + // TODO maybe better put this into the rotation function + if ((priv->rotation / 90) % 2 == 1) { + priv->flip ^= ID_FLIP_MASK; + } + // toggle the direction + priv->flip ^= direction; + if ((priv->flip & ID_FLIP_MASK)== ID_FLIP_MASK) { + priv->flip = ID_FLIP_NONE; + priv->rotation += 180; + priv->rotation %= 360; + } + + cairo_matrix_init_scale (&affine, scale_h, scale_v); + + item_data_get_pos (data, &pos_old); + pos_new = pos_old; + cairo_matrix_transform_point (&affine, &pos_new.x, &pos_new.y); + + g_printf ("\ncenter %p [old] x=%lf,y=%lf -->", data, pos_old.x, pos_old.y); + g_printf (" x=%lf, y=%lf\n", pos_new.x, pos_new.y); + delta.x = - pos_new.x + pos_old.x; + delta.y = - pos_new.y + pos_old.y; + + // flip the pins for (i = 0; i < priv->num_pins; i++) { + x = priv->pins[i].offset.x; y = priv->pins[i].offset.y; cairo_matrix_transform_point (&affine, &x, &y); - + if (fabs (x) < 1e-2) x = 0.0; if (fabs (y) < 1e-2) @@ -674,85 +696,34 @@ part_flip (ItemData *data, gboolean horizontal, SheetPos *center) priv->pins[i].offset.y = y; } - // Tell the views. - signal_struct = g_new0 (SignalFlippedStruct, 1); - signal_struct->part = part; - signal_struct->horizontal = horizontal; - signal_struct->center = center; - g_timeout_add (10, - (gpointer) emit_flipped_signal_when_handler_connected, - (gpointer) signal_struct); -} - -static -gboolean emit_flipped_signal_when_handler_connected (gpointer data) -{ - gboolean handler_connected; - SignalFlippedStruct *signal_struct = (SignalFlippedStruct *) data; - gboolean horizontal; - SheetPos *center; - Part *part; - - SheetPos part_center_before = {0.0, 0.0}, part_center_after = {0.0, 0.0}; - SheetPos b1, b2; - cairo_matrix_t affine; - double x, y; - - - part = signal_struct->part; - horizontal = signal_struct->horizontal; - center = signal_struct->center; - - handler_connected = g_signal_handler_is_connected (G_OBJECT (part), + // tell the view + handler_connected = g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA(part)->flipped_handler_id); if (handler_connected) { - 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) / 2; - part_center_before.y = (b1.y + b2.y) / 2; - } - - // Flip the bounding box. - x = b1.x; - y = b1.y; - cairo_matrix_transform_point (&affine, &x, &y); - b1.x = x; - b1.y = y; - - x = b2.x; - y = b2.y; - cairo_matrix_transform_point (&affine, &x, &y); - b2.x = x; - b2.y = y; - - item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); + g_signal_emit_by_name (G_OBJECT (part), "flipped", priv->flip); - if (center) { - SheetPos part_pos, delta; - double dx, dy; + // TODO - proper boundingbox center calculation - part_center_after.x = b1.x + (b2.x - b1.x) / 2; - part_center_after.y = b1.y + (b2.y - b1.y) / 2; + item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); - dx = part_center_before.x - part_center_after.x; - dy = part_center_before.y - part_center_after.y; + // flip the bounding box. + cairo_matrix_transform_point (&affine, &b1.x, &b1.y); + cairo_matrix_transform_point (&affine, &b2.x, &b2.y); - item_data_get_pos (ITEM_DATA (part), &part_pos); + item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); + item_data_set_pos (ITEM_DATA (part), &pos); - delta.x = dx + part_pos.x; - delta.y = dy + part_pos.y; - item_data_move (ITEM_DATA (part), &delta); - } + // FIXME - proper recenter to boundingbox center } - - return !handler_connected; + if (g_signal_handler_is_connected (G_OBJECT (part), + ITEM_DATA (part)->changed_handler_id)) { + g_signal_emit_by_name (G_OBJECT (part), + "changed"); + } +#endif } -static ItemData * -part_clone (ItemData *src) +static ItemData *part_clone (ItemData *src) { Part *src_part, *new_part; ItemDataClass *id_class; @@ -760,20 +731,20 @@ part_clone (ItemData *src) 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)); + 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 = g_object_new (TYPE_PART, NULL); new_part->priv->pins = g_new0 (Pin, src_part->priv->num_pins); + new_part->priv->pins_orig = 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) +static void part_copy (ItemData *dest, ItemData *src) { Part *dest_part, *src_part; GSList *list; @@ -790,21 +761,26 @@ part_copy (ItemData *dest, ItemData *src) dest_part = PART (dest); src_part = PART (src); - dest_part->priv->rotation = src_part->priv->rotation; + // 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; + if (src_part->priv->pins) { + 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; + } + if (src_part->priv->pins_orig) { + memcpy (dest_part->priv->pins_orig, src_part->priv->pins_orig, src_part->priv->num_pins * sizeof(Pin)); + for (i = 0; i < dest_part->priv->num_pins; i++) + dest_part->priv->pins_orig[i].part = dest_part; + } // Copy properties and labels. - dest_part->priv->properties = - g_slist_copy (src_part->priv->properties); + 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; @@ -828,16 +804,15 @@ part_copy (ItemData *dest, ItemData *src) } } -static void -part_update_bbox (Part *part) +static void part_update_bbox (Part *part) { - GSList *objects; + GSList *iter; LibrarySymbol *symbol; SymbolObject *object; GooCanvasPoints *points; int i; - SheetPos b1, b2; + Coords b1, b2; symbol = library_get_symbol (part->priv->symbol_name); if (symbol == NULL) { @@ -847,9 +822,8 @@ part_update_bbox (Part *part) b1.x = b1.y = b2.x = b2.y = 0.0; - for (objects = symbol->symbol_objects; objects; - objects = objects->next) { - object = objects->data; + for (iter = symbol->symbol_objects; iter; iter = iter->next) { + object = iter->data; switch (object->type) { case SYMBOL_OBJECT_LINE: points = object->u.uline.line; @@ -878,18 +852,16 @@ part_update_bbox (Part *part) 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; - + case SYMBOL_OBJECT_TEXT: { + // FIXME use cairo pango layout + /*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"); @@ -900,8 +872,7 @@ part_update_bbox (Part *part) item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); } -static void -part_unregister (ItemData *data) +static void part_unregister (ItemData *data) { NodeStore *store; @@ -911,21 +882,52 @@ part_unregister (ItemData *data) node_store_remove_part (store, PART (data)); } -static int -part_register (ItemData *data) +/** + * register a part to its nodestore + * @param data the part + * @attention the @data has to have a valid nodestore set + */ +static int part_register (ItemData *data) { NodeStore *store; - g_return_val_if_fail (IS_PART (data), -1); + g_return_val_if_fail (IS_PART (data), FALSE); store = item_data_get_store (data); node_store_add_part (store, PART (data)); - return 0; + return TRUE; +} + +/** + * simply signal a change in the part + */ +static void part_changed (ItemData *data) +{ + g_return_if_fail (IS_PART (data)); + +#if 0 + Part *part; + Coords loc = {0., 0.}; + int angle = 0; + IDFlip flip = ID_FLIP_NONE; + + part = (Part *)data; + + + flip = part_get_flip (part); + angle = part_get_rotation (part); + item_data_get_pos (data, &loc); + + //FIXME isn't it more sane to just emit the changed? + g_signal_emit_by_name (data, "moved", &loc); + g_signal_emit_by_name (data, "flipped", flip); + g_signal_emit_by_name (data, "rotated", angle); +#endif + g_signal_emit_by_name (data, "changed"); } -static char * -part_get_refdes_prefix (ItemData *data) +static char *part_get_refdes_prefix (ItemData *data) { Part *part; char *refdes; @@ -941,17 +943,16 @@ part_get_refdes_prefix (ItemData *data) // 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; + 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) +static void part_set_property (ItemData *data, char *property, char *value) { Part *part; PartPriv *priv; @@ -979,8 +980,11 @@ part_set_property (ItemData *data, char *property, char *value) } } -static void -part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) +/** + * print the part onto a physical sheet of paper or pdf, which is represented by + * @cr + */ +static void part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) { GSList *objects, *labels; SymbolObject *object; @@ -989,7 +993,7 @@ part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) int i, rotation; Part *part; PartPriv *priv; - SheetPos pos; + Coords pos; IDFlip flip; GooCanvasPoints *line; @@ -1010,80 +1014,63 @@ part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) cairo_save (cr); - gdk_cairo_set_source_color (cr, &ctx->colors.components); + gdk_cairo_set_source_rgba (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); - } + flip = part_get_flip (part); + + if ((flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT)) + rotation += 180; + else if (flip == ID_FLIP_HORIZ) + cairo_scale (cr, -1, 1); + else if (flip == ID_FLIP_VERT) + cairo_scale (cr, 1, -1); + + // Rotate around the item position + if (rotation %= 360) { + cairo_translate(cr, x0, y0); + cairo_rotate (cr, rotation * M_PI / 180); + 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); + 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; - default: - g_warning ( - "Print part: Part %s contains unknown object.", - priv->name - ); + 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; + y = (y2 + y1) / 2; + 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; } @@ -1091,7 +1078,7 @@ part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) } // We don't want to rotate labels text, only the (x,y) coordinate - gdk_cairo_set_source_color (cr, &ctx->colors.labels); + gdk_cairo_set_source_rgba (cr, &ctx->colors.labels); for (labels = part_get_labels (part); labels; labels = labels->next) { gdouble x, y; PartLabel *label = (PartLabel *)labels->data; @@ -1104,22 +1091,22 @@ part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) 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; + 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_move_to (cr, x, y); + cairo_show_text (cr, text); cairo_restore (cr); g_free (text); } diff --git a/src/model/part.h b/src/model/part.h index 3d267b0..7a9f7c7 100644 --- a/src/model/part.h +++ b/src/model/part.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * * This program is free software; you can redistribute it and/or @@ -27,8 +29,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_H @@ -36,14 +38,14 @@ #include <gtk/gtk.h> -#include "sheet-pos.h" +#include "coords.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 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; @@ -53,8 +55,9 @@ typedef struct _PartPriv PartPriv; #include "item-data.h" -struct _Pin { - SheetPos offset; +struct _Pin +{ + Coords offset; guint pin_nr; gint node_nr; // Node number into the netlist Part *part; @@ -62,30 +65,32 @@ struct _Pin { #define PIN(x) ((Pin *)(x)) -struct _Part { - ItemData parent; - PartPriv * priv; +struct _Part +{ + ItemData parent; + PartPriv *priv; }; struct _PartClass { ItemDataClass parent_class; - void (*changed) (); + 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); +GType part_get_type (void); +Part *part_new (); +Part *part_new_from_library_part (LibraryPart *library_part); +gint part_get_num_pins (Part *part); +Pin *part_get_pins (Part *part); +gboolean part_set_pins (Part *part, GSList *connections); +gboolean part_get_rotation (Part *part); +IDFlip part_get_flip (Part *part); +void part_labels_rotate (Part *part, int rotation); +char **part_get_property_ref (Part *part, char *name); +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); diff --git a/src/model/schematic-print-context.h b/src/model/schematic-print-context.h index 60ee322..84b67a4 100644 --- a/src/model/schematic-print-context.h +++ b/src/model/schematic-print-context.h @@ -1,15 +1,18 @@ /* * schematic-print-context.h * + * * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,23 +26,25 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, 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; +typedef struct _SchematicColors +{ + GdkRGBA components; + GdkRGBA labels; + GdkRGBA wires; + GdkRGBA text; + GdkRGBA background; } SchematicColors; -typedef struct _SchematicPrintContext { +typedef struct _SchematicPrintContext +{ SchematicColors colors; } SchematicPrintContext; diff --git a/src/model/schematic.c b/src/model/schematic.c index 7329441..2cf6294 100644 --- a/src/model/schematic.c +++ b/src/model/schematic.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> @@ -40,13 +44,15 @@ #include "node-store.h" #include "file-manager.h" #include "settings.h" -#include "sim-settings.h" +#include "../sim-settings-gui.h" #include "simulation.h" #include "errors.h" #include "schematic-print-context.h" +#include "log.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) -typedef struct _SchematicsPrintOptions { +#include "debug.h" +typedef struct _SchematicsPrintOptions +{ GtkColorButton *components; GtkColorButton *labels; GtkColorButton *wires; @@ -54,49 +60,41 @@ typedef struct _SchematicsPrintOptions { GtkColorButton *background; } SchematicPrintOptions; -struct _SchematicPriv { - char *title; - char *filename; - char *author; - char *comments; - char *netlist_filename; +struct _SchematicPriv +{ + char *oregano_version; + char *title; + char *filename; + char *author; + char *comments; + char *netlist_filename; - SchematicColors colors; - SchematicPrintOptions *printoptions; + SchematicColors colors; + SchematicPrintOptions *printoptions; // Data for various dialogs. - gpointer settings; - gpointer sim_settings; - gpointer simulation; + gpointer settings; + SimSettingsGui *sim_settings; + gpointer simulation; - GList *current_items; + GList *current_items; - NodeStore *store; - GHashTable *symbols; - GHashTable *refdes_values; + NodeStore *store; + GHashTable *symbols; + GHashTable *refdes_values; - double zoom; + guint width; + guint height; - gboolean dirty; + double zoom; - GtkTextBuffer *log; - GtkTextTag *tag_error; -}; + gboolean dirty; + + Log *logstore; -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; + GtkTextBuffer *log; + GtkTextTag *tag_error; +}; enum { TITLE_CHANGED, @@ -108,88 +106,66 @@ enum { LAST_SIGNAL }; -G_DEFINE_TYPE (Schematic, schematic, G_TYPE_OBJECT) +G_DEFINE_TYPE (Schematic, schematic, G_TYPE_OBJECT); static void schematic_init (Schematic *schematic); -static void schematic_class_init (SchematicClass *klass); +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 item_moved_callback (ItemData *data, SheetPos *pos, Schematic *sm); +static void item_moved_callback (ItemData *data, Coords *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 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 guint schematic_signals[LAST_SIGNAL] = {0}; static GList *schematic_list = NULL; static int schematic_count_ = 0; -static void -schematic_class_init (SchematicClass *klass) +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[NODE_DOT_ADDED] = g_signal_new ("node_dot_added", - TYPE_SCHEMATIC, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SchematicClass, node_dot_added), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - schematic_signals[NODE_DOT_REMOVED] = g_signal_new ("node_dot_removed", - TYPE_SCHEMATIC, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SchematicClass, node_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); + 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[NODE_DOT_ADDED] = + g_signal_new ("node_dot_added", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SchematicClass, node_dot_added), NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + + schematic_signals[NODE_DOT_REMOVED] = + g_signal_new ("node_dot_removed", TYPE_SCHEMATIC, G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SchematicClass, node_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 -node_dot_added_callback (NodeStore *store, SheetPos *pos, Schematic *schematic) +static void node_dot_added_callback (NodeStore *store, Coords *pos, Schematic *schematic) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); @@ -197,8 +173,7 @@ node_dot_added_callback (NodeStore *store, SheetPos *pos, Schematic *schematic) g_signal_emit_by_name (schematic, "node_dot_added", pos); } -static void -node_dot_removed_callback (NodeStore *store, SheetPos *pos, Schematic *schematic) +static void node_dot_removed_callback (NodeStore *store, Coords *pos, Schematic *schematic) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); @@ -206,8 +181,7 @@ node_dot_removed_callback (NodeStore *store, SheetPos *pos, Schematic *schematic g_signal_emit_by_name (schematic, "node_dot_removed", pos); } -static void -schematic_init (Schematic *schematic) +static void schematic_init (Schematic *schematic) { SchematicPriv *priv; @@ -215,49 +189,52 @@ schematic_init (Schematic *schematic) priv->printoptions = NULL; // Colors - priv->colors.components.red = 65535; + priv->colors.components.red = 1.0; priv->colors.components.green = 0; priv->colors.components.blue = 0; + priv->colors.components.alpha = 1.0; priv->colors.labels.red = 0; - priv->colors.labels.green = 35723; - priv->colors.labels.blue = 35723; + priv->colors.labels.green = 0.5451; + priv->colors.labels.blue = 0.5451; + priv->colors.labels.alpha = 1.0; 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->colors.wires.blue = 1.0; + priv->colors.wires.alpha = 1.0; + priv->colors.text.red = 1.0; + priv->colors.text.green = 1.0; + priv->colors.text.blue = 1.0; + priv->colors.text.alpha = 1.0; priv->symbols = g_hash_table_new (g_str_hash, g_str_equal); priv->refdes_values = g_hash_table_new (g_str_hash, g_str_equal); priv->store = node_store_new (); + priv->width = 0; + priv->height = 0; 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); + priv->logstore = log_new (); + priv->log = gtk_text_buffer_new (NULL); // LEGACY + priv->tag_error = gtk_text_buffer_create_tag (priv->log, "error", "foreground", "red", "weight", + PANGO_WEIGHT_BOLD, NULL); - g_signal_connect_object (priv->store, "node_dot_added", - G_CALLBACK (node_dot_added_callback), G_OBJECT (schematic), - G_CONNECT_AFTER); + g_signal_connect_object (priv->store, "node_dot_added", G_CALLBACK (node_dot_added_callback), + G_OBJECT (schematic), G_CONNECT_AFTER); g_signal_connect_object (priv->store, "node_dot_removed", - G_CALLBACK (node_dot_removed_callback), G_OBJECT (schematic), - G_CONNECT_AFTER); + G_CALLBACK (node_dot_removed_callback), G_OBJECT (schematic), + G_CONNECT_AFTER); - priv->sim_settings = sim_settings_new (schematic); + priv->sim_settings = sim_settings_gui_new (schematic); priv->settings = settings_new (schematic); - priv->simulation = simulation_new (schematic); + priv->simulation = simulation_new (schematic, priv->logstore); priv->filename = NULL; priv->netlist_filename = NULL; - priv->author = g_strdup (""); + priv->author = g_strdup (g_get_user_name ()); priv->comments = g_strdup (""); } -Schematic * -schematic_new (void) +Schematic *schematic_new (void) { Schematic *schematic; @@ -269,8 +246,7 @@ schematic_new (void) return schematic; } -static void -schematic_dispose (GObject *object) +static void schematic_dispose (GObject *object) { Schematic *schematic; GList *list; @@ -278,11 +254,11 @@ schematic_dispose (GObject *object) schematic = SCHEMATIC (object); // Disconnect weak item signal - for (list=schematic->priv->current_items; list; list=list->next) - g_object_weak_unref (G_OBJECT (list->data), - item_data_destroy_callback, G_OBJECT (schematic)); + for (list = schematic->priv->current_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)); + g_clear_object (&schematic->priv->log); schematic_count_--; schematic_list = g_list_remove (schematic_list, schematic); @@ -296,31 +272,29 @@ schematic_dispose (GObject *object) G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (schematic)); } -static void -schematic_finalize (GObject *object) +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; + Schematic *sm = SCHEMATIC (object); + SchematicPriv *priv = sm->priv; + if (priv) { + g_free (priv->simulation); + g_hash_table_destroy (priv->symbols); + g_hash_table_destroy (priv->refdes_values); + g_clear_object (&priv->store); + g_clear_object (&priv->logstore); + g_free (priv->netlist_filename); + g_free (priv->comments); + g_free (priv->author); + g_free (priv->filename); + g_free (priv->settings); + sim_settings_gui_finalize(priv->sim_settings); + g_free (priv); } G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (sm)); } -// Get/set functions. -// ***************** / - -char * -schematic_get_title (Schematic *schematic) +char *schematic_get_title (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -328,8 +302,7 @@ schematic_get_title (Schematic *schematic) return schematic->priv->title; } -char * -schematic_get_author (Schematic *schematic) +char *schematic_get_author (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -337,8 +310,15 @@ schematic_get_author (Schematic *schematic) return schematic->priv->author; } -char * -schematic_get_comments (Schematic *schematic) +char *schematic_get_version (Schematic *schematic) +{ + g_return_val_if_fail (schematic != NULL, NULL); + g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); + + return schematic->priv->oregano_version; +} + +char *schematic_get_comments (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -346,47 +326,54 @@ schematic_get_comments (Schematic *schematic) return schematic->priv->comments; } -void -schematic_set_title (Schematic *schematic, const gchar *title) +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 (!title) + return; - if (schematic->priv->title) - g_free (schematic->priv->title); + g_free (schematic->priv->title); schematic->priv->title = g_strdup (title); g_signal_emit_by_name (schematic, "title_changed", schematic->priv->title); } -void -schematic_set_author (Schematic *schematic, const gchar *author) +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 (!author) + return; - if (schematic->priv->author) - g_free (schematic->priv->author); + g_free (schematic->priv->author); schematic->priv->author = g_strdup (author); } -void -schematic_set_comments (Schematic *schematic, const gchar *comments) +void schematic_set_version (Schematic *schematic, const gchar *oregano_version) +{ + g_return_if_fail (schematic != NULL); + g_return_if_fail (IS_SCHEMATIC (schematic)); + + if (!oregano_version) + return; + + g_free (schematic->priv->oregano_version); + schematic->priv->oregano_version = g_strdup (oregano_version); +} + +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); + g_free (schematic->priv->comments); schematic->priv->comments = g_strdup (comments); } -char * -schematic_get_filename (Schematic *schematic) +char *schematic_get_filename (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -394,8 +381,7 @@ schematic_get_filename (Schematic *schematic) return schematic->priv->filename; } -void -schematic_set_filename (Schematic *schematic, const gchar *filename) +void schematic_set_filename (Schematic *schematic, const gchar *filename) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); @@ -404,8 +390,7 @@ schematic_set_filename (Schematic *schematic, const gchar *filename) schematic->priv->filename = g_strdup (filename); } -char * -schematic_get_netlist_filename (Schematic *schematic) +char *schematic_get_netlist_filename (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -413,20 +398,49 @@ schematic_get_netlist_filename (Schematic *schematic) return schematic->priv->netlist_filename; } -void -schematic_set_netlist_filename (Schematic *schematic, char *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); + g_free (schematic->priv->netlist_filename); schematic->priv->netlist_filename = g_strdup (filename); } -double -schematic_get_zoom (Schematic *schematic) +guint schematic_get_width (const Schematic *schematic) +{ + g_return_val_if_fail (schematic != NULL, 0.); + g_return_val_if_fail (IS_SCHEMATIC (schematic), 0.); + + return schematic->priv->width; +} + +void schematic_set_width (Schematic *schematic, const guint width) +{ + g_return_if_fail (schematic != NULL); + g_return_if_fail (IS_SCHEMATIC (schematic)); + + schematic->priv->width = width; +} + +guint schematic_get_height (const Schematic *schematic) +{ + g_return_val_if_fail (schematic != NULL, 0.); + g_return_val_if_fail (IS_SCHEMATIC (schematic), 0.); + + return schematic->priv->height; +} + +void schematic_set_height (Schematic *schematic, const guint height) +{ + g_return_if_fail (schematic != NULL); + g_return_if_fail (IS_SCHEMATIC (schematic)); + + schematic->priv->height = height; +} + +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); @@ -434,8 +448,7 @@ schematic_get_zoom (Schematic *schematic) return schematic->priv->zoom; } -void -schematic_set_zoom (Schematic *schematic, double zoom) +void schematic_set_zoom (Schematic *schematic, double zoom) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); @@ -443,8 +456,7 @@ schematic_set_zoom (Schematic *schematic, double zoom) schematic->priv->zoom = zoom; } -NodeStore * -schematic_get_store (Schematic *schematic) +NodeStore *schematic_get_store (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -452,8 +464,7 @@ schematic_get_store (Schematic *schematic) return schematic->priv->store; } -gpointer -schematic_get_settings (Schematic *schematic) +gpointer schematic_get_settings (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -461,8 +472,15 @@ schematic_get_settings (Schematic *schematic) return schematic->priv->settings; } -gpointer -schematic_get_sim_settings (Schematic *schematic) +SimSettings *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->sim_settings; +} + +SimSettingsGui *schematic_get_sim_settings_gui (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -470,8 +488,7 @@ schematic_get_sim_settings (Schematic *schematic) return schematic->priv->sim_settings; } -gpointer -schematic_get_simulation (Schematic *schematic) +gpointer schematic_get_simulation (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -479,19 +496,26 @@ schematic_get_simulation (Schematic *schematic) return schematic->priv->simulation; } -void -schematic_log_append (Schematic *schematic, const char *message) +Log *schematic_get_log_store (Schematic *schematic) +{ + g_return_val_if_fail (schematic != NULL, NULL); + g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); + + return schematic->priv->logstore; +} + +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)); + log_append (schematic->priv->logstore, "Schematic Info", message); + + // LEGACY + gtk_text_buffer_insert_at_cursor (schematic->priv->log, message, strlen (message)); } -void -schematic_log_append_error (Schematic *schematic, const char *message) +void schematic_log_append_error (Schematic *schematic, const char *message) { GtkTextIter iter; SchematicPriv *priv; @@ -501,13 +525,14 @@ schematic_log_append_error (Schematic *schematic, const char *message) priv = schematic->priv; + log_append (schematic->priv->logstore, "simulation engine Error", message); + + // LEGACY gtk_text_buffer_get_end_iter (priv->log, &iter); - gtk_text_buffer_insert_with_tags (priv->log, &iter, message, - -1, priv->tag_error, NULL); + gtk_text_buffer_insert_with_tags (priv->log, &iter, message, -1, priv->tag_error, NULL); } -void -schematic_log_show (Schematic *schematic) +void schematic_log_show (Schematic *schematic) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); @@ -515,18 +540,15 @@ schematic_log_show (Schematic *schematic) g_signal_emit_by_name (schematic, "log_updated", schematic->priv->log); } -void -schematic_log_clear (Schematic *schematic) +void schematic_log_clear (Schematic *schematic) { g_return_if_fail (schematic != NULL); g_return_if_fail (IS_SCHEMATIC (schematic)); - gtk_text_buffer_set_text ( - schematic->priv->log, "", -1); + gtk_text_buffer_set_text (schematic->priv->log, "", -1); } -GtkTextBuffer* -schematic_get_log_text (Schematic *schematic) +GtkTextBuffer *schematic_get_log_text (Schematic *schematic) { g_return_val_if_fail (schematic != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); @@ -534,75 +556,56 @@ schematic_get_log_text (Schematic *schematic) return schematic->priv->log; } -int -schematic_count (void) -{ - return schematic_count_; -} +int schematic_count (void) { return schematic_count_; } -Schematic * -schematic_read (char *name, GError **out_error) +Schematic *schematic_read (const char *name, GError **error) { Schematic *new_sm; - int ret; - char *fname; - GError *error = NULL; + const char *fname; + GError *e = NULL; FileType *ft; g_return_val_if_fail (name != NULL, NULL); - fname = g_filename_from_uri (name, NULL, &error); + fname = g_filename_from_uri (name, NULL, &e); if (!fname) { fname = name; - if (error) { - g_error_free (error); - error = NULL; - } + g_clear_error (&e); } 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); + g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND, + _ ("File %s does not exist."), 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); + g_set_error (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)); + ft->load_func (new_sm, fname, &e); + if (e) { + g_propagate_error (error, e); + g_clear_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!.")); - } - else - schematic_set_dirty (new_sm, FALSE); + schematic_set_dirty (new_sm, FALSE); return new_sm; } -gint -schematic_save_file (Schematic *sm, GError **error) +gint schematic_save_file (Schematic *sm, GError **error) { FileType *ft; - GError *internal_error = NULL; + GError *e = NULL; g_return_val_if_fail (sm != NULL, FALSE); @@ -610,44 +613,52 @@ schematic_save_file (Schematic *sm, GError **error) if (ft == NULL) { g_set_error (error, OREGANO_ERROR, OREGANO_SCHEMATIC_FILE_NOT_FOUND, - _("Unknown file format for %s."), schematic_get_filename (sm)); + _ ("Unknown file format for %s."), schematic_get_filename (sm)); return FALSE; } - if (ft->save_func (sm, &internal_error)) { + if (ft->save_func (sm, &e)) { 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_propagate_error (error, e); - g_error_free (internal_error); return FALSE; // Save fails! } -void -schematic_add_item (Schematic *sm, ItemData *data) +/** + * \brief add an ItemData object to a Schematic + * + * @param sm the schematic the item will be added to + * @param data fully initilalized ItemData object + */ +void schematic_add_item (Schematic *sm, ItemData *data) { NodeStore *store; char *prefix = NULL, *refdes = NULL; int num; - g_return_if_fail (sm != NULL); + g_return_if_fail (sm); g_return_if_fail (IS_SCHEMATIC (sm)); - g_return_if_fail (data != NULL); + g_return_if_fail (data); 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_assert (store); + g_assert (IS_NODE_STORE (store)); + + g_object_set (G_OBJECT (data), "store", store, NULL); + + // item data will call the child register function + // for parts e.g. this ends up in <node_store_add_part> + // which requires a valid position to add the node dots + if (item_data_register (data) == -1) { return; + } - // Some items need a reference designator. Find a good one. + // Some items need a reference designator, so get a good one prefix = item_data_get_refdes_prefix (data); if (prefix != NULL) { num = schematic_get_lowest_available_refdes (sm, prefix); @@ -660,21 +671,18 @@ schematic_add_item (Schematic *sm, ItemData *data) g_free (refdes); sm->priv->current_items = g_list_prepend (sm->priv->current_items, data); - g_object_weak_ref (G_OBJECT (data), item_data_destroy_callback, - G_OBJECT (sm)); - - g_signal_connect_object (data, "moved", G_CALLBACK (item_moved_callback), - sm, 0); + g_object_weak_ref (G_OBJECT (data), item_data_destroy_callback, G_OBJECT (sm)); sm->priv->dirty = TRUE; - g_signal_emit_by_name (sm, - "item_data_added", data); + // if the item gets moved mark the schematic as dirty + g_signal_connect_object (data, "moved", G_CALLBACK (item_moved_callback), sm, 0); + + // causes a canvas item (view) to be generated + g_signal_emit_by_name (sm, "item_data_added", data); } -void -schematic_parts_foreach (Schematic *schematic, - ForeachItemDataFunc func, gpointer user_data) +void schematic_parts_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data) { GList *list; @@ -687,12 +695,9 @@ schematic_parts_foreach (Schematic *schematic, for (list = node_store_get_parts (schematic->priv->store); list; list = list->next) { func (list->data, user_data); } - g_list_free_full (list, g_object_unref); } -void -schematic_wires_foreach (Schematic *schematic, - ForeachItemDataFunc func, gpointer user_data) +void schematic_wires_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data) { GList *list; @@ -705,12 +710,9 @@ schematic_wires_foreach (Schematic *schematic, for (list = node_store_get_wires (schematic->priv->store); list; list = list->next) { func (list->data, user_data); } - g_list_free_full (list, g_object_unref); } -void -schematic_items_foreach (Schematic *schematic, - ForeachItemDataFunc func, gpointer user_data) +void schematic_items_foreach (Schematic *schematic, ForeachItemDataFunc func, gpointer user_data) { GList *list; @@ -723,11 +725,9 @@ schematic_items_foreach (Schematic *schematic, for (list = schematic->priv->current_items; list; list = list->next) { func (list->data, user_data); } - g_list_free_full (list, g_object_unref); } -GList * -schematic_get_items (Schematic *sm) +GList *schematic_get_items (Schematic *sm) { g_return_val_if_fail (sm != NULL, NULL); g_return_val_if_fail (IS_SCHEMATIC (sm), NULL); @@ -735,8 +735,7 @@ schematic_get_items (Schematic *sm) return sm->priv->current_items; } -static void -item_data_destroy_callback (gpointer s, GObject *data) +static void item_data_destroy_callback (gpointer s, GObject *data) { Schematic *sm = SCHEMATIC (s); schematic_set_dirty (sm, TRUE); @@ -745,8 +744,7 @@ item_data_destroy_callback (gpointer s, GObject *data) } } -static int -schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix) +static int schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix) { gpointer key, value; @@ -754,18 +752,14 @@ schematic_get_lowest_available_refdes (Schematic *schematic, char *prefix) 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) ) { + if (g_hash_table_lookup_extended (schematic->priv->refdes_values, prefix, &key, &value)) { return GPOINTER_TO_INT (value); - } - else { + } else { return 1; } } -static void -schematic_set_lowest_available_refdes (Schematic *schematic, - char *prefix, int num) +static void schematic_set_lowest_available_refdes (Schematic *schematic, char *prefix, int num) { gpointer key, value; @@ -775,16 +769,13 @@ schematic_set_lowest_available_refdes (Schematic *schematic, // 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)) + 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)); + g_hash_table_insert (schematic->priv->refdes_values, key, GINT_TO_POINTER (num)); } -gboolean -schematic_is_dirty (Schematic *sm) +gboolean schematic_is_dirty (Schematic *sm) { g_return_val_if_fail (sm != NULL, FALSE); g_return_val_if_fail (IS_SCHEMATIC (sm), FALSE); @@ -792,8 +783,7 @@ schematic_is_dirty (Schematic *sm) return sm->priv->dirty; } -void -schematic_set_dirty (Schematic *sm, gboolean b) +void schematic_set_dirty (Schematic *sm, gboolean b) { g_return_if_fail (sm != NULL); g_return_if_fail (IS_SCHEMATIC (sm)); @@ -801,8 +791,7 @@ schematic_set_dirty (Schematic *sm, gboolean b) sm->priv->dirty = b; } -static void -item_moved_callback (ItemData *data, SheetPos *pos, Schematic *sm) +static void item_moved_callback (ItemData *data, Coords *pos, Schematic *sm) { g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); @@ -810,35 +799,33 @@ item_moved_callback (ItemData *data, SheetPos *pos, Schematic *sm) schematic_set_dirty (sm, TRUE); } -static void -schematic_render (Schematic *sm, cairo_t *cr) +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); } -GdkColor -convert_to_grayscale (GdkColor *source) +GdkRGBA convert_to_grayscale (GdkRGBA *source) { - GdkColor color; - int factor; + GdkRGBA color; + gdouble factor; + + factor = source->red * 0.299 + source->green * 0.587 + source->blue * 0.114; - factor = (source->red + source->green + source->blue)/3; color.red = factor; color.green = factor; color.blue = factor; - color.pixel = source->pixel; + color.alpha = source->alpha; return color; } -void -schematic_export (Schematic *sm, const gchar *filename, - gint img_w, gint img_h, int bg, int color, int format) +void schematic_export (Schematic *sm, const gchar *filename, gint img_w, gint img_h, int bg, + int color, int format) { NodeRect bbox; NodeStore *store; @@ -862,36 +849,36 @@ schematic_export (Schematic *sm, const gchar *filename, switch (format) { #ifdef CAIRO_HAS_SVG_SURFACE - case 0: - surface = cairo_svg_surface_create (filename, img_w, img_h); + 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); + 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); + 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); + 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); + 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; @@ -905,8 +892,8 @@ schematic_export (Schematic *sm, const gchar *filename, // 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, (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); @@ -928,26 +915,24 @@ schematic_export (Schematic *sm, const gchar *filename, } } -static void -draw_rotule (Schematic *sm, cairo_t *cr) +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_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) +static void draw_page (GtkPrintOperation *operation, GtkPrintContext *context, int page_nr, + Schematic *sm) { NodeStore *store; NodeRect bbox; gdouble page_w, page_h; - + page_w = gtk_print_context_get_width (context); page_h = gtk_print_context_get_height (context); @@ -955,15 +940,15 @@ draw_page (GtkPrintOperation *operation, // 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_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_translate (cr, page_w - 190, page_h - 40); + draw_rotule (sm, cr); cairo_restore (cr); store = schematic_get_store (sm); @@ -971,117 +956,108 @@ draw_page (GtkPrintOperation *operation, 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_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) +static GObject *print_options (GtkPrintOperation *operation, Schematic *sm) { GtkBuilder *gui; GError *perror = NULL; - - if ((gui = gtk_builder_new ()) == NULL) { - return G_OBJECT (gtk_label_new (_("Error loading print-options.ui"))); - } - if (!g_file_test (OREGANO_UIDIR "/print-options.ui", G_FILE_TEST_EXISTS)) { - return G_OBJECT (gtk_label_new (_("Error loading print-options.ui"))); + if ((gui = gtk_builder_new ()) == NULL) { + return G_OBJECT (gtk_label_new (_ ("Error loading print-options.ui"))); } - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/print-options.ui", - &perror) <= 0) { + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/print-options.ui", &perror) <= 0) { g_error_free (perror); - return G_OBJECT (gtk_label_new (_("Error loading print-options.ui"))); + return G_OBJECT (gtk_label_new (_ ("Error loading print-options.ui"))); } - if (sm->priv->printoptions) - g_free (sm->priv->printoptions); + g_free (sm->priv->printoptions); sm->priv->printoptions = g_new0 (SchematicPrintOptions, 1); - sm->priv->printoptions->components = GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_components")); - sm->priv->printoptions->labels = GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_labels")); - sm->priv->printoptions->wires = GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_wires")); - sm->priv->printoptions->text = GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_text")); - sm->priv->printoptions->background = GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_background")); + sm->priv->printoptions->components = + GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_components")); + sm->priv->printoptions->labels = + GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_labels")); + sm->priv->printoptions->wires = GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_wires")); + sm->priv->printoptions->text = GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_text")); + sm->priv->printoptions->background = + GTK_COLOR_BUTTON (gtk_builder_get_object (gui, "color_background")); // Set default colors - gtk_color_button_set_color (GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_components")), - &sm->priv->colors.components); - gtk_color_button_set_color (GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_labels")), - &sm->priv->colors.labels); - gtk_color_button_set_color (GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_wires")), - &sm->priv->colors.wires); - gtk_color_button_set_color (GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_text")), - &sm->priv->colors.text); - gtk_color_button_set_color (GTK_COLOR_BUTTON ( - gtk_builder_get_object (gui, "color_background")), - &sm->priv->colors.background); + gtk_color_chooser_set_rgba ( + GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_components")), + &sm->priv->colors.components); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_labels")), + &sm->priv->colors.labels); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_wires")), + &sm->priv->colors.wires); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_text")), + &sm->priv->colors.text); + gtk_color_chooser_set_rgba ( + GTK_COLOR_CHOOSER (gtk_builder_get_object (gui, "color_background")), + &sm->priv->colors.background); return gtk_builder_get_object (gui, "widget"); } -static void -read_print_options (GtkPrintOperation *operation, GtkWidget *widget, Schematic *sm) +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); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->components), + &sm->priv->colors.components); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->labels), &sm->priv->colors.labels); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->wires), &sm->priv->colors.wires); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (colors->text), &sm->priv->colors.text); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (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) +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); + if (settings != NULL) + 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); + 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")); + gtk_print_operation_set_custom_tab_label (op, _ ("Schematic")); if (preview) - res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PREVIEW, - NULL, NULL); + 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); + res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, NULL); if (res == GTK_PRINT_OPERATION_RESULT_CANCEL) { + } else if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { + if (settings != NULL) + g_object_unref (settings); + settings = g_object_ref (gtk_print_operation_get_print_settings (op)); } + + g_object_unref (op); } diff --git a/src/model/schematic.h b/src/model/schematic.h index a4f4dab..3637271 100644 --- a/src/model/schematic.h +++ b/src/model/schematic.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,13 +28,16 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SCHEMATIC_H__ #define __SCHEMATIC_H__ +// typedefing before including makes circular dependencies possible. +typedef struct _Schematic Schematic; + #include <gtk/gtk.h> #include <cairo/cairo.h> #include <cairo/cairo-features.h> @@ -49,74 +54,86 @@ #include "part.h" #include "wire.h" #include "node-store.h" +#include "log.h" + +#include "../sim-settings.h" +#include "sim-settings-gui.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)) +#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 struct _SchematicPriv SchematicPriv; -typedef void (*ForeachItemDataFunc) (ItemData *item_data, gpointer user_data); +typedef void (*ForeachItemDataFunc)(ItemData *item_data, gpointer user_data); -struct _Schematic { +struct _Schematic +{ GObject parent; SchematicPriv *priv; }; -struct _SchematicClass { +struct _SchematicClass +{ GObjectClass parent_class; // signals - void (*title_changed) (Schematic* ,gchar*); - void (*item_data_added) (Schematic*, gpointer*); - void (*log_updated) (gpointer); - void (*node_dot_added) (Schematic*); - void (*node_dot_removed) (Schematic*, gpointer*); - void (*last_schematic_destroyed) (Schematic*); + void (*title_changed)(Schematic *, gchar *); + void (*item_data_added)(Schematic *, gpointer *); + void (*log_updated)(gpointer); + void (*node_dot_added)(Schematic *); + void (*node_dot_removed)(Schematic *, gpointer *); + void (*last_schematic_destroyed)(Schematic *); }; -GType schematic_get_type (void); +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); +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_version (Schematic *schematic); +void schematic_set_version (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); +guint schematic_get_width (const Schematic *schematic); +guint schematic_get_height (const Schematic *schematic); +double schematic_get_zoom (Schematic *schematic); +void schematic_set_width (Schematic *schematic, const guint width); +void schematic_set_height (Schematic *schematic, const guint height); +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); +gpointer schematic_get_settings (Schematic *schematic); +SimSettings *schematic_get_sim_settings (Schematic *schematic); +SimSettingsGui *schematic_get_sim_settings_gui (Schematic *schematic); +gpointer schematic_get_simulation (Schematic *schematic); +Log *schematic_get_log_store (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); +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 (const 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/textbox.c b/src/model/textbox.c index 8e7a46c..ba41b67 100644 --- a/src/model/textbox.c +++ b/src/model/textbox.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> @@ -46,34 +46,27 @@ 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_rotate (ItemData *data, int angle, Coords *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); +static void textbox_flip (ItemData *data, IDFlip direction, Coords *center); +enum { TEXT_CHANGED, FONT_CHANGED, LAST_SIGNAL }; -enum { - TEXT_CHANGED, - FONT_CHANGED, - LAST_SIGNAL -}; - -struct _TextboxPriv { +struct _TextboxPriv +{ char *text; char *font; }; G_DEFINE_TYPE (Textbox, textbox, TYPE_ITEM_DATA) -static guint textbox_signals[LAST_SIGNAL] = { 0 }; - +static guint textbox_signals[LAST_SIGNAL] = {0}; -static void -textbox_finalize (GObject *object) +static void textbox_finalize (GObject *object) { Textbox *textbox = TEXTBOX (object); TextboxPriv *priv = textbox->priv; @@ -83,14 +76,12 @@ textbox_finalize (GObject *object) G_OBJECT_CLASS (textbox_parent_class)->finalize (object); } -static void -textbox_dispose (GObject *object) +static void textbox_dispose (GObject *object) { G_OBJECT_CLASS (textbox_parent_class)->dispose (object); } -static void -textbox_class_init (TextboxClass *klass) +static void textbox_class_init (TextboxClass *klass) { GObjectClass *object_class; ItemDataClass *item_data_class; @@ -100,26 +91,14 @@ textbox_class_init (TextboxClass *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); + 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; + 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; @@ -132,15 +111,13 @@ textbox_class_init (TextboxClass *klass) item_data_class->print = textbox_print; } -static void -textbox_init (Textbox *textbox) +static void textbox_init (Textbox *textbox) { TextboxPriv *priv = g_new0 (TextboxPriv, 1); textbox->priv = priv; } -Textbox * -textbox_new (char *font) +Textbox *textbox_new (char *font) { Textbox *textbox; @@ -154,8 +131,7 @@ textbox_new (char *font) return textbox; } -static ItemData * -textbox_clone (ItemData *src) +static ItemData *textbox_clone (ItemData *src) { Textbox *new_textbox; ItemDataClass *id_class; @@ -163,7 +139,7 @@ textbox_clone (ItemData *src) 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)); + id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (src)); if (id_class->copy == NULL) return NULL; @@ -173,8 +149,7 @@ textbox_clone (ItemData *src) return ITEM_DATA (new_textbox); } -static void -textbox_copy (ItemData *dest, ItemData *src) +static void textbox_copy (ItemData *dest, ItemData *src) { Textbox *dest_textbox, *src_textbox; @@ -193,14 +168,13 @@ textbox_copy (ItemData *dest, ItemData *src) dest_textbox->priv->font = src_textbox->priv->font; } -static void -textbox_rotate (ItemData *data, int angle, SheetPos *center) +static void textbox_rotate (ItemData *data, int angle, Coords *center) { cairo_matrix_t affine; double x, y; Textbox *textbox; - SheetPos b1, b2; - SheetPos textbox_center, delta; + Coords b1, b2; + Coords textbox_center, delta; g_return_if_fail (data != NULL); g_return_if_fail (IS_TEXTBOX (data)); @@ -216,13 +190,13 @@ textbox_rotate (ItemData *data, int angle, SheetPos *center) textbox_center.y = b1.y + (b2.y - b1.y) / 2; } - cairo_matrix_init_rotate (&affine, (double) angle * M_PI / 180); + cairo_matrix_init_rotate (&affine, (double)angle * M_PI / 180); // Let the views (canvas items) know about the rotation. g_signal_emit_by_name (G_OBJECT (textbox), "rotated", angle); if (center) { - SheetPos textbox_pos; + Coords textbox_pos; item_data_get_pos (ITEM_DATA (textbox), &textbox_pos); @@ -237,14 +211,13 @@ textbox_rotate (ItemData *data, int angle, SheetPos *center) } } -static void -textbox_flip (ItemData *data, gboolean horizontal, SheetPos *center) +static void textbox_flip (ItemData *data, IDFlip direction, Coords *center) { cairo_matrix_t affine; double x, y; Textbox *textbox; - SheetPos b1, b2; - SheetPos textbox_center = {0.0, 0.0}, delta; + Coords b1, b2; + Coords textbox_center = {0.0, 0.0}, delta; g_return_if_fail (data != NULL); g_return_if_fail (IS_TEXTBOX (data)); @@ -257,16 +230,16 @@ textbox_flip (ItemData *data, gboolean horizontal, SheetPos *center) textbox_center.y = b1.y + (b2.y - b1.y) / 2; } - if (horizontal) + if (direction == ID_FLIP_HORIZ) cairo_matrix_init_scale (&affine, -1, 1); else cairo_matrix_init_scale (&affine, 1, -1); // Let the views (canvas items) know about the rotation. - g_signal_emit_by_name (G_OBJECT (textbox), "flipped", horizontal); + g_signal_emit_by_name (G_OBJECT (textbox), "flipped", direction); if (center) { - SheetPos textbox_pos; + Coords textbox_pos; item_data_get_pos (ITEM_DATA (textbox), &textbox_pos); @@ -281,26 +254,20 @@ textbox_flip (ItemData *data, gboolean horizontal, SheetPos *center) } } -void -textbox_update_bbox (Textbox *textbox) +void textbox_update_bbox (Textbox *textbox) { - SheetPos b1, b2; + Coords b1, b2; b1.x = 0.0; - b1.y = 0.0-5; // - font->ascent; - b2.x = 0.0+5; // + rbearing; - b2.y = 0.0+5; // + font->descent; + 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); } -static gboolean -textbox_has_properties (ItemData *data) -{ - return TRUE; -} +static gboolean textbox_has_properties (ItemData *data) { return TRUE; } -void -textbox_set_text (Textbox *textbox, const char *text) +void textbox_set_text (Textbox *textbox, const char *text) { g_return_if_fail (textbox != NULL); g_return_if_fail (IS_TEXTBOX (textbox)); @@ -312,8 +279,7 @@ textbox_set_text (Textbox *textbox, const char *text) g_signal_emit_by_name (G_OBJECT (textbox), "text_changed", text); } -char * -textbox_get_text (Textbox *textbox) +char *textbox_get_text (Textbox *textbox) { g_return_val_if_fail (textbox != NULL, NULL); g_return_val_if_fail (IS_TEXTBOX (textbox), NULL); @@ -321,8 +287,7 @@ textbox_get_text (Textbox *textbox) return textbox->priv->text; } -void -textbox_set_font (Textbox *textbox, char *font) +void textbox_set_font (Textbox *textbox, char *font) { g_return_if_fail (textbox != NULL); g_return_if_fail (IS_TEXTBOX (textbox)); @@ -335,12 +300,10 @@ textbox_set_font (Textbox *textbox, char *font) textbox_update_bbox (textbox); - g_signal_emit_by_name(G_OBJECT (textbox), - "font_changed", textbox->priv->font); + g_signal_emit_by_name (G_OBJECT (textbox), "font_changed", textbox->priv->font); } -char * -textbox_get_font (Textbox *textbox) +char *textbox_get_font (Textbox *textbox) { g_return_val_if_fail (textbox != NULL, NULL); g_return_val_if_fail (IS_TEXTBOX (textbox), NULL); @@ -348,38 +311,36 @@ textbox_get_font (Textbox *textbox) return textbox->priv->font; } -static void -textbox_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) +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); - */ + /* GnomeCanvasPoints *line; + double x0, y0; + ArtPoint dst, src; + double affine[6]; + int i; + Textbox *textbox; + TextboxPriv *priv; + Coords 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) +static void textbox_unregister (ItemData *data) { NodeStore *store; @@ -389,8 +350,7 @@ textbox_unregister (ItemData *data) node_store_remove_textbox (store, TEXTBOX (data)); } -static int -textbox_register (ItemData *data) +static int textbox_register (ItemData *data) { NodeStore *store; diff --git a/src/model/textbox.h b/src/model/textbox.h index 8e505d9..c7e8260 100644 --- a/src/model/textbox.h +++ b/src/model/textbox.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __TEXTBOX_H @@ -38,35 +38,36 @@ #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 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; +struct _Textbox +{ + ItemData parent; TextboxPriv *priv; - gulong text_changed_handler_id; - gulong font_changed_handler_id; + gulong text_changed_handler_id; + gulong font_changed_handler_id; }; struct _TextboxClass { ItemDataClass parent_class; - Textbox *(*dup) (Textbox *textbox); + Textbox *(*dup)(Textbox *textbox); }; -GType textbox_get_type (void); +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); +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 index 57f2172..10c241c 100644 --- a/src/model/wire-private.h +++ b/src/model/wire-private.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,20 +26,21 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __WIRE_PRIVATE_H #define __WIRE_PRIVATE_H -struct _WirePriv { +struct _WirePriv +{ // Used to traverse the wires during netlist generation. gboolean visited : 1; GSList *nodes; - SheetPos length; + Coords length; WireDir direction; }; diff --git a/src/model/wire.c b/src/model/wire.c index 1ef99d1..8ab064b 100644 --- a/src/model/wire.c +++ b/src/model/wire.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <goocanvas.h> @@ -39,36 +43,30 @@ #include "wire-private.h" #include "clipboard.h" #include "schematic-print-context.h" +#include "oregano-utils.h" -static void wire_class_init (WireClass *klass); -static void wire_init (Wire *wire); -static void wire_copy (ItemData *dest, ItemData *src); +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); - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -enum { - ARG_0, - ARG_CHANGED, - ARG_DELETE, - ARG_LAST_SIGNAL -}; +static void wire_rotate (ItemData *data, int angle, Coords *center); +static void wire_flip (ItemData *data, IDFlip direction, Coords *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); +static void wire_changed (ItemData *data); + +#include "debug.h" + +enum { ARG_0, ARG_DELETE, ARG_LAST_SIGNAL }; G_DEFINE_TYPE (Wire, wire, TYPE_ITEM_DATA) -static guint wire_signals [ARG_LAST_SIGNAL] = { 0 }; +static guint wire_signals[ARG_LAST_SIGNAL] = {0}; static ItemDataClass *parent_class = NULL; -static void -wire_finalize (GObject *object) +static void wire_finalize (GObject *object) { Wire *wire = WIRE (object); WirePriv *priv = wire->priv; @@ -82,15 +80,9 @@ wire_finalize (GObject *object) G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -wire_dispose (GObject *object) -{ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - +static void wire_dispose (GObject *object) { G_OBJECT_CLASS (parent_class)->dispose (object); } -static void -wire_class_init (WireClass *klass) +static void wire_class_init (WireClass *klass) { GObjectClass *object_class; ItemDataClass *item_data_class; @@ -102,25 +94,10 @@ wire_class_init (WireClass *klass) object_class->dispose = wire_dispose; object_class->finalize = wire_finalize; - wire_signals [ARG_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 [ARG_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); + wire_signals[ARG_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; @@ -130,10 +107,10 @@ wire_class_init (WireClass *klass) item_data_class->reg = wire_register; item_data_class->has_properties = wire_has_properties; item_data_class->print = wire_print; + item_data_class->changed = wire_changed; } -static void -wire_init (Wire *wire) +static void wire_init (Wire *wire) { WirePriv *priv = g_new0 (WirePriv, 1); @@ -148,14 +125,17 @@ wire_init (Wire *wire) wire->priv = priv; } -Wire * -wire_new (void) +void wire_dbg_print (Wire *w) { - return WIRE (g_object_new (TYPE_WIRE, NULL)); + Coords pos; + item_data_get_pos (ITEM_DATA (w), &pos); + NG_DEBUG ("Wire %p is defined by (%lf,%lf) + lambda * (%lf,%lf)\n", w, pos.x, pos.y, + w->priv->length.x, w->priv->length.y); } -gint -wire_add_node (Wire *wire, Node *node) +Wire *wire_new () { return WIRE (g_object_new (TYPE_WIRE, NULL)); } + +gint wire_add_node (Wire *wire, Node *node) { WirePriv *priv; @@ -174,8 +154,7 @@ wire_add_node (Wire *wire, Node *node) return TRUE; } -gint -wire_remove_node (Wire *wire, Node *node) +gint wire_remove_node (Wire *wire, Node *node) { WirePriv *priv; @@ -195,8 +174,7 @@ wire_remove_node (Wire *wire, Node *node) return TRUE; } -GSList * -wire_get_nodes (Wire *wire) +GSList *wire_get_nodes (Wire *wire) { WirePriv *priv; @@ -208,8 +186,7 @@ wire_get_nodes (Wire *wire) return priv->nodes; } -void -wire_get_start_pos (Wire *wire, SheetPos *pos) +void wire_get_start_pos (Wire *wire, Coords *pos) { g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); @@ -218,8 +195,7 @@ wire_get_start_pos (Wire *wire, SheetPos *pos) item_data_get_pos (ITEM_DATA (wire), pos); } -void -wire_get_end_pos (Wire *wire, SheetPos *pos) +void wire_get_end_pos (Wire *wire, Coords *pos) { WirePriv *priv; @@ -235,8 +211,22 @@ wire_get_end_pos (Wire *wire, SheetPos *pos) pos->y += priv->length.y; } -void -wire_get_pos_and_length (Wire *wire, SheetPos *pos, SheetPos *length) +void wire_get_start_and_end_pos (Wire *wire, Coords *start, Coords *end) +{ + WirePriv *priv; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + g_return_if_fail (start != NULL); + g_return_if_fail (end != NULL); + + priv = wire->priv; + + item_data_get_pos (ITEM_DATA (wire), start); + *end = coords_sum (start, &(priv->length)); +} + +void wire_get_pos_and_length (Wire *wire, Coords *pos, Coords *length) { WirePriv *priv; @@ -250,8 +240,16 @@ wire_get_pos_and_length (Wire *wire, SheetPos *pos, SheetPos *length) *length = priv->length; } -void -wire_set_length (Wire *wire, SheetPos *length) +void wire_set_pos (Wire *wire, Coords *pos) +{ + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + g_return_if_fail (pos != NULL); + + item_data_set_pos (ITEM_DATA (wire), pos); +} + +void wire_set_length (Wire *wire, Coords *length) { WirePriv *priv; @@ -264,20 +262,16 @@ wire_set_length (Wire *wire, SheetPos *length) if (length->x == 0) { wire->priv->direction = WIRE_DIR_VERT; - } - else if (length->y == 0) { + } else if (length->y == 0) { wire->priv->direction = WIRE_DIR_HORIZ; - } - else { + } else { wire->priv->direction = WIRE_DIR_DIAG; } - g_signal_emit_by_name (G_OBJECT (wire), - "changed"); + g_signal_emit_by_name (G_OBJECT (wire), "changed"); } -gint -wire_is_visited (Wire *wire) +gboolean wire_is_visited (Wire *wire) { WirePriv *priv; @@ -289,8 +283,7 @@ wire_is_visited (Wire *wire) return priv->visited; } -void -wire_set_visited (Wire *wire, gboolean is_visited) +void wire_set_visited (Wire *wire, gboolean is_visited) { WirePriv *priv; @@ -302,8 +295,7 @@ wire_set_visited (Wire *wire, gboolean is_visited) priv->visited = is_visited; } -static ItemData * -wire_clone (ItemData *src) +static ItemData *wire_clone (ItemData *src) { Wire *new_wire; ItemDataClass *id_class; @@ -311,7 +303,7 @@ wire_clone (ItemData *src) 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)); + id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (src)); if (id_class->copy == NULL) return NULL; @@ -321,8 +313,7 @@ wire_clone (ItemData *src) return ITEM_DATA (new_wire); } -static void -wire_copy (ItemData *dest, ItemData *src) +static void wire_copy (ItemData *dest, ItemData *src) { Wire *dest_wire, *src_wire; @@ -341,109 +332,57 @@ wire_copy (ItemData *dest, ItemData *src) dest_wire->priv->length = src_wire->priv->length; } -static void -wire_rotate (ItemData *data, int angle, SheetPos *center_pos) +static void wire_rotate (ItemData *data, int angle, Coords *center_pos) { cairo_matrix_t affine; - double dx, dy, x, y; + Coords start_pos; 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)); + g_return_if_fail (center_pos != NULL); if (angle == 0) return; wire = WIRE (data); - if (center_pos) { - item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2); - wire_center_before.x = (b1.x + b2.x) / 2; - wire_center_before.y = (b1.y + b2.y) / 2; - } + wire_get_start_pos (wire, &start_pos); priv = wire->priv; if (priv->direction == WIRE_DIR_VERT) { priv->direction = WIRE_DIR_HORIZ; - } - else if (priv->direction == WIRE_DIR_HORIZ) { + } else if (priv->direction == WIRE_DIR_HORIZ) { priv->direction = WIRE_DIR_VERT; } - cairo_matrix_init_rotate (&affine, (double) angle * M_PI / 180.0); - - // Rotate the wire's end point. - x = priv->length.x; - y = priv->length.y; - - cairo_matrix_transform_point (&affine, &x, &y); - - if (fabs (x) < 1e-2) - x = 0.0; - if (fabs (y) < 1e-2) - y = 0.0; - - // 'Normalize'. - if (y < 0 || (y == 0 && x < 0)) { - priv->length.x = -x; - priv->length.y = -y; - delta.x = -x; - delta.y = -y; - - item_data_move (ITEM_DATA (wire), &delta); - } - else { - priv->length.x = x; - priv->length.y = 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_pos) { - SheetPos wire_pos; - - item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2); + cairo_matrix_init_identity(&affine); + cairo_matrix_translate(&affine, center_pos->x, center_pos->y); + cairo_matrix_rotate(&affine, (double)angle * M_PI / 180.0); + cairo_matrix_translate(&affine, -center_pos->x, -center_pos->y); - wire_center_after.x = (b1.x + b2.x) / 2; - wire_center_after.y = (b1.y + b2.y) / 2; + cairo_matrix_transform_distance (&affine, &priv->length.x, &priv->length.y); - dx = wire_center_before.x - wire_center_after.x; - dy = wire_center_before.y - wire_center_after.y; + cairo_matrix_transform_point (&affine, &start_pos.x, &start_pos.y); - item_data_get_pos (ITEM_DATA (wire), &wire_pos); + wire_set_pos (wire, &start_pos); - x = wire_center_before.x - center_pos->x; - y = wire_center_before.y - center_pos->y; - dx = dx - x; - dy = dy - y; - cairo_matrix_transform_point (&affine, &x, &y); - - delta.x = dx + x + b1.x; - delta.y = dy + y + b1.y; + // Update bounding box. + wire_update_bbox (wire); - item_data_move (ITEM_DATA (wire), &delta); - } + // Let the views (canvas items) know about the rotation. + g_signal_emit_by_name (G_OBJECT (wire), "rotated", angle); // legacy + g_signal_emit_by_name (G_OBJECT (wire), "changed"); } -static void -wire_flip (ItemData *data, gboolean horizontal, SheetPos *center) -{ - // Do nothing! - return; -} +// FIXME if we have a center pos, this actually needs to do some transform magic +static void wire_flip (ItemData *data, IDFlip direction, Coords *center) { return; } -void -wire_update_bbox (Wire *wire) +void wire_update_bbox (Wire *wire) { - SheetPos b1, b2, pos, length; + Coords b1, b2, pos, length; wire_get_pos_and_length (wire, &pos, &length); @@ -453,14 +392,9 @@ wire_update_bbox (Wire *wire) item_data_set_relative_bbox (ITEM_DATA (wire), &b1, &b2); } -static gboolean -wire_has_properties (ItemData *data) -{ - return FALSE; -} +static gboolean wire_has_properties (ItemData *data) { return FALSE; } -static void -wire_unregister (ItemData *data) +static void wire_unregister (ItemData *data) { NodeStore *store; @@ -470,21 +404,29 @@ wire_unregister (ItemData *data) node_store_remove_wire (store, WIRE (data)); } -static int -wire_register (ItemData *data) +static gboolean wire_register (ItemData *data) { NodeStore *store; - g_return_val_if_fail (IS_WIRE (data), -1); + g_return_val_if_fail (IS_WIRE (data), FALSE); 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) +static void wire_changed (ItemData *data) +{ + Coords loc; + g_return_if_fail (IS_WIRE (data)); + + item_data_get_pos (data, &loc); + g_signal_emit_by_name ((GObject *)data, "moved", &loc); + g_signal_emit_by_name ((GObject *)data, "changed"); +} + +static void wire_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) { - SheetPos start_pos, end_pos; + Coords start_pos, end_pos; Wire *wire; g_return_if_fail (data != NULL); @@ -496,10 +438,10 @@ wire_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) 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); + gdk_cairo_set_source_rgba (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); } diff --git a/src/model/wire.h b/src/model/wire.h index f211cd8..352cd41 100644 --- a/src/model/wire.h +++ b/src/model/wire.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __WIRE_H @@ -35,13 +37,13 @@ #include <gtk/gtk.h> -#include "sheet-pos.h" +#include "coords.h" #include "clipboard.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 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; @@ -50,6 +52,7 @@ typedef struct _WirePriv WirePriv; #include "node-store.h" #include "node.h" +#include "grid.h" typedef enum { WIRE_DIR_NONE = 0, @@ -58,7 +61,8 @@ typedef enum { WIRE_DIR_DIAG = 3 } WireDir; -struct _Wire { +struct _Wire +{ ItemData parent; WirePriv *priv; }; @@ -67,26 +71,28 @@ struct _WireClass { ItemDataClass parent_class; - Wire *(*dup) (Wire *wire); - void (*changed) (); - void (*delete) (); + 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); -void wire_update_bbox (Wire *wire); +GType wire_get_type (void); +Wire *wire_new (); +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, Coords *pos); +void wire_get_end_pos (Wire *wire, Coords *pos); +void wire_get_start_and_end_pos (Wire *wire, Coords *start, Coords *end); +void wire_get_pos_and_length (Wire *wire, Coords *pos, Coords *length); +void wire_set_pos (Wire *wire, Coords *pos); +void wire_set_length (Wire *wire, Coords *length); +gint wire_is_visited (Wire *wire); +void wire_set_visited (Wire *wire, gboolean is_visited); +void wire_delete (Wire *wire); +void wire_update_bbox (Wire *wire); +void wire_dbg_print (Wire *w); #endif diff --git a/src/netlist-editor.c b/src/netlist-editor.c index b9d9d08..25352f5 100644 --- a/src/netlist-editor.c +++ b/src/netlist-editor.c @@ -2,14 +2,16 @@ * netlist-editor.c * * - * Author: + * Authors: * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2004-2008 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,20 +25,25 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <stdio.h> #include <stdlib.h> #include <glib/gi18n.h> -#include <gtksourceview/gtksourcelanguagemanager.h> +#include <glib.h> +#include <gtk/gtk.h> +#include <gtksourceview/gtksource.h> #include "netlist-editor.h" #include "netlist-helper.h" #include "simulation.h" #include "file.h" #include "dialogs.h" +#include "oregano.h" + +#include "debug.h" static void netlist_editor_finalize (GObject *object); static void netlist_editor_dispose (GObject *object); @@ -46,18 +53,18 @@ void netlist_editor_init (NetlistEditor *nle); static GObjectClass *parent_class = NULL; -struct _NetlistEditorPriv { +struct _NetlistEditorPriv +{ SchematicView *sv; - gchar * font; - GdkColor bgcolor, selectcolor, textcolor; + gchar *font; GtkTextView *view; GtkSourceBuffer *buffer; GtkWindow *toplevel; GtkButton *save, *close; -}; +}; -static void -netlist_editor_class_init (NetlistEditorClass *klass) { +static void netlist_editor_class_init (NetlistEditorClass *klass) +{ GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); @@ -67,8 +74,7 @@ netlist_editor_class_init (NetlistEditorClass *klass) { object_class->dispose = netlist_editor_dispose; } -static void -netlist_editor_finalize (GObject *object) +static void netlist_editor_finalize (GObject *object) { NetlistEditor *nle = NETLIST_EDITOR (object); @@ -84,8 +90,7 @@ netlist_editor_finalize (GObject *object) G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -netlist_editor_dispose (GObject *object) +static void netlist_editor_dispose (GObject *object) { NetlistEditor *nle = NETLIST_EDITOR (object); @@ -94,33 +99,23 @@ netlist_editor_dispose (GObject *object) G_OBJECT_CLASS (parent_class)->dispose (object); } -GType -netlist_editor_get_type (void) +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); + 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_save (GtkWidget * widget, NetlistEditor * nle) +void netlist_editor_save (GtkWidget *widget, NetlistEditor *nle) { char *name; @@ -141,8 +136,8 @@ netlist_editor_save (GtkWidget * widget, NetlistEditor * nle) 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); + 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; } @@ -156,11 +151,10 @@ netlist_editor_save (GtkWidget * widget, NetlistEditor * nle) // 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) +void setup_language_manager_path (GtkSourceLanguageManager *lm) { gchar **lang_files; - const gchar * const * temp; + const gchar *const *temp; GPtrArray *dirs; int i, lang_files_count; char **new_langs; @@ -168,108 +162,97 @@ setup_language_manager_path (GtkSourceLanguageManager *lm) dirs = g_ptr_array_new (); // Stolen from gtranslator - for (temp = gtk_source_language_manager_get_search_path(lm); - temp != NULL && *temp != NULL; ++temp) - g_ptr_array_add (dirs, g_strdup(*temp)); + for (temp = gtk_source_language_manager_get_search_path (lm); temp != NULL && *temp != NULL; + ++temp) + g_ptr_array_add (dirs, g_strdup (*temp)); g_ptr_array_add (dirs, NULL); - lang_files = (gchar **) g_ptr_array_free (dirs, FALSE); + lang_files = (gchar **)g_ptr_array_free (dirs, FALSE); lang_files_count = g_strv_length (lang_files); - new_langs = g_new (char*, lang_files_count + 2); + new_langs = g_new (char *, lang_files_count + 2); for (i = 0; lang_files[i]; i++) - new_langs[i] = lang_files[i]; + new_langs[i] = g_strdup (lang_files[i]); new_langs[lang_files_count] = g_strdup (OREGANO_LANGDIR); - new_langs[lang_files_count+1] = NULL; + new_langs[lang_files_count + 1] = NULL; g_strfreev (lang_files); gtk_source_language_manager_set_search_path (lm, new_langs); } -NetlistEditor * -netlist_editor_new (GtkSourceBuffer * textbuffer) { - NetlistEditor * nle; +NetlistEditor *netlist_editor_new (GtkSourceBuffer *textbuffer) +{ + NetlistEditor *nle; GtkBuilder *gui; GError *perror = NULL; - GtkWidget * toplevel; - GtkScrolledWindow * scroll; - GtkSourceView * source_view; - GtkSourceLanguageManager * lm; - GtkButton * save, * close; - GtkSourceLanguage *lang=NULL; - - if (!textbuffer) + GtkWidget *toplevel; + GtkScrolledWindow *scroll; + GtkSourceView *source_view; + GtkSourceLanguageManager *lm; + GtkButton *save, *close; + GtkSourceLanguage *lang = NULL; + + if (!textbuffer) return NULL; if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create the netlist dialog")); - return NULL; - } - else gtk_builder_set_translation_domain (gui, NULL); - - nle = NETLIST_EDITOR (g_object_new (netlist_editor_get_type (), NULL)); - - if (!g_file_test (OREGANO_UIDIR "/view-netlist.ui", 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_UIDIR "/view-netlist.ui"); - oregano_error_with_title (_("Could not create the netlist dialog"), msg); - g_free (msg); + oregano_error (_ ("Could not create the netlist dialog")); return NULL; } + gtk_builder_set_translation_domain (gui, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/view-netlist.ui", - &perror) <= 0) { - gchar *msg; + nle = NETLIST_EDITOR (g_object_new (netlist_editor_get_type (), NULL)); + + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/view-netlist.ui", &perror) <= 0) { + gchar *msg; msg = perror->message; - oregano_error_with_title (_("Could not create the netlist dialog"), msg); + oregano_error_with_title (_ ("Could not create the netlist dialog"), msg); g_error_free (perror); return NULL; } - + toplevel = GTK_WIDGET (gtk_builder_get_object (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 (gtk_builder_get_object (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); + 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) { + const gchar *name = gtk_source_language_get_name (lang); + g_message ("Loading syntax highlighting %s from %s", name, OREGANO_LANGDIR "/netlist.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 { + } 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_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 (gtk_builder_get_object (gui, "btn_close")); - g_signal_connect_swapped (G_OBJECT (close), "clicked", - G_CALLBACK (g_object_unref), G_OBJECT (nle)); + g_signal_connect_swapped (G_OBJECT (close), "clicked", G_CALLBACK (g_object_unref), + G_OBJECT (nle)); save = GTK_BUTTON (gtk_builder_get_object (gui, "btn_save")); - g_signal_connect (G_OBJECT (save), "clicked", - G_CALLBACK (netlist_editor_save), nle); - + g_signal_connect (G_OBJECT (save), "clicked", G_CALLBACK (netlist_editor_save), nle); + // Set tab, fonts, wrap mode, colors, etc. according - // to preferences + // to preferences nle->priv->view = GTK_TEXT_VIEW (source_view); nle->priv->toplevel = GTK_WINDOW (toplevel); nle->priv->save = save; @@ -277,12 +260,11 @@ netlist_editor_new (GtkSourceBuffer * textbuffer) { nle->priv->buffer = textbuffer; gtk_widget_show_all (GTK_WIDGET (toplevel)); - - return nle; + + return nle; } -NetlistEditor * -netlist_editor_new_from_file (gchar * filename) +NetlistEditor *netlist_editor_new_from_file (gchar *filename) { GtkSourceBuffer *buffer; gchar *content; @@ -295,8 +277,8 @@ netlist_editor_new_from_file (gchar * filename) 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); + 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; } @@ -310,39 +292,39 @@ netlist_editor_new_from_file (gchar * filename) return editor; } -void -netlist_editor_init (NetlistEditor * nle) { +void netlist_editor_init (NetlistEditor *nle) +{ nle->priv = g_new0 (NetlistEditorPriv, 1); - + nle->priv->toplevel = NULL; nle->priv->sv = NULL; } -NetlistEditor * -netlist_editor_new_from_schematic_view (SchematicView *sv) +NetlistEditor *netlist_editor_new_from_schematic_view (SchematicView *sv) { NetlistEditor *editor; gchar *name = "/tmp/oregano.netlist"; - GError *error = 0; + GError *e = NULL; 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); + engine = oregano_engine_factory_create_engine (oregano.engine, sm); + oregano_engine_generate_netlist (engine, name, &e); 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")); + if (e) { + if (g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) { + log_append_error (schematic_get_log_store (sm), "Netlist", + _ ("Could not create a netlist"), e); + } else { + log_append_error (schematic_get_log_store (sm), "Netlist", + _ ("Unexpected error occured"), e); } + g_clear_error (&e); return NULL; } diff --git a/src/netlist-editor.h b/src/netlist-editor.h index 8901bf2..08e46cb 100644 --- a/src/netlist-editor.h +++ b/src/netlist-editor.h @@ -6,7 +6,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2004-2008 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber @@ -23,8 +23,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NETLIST_EDIT_H @@ -32,38 +32,41 @@ #include <glib.h> #include <gtk/gtk.h> -#include <gtksourceview/gtksourceview.h> +#include <gtksourceview/gtksource.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)) +#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 _NetlistEditor NetlistEditor; typedef struct _NetlistEditorClass NetlistEditorClass; -typedef struct _NetlistEditorPriv NetlistEditorPriv; +typedef struct _NetlistEditorPriv NetlistEditorPriv; -struct _NetlistEditor { +struct _NetlistEditor +{ GObject parent; NetlistEditorPriv *priv; }; -struct _NetlistEditorClass { +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_file (gchar *filename); NetlistEditor *netlist_editor_new_from_schematic_view (SchematicView *sv); -NetlistEditor *netlist_editor_new (GtkSourceBuffer * textbuffer); +NetlistEditor *netlist_editor_new (GtkSourceBuffer *textbuffer); #endif diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..9d7cd45 --- /dev/null +++ b/src/options.c @@ -0,0 +1,59 @@ +#include "options.h" +#include <gtk/gtk.h> //for gtk_get_option_group + +OreganoOptions opts = { + .debug = {.wires = FALSE, .boxes = FALSE, .dots = FALSE, .directions = FALSE, .all = FALSE}}; + +GOptionEntry entries[] = { + {"version", 0, 0, G_OPTION_ARG_NONE, &(opts.version), + "Print the version and quit.", NULL}, + {"debug-wires", 0, 0, G_OPTION_ARG_NONE, &(opts.debug.wires), + "Give them randomly alternating colors.", NULL}, + {"debug-boundingboxes", 0, 0, G_OPTION_ARG_NONE, &(opts.debug.boxes), + "Draw them in semi transparent purple.", NULL}, + {"debug-dots", 0, 0, G_OPTION_ARG_NONE, &(opts.debug.dots), + "Draw an extra color circle around dots which are always shown.", NULL}, + {"debug-directions", 0, 0, G_OPTION_ARG_NONE, &(opts.debug.directions), + "Draw fancy direction arrows top left edge of the sheet.", NULL}, + {"debug-all", 0, 0, G_OPTION_ARG_NONE, &(opts.debug.all), "Enable all debug-* options.", NULL}, + {NULL}}; + +/** + * parse the commandline options for gtk args and oregano recognized ones + * results will be written to a global Options struct (opts) + * @param argc pointer to argc from main + * @param argv pointer to argv from main + * @param e a GError ptr ptr which will be filled in case of an error + */ +gboolean oregano_options_parse (int *argc, char **argv[], GError **e) +{ + GError *error = NULL; + gboolean r = FALSE; + GOptionContext *context; + context = g_option_context_new ("- electrical engineering tool"); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + r = g_option_context_parse (context, argc, argv, &error); + if (error) { + if (e) + *e = g_error_copy (error); + g_error_free (error); + error = NULL; + } + g_option_context_free (context); + return r; +} + + +inline gboolean oregano_options_version () { return opts.version; } + +inline gboolean oregano_options_debug_wires () { return opts.debug.wires || opts.debug.all; } + +inline gboolean oregano_options_debug_boxes () { return opts.debug.boxes || opts.debug.all; } + +inline gboolean oregano_options_debug_dots () { return opts.debug.dots || opts.debug.all; } + +inline gboolean oregano_options_debug_directions () +{ + return opts.debug.directions || opts.debug.all; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..7157fab --- /dev/null +++ b/src/options.h @@ -0,0 +1,36 @@ +#ifndef OPTION_H__ +#define OPTION_H__ + +#ifndef GETTEXT_PACKAGE +#define GETTEXT_PACKAGE "oregano" +#endif + +#include <glib.h> + +typedef struct +{ + gboolean version; + struct + { + gboolean wires; + gboolean boxes; + gboolean dots; + gboolean directions; + gboolean all; + } debug; + +} OreganoOptions; + +gboolean oregano_options_parse (int *argc, char **argv[], GError **e); + +gboolean oregano_options_version (); + +gboolean oregano_options_debug_wires (); + +gboolean oregano_options_debug_boxes (); + +gboolean oregano_options_debug_dots (); + +gboolean oregano_options_debug_directions (); + +#endif /* OPTION_H__ */ diff --git a/src/oregano-config.c b/src/oregano-config.c index 7e8a184..f73dac8 100644 --- a/src/oregano-config.c +++ b/src/oregano-config.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <sys/types.h> @@ -42,44 +42,33 @@ #include "dialogs.h" #include "engine.h" -#define OREGLIB_EXT "oreglib" +#define OREGLIB_EXT "oreglib" -static gboolean is_oregano_library_name (gchar* name); +static gboolean is_oregano_library_name (gchar *name); static void load_library_error (gchar *name); -void -oregano_config_load (void) +void oregano_config_load (void) { - oregano.settings = g_settings_new ("apps.oregano"); - oregano.engine = g_settings_get_int (oregano.settings, - "engine"); - oregano.compress_files = g_settings_get_boolean (oregano.settings, - "compress-files"); - oregano.show_log = g_settings_get_boolean (oregano.settings, - "show-log"); - oregano.show_splash = g_settings_get_boolean (oregano.settings, - "show-splash"); + oregano.settings = g_settings_new ("io.ahoi.oregano"); + oregano.engine = g_settings_get_int (oregano.settings, "engine"); + oregano.compress_files = g_settings_get_boolean (oregano.settings, "compress-files"); + oregano.show_log = g_settings_get_boolean (oregano.settings, "show-log"); + oregano.show_splash = g_settings_get_boolean (oregano.settings, "show-splash"); // 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) +void oregano_config_save (void) { - g_settings_set_int (oregano.settings, "engine", - oregano.engine); - g_settings_set_boolean (oregano.settings, "compress-files", - oregano.compress_files); - g_settings_set_boolean (oregano.settings, "show-log", - oregano.show_log); - g_settings_set_boolean (oregano.settings, "show-splash", - oregano.show_splash); + g_settings_set_int (oregano.settings, "engine", oregano.engine); + g_settings_set_boolean (oregano.settings, "compress-files", oregano.compress_files); + g_settings_set_boolean (oregano.settings, "show-log", oregano.show_log); + g_settings_set_boolean (oregano.settings, "show-splash", oregano.show_splash); } -void -oregano_lookup_libraries (Splash *sp) +void oregano_lookup_libraries (Splash *sp) { gchar *fname; DIR *libdir; @@ -89,7 +78,8 @@ oregano_lookup_libraries (Splash *sp) oregano.libraries = NULL; libdir = opendir (OREGANO_LIBRARYDIR); - if (libdir == NULL) return; + if (libdir == NULL) + return; if (oregano.libraries != NULL) { closedir (libdir); @@ -104,15 +94,15 @@ oregano_lookup_libraries (Splash *sp) } g_free (fname); - while ((libentry=readdir (libdir)) != NULL) { + while ((libentry = readdir (libdir)) != NULL) { if (is_oregano_library_name (libentry->d_name) && - strcmp (libentry->d_name,"default.oreglib")) { + 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); + sprintf (txt, _ ("Loading %s ..."), libentry->d_name); oregano_splash_step (sp, txt); } @@ -120,7 +110,7 @@ oregano_lookup_libraries (Splash *sp) library = library_parse_xml_file (fname); if (library) - oregano.libraries = g_list_append ( oregano.libraries, library); + oregano.libraries = g_list_append (oregano.libraries, library); else load_library_error (fname); @@ -130,15 +120,14 @@ oregano_lookup_libraries (Splash *sp) closedir (libdir); } -static gboolean -is_oregano_library_name (gchar *name) +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. + dot++; // Points to the extension. if (strcmp (dot, OREGLIB_EXT) == 0) return TRUE; @@ -146,15 +135,13 @@ is_oregano_library_name (gchar *name) return FALSE; } -static void -load_library_error (gchar *name) +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 library or Oregano " - "and try again.")); + 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 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 index 54a7f8e..5f36303 100644 --- a/src/oregano-config.h +++ b/src/oregano-config.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __OREGANO_CONFIG_H diff --git a/src/oregano-utils.c b/src/oregano-utils.c index 779a6e0..099fcdc 100644 --- a/src/oregano-utils.c +++ b/src/oregano-utils.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * * This program is free software; you can redistribute it and/or @@ -27,59 +29,77 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> #include <gtk/gtk.h> +#include "debug.h" #include "oregano-utils.h" -gdouble -oregano_strtod (const gchar *str, const gchar unit) +gdouble oregano_strtod (const gchar *str, const gchar *unit) { + gboolean unit_does_not_match = FALSE; gdouble ret; gchar *endptr, *c; + size_t unit_length = 0; + + if (unit) + unit_length = strlen (unit); if (!str) return 0.0; - + ret = g_ascii_strtod (str, &endptr); + for (c = endptr; *c; c++) { switch (*c) { case 'T': ret *= 1e12; - return ret; + break; case 'G': ret *= 1e9; - return ret; + break; case 'M': ret *= 1e6; - return ret; + break; case 'k': ret *= 1e3; - return ret; + break; case 'm': ret *= 1e-3; - return ret; + break; case 'u': ret *= 1e-6; - return ret; + break; case 'n': ret *= 1e-9; - return ret; + break; case 'p': ret *= 1e-12; - return ret; + break; case 'f': ret *= 1e-15; - return ret; + break; + case ' ': + break; default: - if (*c == unit) - return ret; + if (c) { + if (!g_ascii_strncasecmp (c, unit, unit_length)) { + return ret; + } else { + unit_does_not_match = TRUE; + break; + } + } break; } } + + if (unit_does_not_match) + g_printf ("Unexpected unit of measurement\n"); + return ret; } diff --git a/src/oregano-utils.h b/src/oregano-utils.h index 43d5ed5..4f2c1a0 100644 --- a/src/oregano-utils.h +++ b/src/oregano-utils.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,13 +28,26 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __OREGANO_UTILS_H #define __OREGANO_UTILS_H -gdouble oregano_strtod (const gchar *str, const gchar unit); +gdouble oregano_strtod (const gchar *str, const gchar *unit); + +#define DEGSANITY(x) \ + do { \ + while (rotation < 0) \ + x += 360; \ + x %= 360; \ + } while (0) +#define DEG2RAD(x) ((double)x * M_PI / 180.) +#define RAD2DEG(x) ((double)x * 180. / M_PI) +#define COORDS_AVERAGE(tl, br) \ + { \ + (tl.x + br.x) / 2., (tl.y + br.y) / 2. \ + } #endif diff --git a/src/oregano.c b/src/oregano.c index 25c9c3b..fa45777 100644 --- a/src/oregano.c +++ b/src/oregano.c @@ -1,13 +1,16 @@ /* * oregano.c * - * Author: - * Richard Hult <rhult@hem.passagen.se> - * Ricardo Markiewicz <rmarkie@fi.uba.ar> - * Andres de Barbara <adebarbara@fi.uba.ar> - * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> + * + * Web page: https://ahoi.io/project/oregano * * Oregano, a tool for schematic capture and simulation of electronic * circuits. @@ -15,6 +18,8 @@ * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -28,8 +33,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #define ENABLE_NLS 1 @@ -43,8 +48,8 @@ #include <signal.h> #include <gio/gdesktopappinfo.h> - #include "dialogs.h" +#include "engine.h" #include "schematic.h" #include "schematic-view.h" #include "cursors.h" @@ -66,103 +71,107 @@ int oregano_debugging; static void oregano_application (GApplication *app, GFile *file); static void oregano_activate (GApplication *application); -static void oregano_open (GApplication *application, GFile **files, - gint n_files, const gchar *hint); +static void oregano_open (GApplication *application, GFile **files, gint n_files, + const gchar *hint); -static void -oregano_finalize (GObject *object) +static void oregano_finalize (GObject *object) { - G_OBJECT_CLASS (oregano_parent_class)->finalize (object); + cursors_shutdown (); + G_OBJECT_CLASS (oregano_parent_class)->finalize (object); } -static void -oregano_class_init (OreganoClass *klass) -{ +static void oregano_class_init (OreganoClass *klass) +{ G_APPLICATION_CLASS (klass)->activate = oregano_activate; - G_APPLICATION_CLASS (klass)->open = oregano_open; + G_APPLICATION_CLASS (klass)->open = oregano_open; G_OBJECT_CLASS (klass)->finalize = oregano_finalize; } -static void -oregano_init (Oregano *object) +static void oregano_init (Oregano *object) { + guint i; + gchar *engine_name; + cursors_init (); stock_init (); oregano_config_load (); + + // check if the engine loaded from the configuration exists... + // otherwise pick up the next one ! + engine_name = oregano_engine_get_engine_name_by_index (oregano.engine); + if (g_find_program_in_path (engine_name) == NULL) { + oregano.engine = OREGANO_ENGINE_COUNT; + for (i = 0; i < OREGANO_ENGINE_COUNT; i++) { + g_free (engine_name); + engine_name = oregano_engine_get_engine_name_by_index (i); + if (g_find_program_in_path (engine_name) != NULL) { + oregano.engine = i; + } + } + } + g_free (engine_name); + + // simulation cannot run, disable log + if (oregano.engine < 0 || oregano.engine >= OREGANO_ENGINE_COUNT) + oregano.show_log = FALSE; } -Oregano * -oregano_new (void) +Oregano *oregano_new (void) { - g_type_init (); - - return g_object_new (oregano_get_type (), - "application-id", "org.gnome.oregano", - "flags", G_APPLICATION_HANDLES_OPEN, - NULL); + return g_object_new (oregano_get_type (), "application-id", "org.gnome.oregano", "flags", + G_APPLICATION_HANDLES_OPEN, NULL); } // GApplication implementation -static void -oregano_activate (GApplication *application) +static void oregano_activate (GApplication *application) { - oregano_application (application, NULL); + oregano_application (application, NULL); } -static void -oregano_open (GApplication *application, GFile **files, gint n_files, - const gchar *hint) +static void oregano_open (GApplication *application, GFile **files, gint n_files, const gchar *hint) { - gint i; + gint i; - for (i = 0; i < n_files; i++) - oregano_application (application, files[i]); + for (i = 0; i < n_files; i++) + oregano_application (application, files[i]); } -static gboolean -quit_hook (GSignalInvocationHint *ihint, - guint n_param_values, - const GValue *param_values, - gpointer data) +static gboolean quit_hook (GSignalInvocationHint *ihint, guint n_param_values, + const GValue *param_values, gpointer data) { return FALSE; } -static void -oregano_application (GApplication *app, GFile *file) +static void oregano_application (GApplication *app, GFile *file) { - Schematic *schematic = NULL ; + Schematic *schematic = NULL; SchematicView *schematic_view = NULL; gchar *msg; Splash *splash = NULL; + GError *error = NULL; - if (!g_file_test (OREGANO_UIDIR "/splash.ui", - G_FILE_TEST_EXISTS)) { - msg = g_strdup_printf ( - _("You seem to be running Oregano without " - "having it installed properly on your system.\n\n" - "Please install Oregano and try again.")); - - oregano_error (msg); - g_free (msg); - return; - } - - // Keep non localized input for ngspice + // Keep non localized input for ngspice setlocale (LC_NUMERIC, "C"); + if (oregano.show_splash) { - splash = oregano_splash_new (); + splash = oregano_splash_new (&error); + if (error) { + msg = g_strdup_printf (_ ("Failed to spawn splash-screen \'%s\''. Code %i - %s"), + OREGANO_UIDIR "splash.ui", error->code, error->message); + oregano_error (msg); + g_free (msg); + g_clear_error (&error); + // non fatal issue + } } - //splash == NULL if showing splash is disabled + // 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 " - "of Oregano. Please check your installation.")); + oregano_error (_ ("Could not find a parts library.\n\n" + "Supposed to be in " OREGANO_LIBRARYDIR)); return; } @@ -177,31 +186,39 @@ oregano_application (GApplication *app, GFile *file) schematic = schematic_read (fname, &error); if (schematic) { schematic_view = schematic_view_new (schematic); - gtk_window_set_application (GTK_WINDOW (schematic_view_get_toplevel (schematic_view)), - GTK_APPLICATION (app)); gtk_widget_show_all (schematic_view_get_toplevel (schematic_view)); schematic_set_filename (schematic, fname); schematic_set_title (schematic, g_path_get_basename (fname)); } - } - else { + } else { schematic = schematic_new (); schematic_view = schematic_view_new (schematic); - gtk_window_set_application (GTK_WINDOW (schematic_view_get_toplevel (schematic_view)), - GTK_APPLICATION (app)); gtk_widget_show_all (schematic_view_get_toplevel (schematic_view)); } - g_signal_add_emission_hook ( - g_signal_lookup ("last_schematic_destroyed", TYPE_SCHEMATIC), - 0, - quit_hook, - NULL, - NULL); + 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")); + if (oregano.show_splash && splash) + oregano_splash_done (splash, _ ("Welcome to Oregano")); +} - cursors_shutdown (); +void oregano_deallocate_memory (void) +{ + GList *iter; + Library *l; + + g_object_unref (oregano.settings); + + // Free the memory used by the parts libraries + for (iter = oregano.libraries; iter; iter = iter->next) { + l = (Library *) iter->data; + g_free (l->name); + g_free (l->author); + g_free (l->version); + } + g_list_free_full (oregano.libraries, g_free); + + clipboard_empty (); } diff --git a/src/oregano.h b/src/oregano.h index a60b0d6..e143df3 100644 --- a/src/oregano.h +++ b/src/oregano.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __MAIN_H #define __MAIN_H @@ -38,34 +38,32 @@ G_BEGIN_DECLS -#define OREGANO_TYPE_APPLICATION (oregano_get_type ()) -#define OREGANO_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_APPLICATION, Oregano)) -#define OREGANO_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), OREGANO_TYPE_APPLICATION, OreganoClass)) -#define OREGANO_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_APPLICATION)) -#define OREGANO_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), OREGANO_TYPE_APPLICATION)) -#define OREGANO_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), OREGANO_TYPE_APPLICATION, OreganoClass)) - -typedef struct { +#define OREGANO_TYPE_APPLICATION (oregano_get_type ()) +#define OREGANO_APPLICATION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), OREGANO_TYPE_APPLICATION, Oregano)) +#define OREGANO_APPLICATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), OREGANO_TYPE_APPLICATION, OreganoClass)) +#define OREGANO_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OREGANO_TYPE_APPLICATION)) +#define OREGANO_IS_APPLICATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), OREGANO_TYPE_APPLICATION)) +#define OREGANO_APPLICATION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), OREGANO_TYPE_APPLICATION, OreganoClass)) + +typedef struct +{ GtkApplication parent_instance; } Oregano; -typedef struct { +typedef struct +{ GtkApplicationClass parent_class; } OreganoClass; -typedef struct { +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; - GSettings *settings; gint engine; gboolean compress_files; @@ -76,7 +74,6 @@ typedef struct { extern OreganoApp oregano; extern int oregano_debugging; - Oregano *oregano_new (void); G_END_DECLS diff --git a/src/part-browser.c b/src/part-browser.c index b2b4ed2..a77dd26 100644 --- a/src/part-browser.c +++ b/src/part-browser.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> @@ -42,30 +44,32 @@ #include "part-browser.h" #include "part-item.h" #include "dialogs.h" -#include "sheet-pos.h" +#include "coords.h" #include "sheet.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" typedef struct _Browser Browser; -struct _Browser { - SchematicView *schematic_view; - GtkWidget *viewport; - GtkWidget *list; - GtkWidget *canvas; - GooCanvasText *description; - GooCanvasGroup *preview; - Library *library; - gboolean hidden; +struct _Browser +{ + SchematicView *schematic_view; + GtkWidget *viewport; + GtkWidget *list; + GtkWidget *canvas; + GooCanvasText *description; + GooCanvasGroup *preview; + Library *library; + gboolean hidden; // Models for the TreeView - GtkTreeModel *real_model; - GtkTreeModel *sort_model; - GtkTreeModel *filter_model; - GtkEntry *filter_entry; - gint filter_len; + GtkTreeModel *real_model; + GtkTreeModel *sort_model; + GtkTreeModel *filter_model; + GtkEntry *filter_entry; + gint filter_len; }; -typedef struct { +typedef struct +{ Browser *br; SchematicView *schematic_view; char *library_name; @@ -78,16 +82,15 @@ typedef struct { 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 int part_selected (GtkTreeView *list, GtkTreePath *arg1, GtkTreeViewColumn *col, + Browser *br); static void part_browser_setup_libs (Browser *br, GtkBuilder *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 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) +static gboolean part_list_filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { char *part_name; const char *s; @@ -97,8 +100,10 @@ part_list_filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) s = gtk_entry_get_text (GTK_ENTRY (br->filter_entry)); // Without filter, the part is shown - if (s == NULL) return TRUE; - if (br->filter_len == 0) return TRUE; + if (s == NULL) + return TRUE; + if (br->filter_len == 0) + return TRUE; gtk_tree_model_get (model, iter, 0, &part_name, -1); @@ -118,8 +123,7 @@ part_list_filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) return FALSE; } -static int -part_search_change (GtkWidget *widget, Browser *br) +static int part_search_change (GtkWidget *widget, Browser *br) { const char *s = gtk_entry_get_text (GTK_ENTRY (widget)); @@ -132,8 +136,7 @@ part_search_change (GtkWidget *widget, Browser *br) return TRUE; } -static void -part_search_activate (GtkWidget *widget, Browser *br) +static void part_search_activate (GtkWidget *widget, Browser *br) { GtkTreeSelection *selection; GtkTreePath *path; @@ -149,12 +152,11 @@ part_search_activate (GtkWidget *widget, Browser *br) } } -static void -place_cmd (GtkWidget *widget, Browser *br) +static void place_cmd (GtkWidget *widget, Browser *br) { LibraryPart *library_part; char *part_name; - SheetPos pos; + Coords pos; Sheet *sheet; Part *part; GtkTreeModel *model; @@ -177,24 +179,20 @@ place_cmd (GtkWidget *widget, Browser *br) 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")); + oregano_error (_ ("Unable to load required part")); return; } pos.x = pos.y = 0; item_data_set_pos (ITEM_DATA (part), &pos); + sheet_connect_part_item_to_floating_group (sheet, (gpointer)br->schematic_view); sheet_select_all (sheet, FALSE); sheet_clear_ghosts (sheet); sheet_add_ghost_item (sheet, ITEM_DATA (part)); - - sheet_connect_part_item_to_floating_group (sheet, - (gpointer) br->schematic_view); } -static int -part_selected (GtkTreeView *list, GtkTreePath *arg1, GtkTreeViewColumn *col, - Browser *br) +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); @@ -202,15 +200,14 @@ part_selected (GtkTreeView *list, GtkTreePath *arg1, GtkTreeViewColumn *col, return FALSE; } -static void -update_preview (Browser *br) +static void update_preview (Browser *br) { LibraryPart *library_part; gdouble new_x, new_y, x1, y1, x2, y2; gdouble text_width; gdouble width, height; GooCanvasBounds bounds; - gdouble scale; + gdouble scale; cairo_matrix_t transf, affine; gchar *part_name; char *description; @@ -222,6 +219,9 @@ update_preview (Browser *br) selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (br->list)); + if (!GTK_IS_TREE_SELECTION (selection)) + return; + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { return; } @@ -229,35 +229,28 @@ update_preview (Browser *br) gtk_tree_model_get (model, &iter, 0, &part_name, -1); library_part = library_get_part (br->library, part_name); - + // If there is already a preview part-item, destroy its group and create a // new one. if (br->preview != NULL) { goo_canvas_item_remove (GOO_CANVAS_ITEM (br->preview)); } - - br->preview = GOO_CANVAS_GROUP (goo_canvas_group_new ( - goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), - NULL)); + + br->preview = GOO_CANVAS_GROUP ( + goo_canvas_group_new (goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), NULL)); goo_canvas_set_bounds (GOO_CANVAS (br->canvas), 0.0, 0.0, 250.0, 110.0); - g_object_get (br->preview, - "width", &width, - "height", &height, - NULL); - + g_object_get (br->preview, "width", &width, "height", &height, NULL); + if (!library_part) return; part_item_create_canvas_items_for_preview (br->preview, library_part); // Unconstraint the canvas width & height to adjust for the part description - g_object_set (br->preview, - "width", -1.0, - "height", -1.0, - NULL); - + g_object_set (br->preview, "width", -1.0, "height", -1.0, NULL); + // Get the coordonates */ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (br->preview), &bounds); x1 = bounds.x1; @@ -265,66 +258,52 @@ update_preview (Browser *br) y1 = bounds.y1; y2 = bounds.y2; - // Translate in such a way that the canvas centre remains in (0, 0) + // Translate in such a way that the canvas centre remains in (0, 0) cairo_matrix_init_translate (&transf, -(x2 + x1) / 2.0f + PREVIEW_WIDTH / 2, -(y2 + y1) / 2.0f + PREVIEW_HEIGHT / 2); - // Compute the scale of the widget + // 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 + } else scale = 5; cairo_matrix_init_scale (&affine, scale, scale); cairo_matrix_multiply (&transf, &transf, &affine); - // Apply the transformation + // Apply the transformation goo_canvas_item_set_transform (GOO_CANVAS_ITEM (br->preview), &transf); - - // Compute the motion to centre the Preview widget + + // Compute the motion to centre the Preview widget new_x = 100 + (PREVIEW_WIDTH - x1 - x2) / 2; new_y = (PREVIEW_HEIGHT - y1 - y2) / 2 - 10; - // Apply the transformation - if (scale > 5.0) scale = 3.0; - goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->preview), - new_x, - new_y, - scale, - 0.0); - + // Apply the transformation + if (scale > 5.0) + scale = 3.0; + goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->preview), new_x, new_y, scale, 0.0); + description = g_strdup (library_part->description); wrap_string (description, 20); - g_object_set (br->description, - "text", description, - NULL); + g_object_set (br->description, "text", description, NULL); g_free (description); - g_object_get (G_OBJECT (br->description), - "width", &text_width, - NULL); - - goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->description), - 50.0, - -20.0, - 1.0, - 0.0); + g_object_get (G_OBJECT (br->description), "width", &text_width, NULL); + + goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->description), 50.0, -20.0, 1.0, 0.0); g_free (part_name); } -static gboolean -select_row (GtkTreeView *list, Browser *br) +static gboolean select_row (GtkTreeView *list, Browser *br) { update_preview (br); return FALSE; } -static void -add_part (gpointer key, LibraryPart *part, Browser *br) +static void add_part (gpointer key, LibraryPart *part, Browser *br) { GtkTreeIter iter; GtkListStore *model; @@ -340,50 +319,45 @@ add_part (gpointer key, LibraryPart *part, Browser *br) } // Read the available parts from the library and put them in the browser clist. -static void -update_list (Browser *br) +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); + 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) +void part_browser_toggle_visibility (SchematicView *schematic_view) { Browser *browser = schematic_view_get_browser (schematic_view); - + if (browser == NULL) { - // part_browser_create (schematic_view); - } - else { + // part_browser_create (schematic_view); + g_warning ("This should never happen. No Part Browser."); + } else { browser->hidden = !(browser->hidden); if (browser->hidden) { gtk_widget_hide (browser->viewport); - } - else { + } else { gtk_widget_show_all (browser->viewport); } } } -void -part_browser_dnd (GtkSelectionData *selection_data, int x, int y) +void part_browser_dnd (GtkSelectionData *selection_data, int x, int y) { LibraryPart *library_part; - SheetPos pos; + Coords pos; DndData *data; Sheet *sheet; Part *part; - data = (DndData *) (gtk_selection_data_get_data (selection_data)); + data = (DndData *)(gtk_selection_data_get_data (selection_data)); g_return_if_fail (data != NULL); @@ -397,22 +371,20 @@ part_browser_dnd (GtkSelectionData *selection_data, int x, int 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")); + oregano_error (_ ("Unable to load required part")); return; } item_data_set_pos (ITEM_DATA (part), &pos); + sheet_connect_part_item_to_floating_group (sheet, (gpointer)data->schematic_view); sheet_select_all (sheet, FALSE); sheet_clear_ghosts (schematic_view_get_sheet (data->schematic_view)); sheet_add_ghost_item (sheet, ITEM_DATA (part)); - - sheet_connect_part_item_to_floating_group (sheet, (gpointer) data->schematic_view); } -static void -drag_data_get (GtkWidget *widget, GdkDragContext *context, - GtkSelectionData *selection_data, guint info, guint time, Browser *br) +static void drag_data_get (GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, guint time, Browser *br) { DndData *data; GtkTreeSelection *selection; @@ -429,12 +401,12 @@ drag_data_get (GtkWidget *widget, GdkDragContext *context, data->library_name = br->library->name; - // Get the current selected row + // 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 + // No selection, Action canceled return; } @@ -442,11 +414,8 @@ drag_data_get (GtkWidget *widget, GdkDragContext *context, data->part_name = part_name; - gtk_selection_data_set (selection_data, - gtk_selection_data_get_target (selection_data), - 8, - (gpointer) data, - sizeof (DndData)); + gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, + (gpointer)data, sizeof(DndData)); // gtk_selection_data_set copies the information so we can free it now. g_free (data); @@ -455,28 +424,24 @@ drag_data_get (GtkWidget *widget, GdkDragContext *context, // part_browser_create // // Creates a new part browser. This is only called once per schematic window. -GtkWidget * -part_browser_create (SchematicView *schematic_view) +GtkWidget *part_browser_create (SchematicView *schematic_view) { Browser *br; - GtkBuilder *gui; - GError *perror = NULL; - char *msg; + GtkBuilder *builder; + GError *e = NULL; GtkWidget *w, *view; GtkCellRenderer *cell_text; GtkTreeViewColumn *cell_column; - static GtkTargetEntry dnd_types[] = - { { "x-application/oregano-part", 0, DRAG_PART_INFO } }; + static GtkTargetEntry dnd_types[] = {{"x-application/oregano-part", 0, DRAG_PART_INFO}}; - static int dnd_num_types = sizeof (dnd_types) / sizeof (dnd_types[0]); + static int dnd_num_types = sizeof(dnd_types) / sizeof(dnd_types[0]); GtkTreePath *path; - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create part browser")); + if ((builder = gtk_builder_new ()) == NULL) { + oregano_error (_ ("Could not create part browser")); return NULL; - } - else - gtk_builder_set_translation_domain (gui, NULL); + } else + gtk_builder_set_translation_domain (builder, NULL); br = g_new0 (Browser, 1); br->preview = NULL; @@ -485,102 +450,76 @@ part_browser_create (SchematicView *schematic_view) schematic_view_set_browser (schematic_view, br); - if (!g_file_test (OREGANO_UIDIR "/part-browser.ui", - 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_UIDIR "/part-browser.ui"); - - oregano_error_with_title (_("Could not create part browser"), msg); - g_free (msg); + if (gtk_builder_add_from_file (builder, OREGANO_UIDIR "/part-browser.ui", &e) <= 0) { + oregano_error_with_title (_ ("Could not create part browser"), e->message); + g_error_free (e); return NULL; } - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/part-browser.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create part browser"), msg); - g_error_free (perror); - return NULL; - } + view = GTK_WIDGET (gtk_builder_get_object (builder, "viewport1")); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); - view = GTK_WIDGET (gtk_builder_get_object (gui, "viewport1")); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (view), 115); - gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (view), - 115); - w = goo_canvas_new (); gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (w)); - + br->canvas = w; - g_signal_connect (w, "realize", (GCallback) preview_realized, br); + g_signal_connect (w, "realize", (GCallback)preview_realized, br); - //gtk_widget_set_size_request (w, PREVIEW_WIDTH, + // gtk_widget_set_size_request (w, PREVIEW_WIDTH, // PREVIEW_HEIGHT + PREVIEW_TEXT_HEIGHT); goo_canvas_set_bounds (GOO_CANVAS (w), 0, 0, PREVIEW_WIDTH, - (PREVIEW_HEIGHT + PREVIEW_TEXT_HEIGHT)); + (PREVIEW_HEIGHT + PREVIEW_TEXT_HEIGHT)); br->description = GOO_CANVAS_TEXT (goo_canvas_text_new ( - goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), - "", 0.0, PREVIEW_HEIGHT - 9.0, 100.0, GOO_CANVAS_ANCHOR_NORTH_WEST, - "font", "sans 9", - NULL)); + goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), "", 0.0, PREVIEW_HEIGHT - 9.0, 100.0, + GOO_CANVAS_ANCHOR_NORTH_WEST, "font", "sans 9", NULL)); - // Set up dnd. - g_signal_connect (G_OBJECT (br->canvas), "drag_data_get", - G_CALLBACK (drag_data_get), br); + // Set up dnd. + g_signal_connect (G_OBJECT (br->canvas), "drag_data_get", G_CALLBACK (drag_data_get), br); - gtk_drag_source_set (br->canvas, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, - dnd_types, dnd_num_types, GDK_ACTION_MOVE); + 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 (gtk_builder_get_object (gui, "part_search")); + br->filter_entry = GTK_ENTRY (gtk_builder_get_object (builder, "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); + 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 = GTK_WIDGET (gtk_builder_get_object (gui, "place_button")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (place_cmd), br); + // Buttons. + w = GTK_WIDGET (gtk_builder_get_object (builder, "place_button")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (place_cmd), br); - // Update the libraries option menu + // Update the libraries option menu br->library = g_list_nth_data (oregano.libraries, 0); - part_browser_setup_libs (br, gui); + part_browser_setup_libs (br, builder); - // Parts list. - w = GTK_WIDGET (gtk_builder_get_object (gui, "parts_list")); + // Parts list. + w = GTK_WIDGET (gtk_builder_get_object (builder, "parts_list")); br->list = w; - // Create the List Model for TreeView, this is a Real model + // Create the List Model 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); + 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)); + // 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); + 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 + // 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); + 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 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 @@ -590,19 +529,15 @@ part_browser_create (SchematicView *schematic_view) update_list (br); // Set up TreeView dnd. - g_signal_connect (G_OBJECT (w), "drag_data_get", - G_CALLBACK (drag_data_get), br); + g_signal_connect (G_OBJECT (w), "drag_data_get", G_CALLBACK (drag_data_get), br); - gtk_drag_source_set (w, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, - dnd_types, dnd_num_types, GDK_ACTION_MOVE); + 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); + 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 = GTK_WIDGET (gtk_builder_get_object (gui, - "part_browser_vbox")); + br->viewport = GTK_WIDGET (gtk_builder_get_object (builder, "part_browser_vbox")); path = gtk_tree_path_new_first (); gtk_tree_view_set_cursor (GTK_TREE_VIEW (w), path, NULL, FALSE); @@ -612,65 +547,59 @@ part_browser_create (SchematicView *schematic_view) return br->viewport; } -static void -part_browser_setup_libs (Browser *br, GtkBuilder *gui) { +static void part_browser_setup_libs (Browser *br, GtkBuilder *builder) +{ GtkWidget *combo_box, *w; GList *libs; gboolean first = FALSE; - w = GTK_WIDGET (gtk_builder_get_object (gui, "library_optionmenu")); + w = GTK_WIDGET (gtk_builder_get_object (builder, "library_optionmenu")); gtk_widget_destroy (w); - w = GTK_WIDGET (gtk_builder_get_object (gui, "table1")); + w = GTK_WIDGET (gtk_builder_get_object (builder, "grid1")); combo_box = gtk_combo_box_text_new (); - gtk_table_attach (GTK_TABLE (w), combo_box, 1, 2, 0, 1, - GTK_EXPAND | GTK_FILL, - GTK_SHRINK, - 0, 0); + gtk_grid_attach (GTK_GRID (w), combo_box, 1, 0, 1, 1); libs = oregano.libraries; while (libs) { gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), - ((Library *)libs->data)->name); + ((Library *)libs->data)->name); libs = libs->next; if (!first) { - gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box),0); + 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); + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (library_switch_cb), br); } -static void -library_switch_cb (GtkWidget *combo_box, Browser *br) +static void library_switch_cb (GtkWidget *combo_box, Browser *br) { GtkTreePath *path; GList *libs = oregano.libraries; - br->library = (Library *) g_list_nth_data (libs, - gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box))); + br->library = + (Library *)g_list_nth_data (libs, gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box))); update_list (br); - + path = gtk_tree_path_new_first (); gtk_tree_view_set_cursor (GTK_TREE_VIEW (br->list), path, NULL, FALSE); gtk_tree_path_free (path); } -static void -wrap_string (char* str, int width) +static void wrap_string (char *str, int width) { - const int minl = width/2; + const int minl = width / 2; char *lnbeg, *sppos, *ptr; int te = 0; g_return_if_fail (str != NULL); - lnbeg= sppos = ptr = str; + lnbeg = sppos = ptr = str; while (*ptr) { if (*ptr == '\t') @@ -685,7 +614,7 @@ wrap_string (char* str, int width) te = 0; } - if (*ptr=='\n') { + if (*ptr == '\n') { lnbeg = ptr; te = 0; } @@ -693,18 +622,4 @@ wrap_string (char* str, int width) } } -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); -} +static void preview_realized (GtkWidget *widget, Browser *br) { update_preview (br); } diff --git a/src/part-browser.h b/src/part-browser.h index 6f7c232..6fa3ec7 100644 --- a/src/part-browser.h +++ b/src/part-browser.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_BROWSER_H @@ -37,7 +37,7 @@ #include "schematic.h" -void part_browser_toggle_show (SchematicView *schematic_view); +void part_browser_toggle_visibility (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); diff --git a/src/pixmaps/Makefile.am b/src/pixmaps/Makefile.am deleted file mode 100644 index 1e94d03..0000000 --- a/src/pixmaps/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -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 @@ -4,15 +4,19 @@ * * Authors: * Richard Hult <rhult@hem.passagen.se> - * Ricardo Markiewicz <rmarkie@fi.uba.ar> - * Andres de Barbara <adebarbara@fi.uba.ar> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * - * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2006 Ricardo Markiewicz - * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> @@ -51,64 +55,55 @@ #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)) +#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)) #define PLOT_AXIS_COLOR "black" #define PLOT_CURVE_COLOR "medium sea green" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" static guint next_color = 0; static guint next_pulse = 0; static guint n_curve_colors = 7; -static char *plot_curve_colors[] = { - "white", - "green", - "red", - "yellow", - "cyan", - "pink", - "orange1", - 0 -}; - -typedef struct { - GtkWidget *window; - GtkWidget *canvas; - GtkWidget *coord; // shows the coordinates of the mouse - GtkWidget *combo_box; - - 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; - gdouble plot_max; - gdouble x_min; - gdouble 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; +static char *plot_curve_colors[] = {"white", "green", "red", "yellow", + "cyan", "pink", "orange1", 0}; + +typedef struct +{ + GtkWidget *window; + GtkWidget *canvas; + GtkWidget *coord; // shows the coordinates of the mouse + GtkWidget *combo_box; + + 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; + gdouble plot_max; + gdouble x_min; + gdouble 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; static GtkWidget *plot_window_create (Plot *plot); @@ -118,49 +113,43 @@ static void plot_canvas_movement (GtkWidget *, GdkEventMotion *, Plot *); static void add_function (GtkMenuItem *menuitem, Plot *plot); static void close_window (GtkMenuItem *menuitem, Plot *plot); -static gchar * -get_variable_units (gchar *str) +static gchar *get_variable_units (gchar *str) { -gchar *tmp; + gchar *tmp; if (str == NULL) return g_strdup ("##"); - if (!strcmp (str, _("voltage"))) { + if (!strcmp (str, _ ("voltage"))) { tmp = g_strdup ("V"); - } - else if (!strcmp (str, "db") ) { + } else if (!strcmp (str, "db")) { tmp = g_strdup ("db"); - } - else if (!strcmp (str, _("time")) ) { + } else if (!strcmp (str, _ ("time"))) { tmp = g_strdup ("s"); - } - else if (!strcmp (str, _("frequency")) ) { + } else if (!strcmp (str, _ ("frequency"))) { tmp = g_strdup ("Hz"); - } - else if (!strcmp (str, _("current")) ) { + } else if (!strcmp (str, _ ("current"))) { tmp = g_strdup ("A"); - } - else { + } else if (!strcmp (str, _ ("psd"))) { + tmp = g_strdup ("V^2 / Hz"); + } else { tmp = g_strdup ("##"); } return tmp; } -static gint -delete_event_cb (GtkWidget *widget, GdkEvent *event, Plot *plot) +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->ytitle); g_free (plot); plot = NULL; return FALSE; } -// Call this to close the plot window -static void -destroy_window (GtkWidget *widget, Plot *plot) +// Call this to close the plot window +static void destroy_window (GtkWidget *widget, Plot *plot) { gtk_widget_destroy (plot->canvas); gtk_widget_destroy (plot->coord); @@ -168,43 +157,39 @@ destroy_window (GtkWidget *widget, Plot *plot) gtk_widget_destroy (plot->window); g_object_unref (plot->sim); plot->window = NULL; - if (plot->title) g_free (plot->title); + g_free (plot->title); g_free (plot); plot = NULL; } -static void -on_plot_selected (GtkCellRendererToggle *cell_renderer, gchar *path, Plot *plot) +static void on_plot_selected (GtkCellRendererToggle *cell_renderer, gchar *path, Plot *plot) { GPlotFunction *f; GtkTreeIter iter; GtkTreeModel *model; GtkTreeView *treeview; - gboolean activo; + gboolean visible = FALSE; - treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT (plot->window), - "clist")); + 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)) + 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); + gtk_tree_model_get (model, &iter, 0, &visible, 4, &f, -1); + visible = !visible; + gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 0, visible, -1); - g_object_set (G_OBJECT (f), "visible", activo, NULL); + g_object_set (G_OBJECT (f), "visible", visible, NULL); gtk_widget_queue_draw (plot->plot); } -static GPlotFunction* -create_plot_function_from_simulation_data (guint i, SimulationData *current) +static GPlotFunction *create_plot_function_from_simulation_data (guint i, SimulationData *current) { GPlotFunction *f; double *X; double *Y; - double data; guint len, j; gdouble width = 1; GraphicType graphic_type = FUNCTIONAL_CURVE; @@ -213,40 +198,33 @@ create_plot_function_from_simulation_data (guint i, SimulationData *current) 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); - X[j] = data; + X[j] = g_array_index (current->data[0], double, j); } - if (current->type == FOURIER) { + if (current->type == ANALYSIS_TYPE_FOURIER) { graphic_type = FREQUENCY_PULSE; next_pulse++; width = 5.0; shift_step = X[1] / 20; - NG_DEBUG (g_strdup_printf ("shift_step = %lf\n", shift_step)); - } - else { + NG_DEBUG ("shift_step = %lf\n", shift_step); + } else { next_pulse = 0; width = 1.0; } f = g_plot_lines_new (X, Y, len); - g_object_set (G_OBJECT (f), "color", - plot_curve_colors[(next_color++)%n_curve_colors], NULL); - g_object_set (G_OBJECT (f), "graph-type", - graphic_type, NULL); - g_object_set (G_OBJECT (f), "shift", - shift_step*next_pulse, NULL); - g_object_set (G_OBJECT (f), "width", - width, NULL); - NG_DEBUG (g_strdup_printf ("plot: create_plot_function_from_simulation_data: shift = %lf\n", 0.1*next_pulse)); + g_object_set (G_OBJECT (f), "color", plot_curve_colors[(next_color++) % n_curve_colors], NULL); + g_object_set (G_OBJECT (f), "graph-type", graphic_type, NULL); + g_object_set (G_OBJECT (f), "shift", shift_step * next_pulse, NULL); + g_object_set (G_OBJECT (f), "width", width, NULL); + NG_DEBUG ("plot: create_plot_function_from_simulation_data: shift = %lf\n", 0.1 * next_pulse); return f; } -static void -analysis_selected (GtkWidget *combo_box, Plot *plot) +static void analysis_selected (GtkWidget *combo_box, Plot *plot) { int i; const gchar *ca; @@ -259,7 +237,7 @@ analysis_selected (GtkWidget *combo_box, Plot *plot) list = GTK_TREE_VIEW (g_object_get_data (G_OBJECT (plot->window), "clist")); if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box)) == -1) { // Simulation failed? - g_warning (_("The simulation produced no data!!\n")); + g_warning (_ ("The simulation produced no data!!\n")); return; } ca = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo_box)); @@ -268,15 +246,18 @@ analysis_selected (GtkWidget *combo_box, Plot *plot) 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))) { + gchar *analysis_name = oregano_engine_get_analysis_name (sdat); + if (!strcmp (ca, analysis_name)) { plot->current = sdat; + g_free(analysis_name); break; } + g_free(analysis_name); } if (plot->current == NULL) { // Simulation failed? - g_warning (_("The simulation produced no data!!\n")); + g_warning (_ ("The simulation produced no data!!\n")); return; } @@ -286,8 +267,10 @@ analysis_selected (GtkWidget *combo_box, Plot *plot) 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)); + gchar *analysis_name = oregano_engine_get_analysis_name (plot->current); + plot->title = + g_strdup_printf (_ ("Plot - %s"), analysis_name); + g_free(analysis_name); // Set the variable names in the list model = gtk_tree_view_get_model (GTK_TREE_VIEW (list)); @@ -295,12 +278,12 @@ analysis_selected (GtkWidget *combo_box, Plot *plot) // 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_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); + 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)); @@ -308,27 +291,10 @@ analysis_selected (GtkWidget *combo_box, 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 { + if (plot->current->type != ANALYSIS_TYPE_DC_TRANSFER) { + //FIXME extra axis scaling for current/voltage + //FIXME or extra plot window for current/voltage + //FIXME or extra analysis for current/voltage gchar *color; f = create_plot_function_from_simulation_data (i, plot->current); @@ -336,40 +302,43 @@ analysis_selected (GtkWidget *combo_box, Plot *plot) 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_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) +static void plot_canvas_movement (GtkWidget *w, GdkEventMotion *event, Plot *plot) { - gchar *coord; - gdouble x,y; + gchar *coordstr; + 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); + coordstr = g_strdup_printf ("(%g, %g)", x, y); - gtk_entry_set_text (GTK_ENTRY (plot->coord), coord); + gtk_entry_set_text (GTK_ENTRY (plot->coord), coordstr); - g_free (coord); + g_free (coordstr); } -static GtkWidget * -plot_window_create (Plot *plot) +static GtkWidget *plot_window_create (Plot *plot) { GtkTreeView *list; GtkTreeStore *tree_model; @@ -381,44 +350,31 @@ plot_window_create (Plot *plot) GtkBuilder *gui; GError *perror = NULL; gchar *msg; - GtkAccelGroup *accel_group; + GtkAccelGroup *accel_group; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW (window), _("Oregano - Plot")); - gtk_container_set_border_width (GTK_CONTAINER (window), 0); - g_signal_connect (G_OBJECT (window), "delete-event", - G_CALLBACK (delete_event_cb), plot); + gtk_window_set_title (GTK_WINDOW (window), _ ("Oregano - Plot")); + gtk_container_set_border_width (GTK_CONTAINER (window), 0); + g_signal_connect (G_OBJECT (window), "delete-event", G_CALLBACK (delete_event_cb), plot); accel_group = gtk_accel_group_new (); - gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); + gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create plot window.")); - return NULL; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/plot-window.ui",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_UIDIR "/plot-window.ui"); - oregano_error_with_title (_("Could not create plot window."), msg); - g_free (msg); + oregano_error (_ ("Could not create plot window.")); return NULL; } + gtk_builder_set_translation_domain (gui, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/plot-window.ui", - &perror) <= 0) { + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/plot-window.ui", &perror) <= 0) { msg = perror->message; - oregano_error_with_title (_("Could not create plot window."), msg); + oregano_error_with_title (_ ("Could not create plot window."), msg); g_error_free (perror); return NULL; } - outer_table = GTK_WIDGET (gtk_builder_get_object (gui, "plot_table")); - plot_scrolled = plot->canvas = GTK_WIDGET (gtk_builder_get_object (gui, - "plot_scrolled")); - plot->coord = GTK_WIDGET (gtk_builder_get_object (gui, "pos_label")); + outer_table = GTK_WIDGET (gtk_builder_get_object (gui, "plot_grid")); + plot_scrolled = plot->canvas = GTK_WIDGET (gtk_builder_get_object (gui, "plot_scrolled")); + plot->coord = GTK_WIDGET (gtk_builder_get_object (gui, "pos_label")); plot->plot = g_plot_new (); @@ -426,62 +382,59 @@ plot_window_create (Plot *plot) gtk_widget_set_size_request (plot_scrolled, 600, 400); g_signal_connect (G_OBJECT (plot->plot), "motion_notify_event", - G_CALLBACK (plot_canvas_movement), plot); + G_CALLBACK (plot_canvas_movement), plot); - // Creation of the menubar + // Creation of the menubar vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - menubar = gtk_menu_bar_new (); + menubar = gtk_menu_bar_new (); gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0); - gtk_widget_show (menubar); + gtk_widget_show (menubar); // Creation of the menu menu = gtk_menu_new (); - menuitem = gtk_menu_item_new_with_label (_("Add Function")); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); - g_signal_connect (menuitem, "activate", G_CALLBACK (add_function), - plot); + //add Add Function menuitem + menuitem = gtk_menu_item_new_with_label (_ ("Add Function")); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + g_signal_connect (menuitem, "activate", G_CALLBACK (add_function), plot); gtk_widget_show (menuitem); + //add separator menuitem = gtk_separator_menu_item_new (); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show (menuitem); - menuitem = gtk_menu_item_new_with_label (_("Close")); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); - g_signal_connect (menuitem, "activate", G_CALLBACK (close_window), - plot); + menuitem = gtk_menu_item_new_with_label (_ ("Close")); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + g_signal_connect (menuitem, "activate", G_CALLBACK (close_window), plot); gtk_widget_show (menuitem); // Installation of the menu in the menubar - menuitem = gtk_menu_item_new_with_label (_("File")); - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu); - gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem); - gtk_widget_show (menuitem); - + menuitem = gtk_menu_item_new_with_label (_ ("File")); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu); + gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem); + gtk_widget_show (menuitem); + g_object_ref (outer_table); gtk_widget_unparent (outer_table); gtk_container_add (GTK_CONTAINER (vbox), outer_table); gtk_widget_show (vbox); - + g_object_ref (vbox); gtk_widget_unparent (vbox); gtk_container_add (GTK_CONTAINER (window), vbox); - + button = GTK_WIDGET (gtk_builder_get_object (gui, "close_button")); - g_signal_connect (G_OBJECT (button), "clicked", - G_CALLBACK (destroy_window), plot); + g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (destroy_window), plot); list = GTK_TREE_VIEW (gtk_builder_get_object (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); + 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); + 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); + 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, FALSE); @@ -502,14 +455,10 @@ plot_window_create (Plot *plot) w = GTK_WIDGET (gtk_builder_get_object (gui, "analysis_combobox")); gtk_widget_destroy (w); - w = GTK_WIDGET (gtk_builder_get_object (gui, "table1")); + w = GTK_WIDGET (gtk_builder_get_object (gui, "grid1")); combo_box = gtk_combo_box_text_new_with_entry (); - - gtk_table_attach (GTK_TABLE (w),combo_box,0,1,0,1, - GTK_SHRINK, - //GTK_EXPAND | GTK_FILL, - GTK_SHRINK, - 0, 0); + + gtk_grid_attach (GTK_GRID (w), combo_box, 0, 0, 1, 1); plot->combo_box = combo_box; plot->window = window; @@ -518,17 +467,32 @@ plot_window_create (Plot *plot) return window; } -int -plot_show (OreganoEngine *engine) +int plot_show (OreganoEngine *engine) { GList *analysis = NULL; - GList *combo_items = NULL; + GList *analysis_backup = NULL; Plot *plot; + gchar *str = NULL; gchar *s_current = NULL; SimulationData *first = NULL; + SimulationData *sdat = NULL; + gboolean abort = TRUE; g_return_val_if_fail (engine != NULL, FALSE); + // Get the analysis we have + analysis = analysis_backup = oregano_engine_get_results (engine); + + for (; analysis; analysis = analysis->next) { + sdat = SIM_DATA (analysis->data); + str = oregano_engine_get_analysis_name (sdat); + if (sdat->type != ANALYSIS_TYPE_OP_POINT) + abort = FALSE; + } + + if (abort) + return FALSE; + plot = g_new0 (Plot, 1); g_object_ref (engine); @@ -545,28 +509,25 @@ plot_show (OreganoEngine *engine) 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) { + for (; analysis_backup; analysis_backup = analysis_backup->next) { + sdat = SIM_DATA (analysis_backup->data); + str = oregano_engine_get_analysis_name (sdat); + if (sdat->type == ANALYSIS_TYPE_OP_POINT) { continue; } + if (s_current == NULL) { s_current = str; first = sdat; } - combo_items = g_list_append (combo_items, str); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (plot->combo_box), str); - gtk_combo_box_set_active (GTK_COMBO_BOX (plot->combo_box),0); + gtk_combo_box_set_active (GTK_COMBO_BOX (plot->combo_box), 0); } - - g_signal_connect (G_OBJECT (plot->combo_box), "changed", - G_CALLBACK (analysis_selected), plot); - plot->title = g_strdup_printf (_("Plot - %s"), s_current); + g_signal_connect (G_OBJECT (plot->combo_box), "changed", G_CALLBACK (analysis_selected), plot); + + 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] : "!!"); @@ -577,9 +538,8 @@ plot_show (OreganoEngine *engine) return TRUE; } -static GPlotFunction* -create_plot_function_from_data (SimulationFunction *func, - SimulationData *current) +static GPlotFunction *create_plot_function_from_data (SimulationFunction *func, + SimulationData *current) { GPlotFunction *f; double *X; @@ -595,55 +555,47 @@ create_plot_function_from_data (SimulationFunction *func, 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; + case FUNCTION_SUBTRACT: + 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; - } + case FUNCTION_DIVIDE: + 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); } - if (current->type == FOURIER) { + if (current->type == ANALYSIS_TYPE_FOURIER) { graphic_type = FREQUENCY_PULSE; next_pulse++; width = 5.0; - } - else { + } else { next_pulse = 0; width = 1.0; } f = g_plot_lines_new (X, Y, len); - g_object_set (G_OBJECT (f), - "color", plot_curve_colors[(next_color++)%n_curve_colors], - "graph-type", graphic_type, - "shift", 50.0*next_pulse, - "width", width, - NULL); + g_object_set (G_OBJECT (f), "color", plot_curve_colors[(next_color++) % n_curve_colors], + "graph-type", graphic_type, "shift", 50.0 * next_pulse, "width", width, NULL); return f; } -static void -close_window (GtkMenuItem *menuitem, Plot *plot) +static void close_window (GtkMenuItem *menuitem, Plot *plot) { destroy_window (GTK_WIDGET (menuitem), plot); } -static void -add_function (GtkMenuItem *menuitem, Plot *plot) +static void add_function (GtkMenuItem *menuitem, Plot *plot) { GtkTreeView *tree; GtkTreeModel *model; @@ -654,27 +606,27 @@ add_function (GtkMenuItem *menuitem, Plot *plot) plot_add_function_show (plot->sim, plot->current); - tree = GTK_TREE_VIEW (g_object_get_data(G_OBJECT (plot->window), "clist")); + 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); + 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) { + while (lst) { GPlotFunction *f; GtkTreeIter child; int i; gchar *str = NULL, *name1 = NULL, *name2 = NULL; 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]); @@ -684,12 +636,11 @@ add_function (GtkMenuItem *menuitem, Plot *plot) } } switch (func->type) { - case FUNCTION_MINUS: - str = g_strdup_printf ("%s - %s", name1, name2); + case FUNCTION_SUBTRACT: + str = g_strdup_printf ("%s - %s", name1, name2); break; - case FUNCTION_TRANSFER: - str = g_strdup_printf ("%s(%s, %s)", _("TRANSFER"), name1, - name2); + case FUNCTION_DIVIDE: + str = g_strdup_printf ("%s(%s, %s)", _ ("TRANSFER"), name1, name2); } f = create_plot_function_from_data (func, plot->current); @@ -698,8 +649,8 @@ add_function (GtkMenuItem *menuitem, Plot *plot) 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); + gtk_tree_store_set (GTK_TREE_STORE (model), &child, 0, TRUE, 1, str, 2, TRUE, 3, color, 4, + f, -1); lst = lst->next; } @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PLOT_H diff --git a/src/save-schematic.c b/src/save-schematic.c index 393c7b4..3253eff 100644 --- a/src/save-schematic.c +++ b/src/save-schematic.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,14 +30,14 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "xml-compat.h" #include "oregano.h" #include "schematic.h" -#include "sheet-pos.h" +#include "coords.h" #include "load-library.h" #include "part.h" #include "textbox.h" @@ -45,25 +49,24 @@ #include "save-schematic.h" #include "xml-helper.h" +#include "debug.h" -#define NG_DEBUG(s) if (1) g_print ("%s\n", s) - -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. +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) +static void write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm) { - xmlNodePtr sim_settings_node, child,analysis,options; + xmlNodePtr sim_settings_node, child, analysis, options; gchar *str; SimSettings *s; SimOption *so; @@ -71,17 +74,15 @@ write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm) s = schematic_get_sim_settings (sm); - sim_settings_node = xmlNewChild (cur, ctxt->ns, - BAD_CAST"simulation-settings", NULL); + 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) { + // 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; } @@ -110,20 +111,32 @@ write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm) str = "false"; child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step-enabled", BAD_CAST str); - if (sim_settings_get_trans_init_cond(s)) + 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 (sim_settings_get_trans_analyze_all(s)) + str = "true"; + else + str = "false"; + child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "analyze-all", 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") ); + BAD_CAST (sim_settings_get_ac (s) ? "true" : "false")); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "vout1", BAD_CAST sim_settings_get_ac_vout (s)); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "type", BAD_CAST sim_settings_get_ac_type (s)); str = g_strdup_printf ("%d", sim_settings_get_ac_npoints (s)); child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "npoints", BAD_CAST str); @@ -137,18 +150,20 @@ write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm) 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); + // 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") ); + 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)); + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "vsrc1", BAD_CAST sim_settings_get_dc_vsrc (s)); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "vout1", BAD_CAST sim_settings_get_dc_vout (s)); str = g_strdup_printf ("%g", sim_settings_get_dc_start (s)); child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start1", BAD_CAST str); @@ -161,68 +176,94 @@ write_xml_sim_settings (xmlNodePtr cur, parseXmlContext *ctxt, Schematic *sm) str = g_strdup_printf ("%g", sim_settings_get_dc_step (s)); child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "step1", BAD_CAST str); g_free (str); - - // Fourier analysis - analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "fourier", NULL); + + // Fourier analysis + analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "fourier", NULL); if (!analysis) { g_warning ("Failed during save of Fourier analysis settings.\n"); return; } child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "enabled", - BAD_CAST (sim_settings_get_fourier (s) ? "true" : "false") ); + BAD_CAST (sim_settings_get_fourier (s) ? "true" : "false")); - str = g_strdup_printf ("%d", sim_settings_get_fourier_frequency (s)); + str = g_strdup_printf ("%.3f", sim_settings_get_fourier_frequency (s)); child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "freq", BAD_CAST str); g_free (str); - + str = g_strdup_printf ("%s", sim_settings_get_fourier_vout (s)); child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "vout", BAD_CAST str); g_free (str); - // Save the options + // Noise analysis + analysis = xmlNewChild (sim_settings_node, ctxt->ns, BAD_CAST "noise", NULL); + if (!analysis) { + g_warning ("Failed during save of Noise analysis settings.\n"); + return; + } + child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "enabled", + BAD_CAST (sim_settings_get_noise (s) ? "true" : "false")); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "vsrc1", BAD_CAST sim_settings_get_noise_vsrc (s)); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "vout1", BAD_CAST sim_settings_get_noise_vout (s)); + + child = + xmlNewChild (analysis, ctxt->ns, BAD_CAST "type", BAD_CAST sim_settings_get_noise_type (s)); + + str = g_strdup_printf ("%d", sim_settings_get_noise_npoints (s)); + child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "npoints", BAD_CAST str); + g_free (str); + + str = g_strdup_printf ("%g", sim_settings_get_noise_start (s)); + child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "start", BAD_CAST str); + g_free (str); + + str = g_strdup_printf ("%g", sim_settings_get_noise_stop (s)); + child = xmlNewChild (analysis, ctxt->ns, BAD_CAST "stop", 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 (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); + 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) +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); + // 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. + // Store the name. xmlNewChild (node_property, ctxt->ns, BAD_CAST "name", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->name)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->name)); - // Store the value. + // Store the value. xmlNewChild (node_property, ctxt->ns, BAD_CAST "value", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->value)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST prop->value)); } -static void -write_xml_label (PartLabel *label, parseXmlContext *ctxt) +static void write_xml_label (PartLabel *label, parseXmlContext *ctxt) { xmlNodePtr node_label; gchar *str; @@ -234,101 +275,103 @@ write_xml_label (PartLabel *label, parseXmlContext *ctxt) return; } - // Store the name. + // Store the name. xmlNewChild (node_label, ctxt->ns, BAD_CAST "name", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST label->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)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST label->text)); - str = g_strdup_printf ("(%g %g)", label->pos.x, label->pos.y); + 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); } -static void -write_xml_part (Part *part, parseXmlContext *ctxt) +static void write_xml_part (Part *part, parseXmlContext *ctxt) { PartPriv *priv; xmlNodePtr node_part; gchar *str; - SheetPos pos; + Coords pos; + + g_return_if_fail (part != NULL); + g_return_if_fail (IS_PART (part)); priv = part->priv; - // Create a node for the part. + // 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); + str = g_strdup_printf ("%d", part_get_rotation (part)); xmlNewChild (node_part, ctxt->ns, BAD_CAST "rotation", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str)); + 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")); + 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")); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "vertical")); - // Store the name. + // Store the name. xmlNewChild (node_part, ctxt->ns, BAD_CAST "name", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->name)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->name)); - // Store the name of the library the part resides in. + // 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)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->library->name)); - // Which symbol to use. + // Which symbol to use. xmlNewChild (node_part, ctxt->ns, BAD_CAST "symbol", - xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->symbol_name)); + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->symbol_name)); - // Position. + // Position. item_data_get_pos (ITEM_DATA (part), &pos); str = g_strdup_printf ("(%g %g)", pos.x, pos.y); 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); + // 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); + } else { + g_slist_foreach (priv->properties, (GFunc)write_xml_property, ctxt); } - // Create a node for the labels. + // 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); } - else{ - g_slist_foreach (priv->labels, (GFunc) write_xml_label, ctxt); - } } -static void -write_xml_wire (Wire *wire, parseXmlContext *ctxt) +static gint cmp_nodes (gconstpointer a, gconstpointer b) +{ + return coords_compare (&(((const Node *)a)->key), &(((const Node *)b)->key)); +} + +static void write_xml_wire (Wire *wire, parseXmlContext *ctxt) { xmlNodePtr node_wire; gchar *str; - SheetPos start_pos, end_pos; + Coords start_pos, end_pos; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); - // Create a node for the 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"); @@ -338,26 +381,56 @@ write_xml_wire (Wire *wire, parseXmlContext *ctxt) 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); + Node *node; + Coords last, current, tmp; + GSList *iter, *copy; + + copy = g_slist_sort (g_slist_copy (wire_get_nodes (wire)), cmp_nodes); + current = last = start_pos; + + for (iter = copy; iter; iter = iter->next) { + node = iter->data; + if (node == NULL) { + g_warning ("Node of wire did not exist [%p].", node); + continue; + } + + tmp = node->key; + if (coords_equal (&tmp, &start_pos)) + continue; + if (coords_equal (&tmp, &end_pos)) + continue; + + last = current; + current = tmp; + + str = g_strdup_printf ("(%g %g)(%g %g)", last.x, last.y, current.x, current.y); + + xmlNewChild (node_wire, ctxt->ns, BAD_CAST "points", BAD_CAST str); + g_free (str); + } + last = current; + current = end_pos; + str = g_strdup_printf ("(%g %g)(%g %g)", last.x, last.y, current.x, current.y); + xmlNewChild (node_wire, ctxt->ns, BAD_CAST "points", BAD_CAST str); g_free (str); + + g_slist_free (copy); } -static void -write_xml_textbox (Textbox *textbox, parseXmlContext *ctxt) +static void write_xml_textbox (Textbox *textbox, parseXmlContext *ctxt) { xmlNodePtr node_textbox; gchar *str; - SheetPos pos; + Coords pos; g_return_if_fail (textbox != NULL); if (!IS_TEXTBOX (textbox)) return; // Create a node for the textbox. - node_textbox = xmlNewChild (ctxt->node_textboxes, ctxt->ns, - BAD_CAST "textbox", NULL); + 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; @@ -373,9 +446,8 @@ write_xml_textbox (Textbox *textbox, parseXmlContext *ctxt) 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) +// 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; @@ -384,51 +456,56 @@ write_xml_schematic (parseXmlContext *ctxt, Schematic *sm, GError **error) cur = xmlNewDocNode (ctxt->doc, ctxt->ns, BAD_CAST "schematic", NULL); if (cur == NULL) { - printf ("%s:%d NULL that shall be not NULL!\n", __FILE__, - __LINE__); + printf ("%s:%d NULL that shall be not 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); + ogo = xmlNewNs (cur, BAD_CAST "https://beerbach.me/project/oregano/ns/v1", BAD_CAST "ogo"); + xmlSetNs (cur, ogo); ctxt->ns = ogo; } - // General information about the Schematic. + // 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)); + 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)); + xmlNewChild (cur, ctxt->ns, BAD_CAST "title", + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str)); + g_free (str); + + str = g_strdup_printf ("%s", schematic_get_version (sm)); + xmlNewChild (cur, ctxt->ns, BAD_CAST "version", + 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)); + xmlNewChild (cur, ctxt->ns, BAD_CAST "comments", + xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str)); g_free (str); - // Grid. + // 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. + + // Simulation settings. write_xml_sim_settings (cur, ctxt, sm); - // Parts. + // Parts. ctxt->node_parts = xmlNewChild (cur, ctxt->ns, BAD_CAST "parts", NULL); - schematic_parts_foreach (sm, (gpointer) write_xml_part, ctxt); + schematic_parts_foreach (sm, (gpointer)write_xml_part, ctxt); - // Wires. + // Wires. ctxt->node_wires = xmlNewChild (cur, ctxt->ns, BAD_CAST "wires", NULL); - schematic_wires_foreach (sm, (gpointer) write_xml_wire, ctxt); + schematic_wires_foreach (sm, (gpointer)write_xml_wire, ctxt); - // Text boxes. + // Text boxes. ctxt->node_textboxes = xmlNewChild (cur, ctxt->ns, BAD_CAST "textboxes", NULL); - schematic_items_foreach (sm, (gpointer) write_xml_textbox, ctxt); + schematic_items_foreach (sm, (gpointer)write_xml_textbox, ctxt); return cur; } @@ -436,17 +513,16 @@ write_xml_schematic (parseXmlContext *ctxt, Schematic *sm, GError **error) // schematic_write_xml // // Save a Sheet to an XML file. -gint -schematic_write_xml (Schematic *sm, GError **error) +gboolean schematic_write_xml (Schematic *sm, GError **error) { int ret = -1; xmlDocPtr xml; parseXmlContext ctxt; - GError *internal_error = NULL; + GError *err = NULL; g_return_val_if_fail (sm != NULL, FALSE); - // Create the tree. + // Create the tree. xml = xmlNewDoc (BAD_CAST "1.0"); if (xml == NULL) { return FALSE; @@ -455,34 +531,33 @@ schematic_write_xml (Schematic *sm, GError **error) ctxt.ns = NULL; ctxt.doc = xml; - xmlDocSetRootElement (xml, write_xml_schematic (&ctxt, sm, &internal_error)); + xmlDocSetRootElement (xml, write_xml_schematic (&ctxt, sm, &err)); - if (internal_error) { - g_propagate_error (error, internal_error); + if (err) { + g_propagate_error (error, err); return FALSE; } - // Dump the tree. + // Dump the tree. xmlSetDocCompressMode (xml, oregano.compress_files ? 9 : 0); { - char *s =schematic_get_filename (sm); + char *s = schematic_get_filename (sm); if (s != NULL) { ret = xmlSaveFormatFile (s, xml, 1); - } - else { + } else { g_warning ("Schematic has no filename!!\n"); } } if (xml != NULL) { xmlFreeDoc (xml); - } - else { + } 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 index b65e66d..8748aa3 100644 --- a/src/save-schematic.h +++ b/src/save-schematic.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _SAVE_SCHEMATIC_H @@ -37,6 +37,6 @@ #include "schematic.h" -gint schematic_write_xml (Schematic *sm, GError **error); +gboolean schematic_write_xml (Schematic *sm, GError **error); #endif diff --git a/src/schematic-view-menu.h b/src/schematic-view-menu.h index e13d1f7..7855dda 100644 --- a/src/schematic-view-menu.h +++ b/src/schematic-view-menu.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,165 +28,212 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _SCHEMATIC_VIEW_MENU_ #define _SCHEMATIC_VIEW_MENU_ + +#include "sim-settings-gui.h" + +// TODO: Create only two entries instead of four for stretching the schematic horizontally +// or vertically (needs proper icons not provided by Gtk). 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}, - {"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)}, - {"PrintProperties", NULL, N_("Print 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)}, - {"SmartSearch", NULL, N_("Smart Search"), NULL, N_("Search a part within all the librarys"), G_CALLBACK (smartsearch_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)}, - {"UserManual", NULL, N_("User's Manual"), NULL, N_("Oregano User's Manual"), G_CALLBACK (show_help)}, - {"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)}, + // 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}, + {"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)}, + {"PrintProperties", NULL, N_ ("Print 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 (schematic_view_simulate_cmd)}, + {"Netlist", NULL, N_ ("_Generate netlist"), NULL, N_ ("Generate a netlist"), + G_CALLBACK (netlist_cmd)}, + {"SmartSearch", NULL, N_ ("Smart Search"), NULL, N_ ("Search a part within all the librarys"), + G_CALLBACK (smartsearch_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)}, + {"UserManual", NULL, N_ ("User's Manual"), NULL, N_ ("Oregano User's Manual"), + G_CALLBACK (show_help)}, + {"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)}, + {"StretchLeft", GTK_STOCK_GO_BACK, N_ ("Stretch to the left"), NULL, N_ ("Stretch to the left"), + G_CALLBACK (stretch_horizontal_cmd)}, + {"StretchRight", GTK_STOCK_GO_FORWARD, N_ ("Stretch to the right"), NULL, N_ ("Stretch to the right"), + G_CALLBACK (stretch_horizontal_cmd)}, + {"StretchTop", GTK_STOCK_GO_UP, N_ ("Stretch the top"), NULL, N_ ("Stretch the top"), + G_CALLBACK (stretch_vertical_cmd)}, + {"StretchBottom", GTK_STOCK_GO_DOWN, N_ ("Stretch the bottom"), NULL, N_ ("Stretch the bottom"), + G_CALLBACK (stretch_vertical_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}, + {"Labels", NULL, N_ ("_Node labels"), NULL, N_ ("Toggle node label visibility"), + G_CALLBACK (show_label_cmd), FALSE}, + {"Parts", STOCK_PIXMAP_PART_BROWSER, N_ ("_Parts"), NULL, N_ ("Toggle part browser visibility"), + G_CALLBACK (part_browser_cmd), TRUE}, + {"Grid", STOCK_PIXMAP_GRID, N_ ("_Grid"), NULL, N_ ("Toggle grid visibility"), + G_CALLBACK (grid_toggle_snap_cmd), TRUE}, + {"LogView", GTK_STOCK_DIALOG_WARNING, N_ ("LogView"), NULL, N_ ("Toggle log view visibility"), + G_CALLBACK (log_toggle_visibility_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}, + {"Zoom50", NULL, "50%", NULL, N_ ("Set the zoom to 50%"), 0}, + {"Zoom75", NULL, "75%", NULL, N_ ("Set the zoom to 75%"), 1}, + {"Zoom100", NULL, "100%", "1", N_ ("Set the zoom to 100%"), 2}, + {"Zoom125", NULL, "125%", NULL, N_ ("Set the zoom to 125%"), 3}, + {"Zoom150", NULL, "150%", NULL, N_ ("Set the zoom 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}, + {"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='PrintProperties'/>" -" <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'/>" -" <separator/>" -" <menuitem action='SmartSearch'/>" -" </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='UserManual'/>" -" <menuitem action='About'/>" -" </menu>" -" </menubar>" -" <toolbar name='StandardToolbar'>" -" <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>"; +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='PrintProperties'/>" + " <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'/>" + " <separator/>" + " <menuitem action='SmartSearch'/>" + " </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='UserManual'/>" + " <menuitem action='About'/>" + " </menu>" + " </menubar>" + " <toolbar name='StandardToolbar'>" + " <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='StretchLeft'/>" + " <toolitem action='StretchRight'/>" + " <toolitem action='StretchTop'/>" + " <toolitem action='StretchBottom'/>" + " <separator/>" + " <toolitem action='Grid'/>" + " <toolitem action='Parts'/>" + " <toolitem action='LogView'/>" + " </toolbar>" + " <popup name='MainPopup'>" + " <menuitem action='Paste'/>" + " </popup>" + "</ui>"; #endif diff --git a/src/schematic-view.c b/src/schematic-view.c index d2274fc..f1cf234 100644 --- a/src/schematic-view.c +++ b/src/schematic-view.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> @@ -39,6 +43,7 @@ #include <sys/time.h> #include <cairo/cairo-features.h> +#include "schematic.h" #include "schematic-view.h" #include "part-browser.h" #include "stock.h" @@ -55,17 +60,14 @@ #include "sheet.h" #include "sheet-item-factory.h" #include "textbox-item.h" - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "log-view.h" +#include "log.h" +#include "debug.h" #define ZOOM_MIN 0.35 #define ZOOM_MAX 3 -enum { - CHANGED, - RESET_TOOL, - LAST_SIGNAL -}; +enum { CHANGED, RESET_TOOL, LAST_SIGNAL }; typedef enum { SCHEMATIC_TOOL_ARROW, @@ -74,86 +76,86 @@ typedef enum { SCHEMATIC_TOOL_TEXT } SchematicTool; -typedef struct { - GtkWidget *log_window; - GtkTextView *log_text; - GtkBuilder *log_gui; +typedef struct +{ + GtkWidget *log_window; + GtkTextView *log_text; + GtkBuilder *log_gui; } LogInfo; struct _SchematicView { - GObject parent; - GtkWidget *toplevel; + GObject parent; + GtkWidget *toplevel; SchematicViewPriv *priv; }; struct _SchematicViewClass { - GObjectClass parent_class; + GObjectClass parent_class; // Signals go here - void (*changed) (SchematicView *schematic_view); - void (*reset_tool) (SchematicView *schematic_view); + void (*changed)(SchematicView *schematic_view); + void (*reset_tool)(SchematicView *schematic_view); }; -struct _SchematicViewPriv { - Schematic *schematic; +struct _SchematicViewPriv +{ + Schematic *schematic; - Sheet *sheet; + Sheet *sheet; - GtkPageSetup *page_setup; - GtkPrintSettings *print_settings; + GtkPageSetup *page_setup; + GtkPrintSettings *print_settings; - gboolean empty; + gboolean empty; - GtkActionGroup *action_group; - GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; - GtkWidget *floating_part_browser; - gpointer browser; + gpointer browser; - guint grid : 1; - SchematicTool tool; - int cursor; + GtkWidget *logview; + GtkPaned *paned; + guint grid : 1; + SchematicTool tool; + int cursor; - LogInfo *log_info; + LogInfo *log_info; }; G_DEFINE_TYPE (SchematicView, schematic_view, G_TYPE_OBJECT) // 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 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 void schematic_view_load (SchematicView *sv, Schematic *sm); +static void schematic_view_do_load (SchematicView *sv, Schematic *sm, const gboolean reload); +static void schematic_view_reload (SchematicView *sv, Schematic *sm); // Signal callbacks. -static void title_changed_callback (Schematic *schematic, char *new_title, - SchematicView *sv); +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 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); +static int delete_event (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 void set_tool (SchematicView *sv, SchematicTool tool); +static int can_close (SchematicView *sv); +static void setup_dnd (SchematicView *sv); +static void set_tool (SchematicView *sv, SchematicTool tool); -static GList *schematic_view_list = NULL; +static GList *schematic_view_list = NULL; static GObjectClass *parent_class = NULL; -static guint schematic_view_signals[LAST_SIGNAL] = { 0 }; +static guint schematic_view_signals[LAST_SIGNAL] = {0}; -static void -new_cmd (GtkWidget *widget, Schematic *sv) +static void new_cmd (GtkWidget *widget, Schematic *sv) { Schematic *new_sm; SchematicView *new_sv; @@ -164,13 +166,11 @@ new_cmd (GtkWidget *widget, Schematic *sv) gtk_widget_show_all (new_sv->toplevel); } -static void -properties_cmd (GtkWidget *widget, SchematicView *sv) +static void properties_cmd (GtkWidget *widget, SchematicView *sv) { Schematic *s; - GtkBuilder *gui; - GError *perror = NULL; - gchar *msg; + GtkBuilder *builder; + GError *e = NULL; GtkWidget *window; GtkEntry *title, *author; GtkTextView *comments; @@ -180,46 +180,38 @@ properties_cmd (GtkWidget *widget, SchematicView *sv) s = schematic_view_get_schematic (sv); - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create properties dialog")); - return; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/properties.ui", 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_UIDIR "/properties.ui"); - - oregano_error_with_title (_("Could not create properties dialog"), msg); + if ((builder = gtk_builder_new ()) == NULL) { + log_append (schematic_get_log_store (s), _ ("SchematicView"), + _ ("Could not create properties dialog")); return; } - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/properties.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create properties dialog"), msg); - g_error_free (perror); + gtk_builder_set_translation_domain (builder, NULL); + + if (gtk_builder_add_from_file (builder, OREGANO_UIDIR "/properties.ui", &e) <= 0) { + log_append_error (schematic_get_log_store (s), _ ("SchematicView"), + _ ("Could not create properties dialog due to issues with " OREGANO_UIDIR + "/properties.ui file."), + e); + g_clear_error (&e); return; } - window = GTK_WIDGET (gtk_builder_get_object (gui, "properties")); - title = GTK_ENTRY (gtk_builder_get_object (gui, "title")); - author = GTK_ENTRY (gtk_builder_get_object (gui, "author")); - comments = GTK_TEXT_VIEW (gtk_builder_get_object (gui, "comments")); + window = GTK_WIDGET (gtk_builder_get_object (builder, "properties")); + title = GTK_ENTRY (gtk_builder_get_object (builder, "title")); + author = GTK_ENTRY (gtk_builder_get_object (builder, "author")); + comments = GTK_TEXT_VIEW (gtk_builder_get_object (builder, "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)); + gtk_text_buffer_set_text (buffer, s_comments, strlen (s_comments)); button = gtk_dialog_run (GTK_DIALOG (window)); @@ -229,8 +221,8 @@ properties_cmd (GtkWidget *widget, SchematicView *sv) 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_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); @@ -243,18 +235,18 @@ properties_cmd (GtkWidget *widget, SchematicView *sv) gtk_widget_destroy (window); } -static void -find_file (GtkButton *button, GtkEntry *text) +static void find_file (GtkButton *button, 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); + dialog = gtk_file_chooser_dialog_new (_ ("Export to..."), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), + GTK_RESPONSE_CANCEL, + _("_Open"), + GTK_RESPONSE_ACCEPT, + NULL); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename; @@ -267,15 +259,13 @@ find_file (GtkButton *button, GtkEntry *text) gtk_widget_destroy (dialog); } -static void -export_cmd (GtkWidget *widget, SchematicView *sv) +static void export_cmd (GtkWidget *widget, SchematicView *sv) { Schematic *s; - GtkBuilder *gui; - GError *perror = NULL; - gchar *msg; + GtkBuilder *builder; + GError *e = NULL; GtkWidget *window; - GtkWidget* warning; + GtkWidget *warning; GtkWidget *w; GtkEntry *file = NULL; GtkComboBoxText *combo; @@ -284,33 +274,25 @@ export_cmd (GtkWidget *widget, SchematicView *sv) s = schematic_view_get_schematic (sv); - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create export dialog.")); - return; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/export.ui", 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_UIDIR "/export.xml"); - - oregano_error_with_title (_("Could not create export dialog."), msg); - g_free (msg); + if ((builder = gtk_builder_new ()) == NULL) { + log_append (schematic_get_log_store (s), _ ("SchematicView"), + _ ("Could not create properties dialog")); return; } - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/export.ui", &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create export dialog."), msg); - g_error_free (perror); + gtk_builder_set_translation_domain (builder, NULL); + + if (gtk_builder_add_from_file (builder, OREGANO_UIDIR "/export.ui", &e) <= 0) { + log_append_error (schematic_get_log_store (s), _ ("SchematicView"), + _ ("Could not create properties dialog due to issues with " OREGANO_UIDIR + "/exportp.ui file."), + e); + g_clear_error (&e); return; } - window = GTK_WIDGET (gtk_builder_get_object (gui, "export")); - - combo = GTK_COMBO_BOX_TEXT (gtk_builder_get_object (gui, "format")); + window = GTK_WIDGET (gtk_builder_get_object (builder, "export")); + + combo = GTK_COMBO_BOX_TEXT (gtk_builder_get_object (builder, "format")); fc = 0; #ifdef CAIRO_HAS_SVG_SURFACE gtk_combo_box_text_append_text (combo, "Scalar Vector Graphics (SVG)"); @@ -329,113 +311,130 @@ export_cmd (GtkWidget *widget, SchematicView *sv) formats[fc++] = 3; #endif - file = GTK_ENTRY (gtk_builder_get_object (gui, "file")); + file = GTK_ENTRY (gtk_builder_get_object (builder, "file")); - w = GTK_WIDGET (gtk_builder_get_object (gui, "find")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (find_file), file); + w = GTK_WIDGET (gtk_builder_get_object (builder, "find")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (find_file), file); gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); button = gtk_dialog_run (GTK_DIALOG (window)); - + if (button == GTK_RESPONSE_OK) { - - if (g_path_skip_root (gtk_entry_get_text(file)) == NULL) { + + if (g_path_skip_root (gtk_entry_get_text (file)) == NULL) { warning = gtk_message_dialog_new_with_markup ( - NULL, - GTK_DIALOG_MODAL, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_OK, - _("<span weight=\"bold\" size=\"large\">No filename has " - "been chosen</span>\n\n" - "Please, click on the tag, beside, to select an output.")); - - if (gtk_dialog_run (GTK_DIALOG (warning)) == GTK_RESPONSE_OK) { + NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, + _ ("<span weight=\"bold\" size=\"large\">No filename has " + "been chosen</span>\n\n" + "Please, click on the tag, beside, to select an output.")); + + if (gtk_dialog_run (GTK_DIALOG (warning)) == GTK_RESPONSE_OK) { gtk_widget_destroy (GTK_WIDGET (warning)); export_cmd (widget, sv); gtk_widget_destroy (GTK_WIDGET (window)); return; } - } - else { - + } else { + int bg = 0; GtkSpinButton *spinw, *spinh; int color_scheme = 0; GtkWidget *w; int i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); - w = GTK_WIDGET (gtk_builder_get_object (gui, "bgwhite")); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) bg = 1; - w = GTK_WIDGET (gtk_builder_get_object (gui, "bgblack")); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) bg = 2; - w = GTK_WIDGET (gtk_builder_get_object (gui, "color")); - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) + w = GTK_WIDGET (gtk_builder_get_object (builder, "bgwhite")); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) + bg = 1; + w = GTK_WIDGET (gtk_builder_get_object (builder, "bgblack")); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) + bg = 2; + w = GTK_WIDGET (gtk_builder_get_object (builder, "color")); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) color_scheme = 1; - spinw = GTK_SPIN_BUTTON (gtk_builder_get_object (gui, "export_width")); - spinh = GTK_SPIN_BUTTON (gtk_builder_get_object (gui, "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]); + spinw = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "export_width")); + spinh = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "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 (window); } -static void -page_properties_cmd (GtkWidget *widget, SchematicView *sv) +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); + if (sv->priv->page_setup) { + g_object_unref (sv->priv->page_setup); + } + + 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) +static void open_cmd (GtkWidget *widget, SchematicView *sv) { Schematic *new_sm; - SchematicView *new_sv; + SchematicView *new_sv, *t; char *fname, *uri = NULL; - GList *list; - GError *error = NULL; + GList *iter; + GError *e = NULL; fname = dialog_open_file (sv); - if (fname == NULL) - return; + if (!fname) + 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)); + for (iter = schematic_view_list; iter; iter = iter->next) { + t = SCHEMATIC_VIEW (iter->data); + if (t->priv->empty) + new_sv = t; + gtk_widget_queue_draw (GTK_WIDGET (t->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); + new_sm = schematic_read (fname, &e); + if (e) { + gchar *const msg = g_strdup_printf (_ ("Could not load file \"file://%s\""), fname); + Schematic *old = schematic_view_get_schematic (sv); + log_append_error (schematic_get_log_store (old), _ ("SchematicView"), msg, e); + g_clear_error (&e); + g_free (msg); } if (new_sm) { GtkRecentManager *manager; - gchar *uri; - + GtkRecentInfo *rc; + 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 (uri) { + rc = gtk_recent_manager_lookup_item (manager, uri, &e); + if (e) { + g_clear_error (&e); + } else { + gtk_recent_manager_remove_item (manager, uri, &e); + if (e) { + gchar *const msg = + g_strdup_printf (_ ("Could not load recent file \"%s\""), uri); + Schematic *old = schematic_view_get_schematic (sv); + log_append_error (schematic_get_log_store (old), _ ("SchematicView"), msg, e); + g_clear_error (&e); + g_free (msg); + } + } + gtk_recent_manager_add_item (manager, uri); + if (rc) + gtk_recent_info_unref (rc); + } + if (!new_sv) new_sv = schematic_view_new (new_sm); else @@ -443,44 +442,54 @@ open_cmd (GtkWidget *widget, SchematicView *sv) gtk_widget_show_all (new_sv->toplevel); schematic_set_filename (new_sm, fname); - schematic_set_title (new_sm, g_path_get_basename(fname)); - } + schematic_set_title (new_sm, g_path_get_basename (fname)); - g_list_free_full (list, g_object_unref); + g_free (uri); + } g_free (fname); - g_free (uri); } - -static void -oregano_recent_open (GtkRecentChooser *chooser, SchematicView *sv) +static void oregano_recent_open (GtkRecentChooser *chooser, SchematicView *sv) { gchar *uri; - const gchar *mime; - GtkRecentInfo *item; + const gchar *mime; + GtkRecentInfo *item; GtkRecentManager *manager; Schematic *new_sm; SchematicView *new_sv = NULL; - GError *error = NULL; + GError *e = NULL; uri = gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (chooser)); - if (!uri) return; + 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")); + if (!item) return; + // remove and re-add in order to update the ordering + gtk_recent_manager_remove_item (manager, uri, &e); + if (e) { + gchar *const msg = + g_strdup_printf (_ ("Could not load recent file \"%s\"\n%s"), uri, e->message); + oregano_error_with_title (_ ("Could not load recent file"), msg); + g_clear_error (&e); + g_free (msg); } + gtk_recent_manager_add_item (manager, uri); - 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); + mime = gtk_recent_info_get_mime_type (item); + if (!mime || strcmp (mime, "application/x-oregano") != 0) { + gchar *const msg = g_strdup_printf (_ ("Can not handle file with mimetype \"%s\""), mime); + oregano_error_with_title (_ ("Could not load recent file"), msg); + g_clear_error (&e); + g_free (msg); + + } else { + new_sm = schematic_read (uri, &e); + if (e) { + oregano_error_with_title (_ ("Could not load recent file"), e->message); + g_clear_error (&e); } if (new_sm) { if (!new_sv) @@ -490,19 +499,15 @@ oregano_recent_open (GtkRecentChooser *chooser, SchematicView *sv) gtk_widget_show_all (new_sv->toplevel); schematic_set_filename (new_sm, uri); - schematic_set_title (new_sm, g_path_get_basename(uri)); + schematic_set_title (new_sm, g_path_get_basename (uri)); } } - else - g_warning (_("Unknown type of file can't open")); - g_free(uri); + g_free (uri); gtk_recent_info_unref (item); } - -static GtkWidget * -create_recent_chooser_menu (GtkRecentManager *manager) +static GtkWidget *create_recent_chooser_menu (GtkRecentManager *manager) { GtkWidget *menu; GtkRecentFilter *filter; @@ -512,25 +517,22 @@ create_recent_chooser_menu (GtkRecentManager *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); - + 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_filter_add_application (filter, g_get_application_name()); + gtk_recent_filter_add_application (filter, g_get_application_name ()); 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); + 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) +static void display_recent_files (GtkWidget *menu, SchematicView *sv) { SchematicViewPriv *priv = sv->priv; GtkWidget *menuitem; @@ -538,108 +540,103 @@ display_recent_files (GtkWidget *menu, SchematicView *sv) GtkRecentManager *manager = NULL; manager = gtk_recent_manager_get_default (); - menuitem = gtk_ui_manager_get_widget (priv->ui_manager, - "/MainMenu/MenuFile/DisplayRecentFiles"); + 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) +static void save_cmd (GtkWidget *widget, SchematicView *sv) { Schematic *sm; char *filename; - GError *error = NULL; + GError *e = NULL; sm = sv->priv->schematic; filename = schematic_get_filename (sm); - if (filename == NULL || !strcmp (filename, _("Untitled.oregano"))) { + 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); + } else { + if (!schematic_save_file (sm, &e)) { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Failed to save schematic file."), e); + g_clear_error (&e); } } } -static void -save_as_cmd (GtkWidget *widget, SchematicView *sv) -{ - dialog_save_as (sv); -} +static void save_as_cmd (GtkWidget *widget, SchematicView *sv) { dialog_save_as (sv); } -static void -close_cmd (GtkWidget *widget, SchematicView *sv) +static void close_cmd (GtkWidget *widget, SchematicView *sv) { if (can_close (sv)) { + NG_DEBUG (" --- not dirty (anymore), do close schematic_view: %p -- vs -- " + "toplevel: %p", + sv, schematic_view_get_toplevel (sv)); + + gtk_widget_destroy (GTK_WIDGET (schematic_view_get_toplevel (sv))); + sv->toplevel = NULL; + g_object_unref (G_OBJECT (sv)); } } -static void -select_all_cmd (GtkWidget *widget, SchematicView *sv) +static void select_all_cmd (GtkWidget *widget, SchematicView *sv) { sheet_select_all (sv->priv->sheet, TRUE); } -static void -deselect_all_cmd (GtkWidget *widget, SchematicView *sv) +static void deselect_all_cmd (GtkWidget *widget, SchematicView *sv) { sheet_select_all (sv->priv->sheet, FALSE); } -static void -delete_cmd (GtkWidget *widget, SchematicView *sv) +static void delete_cmd (GtkWidget *widget, SchematicView *sv) { sheet_delete_selection (sv->priv->sheet); } -static void -rotate_cmd (GtkWidget *widget, SchematicView *sv) +static void rotate_cmd (GtkWidget *widget, SchematicView *sv) { if (sv->priv->sheet->state == SHEET_STATE_NONE) - sheet_rotate_selection (sv->priv->sheet); + sheet_rotate_selection (sv->priv->sheet, 90); else if (sv->priv->sheet->state == SHEET_STATE_FLOAT || - sv->priv->sheet->state == SHEET_STATE_FLOAT_START) + sv->priv->sheet->state == SHEET_STATE_FLOAT_START) sheet_rotate_ghosts (sv->priv->sheet); } -static void -flip_horizontal_cmd (GtkWidget *widget, SchematicView *sv) +static void flip_horizontal_cmd (GtkWidget *widget, SchematicView *sv) { if (sv->priv->sheet->state == SHEET_STATE_NONE) sheet_flip_selection (sv->priv->sheet, TRUE); else if (sv->priv->sheet->state == SHEET_STATE_FLOAT || - sv->priv->sheet->state == SHEET_STATE_FLOAT_START) + sv->priv->sheet->state == SHEET_STATE_FLOAT_START) sheet_flip_ghosts (sv->priv->sheet, TRUE); } -static void -flip_vertical_cmd (GtkWidget *widget, SchematicView *sv) +static void flip_vertical_cmd (GtkWidget *widget, SchematicView *sv) { if (sv->priv->sheet->state == SHEET_STATE_NONE) sheet_flip_selection (sv->priv->sheet, FALSE); else if (sv->priv->sheet->state == SHEET_STATE_FLOAT || - sv->priv->sheet->state == SHEET_STATE_FLOAT_START) + sv->priv->sheet->state == SHEET_STATE_FLOAT_START) sheet_flip_ghosts (sv->priv->sheet, FALSE); } -static void -object_properties_cmd (GtkWidget *widget, SchematicView *sv) +static void object_properties_cmd (GtkWidget *widget, SchematicView *sv) { sheet_provide_object_properties (sv->priv->sheet); } -static void -copy_cmd (GtkWidget *widget, SchematicView *sv) +/** + * copy the currently selected items + */ +static void copy_cmd (GtkWidget *widget, SchematicView *sv) { SheetItem *item; - GList *list; + GList *iter; if (sv->priv->sheet->state != SHEET_STATE_NONE) return; @@ -647,23 +644,24 @@ copy_cmd (GtkWidget *widget, SchematicView *sv) sheet_clear_ghosts (sv->priv->sheet); clipboard_empty (); - list = sheet_get_selection (sv->priv->sheet); - for (; list; list = list->next) { - item = list->data; + iter = sheet_get_selection (sv->priv->sheet); + for (; iter; iter = iter->next) { + item = iter->data; clipboard_add_object (G_OBJECT (item)); } - g_list_free_full (list, g_object_unref); if (clipboard_is_empty ()) - gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, - "/MainMenu/MenuEdit/Paste"), FALSE); + 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); + gtk_action_set_sensitive ( + gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), TRUE); } -static void -cut_cmd (GtkWidget *widget, SchematicView *sv) +/** + * snip the currently selected items + */ +static void cut_cmd (GtkWidget *widget, SchematicView *sv) { if (sv->priv->sheet->state != SHEET_STATE_NONE) return; @@ -672,21 +670,16 @@ cut_cmd (GtkWidget *widget, SchematicView *sv) sheet_delete_selection (sv->priv->sheet); if (clipboard_is_empty ()) - gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, - "/MainMenu/MenuEdit/Paste"), FALSE); + 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); + gtk_action_set_sensitive ( + gtk_ui_manager_get_action (sv->priv->ui_manager, "/MainMenu/MenuEdit/Paste"), TRUE); } -static void -paste_objects (gpointer data, Sheet *sheet) -{ - sheet_item_paste (sheet, data); -} +static void paste_objects (gpointer data, Sheet *sheet) { sheet_item_paste (sheet, data); } -static void -paste_cmd (GtkWidget *widget, SchematicView *sv) +static void paste_cmd (GtkWidget *widget, SchematicView *sv) { if (sv->priv->sheet->state != SHEET_STATE_NONE) return; @@ -694,92 +687,76 @@ paste_cmd (GtkWidget *widget, SchematicView *sv) sheet_clear_ghosts (sv->priv->sheet); sheet_select_all (sv->priv->sheet, FALSE); - clipboard_foreach ((ClipBoardFunction) paste_objects, sv->priv->sheet); + clipboard_foreach ((ClipBoardFunction)paste_objects, sv->priv->sheet); if (sheet_get_floating_objects (sv->priv->sheet)) - sheet_connect_part_item_to_floating_group (sv->priv->sheet, (gpointer) sv); + sheet_connect_part_item_to_floating_group (sv->priv->sheet, (gpointer)sv); } -static void -about_cmd (GtkWidget *widget, Schematic *sm) -{ - dialog_about (); -} +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 log_cmd (GtkWidget *widget, SchematicView *sv) { schematic_view_log_show (sv, TRUE); } -static void -show_label_cmd (GtkToggleAction *toggle, SchematicView *sv) +static void show_label_cmd (GtkToggleAction *toggle, SchematicView *sv) { gboolean show; - Schematic *sm; + Schematic *sm; Netlist netlist; - GError *error = 0; + GError *e = NULL; show = gtk_toggle_action_get_active (toggle); - // Use of netlist_helper_create + // Use of netlist_helper_create sm = sv->priv->schematic; - netlist_helper_create (sm, &netlist, &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")); - g_clear_error (&error); + netlist_helper_create (sm, &netlist, &e); + if (e != NULL) { + if (g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Could not create a netlist."), e); + } else { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Unexpect failure occured."), e); + } + g_clear_error (&e); return; } - + sheet_show_node_labels (sv->priv->sheet, show); - sheet_update_parts (sv->priv->sheet); + sheet_update_parts (sv->priv->sheet); } -static void -print_cmd (GtkWidget *widget, SchematicView *sv) +static void print_cmd (GtkWidget *widget, SchematicView *sv) { - schematic_print (schematic_view_get_schematic (sv), - sv->priv->page_setup, - sv->priv->print_settings, FALSE); + 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) +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); + schematic_print (schematic_view_get_schematic (sv), sv->priv->page_setup, + sv->priv->print_settings, TRUE); } -static void -quit_cmd (GtkWidget *widget, SchematicView *sv) +static void quit_cmd (GtkWidget *widget, SchematicView *sv) { - GList *list, *copy; + GList *iter, *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); + for (iter = copy; iter; iter = iter->next) { + close_cmd (NULL, iter->data); } g_list_free (copy); - g_list_free_full (list, g_object_unref); + g_application_quit (g_application_get_default ()); } -static void -v_clamp_cmd (SchematicView *sv) +static void v_clamp_cmd (SchematicView *sv) { LibraryPart *library_part; - SheetPos pos; + Coords pos; Sheet *sheet; Part *part; GList *lib; @@ -788,14 +765,15 @@ v_clamp_cmd (SchematicView *sv) set_tool (sv, SCHEMATIC_TOOL_PART); sheet = sv->priv->sheet; - // Find default lib + // Find default lib for (lib = oregano.libraries; lib; lib = lib->next) { l = (Library *)(lib->data); - if (!g_ascii_strcasecmp(l->name, "Default")) break; + if (!g_ascii_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!"); @@ -808,35 +786,32 @@ v_clamp_cmd (SchematicView *sv) sheet_select_all (sv->priv->sheet, FALSE); sheet_clear_ghosts (sv->priv->sheet); sheet_add_ghost_item (sv->priv->sheet, ITEM_DATA (part)); - sheet_connect_part_item_to_floating_group (sheet, (gpointer) sv); + sheet_connect_part_item_to_floating_group (sheet, (gpointer)sv); } -static void -tool_cmd (GtkAction *action, GtkRadioAction *current, SchematicView *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); + 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) +static void part_browser_cmd (GtkToggleAction *action, SchematicView *sv) { - part_browser_toggle_show (sv); + part_browser_toggle_visibility (sv); } -static void -grid_toggle_snap_cmd (GtkToggleAction *action, SchematicView *sv) +static void grid_toggle_snap_cmd (GtkToggleAction *action, SchematicView *sv) { sv->priv->grid = !sv->priv->grid; @@ -844,19 +819,32 @@ grid_toggle_snap_cmd (GtkToggleAction *action, SchematicView *sv) grid_show (sv->priv->sheet->grid, sv->priv->grid); } -static void -smartsearch_cmd (GtkWidget *widget, SchematicView *sv) +static void log_toggle_visibility_cmd (GtkToggleAction *action, SchematicView *sv) +{ + g_return_if_fail (sv); + g_return_if_fail (sv->priv->logview); + + GtkAllocation allocation; + gboolean b = gtk_toggle_action_get_active (action); + static int pos = 0; + if (pos == 0) + pos = gtk_paned_get_position (GTK_PANED (sv->priv->paned)); + + gtk_widget_get_allocation (GTK_WIDGET (sv->priv->paned), &allocation); + gtk_paned_set_position (GTK_PANED (sv->priv->paned), b ? pos : allocation.height); +} + +static void smartsearch_cmd (GtkWidget *widget, SchematicView *sv) { // Perform a research of a part within all librarys g_print ("TODO: search_smart ()\n"); } -static void -netlist_cmd (GtkWidget *widget, SchematicView *sv) +static void netlist_cmd (GtkWidget *widget, SchematicView *sv) { Schematic *sm; gchar *netlist_name; - GError *error = 0; + GError *e = NULL; OreganoEngine *engine; g_return_if_fail (sv != NULL); @@ -869,34 +857,33 @@ netlist_cmd (GtkWidget *widget, SchematicView *sv) schematic_set_netlist_filename (sm, netlist_name); engine = oregano_engine_factory_create_engine (oregano.engine, sm); - oregano_engine_generate_netlist (engine, netlist_name, &error); + oregano_engine_generate_netlist (engine, netlist_name, &e); sheet_update_parts (sv->priv->sheet); 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; + + if (e) { + if (g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_CLAMP) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_NO_GND) || + g_error_matches (e, OREGANO_ERROR, OREGANO_SIMULATE_ERROR_IO_ERROR)) { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Could not create a netlist."), e); + } else { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Unexpect failure occured."), e); + } + g_clear_error (&e); + return; } } -static void -netlist_view_cmd (GtkWidget *widget, SchematicView *sv) +static void netlist_view_cmd (GtkWidget *widget, SchematicView *sv) { netlist_editor_new_from_schematic_view (sv); } -static void -zoom_check (SchematicView *sv) +static void zoom_check (SchematicView *sv) { double zoom; @@ -905,94 +892,153 @@ zoom_check (SchematicView *sv) sheet_get_zoom (sv->priv->sheet, &zoom); - gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, - "/StandardToolbar/ZoomIn"), zoom < ZOOM_MAX); - gtk_action_set_sensitive (gtk_ui_manager_get_action (sv->priv->ui_manager, - "/StandardToolbar/ZoomOut"), zoom > ZOOM_MIN); + gtk_action_set_sensitive ( + gtk_ui_manager_get_action (sv->priv->ui_manager, "/StandardToolbar/ZoomIn"), + zoom < ZOOM_MAX); + gtk_action_set_sensitive ( + gtk_ui_manager_get_action (sv->priv->ui_manager, "/StandardToolbar/ZoomOut"), + zoom > ZOOM_MIN); } -static void -zoom_in_cmd (GtkWidget *widget, SchematicView *sv) +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); + sheet_zoom_step (sv->priv->sheet, 1.1); zoom_check (sv); } -static void -zoom_out_cmd (GtkWidget *widget, SchematicView *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); + sheet_zoom_step (sv->priv->sheet, 0.9); zoom_check (sv); } -static void -zoom_cmd (GtkAction *action, GtkRadioAction *current, SchematicView *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); + 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) +/* + * Stretch the sheet horizontally. + */ +static void stretch_horizontal_cmd (GtkWidget *widget, SchematicView *sv) +{ + Schematic *sm; + guint width; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sm = sv->priv->schematic; + + g_return_if_fail (sm != NULL); + g_return_if_fail (IS_SCHEMATIC (sm)); + + width = schematic_get_width (sm); + schematic_set_width (sm, width * (1.0 + SCHEMATIC_STRETCH_FACTOR)); + + if (sheet_replace (sv)) { + schematic_view_reload (sv, sm); + + gtk_widget_show_all (schematic_view_get_toplevel (sv)); + } +} + +/* + * Stretch the sheet vertically. + */ +static void stretch_vertical_cmd (GtkWidget *widget, SchematicView *sv) +{ + Schematic *sm; + guint height; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sm = sv->priv->schematic; + + g_return_if_fail (sm != NULL); + g_return_if_fail (IS_SCHEMATIC (sm)); + + height = schematic_get_height (sm); + schematic_set_height (sm, height * (1.0 + SCHEMATIC_STRETCH_FACTOR)); + + if (sheet_replace (sv)) { + schematic_view_reload (sv, sm); + + gtk_widget_show_all (schematic_view_get_toplevel (sv)); + } +} + +void schematic_view_simulate_cmd (GtkWidget *widget, SchematicView *sv) { - simulation_show (NULL, sv); + Schematic *sm; + SimSettings *sim_settings; + + sm = schematic_view_get_schematic (sv); + sim_settings = schematic_get_sim_settings (sm); + + // Before running the simulation for the first time, make + // sure that the simulation settings are configured (this + // includes the removal of missing output vectors from + // previous application runs). + if (!sim_settings->configured) { + // The response_callback() function will take care + // of launching the simulation again when the + // simulation settings have been accepted. + sim_settings->simulation_requested = TRUE; + sim_settings_show (NULL, sv); + return; + } + + simulation_show_progress_bar (NULL, sv); sheet_update_parts (sv->priv->sheet); return; } -static void -schematic_view_class_init (SchematicViewClass *klass) +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); - schematic_view_signals[RESET_TOOL] = g_signal_new ("reset_tool", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (SchematicViewClass, reset_tool), - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + 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); + schematic_view_signals[RESET_TOOL] = + g_signal_new ("reset_tool", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SchematicViewClass, reset_tool), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); object_class->finalize = schematic_view_finalize; object_class->dispose = schematic_view_dispose; } -static void -schematic_view_init (SchematicView *sv) +static void schematic_view_init (SchematicView *sv) { sv->priv = g_new0 (SchematicViewPriv, 1); sv->priv->log_info = g_new0 (LogInfo, 1); @@ -1001,152 +1047,213 @@ schematic_view_init (SchematicView *sv) sv->priv->page_setup = NULL; sv->priv->print_settings = gtk_print_settings_new (); sv->priv->grid = TRUE; + sv->priv->logview = NULL; } -static void -schematic_view_finalize (GObject *object) +static void schematic_view_finalize (GObject *object) { SchematicView *sv = SCHEMATIC_VIEW (object); if (sv->priv) { + if (sv->priv->page_setup) { + g_object_unref (sv->priv->page_setup); + } + + g_object_unref (sv->priv->print_settings); + g_object_unref (sv->priv->action_group); + g_object_unref (sv->priv->ui_manager); + g_object_unref (sv->priv->paned); + + g_free (sv->priv->log_info); g_free (sv->priv); sv->priv = NULL; } if (sv->toplevel) { - gtk_widget_destroy (GTK_WIDGET (sv->toplevel)); + g_object_unref (G_OBJECT (sv->toplevel)); sv->toplevel = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -schematic_view_dispose (GObject *object) +static void schematic_view_dispose (GObject *object) { SchematicView *sv = SCHEMATIC_VIEW (object); 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->priv->sheet); - - // Disconnect focus signal - g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel), - G_CALLBACK (set_focus), sv); + if (sv->toplevel) { + // Disconnect focus signal + g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel), G_CALLBACK (set_focus), sv); - // Disconnect destroy event from toplevel - g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel), - G_CALLBACK (delete_event), sv); + // Disconnect destroy event from toplevel + g_signal_handlers_disconnect_by_func (G_OBJECT (sv->toplevel), G_CALLBACK (delete_event), sv); + } if (sv->priv) { + if (IS_SHEET (sv->priv->sheet)) { + // Disconnect sheet's events + g_signal_handlers_disconnect_by_func (G_OBJECT (sv->priv->sheet), + G_CALLBACK (sheet_event_callback), sv->priv->sheet); + } + g_object_unref (G_OBJECT (sv->priv->schematic)); } + G_OBJECT_CLASS (parent_class)->dispose (object); } -static void -show_help (GtkWidget *widget, SchematicView *sv) +static void show_help (GtkWidget *widget, SchematicView *sv) { - GError *error = NULL; + GError *e = NULL; - GtkWidget *temp; - temp = sv->toplevel; +#if GTK_CHECK_VERSION (3,22,0) + if (!gtk_show_uri_on_window (GTK_WINDOW (sv->toplevel), "help:oregano", gtk_get_current_event_time (), +#else + if (!gtk_show_uri (gtk_widget_get_screen (sv->toplevel), "help:oregano", gtk_get_current_event_time (), +#endif + &e)) { + NG_DEBUG ("Error %s\n", e->message); + g_clear_error (&e); + } +} - if (!gtk_show_uri (gtk_widget_get_screen (temp), "ghelp:oregano", - gtk_get_current_event_time (), &error)) { - NG_DEBUG (g_strdup_printf ("Error %s\n", error->message)); - g_error_free (error); +/** + * Get a suitable value for the window size. + * + * Make the window occupy 3/4 of the screen with a padding of 50px in each + * direction + */ +static void get_window_size (GtkWindow *window, GdkRectangle *rect) +{ + GdkRectangle monitor_rect; +#if GTK_CHECK_VERSION (3,22,0) + GdkMonitor *monitor; + GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (window)); +#else + gint monitor; + GdkScreen *screen = gdk_screen_get_default (); +#endif + +#if GTK_CHECK_VERSION (3,22,0) + if (display) { + monitor = gdk_display_get_primary_monitor (display); + gdk_monitor_get_geometry (monitor, &monitor_rect); +#else + if (screen) { + monitor = gdk_screen_get_primary_monitor (screen); + gdk_screen_get_monitor_geometry (screen, monitor, &monitor_rect); +#endif + + rect->width = 3 * (monitor_rect.width - 50) / 4; + rect->height = 3 * (monitor_rect.height - 50) / 4; + } else { + g_warning ("No default screen found. Falling back to 1024x768 window size."); + rect->width = 1024; + rect->height = 768; } } +static void set_window_size (SchematicView *sv) +{ + GdkRectangle rect; + + get_window_size (GTK_WINDOW (sv->toplevel), &rect); + + gtk_window_set_default_size (GTK_WINDOW (sv->toplevel), rect.width, rect.height); +} + #include "schematic-view-menu.h" -SchematicView * -schematic_view_new (Schematic *schematic) +SchematicView *schematic_view_new (Schematic *schematic) { SchematicView *sv; SchematicViewPriv *priv; + Schematic *sm; GtkWidget *w, *hbox, *vbox; GtkWidget *toolbar, *part_browser; + GtkWidget *logview; + GtkWidget *logview_scrolled; GtkActionGroup *action_group; GtkUIManager *ui_manager; GtkAccelGroup *accel_group; GtkWidget *menubar; - GtkTable *table; - GError *error = NULL; - GtkBuilder *gui; - gchar *msg; + GtkGrid *grid; + GtkPaned *paned; + GError *e = NULL; + GtkBuilder *builder; + GdkRectangle window_size; - g_return_val_if_fail (schematic != NULL, NULL); + g_return_val_if_fail (schematic, NULL); g_return_val_if_fail (IS_SCHEMATIC (schematic), NULL); - sv = SCHEMATIC_VIEW (g_object_new (schematic_view_get_type(), NULL)); + sv = SCHEMATIC_VIEW (g_object_new (schematic_view_get_type (), NULL)); schematic_view_list = g_list_prepend (schematic_view_list, sv); - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create main window.")); - return NULL; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/oregano-main.ui", - 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_UIDIR "/oregano-main.ui"); - oregano_error_with_title (_("Could not create main window."), msg); - g_free (msg); + sm = schematic_view_get_schematic (sv); + + if ((builder = gtk_builder_new ()) == NULL) { + log_append (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Failed to spawn builder object.")); return NULL; } + gtk_builder_set_translation_domain (builder, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/oregano-main.ui", - &error) <= 0) { - msg = error->message; - oregano_error_with_title (_("Could not create main window."), msg); - g_error_free (error); + if (gtk_builder_add_from_file (builder, OREGANO_UIDIR "/oregano-main.ui", &e) <= 0) { + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Could not create main window from file."), e); + g_clear_error (&e); return NULL; } - sv->toplevel = GTK_WIDGET (gtk_builder_get_object (gui, "toplevel")); - table = GTK_TABLE (gtk_builder_get_object (gui, "table1")); + sv->toplevel = GTK_WIDGET (gtk_builder_get_object (builder, "toplevel")); + grid = GTK_GRID (gtk_builder_get_object (builder, "grid")); + paned = GTK_PANED (gtk_builder_get_object (builder, "paned")); + sv->priv->paned = paned; + + get_window_size (GTK_WINDOW (sv->toplevel), &window_size); + if (schematic_get_width (schematic) < window_size.width) + schematic_set_width (schematic, window_size.width); + if (schematic_get_height (schematic) < window_size.height) + schematic_set_height (schematic, window_size.height); - sv->priv->sheet = SHEET (sheet_new (10000, 10000)); + sv->priv->sheet = SHEET (sheet_new ((double) schematic_get_width (schematic) + SHEET_BORDER, (double) schematic_get_height (schematic) + SHEET_BORDER)); - g_signal_connect (G_OBJECT (sv->priv->sheet), - "event", G_CALLBACK (sheet_event_callback), - sv->priv->sheet); + g_signal_connect (G_OBJECT (sv->priv->sheet), "event", G_CALLBACK (sheet_event_callback), + sv->priv->sheet); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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_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_widget_set_hexpand (w, TRUE); + gtk_widget_set_vexpand (w, TRUE); + gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (sv->priv->sheet)); gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 5); - + part_browser = part_browser_create (sv); gtk_widget_set_hexpand (part_browser, FALSE); - gtk_box_pack_start (GTK_BOX (hbox), part_browser, FALSE, FALSE, 5); - + gtk_grid_attach_next_to (grid, part_browser, GTK_WIDGET (paned), GTK_POS_RIGHT, 1, 1); + priv = sv->priv; priv->log_info->log_window = NULL; 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); + gtk_action_group_set_translation_domain (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); + g_assert_cmpint (G_N_ELEMENTS (toggle_entries), >=, 4); + 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); @@ -1154,16 +1261,14 @@ schematic_view_new (Schematic *schematic) 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); + if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &e)) { + g_message ("building menus failed: %s", e->message); + g_clear_error (&e); return NULL; } 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); @@ -1174,62 +1279,75 @@ schematic_view_new (Schematic *schematic) // Fill the window gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); - gtk_table_attach_defaults (table, vbox, 0, 1, 0, 1); - gtk_window_set_focus (GTK_WINDOW (sv->toplevel), - GTK_WIDGET (sv->priv->sheet)); + gtk_paned_pack1 (paned, vbox, FALSE, TRUE); 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_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); + + sv->priv->logview = logview = GTK_WIDGET (log_view_new ()); + log_view_set_store (LOG_VIEW (logview), schematic_get_log_store (schematic)); + + logview_scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (logview_scrolled), logview); + gtk_paned_pack2 (paned, logview_scrolled, FALSE, TRUE); 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 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. - gtk_window_set_default_size (GTK_WINDOW (sv->toplevel), - 3 * gdk_screen_width () / 5, 3 * gdk_screen_height () / 5); + g_signal_connect_object (G_OBJECT (sv), "reset_tool", G_CALLBACK (reset_tool_cb), G_OBJECT (sv), + 0); - g_signal_connect_object (G_OBJECT (sv), "reset_tool", - G_CALLBACK (reset_tool_cb), G_OBJECT (sv), 0); + set_window_size (sv); 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)); + 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")); + schematic_set_filename (sv->priv->schematic, _ ("Untitled.oregano")); + schematic_set_netlist_filename (sv->priv->schematic, _ ("Untitled.netlist")); + + gtk_window_set_application (GTK_WINDOW (schematic_view_get_toplevel (sv)), + GTK_APPLICATION (g_application_get_default ())); return sv; } -static void -schematic_view_load (SchematicView *sv, Schematic *sm) +static void schematic_view_load (SchematicView *sv, Schematic *sm) { - GList *list; - g_return_if_fail (sv->priv->empty != FALSE); + schematic_view_do_load (sv, sm, FALSE); +} + +static void schematic_view_do_load (SchematicView *sv, Schematic *sm, const gboolean reload) +{ + GList *list; + g_return_if_fail (sv != NULL); g_return_if_fail (sm != NULL); + g_return_if_fail (IS_SCHEMATIC (sm)); + + if (!reload) + g_return_if_fail (sv->priv->empty != FALSE); - if (sv->priv->schematic) g_object_unref (G_OBJECT (sv->priv->schematic)); + if (!reload && 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); + if (!reload) { + 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); + } list = schematic_get_items (sm); @@ -1239,35 +1357,43 @@ schematic_view_load (SchematicView *sv, Schematic *sm) sheet_connect_node_dots_to_signals (sv->priv->sheet); + // connect logview with logstore + if (!reload) + log_view_set_store (LOG_VIEW (sv->priv->logview), schematic_get_log_store (sm)); + + schematic_set_dirty (sm, FALSE); + g_list_free_full (list, g_object_unref); } -static void -item_selection_changed_callback (SheetItem *item, gboolean selected, - SchematicView *sv) +static void schematic_view_reload (SchematicView *sv, Schematic *sm) +{ + schematic_view_do_load (sv, sm, TRUE); +} + +static void item_selection_changed_callback (SheetItem *item, gboolean selected, SchematicView *sv) { guint length; if (selected) { sheet_prepend_selected_object (sv->priv->sheet, item); - } - else { + } else { sheet_remove_selected_object (sv->priv->sheet, item); } length = sheet_get_selected_objects_length (sv->priv->sheet); 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); + 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); + 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) +static void item_data_added_callback (Schematic *schematic, ItemData *data, SchematicView *sv) { Sheet *sheet; SheetItem *item; @@ -1279,21 +1405,23 @@ item_data_added_callback (Schematic *schematic, ItemData *data, if (item != NULL) { sheet_item_place (item, sv->priv->sheet); - g_object_set (G_OBJECT (item), "action_group", sv->priv->action_group, - NULL); + g_object_set (G_OBJECT (item), "action_group", sv->priv->action_group, NULL); g_signal_connect (G_OBJECT (item), "selection_changed", - G_CALLBACK (item_selection_changed_callback), sv); + G_CALLBACK (item_selection_changed_callback), sv); sheet_add_item (sheet, item); sv->priv->empty = FALSE; if (sv->priv->tool == SCHEMATIC_TOOL_PART) schematic_view_reset_tool (sv); + + // refresh _after_ we added it to the Sheet + // this is required to properly display rotation, flip and others + item_data_changed (data); } } -static void -title_changed_callback (Schematic *schematic, char *new_title, SchematicView *sv) +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)); @@ -1303,8 +1431,7 @@ title_changed_callback (Schematic *schematic, char *new_title, SchematicView *sv gtk_window_set_title (GTK_WINDOW (sv->toplevel), new_title); } -static void -set_focus (GtkWindow *window, GtkWidget *focus, SchematicView *sv) +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); @@ -1313,47 +1440,38 @@ set_focus (GtkWindow *window, GtkWidget *focus, SchematicView *sv) gtk_widget_grab_focus (GTK_WIDGET (sv->priv->sheet)); } -static int -delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv) +static int delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv) { if (can_close (sv)) { g_object_unref (G_OBJECT (sv)); return FALSE; - } - else + } else return TRUE; } -static int -can_close (SchematicView *sv) +static int can_close (SchematicView *sv) { GtkWidget *dialog; gchar *text, *filename; - GError *error = NULL; + GError *e = NULL; gint result; if (!schematic_is_dirty (schematic_view_get_schematic (sv))) - return TRUE; - + 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\nIf you don't save, " - "all changes since you last saved will be permanently lost."), - filename ? g_path_get_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); + text = + g_strdup_printf (_ ("<span weight=\"bold\" size=\"large\">Save " + "changes to schematic %s before closing?</span>\n\nIf you don't save, " + "all changes since you last saved will be permanently lost."), + filename ? g_path_get_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); @@ -1362,21 +1480,23 @@ can_close (SchematicView *sv) gtk_widget_destroy (dialog); switch (result) { - case GTK_RESPONSE_YES: - schematic_save_file (sv->priv->schematic, &error); - break; - case GTK_RESPONSE_NO: - schematic_set_dirty (sv->priv->schematic, FALSE); - break; - case GTK_RESPONSE_CANCEL: - default: - return FALSE; + case GTK_RESPONSE_YES: + schematic_save_file (sv->priv->schematic, &e); + if (e) { + g_clear_error (&e); + } + break; + case GTK_RESPONSE_NO: + schematic_set_dirty (sv->priv->schematic, FALSE); + break; + case GTK_RESPONSE_CANCEL: + default: + return FALSE; } return TRUE; } -Sheet * -schematic_view_get_sheet (SchematicView *sv) +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); @@ -1384,8 +1504,15 @@ schematic_view_get_sheet (SchematicView *sv) return sv->priv->sheet; } -Schematic * -schematic_view_get_schematic (SchematicView *sv) +void schematic_view_set_sheet (SchematicView *sv, Sheet *sheet) +{ + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sv->priv->sheet = 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); @@ -1393,8 +1520,7 @@ schematic_view_get_schematic (SchematicView *sv) return sv->priv->schematic; } -void -schematic_view_reset_tool (SchematicView *sv) +void schematic_view_reset_tool (SchematicView *sv) { g_return_if_fail (sv != NULL); g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); @@ -1402,79 +1528,75 @@ schematic_view_reset_tool (SchematicView *sv) g_signal_emit_by_name (G_OBJECT (sv), "reset_tool"); } -static void -setup_dnd (SchematicView *sv) +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]); + 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); + 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"); + 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) +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; + GError *e = NULL; // Extract the filenames from the URI-list we received. switch (info) { - case DRAG_PART_INFO: - part_browser_dnd (selection_data, x, y); + case DRAG_PART_INFO: + part_browser_dnd (selection_data, x, y); break; - case DRAG_URI_INFO: - files = g_strsplit ((gchar *) gtk_selection_data_get_data (selection_data), "\n", -1); - if (files) { - int i=0; - while (files[i]) { - Schematic *new_sm = NULL; - int l = strlen (files[i]); - // Algo remains bad after the split: we agregate back into one \0 - files[i][l-1] = '\0'; - - if (l <= 0) { - // Empty file name, ignore! - i++; - continue; - } + case DRAG_URI_INFO: + files = g_strsplit ((gchar *)gtk_selection_data_get_data (selection_data), "\n", -1); + if (files) { + int i = 0; + while (files[i]) { + Schematic *new_sm = NULL; + int l = strlen (files[i]); + // Algo remains bad after the split: we agregate back into one \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++; + gchar *fname = files[i]; + + new_sm = schematic_read (fname, &e); + if (e) { + // g_warning () + g_clear_error (&e); + } + 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++; + } + g_strfreev (files); + } } gtk_drag_finish (context, TRUE, TRUE, time); } - -static void -set_tool (SchematicView *sv, SchematicTool tool) +static void set_tool (SchematicView *sv, SchematicTool tool) { // Switch from this tool... switch (sv->priv->tool) { @@ -1484,19 +1606,19 @@ set_tool (SchematicView *sv, SchematicTool tool) sheet_item_cancel_floating (sv->priv->sheet); 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; - }*/ - sheet_stop_create_wire (sv->priv->sheet); - break; + /*if (sv->priv->create_wire_context) { + create_wire_exit (sv->priv->create_wire_context); + sv->priv->create_wire_context = NULL; + }*/ + sheet_stop_create_wire (sv->priv->sheet); + break; case SCHEMATIC_TOOL_TEXT: - textbox_item_cancel_listen (sv->priv->sheet); - break; + textbox_item_cancel_listen (sv->priv->sheet); + break; case SCHEMATIC_TOOL_PART: - sheet_item_cancel_floating (sv->priv->sheet); + sheet_item_cancel_floating (sv->priv->sheet); default: - break; + break; } // ...to this tool. @@ -1506,8 +1628,7 @@ set_tool (SchematicView *sv, SchematicTool tool) sv->priv->sheet->state = SHEET_STATE_NONE; break; case SCHEMATIC_TOOL_WIRE: - cursor_set_widget (GTK_WIDGET (sv->priv->sheet), - OREGANO_CURSOR_PENCIL); + cursor_set_widget (GTK_WIDGET (sv->priv->sheet), OREGANO_CURSOR_PENCIL); sv->priv->sheet->state = SHEET_STATE_WIRE; sheet_initiate_create_wire (sv->priv->sheet); break; @@ -1518,8 +1639,7 @@ set_tool (SchematicView *sv, SchematicTool tool) textbox_item_listen (sv->priv->sheet); break; case SCHEMATIC_TOOL_PART: - cursor_set_widget (GTK_WIDGET (sv->priv->sheet), - OREGANO_CURSOR_LEFT_PTR); + cursor_set_widget (GTK_WIDGET (sv->priv->sheet), OREGANO_CURSOR_LEFT_PTR); default: break; } @@ -1527,18 +1647,16 @@ set_tool (SchematicView *sv, SchematicTool tool) sv->priv->tool = tool; } -static void -reset_tool_cb (Sheet *sheet, SchematicView *sv) +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, - "/StandardToolbar/Arrow")), 0); + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (gtk_ui_manager_get_action ( + sv->priv->ui_manager, "/StandardToolbar/Arrow")), + 0); } -gpointer -schematic_view_get_browser (SchematicView *sv) +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); @@ -1546,8 +1664,7 @@ schematic_view_get_browser (SchematicView *sv) return sv->priv->browser; } -void -schematic_view_set_browser (SchematicView *sv, gpointer p) +void schematic_view_set_browser (SchematicView *sv, gpointer p) { g_return_if_fail (sv != NULL); g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); @@ -1555,28 +1672,24 @@ schematic_view_set_browser (SchematicView *sv, gpointer p) sv->priv->browser = p; } -static gboolean -log_window_delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv) +static gboolean log_window_delete_event (GtkWidget *widget, GdkEvent *event, SchematicView *sv) { sv->priv->log_info->log_window = NULL; return FALSE; } -static void -log_window_destroy_event (GtkWidget *widget, SchematicView *sv) +static void log_window_destroy_event (GtkWidget *widget, SchematicView *sv) { sv->priv->log_info->log_window = NULL; } -static void -log_window_close_cb (GtkWidget *widget, SchematicView *sv) +static void log_window_close_cb (GtkWidget *widget, SchematicView *sv) { gtk_widget_destroy (sv->priv->log_info->log_window); sv->priv->log_info->log_window = NULL; } -static void -log_window_clear_cb (GtkWidget *widget, SchematicView *sv) +static void log_window_clear_cb (GtkWidget *widget, SchematicView *sv) { GtkTextTagTable *tag; GtkTextBuffer *buf; @@ -1586,23 +1699,19 @@ log_window_clear_cb (GtkWidget *widget, SchematicView *sv) schematic_log_clear (sv->priv->schematic); - gtk_text_view_set_buffer (GTK_TEXT_VIEW (sv->priv->log_info->log_text), - GTK_TEXT_BUFFER (buf)); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (sv->priv->log_info->log_text), GTK_TEXT_BUFFER (buf)); } -static void -log_updated_callback (Schematic *sm, SchematicView *sv) +static void log_updated_callback (Schematic *sm, SchematicView *sv) { schematic_view_log_show (sv, FALSE); } -void -schematic_view_log_show (SchematicView *sv, gboolean explicit) +void schematic_view_log_show (SchematicView *sv, gboolean explicit) { GtkWidget *w; - gchar *msg; Schematic *sm; - GError *perror = NULL; + GError *e = NULL; g_return_if_fail (sv != NULL); g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); @@ -1610,129 +1719,105 @@ schematic_view_log_show (SchematicView *sv, gboolean explicit) sm = sv->priv->schematic; if ((sv->priv->log_info->log_gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create the log window.")); + log_append (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Could not create the log window.")); return; - } - else - gtk_builder_set_translation_domain (sv->priv->log_info->log_gui, NULL); + } + gtk_builder_set_translation_domain (sv->priv->log_info->log_gui, NULL); if (sv->priv->log_info->log_window == NULL) { // Create the log window if not already done. if (!explicit && !oregano.show_log) return; - if (!g_file_test (OREGANO_UIDIR "/log-window.ui", 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_UIDIR "/log-window.ui"); - oregano_error_with_title ( _("Could not create the log window"), msg); - g_free (msg); - return; - } + if (gtk_builder_add_from_file (sv->priv->log_info->log_gui, OREGANO_UIDIR "/log-window.ui", + &e) <= 0) { - if (gtk_builder_add_from_file (sv->priv->log_info->log_gui, - OREGANO_UIDIR "/log-window.ui", &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create the log window."), msg); - g_error_free (perror); + log_append_error (schematic_get_log_store (sm), _ ("SchematicView"), + _ ("Could not create the log window."), e); + g_clear_error (&e); return; } - sv->priv->log_info->log_window = GTK_WIDGET ( - gtk_builder_get_object (sv->priv->log_info->log_gui, - "log-window")); - sv->priv->log_info->log_text = GTK_TEXT_VIEW ( - gtk_builder_get_object (sv->priv->log_info->log_gui, - "log-text")); + sv->priv->log_info->log_window = + GTK_WIDGET (gtk_builder_get_object (sv->priv->log_info->log_gui, "log-window")); + sv->priv->log_info->log_text = + GTK_TEXT_VIEW (gtk_builder_get_object (sv->priv->log_info->log_gui, "log-text")); - gtk_window_set_default_size (GTK_WINDOW (sv->priv->log_info->log_window), - 500, 250); + gtk_window_set_default_size (GTK_WINDOW (sv->priv->log_info->log_window), 500, 250); // Delete event. - g_signal_connect (G_OBJECT (sv->priv->log_info->log_window), - "delete_event", G_CALLBACK (log_window_delete_event), sv); - - g_signal_connect (G_OBJECT (sv->priv->log_info->log_window), - "destroy_event", G_CALLBACK (log_window_destroy_event), sv); - - w = GTK_WIDGET (gtk_builder_get_object (sv->priv->log_info->log_gui, - "close-button")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (log_window_close_cb), sv); - - w = GTK_WIDGET (gtk_builder_get_object (sv->priv->log_info->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 { + g_signal_connect (G_OBJECT (sv->priv->log_info->log_window), "delete_event", + G_CALLBACK (log_window_delete_event), sv); + + g_signal_connect (G_OBJECT (sv->priv->log_info->log_window), "destroy_event", + G_CALLBACK (log_window_destroy_event), sv); + + w = GTK_WIDGET (gtk_builder_get_object (sv->priv->log_info->log_gui, "close-button")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (log_window_close_cb), sv); + + w = GTK_WIDGET (gtk_builder_get_object (sv->priv->log_info->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 (gtk_widget_get_window (sv->priv->log_info->log_window)); } - gtk_text_view_set_buffer (sv->priv->log_info->log_text, - schematic_get_log_text (sm)); + gtk_text_view_set_buffer (sv->priv->log_info->log_text, schematic_get_log_text (sm)); gtk_widget_show_all (sv->priv->log_info->log_window); } -gboolean -schematic_view_get_log_window_exists (SchematicView *sv) +gboolean schematic_view_get_log_window_exists (SchematicView *sv) { if (sv->priv->log_info->log_window != NULL) - return TRUE; + return TRUE; else - return FALSE; + return FALSE; } -GtkWidget * -schematic_view_get_toplevel (SchematicView *sv) -{ - return sv->toplevel; -} +GtkWidget *schematic_view_get_toplevel (SchematicView *sv) { return sv->toplevel; } -Schematic * -schematic_view_get_schematic_from_sheet (Sheet *sheet) +Schematic *schematic_view_get_schematic_from_sheet (Sheet *sheet) { g_return_val_if_fail ((sheet != NULL), NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); - - GList *list, *copy; - copy = g_list_copy (schematic_view_list); + GList *iter, *copy; + Schematic *s = NULL; + copy = g_list_copy (schematic_view_list); // really needed? probably not - for (list=copy; list; list = list->next) { - if (SCHEMATIC_VIEW (list->data)->priv->sheet == sheet) { - return SCHEMATIC_VIEW (list->data)->priv->schematic; + for (iter = copy; iter; iter = iter->next) { + SchematicView *sv = SCHEMATIC_VIEW (iter->data); + if (sv->priv->sheet == sheet) { + s = sv->priv->schematic; + break; } } g_list_free (copy); - g_list_free_full (list, g_object_unref); - return NULL; + return s; } -SchematicView * -schematic_view_get_schematicview_from_sheet (Sheet *sheet) +SchematicView *schematic_view_get_schematicview_from_sheet (Sheet *sheet) { - g_return_val_if_fail ((sheet != NULL), NULL); + g_return_val_if_fail (sheet, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); - - GList *list, *copy; - copy = g_list_copy (schematic_view_list); + GList *iter, *copy; + SchematicView *sv = NULL; + + copy = g_list_copy (schematic_view_list); // really needed? probably not - for (list=copy; list; list = list->next) { - if (SCHEMATIC_VIEW (list->data)->priv->sheet == sheet) - return SCHEMATIC_VIEW (list->data); + for (iter = copy; iter; iter = iter->next) { + sv = SCHEMATIC_VIEW (iter->data); + if (sv->priv->sheet == sheet) + break; } g_list_free (copy); - g_list_free_full (list, g_object_unref); - return NULL; + return sv; } -void -run_context_menu (SchematicView *sv, GdkEventButton *event) +void run_context_menu (SchematicView *sv, GdkEventButton *event) { GtkWidget *menu; @@ -1741,6 +1826,5 @@ run_context_menu (SchematicView *sv, GdkEventButton *event) 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); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sv, event->button, event->time); } diff --git a/src/schematic-view.h b/src/schematic-view.h index f77bb7d..dd63a08 100644 --- a/src/schematic-view.h +++ b/src/schematic-view.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,54 +28,67 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SCHEMATIC_VIEW_H #define __SCHEMATIC_VIEW_H +// typedefing before including makes circular dependencies possible +typedef struct _SchematicView SchematicView; + #include <gtk/gtk.h> #include "schematic.h" #include "sheet.h" -typedef enum { - DRAG_URI_INFO, - DRAG_PART_INFO -} DragTypes; +/* + * When stretching a schematic to resize + * it, increase its width or height of + * this percentage (the recommended factor + * is 0.15 for a 15% increase). + */ +#define SCHEMATIC_STRETCH_FACTOR 0.15 + +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)) +#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; +typedef struct _SchematicViewPriv SchematicViewPriv; + +GType schematic_view_get_type (void); + +void schematic_view_simulate_cmd (GtkWidget *widget, SchematicView *sv); -GType schematic_view_get_type (void); -SchematicView *schematic_view_new (Schematic *schematic); -Sheet *schematic_view_get_sheet (SchematicView *sv); -Schematic *schematic_view_get_schematic (SchematicView *sv); -Schematic *schematic_view_get_schematic_from_sheet (Sheet *sheet); -SchematicView *schematic_view_get_schematicview_from_sheet (Sheet *sheet); -void run_context_menu (SchematicView *sv, GdkEventButton *event); +SchematicView *schematic_view_new (Schematic *schematic); +Sheet *schematic_view_get_sheet (SchematicView *sv); +void schematic_view_set_sheet (SchematicView *sv, Sheet *sheet); +Schematic *schematic_view_get_schematic (SchematicView *sv); +Schematic *schematic_view_get_schematic_from_sheet (Sheet *sheet); +SchematicView *schematic_view_get_schematicview_from_sheet (Sheet *sheet); +void run_context_menu (SchematicView *sv, GdkEventButton *event); // Signal emission wrappers. -void schematic_view_reset_tool (SchematicView *sv); +void schematic_view_reset_tool (SchematicView *sv); // Misc. -void schematic_view_set_browser (SchematicView *sv, gpointer p); -gpointer schematic_view_get_browser (SchematicView *sv); -void schematic_view_set_parent (SchematicView *sv, GtkDialog *dialog); +void schematic_view_set_browser (SchematicView *sv, gpointer p); +gpointer schematic_view_get_browser (SchematicView *sv); +void schematic_view_set_parent (SchematicView *sv, GtkDialog *dialog); // Logging. -void schematic_view_log_show (SchematicView *sv, gboolean explicit); -gboolean schematic_view_get_log_window_exists (SchematicView *sv); +void schematic_view_log_show (SchematicView *sv, gboolean explicit); +gboolean schematic_view_get_log_window_exists (SchematicView *sv); // Windows services. -GtkWidget * schematic_view_get_toplevel (SchematicView *sv); +GtkWidget *schematic_view_get_toplevel (SchematicView *sv); #endif /* __SCHEMATIC_VIEW_H */ diff --git a/src/settings.c b/src/settings.c index ca56372..f54f39d 100644 --- a/src/settings.c +++ b/src/settings.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2008-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <unistd.h> @@ -44,14 +48,8 @@ #include "oregano-utils.h" #include "oregano-config.h" -// Engines Types -static const gchar* -engine[] = { - "gnucap", - "ngspice" -}; - -typedef struct { +typedef struct +{ Schematic *sm; GtkWidget *pbox; // Property box @@ -62,23 +60,20 @@ typedef struct { GtkWidget *w_engine; } Settings; -#define SETTINGS(x) ((Settings*)(x)) +#define SETTINGS(x) ((Settings *)(x)) -GtkWidget *engine_path; -GtkWidget *button[2]; +GtkWidget *engine_path; +GtkWidget *button[OREGANO_ENGINE_COUNT]; - -static void -apply_callback (GtkWidget *w, Settings *s) +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.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)); + if (s->w_show_log) + oregano.show_log = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->w_show_log)); + else + oregano.show_log = FALSE; + oregano.show_splash = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s->w_show_splash)); oregano_config_save (); @@ -86,43 +81,37 @@ apply_callback (GtkWidget *w, Settings *s) s->pbox = NULL; } -static void -delete_event_callback (GtkWidget *w, GdkEvent *event, Settings *s) +static void delete_event_callback (GtkWidget *w, GdkEvent *event, Settings *s) { - apply_callback(w, s); + apply_callback (w, s); } -static void -set_engine_name (GtkWidget *w, Settings *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) { - 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); + gchar *engine_name; + + s->w_engine = w; + engine_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (s->w_engine), "id")); + + engine_name = oregano_engine_get_engine_name_by_index (engine_id); + if (g_find_program_in_path (engine_name) == NULL) { + 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_name); + 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); + engine_id = (engine_id + 1) % OREGANO_ENGINE_COUNT; + 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); } + g_free (engine_name); } -gpointer -settings_new (Schematic *sm) +gpointer settings_new (Schematic *sm) { Settings *s; @@ -132,10 +121,11 @@ settings_new (Schematic *sm) return s; } -void -settings_show (GtkWidget *widget, SchematicView *sv) +void settings_show (GtkWidget *widget, SchematicView *sv) { gint i; + gboolean engine_available = FALSE; + gchar *engine_name; GtkWidget *engine_group = NULL; GtkWidget *w, *pbox, *toplevel; GtkBuilder *gui; @@ -145,71 +135,58 @@ settings_show (GtkWidget *widget, SchematicView *sv) Schematic *sm; g_return_if_fail (sv != NULL); - - // If no engine available, stop oregano - if ((g_find_program_in_path (engine[0]) == NULL) && - (g_find_program_in_path (engine[1]) == NULL)) { + + for (i = 0; i < OREGANO_ENGINE_COUNT; i++) { + engine_name = oregano_engine_get_engine_name_by_index (i); + if (g_find_program_in_path(engine_name) != NULL) { + engine_available = TRUE; + } + g_free (engine_name); + } + + if (!engine_available) { gchar *msg; - msg = g_strdup_printf ( - _("No engine allowing analysis is available.\n" - "You might install one, at least! \n" - "Either ngspice or gnucap.")); - oregano_error_with_title (_("Could not create settings dialog"), msg); + msg = g_strdup_printf (_ ("No engine allowing analysis is available.\n" + "You need to install at least one engine ! \n" + "spice3, ngspice or gnucap.")); + oregano_error_with_title (_ ("Could not create engine settings dialog"), msg); g_free (msg); - return; } - - g_return_if_fail (sv != NULL); if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create settings dialog")); + oregano_error (_ ("Could not create settings dialog")); return; - } - else - gtk_builder_set_translation_domain (gui, NULL); + } + gtk_builder_set_translation_domain (gui, 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_get_window ( - GTK_WIDGET (SETTINGS (s)->pbox))); + gdk_window_raise (gtk_widget_get_window (GTK_WIDGET (SETTINGS (s)->pbox))); return; } - - if (!g_file_test (OREGANO_UIDIR "/settings.ui", 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_UIDIR "/settings.ui"); - oregano_error_with_title (_("Could not create settings dialog"), msg); - g_free (msg); - return; - } - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/settings.ui", - &perror) <= 0) { + + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/settings.ui", &perror) <= 0) { msg = perror->message; - oregano_error_with_title (_("Could not create settings dialog"), msg); + oregano_error_with_title (_ ("Could not create settings dialog"), msg); g_error_free (perror); return; - } + } w = toplevel = GTK_WIDGET (gtk_builder_get_object (gui, "toplevel")); if (!w) { - oregano_error (_("Could not create settings dialog")); + oregano_error (_ ("Could not create settings dialog")); return; } - g_signal_connect (G_OBJECT (w), "delete_event", - G_CALLBACK (delete_event_callback), s); + g_signal_connect (G_OBJECT (w), "delete_event", G_CALLBACK (delete_event_callback), s); pbox = toplevel; s->pbox = GTK_WIDGET (pbox); w = GTK_WIDGET (gtk_builder_get_object (gui, "close_bt")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (apply_callback), s); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (apply_callback), s); w = GTK_WIDGET (gtk_builder_get_object (gui, "splash-enable")); s->w_show_splash = w; @@ -217,56 +194,51 @@ settings_show (GtkWidget *widget, SchematicView *sv) w = GTK_WIDGET (gtk_builder_get_object (gui, "compress-enable")); s->w_compress_files = w; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), - oregano.compress_files); - w = GTK_WIDGET (gtk_builder_get_object (gui, "log-enable")); - s->w_show_log = w; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), oregano.show_log); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), oregano.compress_files); + if (engine_available) { + w = GTK_WIDGET (gtk_builder_get_object (gui, "log-enable")); + s->w_show_log = w; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), oregano.show_log); + } else { + w = GTK_WIDGET (gtk_builder_get_object (gui, "log-enable")); + s->w_show_log = NULL; + gtk_widget_destroy (w); + } - w = GTK_WIDGET (gtk_builder_get_object (gui, "grid-size")); - gtk_widget_set_sensitive (w, FALSE); w = GTK_WIDGET (gtk_builder_get_object (gui, "realtime-enable")); gtk_widget_set_sensitive (w, FALSE); - w = GTK_WIDGET (gtk_builder_get_object (gui, "engine_table")); - for (i = 0; i < OREGANO_ENGINE_COUNT; i++) { - if (engine_group) - button[i] = gtk_radio_button_new_with_label_from_widget ( - GTK_RADIO_BUTTON (engine_group), engine[i]); - else - button[i] = engine_group = - gtk_radio_button_new_with_label_from_widget (NULL, engine[i]); - - 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); - + if (engine_available) { + w = GTK_WIDGET (gtk_builder_get_object (gui, "engine_table")); + for (i = 0; i < OREGANO_ENGINE_COUNT; i++) { + engine_name = oregano_engine_get_engine_name_by_index (i); + if (engine_group) + button[i] = gtk_radio_button_new_with_label_from_widget ( + GTK_RADIO_BUTTON (engine_group), engine_name); + else + button[i] = engine_group = + gtk_radio_button_new_with_label_from_widget (NULL, engine_name); + g_free (engine_name); + g_object_set_data (G_OBJECT (button[i]), "id", GUINT_TO_POINTER (i)); + + gtk_grid_attach (GTK_GRID (w), button[i], 0, i, 1, 1); + g_signal_connect (G_OBJECT (button[i]), "clicked", G_CALLBACK (set_engine_name), s); + } + + } else { + w = GTK_WIDGET (gtk_builder_get_object (gui, "label50")); + gtk_widget_destroy (w); } - + // Is the engine available? // In that case the button is active - if (g_find_program_in_path (engine[oregano.engine]) != NULL) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[oregano.engine]), - TRUE); + engine_name = oregano_engine_get_engine_name_by_index (oregano.engine); + if (g_find_program_in_path (engine_name) != NULL) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[oregano.engine]), TRUE); // Otherwise the button is inactive else - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[oregano.engine]), - FALSE); - - // If no engine available, stop oregano - if ((g_find_program_in_path (engine[0]) == NULL) && - (g_find_program_in_path (engine[1]) == NULL)) { - gchar *msg; - msg = g_strdup_printf ( - _("No engine allowing analysis is available.\n" - "You might install one, at least! \n" - "Either ngspice or gnucap.")); - oregano_error_with_title (_("Could not create settings dialog"), msg); - g_free (msg); - } + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button[oregano.engine]), FALSE); + g_free (engine_name); gtk_widget_show_all (toplevel); } diff --git a/src/settings.h b/src/settings.h index d5532a6..8d8992d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -27,8 +27,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SETTINGS_H diff --git a/src/sheet/Makefile.am b/src/sheet/Makefile.am deleted file mode 100644 index d83a960..0000000 --- a/src/sheet/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -oreganodir = $(datadir)/oregano - -AM_CFLAGS = -Wall -DG_DISABLE_DEPRECATED -DGSEAL_ENABLE \ - -DGDK_PIXBUF_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ - -DGTK_DISABLE_DEPRECATED -DG_DISABLE_SINGLE_INCLUDES \ - -DGDK_PIXBUF_DISABLE_SINGLE_INCLUDES -DGTK_DISABLE_SINGLE_INCLUDES - -INCLUDES = \ - $(OREGANO_CFLAGS) -I$(top_srcdir)/src -I$(top_srcdir)/src/model \ - -I$(top_srcdir)/src/engines \ - -DOREGANO_UIDIR=\""$(oreganodir)/xml"\" \ - -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \ - -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" - -noinst_LIBRARIES = libsheet.a - -libsheet_a_SOURCES = \ - create-wire.h \ - create-wire.c \ - grid.c \ - grid.h \ - node-item.c \ - node-item.h \ - part-item.c \ - part-item.h \ - plot-add-function.c \ - plot-add-function.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 diff --git a/src/sheet/create-wire.c b/src/sheet/create-wire.c index ca71e7a..60c2e57 100644 --- a/src/sheet/create-wire.c +++ b/src/sheet/create-wire.c @@ -1,21 +1,24 @@ /* * create-wire.c * + * * Authors: * Richard Hult <rhult@hem.passagen.se> * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * * Description: 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: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2012-2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -29,525 +32,498 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> #include <stdlib.h> #include "cursors.h" -#include "sheet-pos.h" +#include "coords.h" #include "node-store.h" #include "wire-item.h" #include "create-wire.h" #include "wire.h" +#include "sheet-private.h" + +#include "debug.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -struct _CreateWireContext { - guint active : 1; - gint start_handler_id; - gint draw_handler_id; - gint create_wire_cancel_id; - gdouble old_x, old_y; - Sheet * sheet; - GooCanvasItem * 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 create_wire_cancel (Sheet *sheet, CreateWireContext *cwc); -static void fixate_wire (CreateWireContext *cwc, gboolean always_fixate_both, - gdouble x, gdouble y); -static Wire * create_wire_and_place_item (Sheet *sheet, 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) +CreateWireInfo *create_wire_info_new (Sheet *sheet) { - GooCanvasPoints *points; + CreateWireInfo *create_wire_info; GooCanvasLineDash *dash; - CreateWire *wire; - double new_x, new_y; - if (cwc->active) - return FALSE; + create_wire_info = g_new0 (CreateWireInfo, 1); - if (event->button.button == 2 || event->button.button > 3) - return FALSE; + create_wire_info->state = WIRE_DISABLED; - if (event->type == GDK_2BUTTON_PRESS || - event->type == GDK_3BUTTON_PRESS) - return FALSE; + create_wire_info->direction = WIRE_DIR_HORIZ; + // create_wire_info->direction_auto_toggle = + // WIRE_DIRECTION_AUTO_TOGGLE_OFF; - g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + create_wire_info->points = goo_canvas_points_new (3); + create_wire_info->points->coords[0] = 0.; + create_wire_info->points->coords[1] = 0.; + create_wire_info->points->coords[2] = 0.; + create_wire_info->points->coords[3] = 0.; + create_wire_info->points->coords[4] = 0.; + create_wire_info->points->coords[5] = 0.; - if (event->type != GDK_BUTTON_PRESS) - return FALSE; + dash = goo_canvas_line_dash_new (2, 5.0, 5.0); - // Button 3 resets the sheet mode. - if (event->button.button == 3) { - exit_wire_mode (cwc); - return TRUE; - } + create_wire_info->line = GOO_CANVAS_POLYLINE (goo_canvas_polyline_new ( + GOO_CANVAS_ITEM (sheet->grid), FALSE, 0, "points", create_wire_info->points, + "stroke-color-rgba", 0x92BA52C3, "line-dash", dash, "line-width", 2.0, "visibility", + GOO_CANVAS_ITEM_INVISIBLE, NULL)); + goo_canvas_line_dash_unref (dash); - // Button 1 starts a new wire. Start by deselecting all objects. - sheet_select_all (sheet, FALSE); + create_wire_info->dot = GOO_CANVAS_ELLIPSE (goo_canvas_ellipse_new ( + GOO_CANVAS_ITEM (sheet->object_group), -3.0, -3.0, 6.0, 6.0, "fill-color", "red", + "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL)); - sheet_get_pointer (sheet, &new_x, &new_y); + return create_wire_info; +} + +void create_wire_info_destroy (CreateWireInfo *create_wire_info) +{ + g_return_if_fail (create_wire_info); - points = goo_canvas_points_new (3); - points->coords[0] = new_x; - points->coords[1] = new_y; - points->coords[2] = new_x; - points->coords[3] = new_y; - points->coords[4] = new_x; - points->coords[5] = new_y; + goo_canvas_item_remove (GOO_CANVAS_ITEM (create_wire_info->dot)); + goo_canvas_item_remove (GOO_CANVAS_ITEM (create_wire_info->line)); + goo_canvas_points_unref (create_wire_info->points); + g_free (create_wire_info); +} - wire = g_new0 (CreateWire, 1); - cwc->create_wire = wire; - cwc->old_x = new_x; - cwc->old_y = new_y; +inline static gboolean create_wire_start (Sheet *sheet, GdkEvent *event) +{ + double x, y; + GooCanvasPoints *points; + GooCanvasPolyline *line; + CreateWireInfo *create_wire_info; - dash = goo_canvas_line_dash_new (2, 5.0, 5.0); - - wire->line = GOO_CANVAS_POLYLINE ( - goo_canvas_polyline_new (GOO_CANVAS_ITEM (sheet->object_group), - FALSE, 0, - "points", points, - "stroke-color", "red", - "line-dash", dash, - "line-width", 1.0, - NULL)); - - goo_canvas_line_dash_unref (dash); - - wire->points = points; - wire->direction = WIRE_DIR_NONE; - - cwc->draw_handler_id = g_signal_connect (sheet, "event", - G_CALLBACK (create_wire_event), cwc); - - cwc->active = TRUE; + // g_signal_stop_emission_by_name (sheet, "event"); + create_wire_info = sheet->priv->create_wire_info; +#if 0 + //TODO save and restore the selection? + sheet_select_all (sheet, FALSE); +#endif + points = create_wire_info->points; + line = create_wire_info->line; + + g_assert (points); + g_assert (line); + + x = event->button.x; + y = event->button.y; + + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &x, &y); + snap_to_grid (sheet->grid, &x, &y); + +#if 0 + Coords p; + sheet_get_pointer (sheet, &p.x, &p.y); + NG_DEBUG ("diff_x=%lf; diff_y=%lf;", p.x-x, p.y-y); +#endif + + // start point + points->coords[0] = x; + points->coords[1] = y; + // mid point + points->coords[2] = x; + points->coords[3] = y; + // end point + points->coords[4] = x; + points->coords[5] = y; + + g_object_set (G_OBJECT (line), "points", points, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + + goo_canvas_item_raise (GOO_CANVAS_ITEM (create_wire_info->line), NULL); + goo_canvas_item_raise (GOO_CANVAS_ITEM (create_wire_info->dot), NULL); + + create_wire_info->state = WIRE_ACTIVE; + sheet_pointer_grab (sheet, event); + sheet_keyboard_grab (sheet, event); 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 (Sheet *sheet) +inline static gboolean create_wire_update (Sheet *sheet, GdkEvent *event) { - CreateWireContext *cwc; + CreateWireInfo *create_wire_info; + double new_x, new_y, x1, y1; + gint32 snapped_x, snapped_y; + Coords pos; - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (IS_SHEET (sheet), NULL); + Schematic *schematic; + NodeStore *store; - cwc = g_new0 (CreateWireContext, 1); - cwc->sheet = sheet; - cwc->active = FALSE; - cwc->dot_item = NULL; + // g_signal_stop_emission_by_name (sheet, "event"); - cwc->start_handler_id = g_signal_connect (sheet, "event", - G_CALLBACK (create_wire_pre_create_event), cwc); + create_wire_info = sheet->priv->create_wire_info; - cwc->create_wire_cancel_id = g_signal_connect (sheet, "cancel", - G_CALLBACK (create_wire_cancel), cwc); + schematic = schematic_view_get_schematic_from_sheet (sheet); + g_assert (schematic); - return cwc; + store = schematic_get_store (schematic); + g_assert (store); + + new_x = event->button.x; + new_y = event->button.y; + + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &new_x, &new_y); + snap_to_grid (sheet->grid, &new_x, &new_y); + +#if 0 + Coords p; + sheet_get_pointer (sheet, &p.x, &p.y); + NG_DEBUG ("diff_x=%lf; diff_y=%lf;", p.x-new_x, p.y-new_y); +#endif + + snapped_x = (gint32)new_x; + snapped_y = (gint32)new_y; + + /* start pos (do not update, + * was fixed in _start func and will not change + * until _discard or _fixate) + */ + x1 = create_wire_info->points->coords[0]; + y1 = create_wire_info->points->coords[1]; + + // mid pos + if (create_wire_info->direction == WIRE_DIR_VERT) { + create_wire_info->points->coords[2] = x1; + create_wire_info->points->coords[3] = snapped_y; + } else if (create_wire_info->direction == WIRE_DIR_HORIZ) { + create_wire_info->points->coords[2] = snapped_x; + create_wire_info->points->coords[3] = y1; + } else { + create_wire_info->points->coords[2] = x1; + create_wire_info->points->coords[3] = y1; + } + NG_DEBUG ("update ~._.~ start=(%lf,%lf) → end=(%i,%i)", x1, y1, snapped_x, snapped_y); + // end pos + create_wire_info->points->coords[4] = snapped_x; + create_wire_info->points->coords[5] = snapped_y; + + g_assert (create_wire_info->line); + // required to trigger goocanvas update of the line object + g_object_set (G_OBJECT (create_wire_info->line), "points", create_wire_info->points, NULL); + pos.x = (gdouble)snapped_x; + pos.y = (gdouble)snapped_y; + /* Check if the pre-wire intersect another wire, and + * draw a small red circle to indicate the connection + */ + g_assert (create_wire_info->dot); + + const guint8 is_pin = node_store_is_pin_at_pos (store, pos); + const guint8 is_wire = node_store_is_wire_at_pos (store, pos); + + if (is_pin || is_wire) { + g_object_set (G_OBJECT (create_wire_info->dot), "x", new_x - 3.0, "y", new_y - 3.0, "width", + 6.0, "height", 6.0, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + } else { + g_object_set (G_OBJECT (create_wire_info->dot), "visibility", GOO_CANVAS_ITEM_INVISIBLE, + NULL); + } + goo_canvas_item_raise (GOO_CANVAS_ITEM (create_wire_info->line), NULL); + return TRUE; } -static int -create_wire_event (Sheet *sheet, const GdkEvent *event, CreateWireContext *cwc) +inline static gboolean create_wire_discard (Sheet *sheet, GdkEvent *event) { - int x1, y1, x2, y2; - gdouble snapped_x, snapped_y; - gboolean diagonal; - CreateWire *wire = cwc->create_wire; - Schematic *s; - NodeStore *store; - SheetPos pos; - int intersect; + CreateWireInfo *create_wire_info; - s = schematic_view_get_schematic_from_sheet (cwc->sheet); - store = schematic_get_store (s); + // g_signal_stop_emission_by_name (sheet, "event"); + NG_DEBUG ("wire got discarded"); + sheet_keyboard_ungrab (sheet, event); + sheet_pointer_ungrab (sheet, event); - if (event->type == GDK_2BUTTON_PRESS || - event->type == GDK_3BUTTON_PRESS) { - return FALSE; - } + create_wire_info = sheet->priv->create_wire_info; + g_object_set (G_OBJECT (create_wire_info->dot), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - 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: - - // 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 (sheet, "event"); - sheet_get_pointer (sheet, &snapped_x, &snapped_y); - fixate_wire (cwc, FALSE, snapped_x, snapped_y); - break; - - case 3: - g_signal_stop_emission_by_name (sheet, "event"); - cancel_wire (cwc); - break; - } - break; + g_object_set (G_OBJECT (create_wire_info->line), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); - case GDK_BUTTON_RELEASE: - g_signal_stop_emission_by_name (sheet, "event"); - - switch (event->button.button) { - case 1: - case 2: - case 4: - case 5: - // Don't care about middle button or mouse wheel. - return FALSE; - break; - } - return TRUE; - - case GDK_MOTION_NOTIFY: - g_signal_stop_emission_by_name (sheet, "event"); - - diagonal = event->button.state & GDK_SHIFT_MASK; - if (!diagonal && wire->direction == WIRE_DIR_DIAG) - wire->direction = WIRE_DIR_NONE; - - sheet_get_pointer (sheet, &snapped_x, &snapped_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; - } - - wire->points->coords[2] = x2; - wire->points->coords[3] = y2; - wire->points->coords[4] = snapped_x; - wire->points->coords[5] = snapped_y; - - g_object_set (G_OBJECT (wire->line), - "points", wire->points, - NULL); - - pos.x = snapped_x; - pos.y = snapped_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) { - g_object_set (G_OBJECT (cwc->dot_item), - "x", -3.0 + snapped_x, - "y", -3.0 + snapped_y, - "width", 6.0, - "height", 6.0, - NULL); - - g_object_set (cwc->dot_item, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); - } - else { - cwc->dot_item = goo_canvas_ellipse_new ( - GOO_CANVAS_ITEM (sheet->object_group), - snapped_x, snapped_y, 3.0, 3.0, - "fill_color", "red", - NULL); - - g_object_set (cwc->dot_item, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); - } - } - else { - if (cwc->dot_item) - g_object_set (cwc->dot_item, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); - } - - cwc->old_x = snapped_x; - cwc->old_y = snapped_y; - break; + create_wire_info->state = WIRE_START; - default: - return FALSE; - } - return TRUE; + return TRUE; } -static void -fixate_wire (CreateWireContext *cwc, gboolean always_fixate_both, - gdouble x, gdouble y) +inline static Wire *create_wire_spawn (Sheet *sheet, Coords start_pos, Coords end_pos) { - CreateWire *create_wire = cwc->create_wire; - SheetPos p1, p2, start_pos, end_pos, start_pos2, end_pos2; - gboolean cancel = FALSE; - NodeStore *store; - Schematic *schematic; + Wire *wire = NULL; + Coords length; - g_return_if_fail (cwc != NULL); - g_return_if_fail (create_wire != NULL); + NG_DEBUG ("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- spawning..."); + g_assert (sheet); + g_assert (IS_SHEET (sheet)); - schematic = schematic_view_get_schematic_from_sheet (cwc->sheet); - store = schematic_get_store (schematic); + wire = wire_new (sheet->grid); - 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; - } + length.x = end_pos.x - start_pos.x; + length.y = end_pos.y - start_pos.y; + wire_set_length (wire, &length); - // 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; - } + item_data_set_pos (ITEM_DATA (wire), &start_pos); + schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), ITEM_DATA (wire)); - 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; - } + NG_DEBUG ("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- spawning wire %p", wire); - // 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; - } - - create_wire_and_place_item (cwc->sheet, - start_pos2, end_pos2); - } - cancel_wire (cwc); - cancel = TRUE; - } + return wire; +} - create_wire_and_place_item (cwc->sheet, start_pos, end_pos); +#define FINISH_ON_WIRE_CLICK 0 +inline static gboolean create_wire_fixate (Sheet *sheet, GdkEvent *event) +{ + NodeStore *store; + Schematic *schematic; - if (cancel) - return; + Coords p1, p2, start_pos, end_pos, mid_pos; + CreateWireInfo *create_wire_info; + Wire *wire = NULL; + gboolean b_start_eq_mid, b_mid_eq_end; +#if FINISH_ON_WIRE_CLICK + gboolean b_finish; +#endif + double x, y; - // 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; + // g_signal_stop_emission_by_name (sheet, "event"); - g_object_set (G_OBJECT (create_wire->line), - "points", create_wire->points, - NULL); + x = event->button.x; + y = event->button.y; + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &x, &y); + snap_to_grid (sheet->grid, &x, &y); - // Raise the floting wire so that we can see it when it is overlapping - // other wires. - goo_canvas_item_raise (GOO_CANVAS_ITEM (create_wire->line), NULL); -} + create_wire_info = sheet->priv->create_wire_info; + g_assert (create_wire_info); + g_assert (create_wire_info->points); + g_assert (create_wire_info->line); -Wire * -create_wire_and_place_item (Sheet *sheet, SheetPos start_pos, - SheetPos end_pos) -{ - Wire *wire; - SheetPos length; + p1.x = create_wire_info->points->coords[0]; + p1.y = create_wire_info->points->coords[1]; - g_return_val_if_fail (sheet != NULL, NULL); - g_return_val_if_fail (IS_SHEET (sheet), NULL); + p2.x = x; + p2.y = y; - wire = wire_new (); - item_data_set_pos (ITEM_DATA (wire), &start_pos); + NG_DEBUG ("x: %lf ?= %lf | y: %lf ?= %lf", p1.x, p2.x, p1.y, p2.y); - length.x = end_pos.x - start_pos.x; - length.y = end_pos.y - start_pos.y; - wire_set_length (wire, &length); + // if we are back at the starting point of our wire, + // and the user tries to fixate, just ignore it + // and mark the event as handled + if (coords_equal (&p1, &p2)) + return TRUE; - schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), - ITEM_DATA (wire)); + schematic = schematic_view_get_schematic_from_sheet (sheet); + g_assert (schematic); + store = schematic_get_store (schematic); + g_assert (store); - return wire; -} + start_pos.x = create_wire_info->points->coords[0]; + start_pos.y = create_wire_info->points->coords[1]; -static void -cancel_wire (CreateWireContext *cwc) -{ - CreateWire *create_wire; + mid_pos.x = create_wire_info->points->coords[2]; + mid_pos.y = create_wire_info->points->coords[3]; - create_wire = cwc->create_wire; + end_pos.x = create_wire_info->points->coords[4]; + end_pos.y = create_wire_info->points->coords[5]; - g_return_if_fail (create_wire != NULL); + NG_DEBUG ("A(%g, %g) B(%g, %g) -> same = %i", start_pos.x, start_pos.y, mid_pos.x, mid_pos.y, + coords_equal (&start_pos, &mid_pos)); + NG_DEBUG ("A(%g, %g) B(%g, %g) -> same = %i", mid_pos.x, mid_pos.y, end_pos.x, end_pos.y, + coords_equal (&mid_pos, &end_pos)); - g_signal_handler_disconnect (G_OBJECT (cwc->sheet), cwc->draw_handler_id); - cwc->draw_handler_id = 0; - cwc->active = FALSE; +#if FINISH_ON_WIRE_CLICK + // check for wires _before_ spawning wires + // otherwise we will end up with 1 anyways + b_finish = node_store_get_node (store, end_pos) || node_store_is_wire_at_pos (store, end_pos); +#endif + b_start_eq_mid = coords_equal (&start_pos, &mid_pos); + b_mid_eq_end = coords_equal (&mid_pos, &end_pos); - goo_canvas_points_unref (create_wire->points); + if (!b_mid_eq_end && !b_start_eq_mid) { + NG_DEBUG ("we should get exactly 2 wires"); + } - if (cwc->dot_item) { - goo_canvas_item_remove (GOO_CANVAS_ITEM (cwc->dot_item)); - cwc->dot_item = NULL; + if (!b_start_eq_mid) { + wire = create_wire_spawn (sheet, start_pos, mid_pos); + g_assert (wire); + g_assert (IS_WIRE (wire)); + } else { + NG_DEBUG ("looks like start == midpos"); } - goo_canvas_item_remove (GOO_CANVAS_ITEM (create_wire->line)); - g_free (create_wire); + if (!b_mid_eq_end) { + wire = create_wire_spawn (sheet, mid_pos, end_pos); + g_assert (wire); + g_assert (IS_WIRE (wire)); + } else { + NG_DEBUG ("looks like midpos == endpos"); + } - // Setup the sheet for a new wire creation process. +/* check if target location is either wire or node, + * if so, + * set state to START and fixate both ends of the current wire + */ +#if FINISH_ON_WIRE_CLICK + /* + * auto-finish if the target is a wire or node + */ + if (b_finish) + return create_wire_discard (sheet, event); +#endif + /* + * update all position/cursor data for the next wire + * (consider this is a implicit START call, with less bloat) + */ + x = create_wire_info->points->coords[4]; + y = create_wire_info->points->coords[5]; + create_wire_info->points->coords[0] = x; + create_wire_info->points->coords[1] = y; + create_wire_info->points->coords[2] = x; + create_wire_info->points->coords[3] = y; + + // required to trigger goocanvas update of the line object + g_object_set (G_OBJECT (create_wire_info->line), "points", create_wire_info->points, NULL); + + // toggle wire direction + if (create_wire_info->direction == WIRE_DIR_VERT) + create_wire_info->direction = WIRE_DIR_HORIZ; + else + create_wire_info->direction = WIRE_DIR_VERT; + + goo_canvas_item_raise (GOO_CANVAS_ITEM (create_wire_info->line), NULL); + + /* + * do NOT ungrab sheet here, as we are still creating another wire + * this should be changed if on fixate means that we are back in state + * WIRE_START + */ + return TRUE; } -static void -exit_wire_mode (CreateWireContext *cwc) +gboolean create_wire_setup (Sheet *sheet) { - if (cwc->draw_handler_id != 0) - cancel_wire (cwc); + CreateWireInfo *create_wire_info; - if (cwc->start_handler_id != 0) { - g_signal_handler_disconnect (G_OBJECT (cwc->sheet), - cwc->start_handler_id); - cwc->start_handler_id = 0; - } + g_return_val_if_fail (sheet, FALSE); + g_return_val_if_fail (IS_SHEET (sheet), FALSE); - if (cwc->create_wire_cancel_id != 0) { - g_signal_handler_disconnect (G_OBJECT (cwc->sheet), - cwc->create_wire_cancel_id); - cwc->create_wire_cancel_id = 0; + create_wire_info = sheet->priv->create_wire_info; + g_return_val_if_fail (create_wire_info, FALSE); + + if (create_wire_info->state == WIRE_DISABLED) { + create_wire_info->event_handler_id = + g_signal_connect (sheet, "event", G_CALLBACK (create_wire_event), NULL); + // this is also handled by event + // create_wire_info->cancel_handler_id = g_signal_connect (sheet, + //"cancel", + // G_CALLBACK + //(create_wire_discard), NULL); } - schematic_view_reset_tool ( - schematic_view_get_schematicview_from_sheet (cwc->sheet)); - g_free (cwc); + create_wire_info->state = WIRE_START; + return TRUE; } -void -create_wire_exit (CreateWireContext *cwc) +gboolean create_wire_orientationtoggle (Sheet *sheet) { - if (cwc->draw_handler_id != 0) - cancel_wire (cwc); + CreateWireInfo *create_wire_info; + GooCanvasPoints *points; + g_return_val_if_fail (sheet, FALSE); + g_return_val_if_fail (IS_SHEET (sheet), FALSE); - if (cwc->start_handler_id != 0) { - g_signal_handler_disconnect (G_OBJECT (cwc->sheet), - cwc->start_handler_id); - cwc->start_handler_id = 0; + NG_DEBUG ("toggle orientation") + create_wire_info = sheet->priv->create_wire_info; + g_return_val_if_fail (create_wire_info, FALSE); + + points = create_wire_info->points; + + switch (create_wire_info->direction) { + case WIRE_DIR_HORIZ: + create_wire_info->direction = WIRE_DIR_VERT; + points->coords[2] = points->coords[0]; + points->coords[3] = points->coords[5]; + g_object_set (G_OBJECT (create_wire_info->line), "points", points, NULL); + break; + case WIRE_DIR_VERT: + create_wire_info->direction = WIRE_DIR_HORIZ; + points->coords[2] = points->coords[4]; + points->coords[3] = points->coords[1]; + g_object_set (G_OBJECT (create_wire_info->line), "points", points, NULL); + break; + default: + break; } + return TRUE; } -// Signal handler for the "cancel" signal that the sheet emits -// when <escape> is pressed. -static int -create_wire_cancel (Sheet *sheet, CreateWireContext *cwc) +gboolean create_wire_event (Sheet *sheet, GdkEvent *event, gpointer data) { - g_return_val_if_fail (sheet != NULL, FALSE); + CreateWireInfo *create_wire_info; + g_return_val_if_fail (sheet, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); - if (cwc->active) - cancel_wire (cwc); - else - exit_wire_mode (cwc); + create_wire_info = sheet->priv->create_wire_info; + g_return_val_if_fail (create_wire_info, FALSE); + + switch (event->type) { + case GDK_3BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + break; + case GDK_BUTTON_PRESS: + switch (event->button.button) { + case 1: + if (create_wire_info->state == WIRE_START) + return create_wire_start (sheet, event); + else if (create_wire_info->state == WIRE_ACTIVE) + return create_wire_fixate (sheet, event); + break; + case 3: + if (create_wire_info->state == WIRE_ACTIVE) + return create_wire_discard (sheet, event); + break; + default: + break; + } + break; + case GDK_MOTION_NOTIFY: + if (create_wire_info->state == WIRE_ACTIVE) + return create_wire_update (sheet, event); + break; + case GDK_KEY_PRESS: + NG_DEBUG ("keypress 0"); + if (create_wire_info->state != WIRE_ACTIVE) + return FALSE; + NG_DEBUG ("keypress 1"); + switch (event->key.keyval) { + case GDK_KEY_Escape: + return create_wire_discard (sheet, event); + case GDK_KEY_R: + case GDK_KEY_r: + return create_wire_orientationtoggle (sheet); + default: + break; + } + default: + break; + } + return FALSE; +} + +gboolean create_wire_cleanup (Sheet *sheet) +{ + CreateWireInfo *create_wire_info; + + create_wire_info = sheet->priv->create_wire_info; + if (create_wire_info && create_wire_info->state != WIRE_DISABLED) { + g_object_set (G_OBJECT (create_wire_info->line), "visibility", GOO_CANVAS_ITEM_INVISIBLE, + NULL); + create_wire_info->state = WIRE_DISABLED; + g_signal_handler_disconnect (G_OBJECT (sheet), create_wire_info->event_handler_id); + // g_signal_handler_disconnect (G_OBJECT (sheet), + // create_wire_info->cancel_handler_id); + } return TRUE; } diff --git a/src/sheet/create-wire.h b/src/sheet/create-wire.h index 87c622d..947a98e 100644 --- a/src/sheet/create-wire.h +++ b/src/sheet/create-wire.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __CREATE_WIRE_H @@ -40,15 +40,26 @@ #include "wire-item.h" #include "schematic-view.h" -typedef struct _CreateWireContext CreateWireContext; +typedef enum { WIRE_START, WIRE_ACTIVE, WIRE_DISABLED } WireState; -typedef struct { +typedef struct _CreateWireInfo CreateWireInfo; + +struct _CreateWireInfo +{ + WireState state; GooCanvasPolyline *line; - GooCanvasPoints * points; - WireDir direction; // Direction of the first wire segment. -} CreateWire; + GooCanvasPoints *points; + GooCanvasEllipse *dot; + WireDir direction; + gulong event_handler_id; + // gulong cancel_handler_id; +}; -CreateWireContext * create_wire_initiate (Sheet *sheet); -void create_wire_exit (CreateWireContext *cwc); +CreateWireInfo *create_wire_info_new (Sheet *sheet); +void create_wire_destroy (CreateWireInfo *wire_info); +gboolean create_wire_setup (Sheet *sheet); +gboolean create_wire_orientationtoggle (Sheet *sheet); +gboolean create_wire_event (Sheet *sheet, GdkEvent *event, gpointer data); +gboolean create_wire_cleanup (Sheet *sheet); #endif diff --git a/src/sheet/grid.c b/src/sheet/grid.c index 63cbd8c..f2db618 100644 --- a/src/sheet/grid.c +++ b/src/sheet/grid.c @@ -6,12 +6,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * * Web page: https://github.com/marc-lorber/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,32 +29,27 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <math.h> #include "grid.h" +#define ROUND(x) (floor ((x)+0.5)) +#include "debug.h" -#define ROUND(x) (floor((x)+0.5)) -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +enum { ARG_0, ARG_COLOR, ARG_SPACING, ARG_SNAP }; -enum { - ARG_0, - ARG_COLOR, - ARG_SPACING, - ARG_SNAP -}; - -struct _GridPriv{ - GooCanvasItem * canvas_grid; - guint snap; - GdkColor color; - gdouble spacing; - gdouble cached_zoom; - cairo_t * cairo; +struct _GridPriv +{ + GooCanvasItem *canvas_grid; + guint snap; + GdkRGBA color; + gdouble spacing; + gdouble cached_zoom; + cairo_t *cairo; }; G_DEFINE_TYPE (Grid, grid, GOO_TYPE_CANVAS_GROUP) @@ -58,14 +57,12 @@ G_DEFINE_TYPE (Grid, grid, GOO_TYPE_CANVAS_GROUP) static void grid_class_init (GridClass *class); static void grid_init (Grid *grid); static void grid_finalize (GObject *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_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_dispose (GObject *object); -static void -grid_class_init (GridClass *class) +static void grid_class_init (GridClass *class) { GObjectClass *object_class; @@ -73,27 +70,26 @@ grid_class_init (GridClass *class) grid_parent_class = g_type_class_peek_parent (class); object_class->dispose = grid_dispose; - object_class->finalize = grid_finalize; + object_class->finalize = grid_finalize; 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_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)); + 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)); } -static void -grid_init (Grid *grid) +static void grid_init (Grid *grid) { GridPriv *priv; @@ -105,14 +101,9 @@ grid_init (Grid *grid) priv->snap = TRUE; } -static void -grid_dispose (GObject *object) -{ - G_OBJECT_CLASS (grid_parent_class)->dispose (object); -} +static void grid_dispose (GObject *object) { G_OBJECT_CLASS (grid_parent_class)->dispose (object); } -static void -grid_finalize (GObject *object) +static void grid_finalize (GObject *object) { Grid *grid; @@ -122,9 +113,8 @@ grid_finalize (GObject *object) G_OBJECT_CLASS (grid_parent_class)->finalize (object); } -static void -grid_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *spec) +static void grid_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) { Grid *grid; GridPriv *priv; @@ -146,9 +136,7 @@ grid_set_property (GObject *object, guint prop_id, const GValue *value, } } -static void -grid_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +static void grid_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) { Grid *grid; GridPriv *priv; @@ -169,39 +157,25 @@ grid_get_property (GObject *object, guint prop_id, GValue *value, } } -Grid * -grid_create (GooCanvasItem *root, gdouble width, gdouble height) +Grid *grid_new (GooCanvasItem *root, gdouble width, gdouble height) { - Grid * grid = NULL; + Grid *grid = NULL; grid = g_object_new (TYPE_GRID, NULL); - g_object_set (G_OBJECT (grid), - "parent", root, - NULL); - - grid->priv->canvas_grid = goo_canvas_grid_new (GOO_CANVAS_ITEM (grid), - 0.0, - 0.0, - width, - height, - 10.0, - 10.0, - 0.0, - 0.0, - "horz-grid-line-width", 0.05, - "horz-grid-line-color", "dark gray", - "vert-grid-line-width", 0.05, - "vert-grid-line-color", "dark gray", - NULL); - + g_object_set (G_OBJECT (grid), "parent", root, NULL); + + grid->priv->canvas_grid = goo_canvas_grid_new ( + GOO_CANVAS_ITEM (grid), 0.0, 0.0, width, height, 10.0, 10.0, 0.0, 0.0, + "horz-grid-line-width", 0.05, "horz-grid-line-color", "dark gray", "vert-grid-line-width", + 0.05, "vert-grid-line-color", "dark gray", NULL); + grid_show (grid, TRUE); grid_snap (grid, TRUE); return grid; } -inline void -snap_to_grid (Grid *grid, gdouble *x, gdouble *y) +inline gboolean snap_to_grid (Grid *grid, gdouble *x, gdouble *y) { GridPriv *priv; gdouble spacing; @@ -209,27 +183,33 @@ snap_to_grid (Grid *grid, gdouble *x, gdouble *y) priv = grid->priv; spacing = priv->spacing; - if (priv->snap) { - if (x) *x = ROUND ((*x) / spacing) * spacing; - if (y) *y = ROUND ((*y) / spacing) * spacing; + Coords old = {0., 0.}; + gboolean moved = FALSE; + + if (G_LIKELY (priv->snap)) { + if (G_LIKELY (x)) { + old.x = *x; + *x = ROUND ((*x) / spacing) * spacing; + moved = moved || (fabs ((*x) - old.x) > COORDS_DELTA); + } + if (G_LIKELY (y)) { + old.y = *y; + *y = ROUND ((*y) / spacing) * spacing; + moved = moved || (fabs ((*y) - old.y) > COORDS_DELTA); + } } + return moved; } -void -grid_show (Grid *grid, gboolean show) +void grid_show (Grid *grid, gboolean show) { if (show) - g_object_set (G_OBJECT (grid), - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); + g_object_set (G_OBJECT (grid), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); else - g_object_set (G_OBJECT (grid), - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + g_object_set (G_OBJECT (grid), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } -void -grid_snap (Grid *grid, gboolean snap) +void grid_snap (Grid *grid, gboolean snap) { GridPriv *priv; diff --git a/src/sheet/grid.h b/src/sheet/grid.h index 88bba3f..868b7cb 100644 --- a/src/sheet/grid.h +++ b/src/sheet/grid.h @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __GRID_H @@ -36,34 +36,36 @@ #include <glib.h> #include <gtk/gtk.h> #include <goocanvas.h> +#include "coords.h" -#define TYPE_GRID (grid_get_type()) -#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 ())) +#define TYPE_GRID (grid_get_type ()) +#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; typedef struct _GridPriv GridPriv; - -struct _Grid { +struct _Grid +{ GooCanvasGroup canvas_group; - gdouble width; - gdouble height; - gdouble x; - gdouble y; - GridPriv * priv; + gdouble width; + gdouble height; + gdouble x; + gdouble y; + GridPriv *priv; }; -struct _GridClass { - GooCanvasGroupClass parent_class; +struct _GridClass +{ + GooCanvasGroupClass parent_class; }; -Grid *grid_create (GooCanvasItem *root, gdouble width, gdouble height); +Grid *grid_new (GooCanvasItem *root, gdouble width, gdouble height); 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); +gboolean snap_to_grid (Grid *grid, double *x, double *y); +void grid_show (Grid *grid, gboolean snap); +void grid_snap (Grid *grid, gboolean snap); #endif diff --git a/src/sheet/node-item.c b/src/sheet/node-item.c index cd20020..d7a6d95 100644 --- a/src/sheet/node-item.c +++ b/src/sheet/node-item.c @@ -6,7 +6,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -24,53 +24,65 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> - +#include "options.h" #include "node-item.h" -static void node_item_init (NodeItem *item); -static void node_item_class_init (NodeItemClass *klass); +static void node_item_init (NodeItem *item); +static void node_item_class_init (NodeItemClass *klass); -struct _NodeItemPriv { +struct _NodeItemPriv +{ GooCanvasItem *dot_item; + GooCanvasItem *circle_item; // debug }; -G_DEFINE_TYPE (NodeItem, node_item, GOO_TYPE_CANVAS_GROUP) +G_DEFINE_TYPE (NodeItem, node_item, GOO_TYPE_CANVAS_GROUP); + +static void node_item_dispose (GObject *object) +{ + NodeItem *item = NODE_ITEM (object); + NodeItemPriv *priv = item->priv; + + g_clear_object (&(priv->dot_item)); + g_clear_object (&(priv->circle_item)); + G_OBJECT_CLASS (node_item_parent_class)->dispose (object); +} -static void -node_item_class_init (NodeItemClass *klass) +static void node_item_finalize (GObject *object) { - node_item_parent_class = g_type_class_peek_parent (klass); + G_OBJECT_CLASS (node_item_parent_class)->finalize (object); +} + +static void node_item_class_init (NodeItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = node_item_finalize; + object_class->dispose = node_item_dispose; } -static void -node_item_init (NodeItem *item) +static void node_item_init (NodeItem *item) { item->priv = g_new0 (NodeItemPriv, 1); + item->priv->dot_item = + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (item), 0.0, 0.0, 2.0, 2.0, "fill-color", "black", + "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + item->priv->circle_item = goo_canvas_ellipse_new ( + GOO_CANVAS_ITEM (item), 0.0, 0.0, 3.0, 3.0, "stroke-color-rgba", 0x3399FFFF, "line-width", + 1.0, "visibility", + oregano_options_debug_dots () ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); } -void -node_item_show_dot (NodeItem *item, gboolean show) +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 = goo_canvas_ellipse_new ( - GOO_CANVAS_ITEM (item), - 0.0, 0.0, 2.0, 2.0, - "fill_color", "black", - NULL); - } - g_object_set (item->priv->dot_item, - "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); - } - else if (item->priv->dot_item != NULL) - g_object_set (item->priv->dot_item, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + g_object_set (item->priv->dot_item, "visibility", + show ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); } diff --git a/src/sheet/node-item.h b/src/sheet/node-item.h index f64bba1..c1f5986 100644 --- a/src/sheet/node-item.h +++ b/src/sheet/node-item.h @@ -9,7 +9,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -27,8 +27,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __NODE_ITEM_H__ #define __NODE_ITEM_H__ @@ -36,21 +36,20 @@ #include <gtk/gtk.h> #include <goocanvas.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 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 _NodeItem NodeItem; typedef struct _NodeItemClass NodeItemClass; -typedef struct _NodeItemPriv NodeItemPriv; +typedef struct _NodeItemPriv NodeItemPriv; struct _NodeItem { GooCanvasGroup parent; - NodeItemPriv *priv; + NodeItemPriv *priv; }; struct _NodeItemClass @@ -58,9 +57,8 @@ struct _NodeItemClass GooCanvasGroupClass parent_class; }; - -GType node_item_get_type (void); -GtkWidget *node_item_new (void); -void node_item_show_dot (NodeItem *item, gboolean show); +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 index 1d7a3de..90cdd44 100644 --- a/src/sheet/part-item.c +++ b/src/sheet/part-item.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <glib/gi18n.h> @@ -46,52 +48,46 @@ #include "stock.h" #include "dialogs.h" #include "sheet.h" - +#include "oregano-utils.h" +#include "options.h" #define NORMAL_COLOR "red" #define LABEL_COLOR "dark cyan" #define SELECTED_COLOR "green" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -static void part_item_class_init (PartItemClass *klass); -static void part_item_init (PartItem *gspart); -static void part_item_finalize (GObject *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 (Sheet *sheet, 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, Sheet *sheet); -static void part_item_place_ghost (SheetItem *item, Sheet *sheet); -static void create_canvas_items (GooCanvasGroup *group, - LibraryPart *library_part); -static void create_canvas_labels (PartItem *item, Part *part); -static void create_canvas_label_nodes (PartItem *item, Part *part); -static PartItem * part_item_canvas_new (Sheet *sheet, 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); -static void part_item_dispose (GObject *object); +#include "debug.h" + +static void part_item_class_init (PartItemClass *klass); +static void part_item_init (PartItem *gspart); +static void part_item_finalize (GObject *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, Coords *p1, Coords *p2); +inline static void get_cached_bounds (PartItem *item, Coords *p1, Coords *p2); +static void show_labels (SheetItem *sheet_item, gboolean show); +static void part_item_paste (Sheet *sheet, ItemData *data); +static void part_rotated_callback (ItemData *data, int angle, SheetItem *item); +static void part_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item); +static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item); +static void part_changed_callback (ItemData *data, SheetItem *sheet_item); + +static void part_item_place (SheetItem *item, Sheet *sheet); +static void part_item_place_ghost (SheetItem *item, Sheet *sheet); +static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part); +static void create_canvas_labels (PartItem *item, Part *part); +static void create_canvas_label_nodes (PartItem *item, Part *part); +static PartItem *part_item_canvas_new (Sheet *sheet, 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); +static void part_item_dispose (GObject *object); static GooCanvasAnchorType part_item_get_anchor_from_part (Part *part); - enum { ARG_0, ARG_DATA, @@ -105,52 +101,47 @@ enum { ARG_MODEL }; -struct _PartItemPriv { - guint cache_valid : 1; - GooCanvasItem *label_group; - GSList *label_items; - GooCanvasItem *node_group; - GSList *label_nodes; +struct _PartItemPriv +{ + guint cache_valid : 1; + GooCanvasItem *label_group; + GSList *label_items; + GooCanvasItem *node_group; + GSList *label_nodes; + + GooCanvasItem *rect; // Cached bounding box. This is used to make // the rubberband selection a bit faster. - SheetPos bbox_start; - SheetPos bbox_end; + Coords bbox_start; + Coords bbox_end; }; -typedef struct { +typedef struct +{ GtkDialog *dialog; - PartItem *part_item; + PartItem *part_item; // List of GtkEntry's - GList *widgets; + 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} -}; +static const char *part_item_context_menu = "<ui>" + " <popup name='ItemMenu'>" + " <menuitem action='ObjectProperties'/>" + " </popup>" + "</ui>"; -enum { - ANCHOR_NORTH, - ANCHOR_SOUTH, - ANCHOR_WEST, - ANCHOR_EAST -}; +static GtkActionEntry action_entries[] = {{"ObjectProperties", GTK_STOCK_PROPERTIES, + N_ ("_Object Properties..."), NULL, + N_ ("Modify object properties"), NULL}}; + +enum { ANCHOR_NORTH, ANCHOR_SOUTH, ANCHOR_WEST, ANCHOR_EAST }; G_DEFINE_TYPE (PartItem, part_item, TYPE_SHEET_ITEM) -static void -part_item_class_init (PartItemClass *part_item_class) +static void part_item_class_init (PartItemClass *part_item_class) { GObjectClass *object_class; SheetItemClass *sheet_item_class; @@ -169,33 +160,31 @@ part_item_class_init (PartItemClass *part_item_class) 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->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) +static void part_item_init (PartItem *item) { PartItemPriv *priv; - priv = g_new0 (PartItemPriv, 1); - + priv = g_slice_new0 (PartItemPriv); + priv->rect = NULL; 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)); + 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) +static void part_item_set_property (GObject *object, guint propety_id, const GValue *value, + GParamSpec *pspec) { g_return_if_fail (object != NULL); - g_return_if_fail (IS_PART_ITEM(object)); + g_return_if_fail (IS_PART_ITEM (object)); switch (propety_id) { default: @@ -203,9 +192,8 @@ part_item_set_property (GObject *object, guint propety_id, const GValue *value, } } -static void -part_item_get_property (GObject *object, guint propety_id, GValue *value, - GParamSpec *pspec) +static void part_item_get_property (GObject *object, guint propety_id, GValue *value, + GParamSpec *pspec) { g_return_if_fail (object != NULL); g_return_if_fail (IS_PART_ITEM (object)); @@ -217,32 +205,26 @@ part_item_get_property (GObject *object, guint propety_id, GValue *value, } } -static void -part_item_dispose (GObject *object) -{ - G_OBJECT_CLASS (parent_class)->dispose (object); -} +static void part_item_dispose (GObject *object) { G_OBJECT_CLASS (parent_class)->dispose (object); } -static void -part_item_finalize (GObject *object) +static void part_item_finalize (GObject *object) { PartItemPriv *priv; priv = PART_ITEM (object)->priv; - if (priv->label_nodes) { - g_slist_free (priv->label_nodes); - } - if (priv->label_items) { - g_slist_free (priv->label_items); - } - g_free (priv); + g_slist_free (priv->label_nodes); + g_slist_free (priv->label_items); + g_slice_free (PartItemPriv, priv); priv = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } -static void -part_item_set_label_items (PartItem *item, GSList *item_list) +//////////////////////////////////////////////////////////////////////////////// +// END BOILER PLATE +//////////////////////////////////////////////////////////////////////////////// + +static void part_item_set_label_items (PartItem *item, GSList *item_list) { PartItemPriv *priv; @@ -257,22 +239,16 @@ part_item_set_label_items (PartItem *item, GSList *item_list) priv->label_items = item_list; } -// "moved" signal handler. Invalidates the bounding box cache. -static void -part_item_moved (SheetItem *sheet_item) +static void part_item_moved (SheetItem *sheet_item) { - PartItem *part_item; - - part_item = PART_ITEM (sheet_item); - part_item->priv->cache_valid = FALSE; + // g_warning ("part MOVED callback called - LEGACY"); } -PartItem * -part_item_canvas_new (Sheet *sheet, Part *part) +PartItem *part_item_canvas_new (Sheet *sheet, Part *part) { PartItem *part_item; PartItemPriv *priv; - GooCanvasItem *item; + GooCanvasItem *goo_item; ItemData *item_data; g_return_val_if_fail (sheet != NULL, NULL); @@ -280,52 +256,43 @@ part_item_canvas_new (Sheet *sheet, Part *part) g_return_val_if_fail (part != NULL, NULL); g_return_val_if_fail (IS_PART (part), NULL); - item = g_object_new (TYPE_PART_ITEM, NULL); - - g_object_set (item, - "parent", sheet->object_group, - NULL); - - part_item = PART_ITEM (item); - g_object_set (part_item, - "data", part, - NULL); + part_item = g_object_new (TYPE_PART_ITEM, NULL); + goo_item = GOO_CANVAS_ITEM (part_item); + + g_object_set (part_item, "parent", sheet->object_group, NULL); + + g_object_set (part_item, "data", part, NULL); + priv = part_item->priv; - - priv->label_group = GOO_CANVAS_ITEM (goo_canvas_group_new ( - GOO_CANVAS_ITEM (part_item), - "width", -1.0, - "height", -1.0, - NULL)); - g_object_unref (item); - - priv->node_group = GOO_CANVAS_ITEM (goo_canvas_group_new ( - GOO_CANVAS_ITEM (part_item), - NULL)); - - g_object_set (GOO_CANVAS_ITEM (priv->node_group), - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + + Coords b1, b2; + item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); + + priv->rect = goo_canvas_rect_new ( + goo_item, b1.x, b1.y, b2.x - b1.x, b2.y - b1.y, "stroke-color", "green", "line-width", .0, + "fill-color-rgba", 0x7733aa66, "radius-x", 1.0, "radius-y", 1.0, "visibility", + oregano_options_debug_boxes () ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); + + priv->label_group = goo_canvas_group_new (goo_item, "width", -1.0, "height", -1.0, NULL); + + priv->node_group = goo_canvas_group_new (goo_item, NULL); + + g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); item_data = ITEM_DATA (part); - item_data->rotated_handler_id = g_signal_connect_object (G_OBJECT (part), - "rotated", - G_CALLBACK (part_rotated_callback), - G_OBJECT (part_item), 0); - item_data->flipped_handler_id = g_signal_connect_object (G_OBJECT (part), - "flipped", - G_CALLBACK (part_flipped_callback), - G_OBJECT (part_item), 0); - item_data->moved_handler_id = g_signal_connect_object (G_OBJECT (part), - "moved", - G_CALLBACK (part_moved_callback), - G_OBJECT (part_item), 0); + item_data->rotated_handler_id = + g_signal_connect_object (part, "rotated", G_CALLBACK (part_rotated_callback), part_item, 0); + item_data->flipped_handler_id = + g_signal_connect_object (part, "flipped", G_CALLBACK (part_flipped_callback), part_item, 0); + item_data->moved_handler_id = + g_signal_connect_object (part, "moved", G_CALLBACK (part_moved_callback), part_item, 0); + item_data->changed_handler_id = + g_signal_connect_object (part, "changed", G_CALLBACK (part_changed_callback), part_item, 0); return part_item; } -static void -update_canvas_labels (PartItem *item) +static void update_canvas_labels (PartItem *item) { PartItemPriv *priv; Part *part; @@ -344,20 +311,17 @@ update_canvas_labels (PartItem *item) for (labels = part_get_labels (part); labels; labels = labels->next, label_items = label_items->next) { char *text; - PartLabel *label = (PartLabel*) labels->data; + PartLabel *label = (PartLabel *)labels->data; g_assert (label_items != NULL); canvas_item = label_items->data; text = part_property_expand_macros (part, label->text); - g_object_set (canvas_item, - "text", text, - NULL); + g_object_set (canvas_item, "text", text, NULL); g_free (text); } } -void -part_item_update_node_label (PartItem *item) +void part_item_update_node_label (PartItem *item) { PartItemPriv *priv; Part *part; @@ -371,60 +335,51 @@ part_item_update_node_label (PartItem *item) priv = item->priv; part = PART (sheet_item_get_data (SHEET_ITEM (item))); - g_return_if_fail (IS_PART (part) ); + g_return_if_fail (IS_PART (part)); // Put the label of each node num_pins = part_get_num_pins (part); - + if (num_pins == 1) { pins = part_get_pins (part); labels = priv->label_nodes; - for (labels = priv->label_nodes; labels; labels=labels->next) { + for (labels = priv->label_nodes; labels; labels = labels->next) { char *txt; txt = g_strdup_printf ("V(%d)", pins[0].node_nr); canvas_item = labels->data; if (pins[0].node_nr != 0) - g_object_set (canvas_item, - "text", txt, - "fill_color", LABEL_COLOR, - "font", "Sans 8", - NULL); + g_object_set (canvas_item, "text", txt, "fill_color", LABEL_COLOR, "font", "Sans 8", + NULL); else - g_object_set (canvas_item, - "text", "", - NULL); + g_object_set (canvas_item, "text", "", NULL); g_free (txt); } } } -static void -prop_dialog_destroy (GtkWidget *widget, PartPropDialog *prop_dialog) +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) +static void prop_dialog_response (GtkWidget *dialog, gint response, PartPropDialog *prop_dialog) { - GSList *props; - GList *widget; - Property *prop; - PartItem *item; - Part *part; - gchar *prop_name; - const gchar *prop_value; - GtkWidget *w; + GSList *props = NULL; + GList *widget; + Property *prop; + PartItem *item; + Part *part; + gchar *prop_name; + const gchar *prop_value; + GtkWidget *w; item = prop_dialog->part_item; part = PART (sheet_item_get_data (SHEET_ITEM (item))); - for (widget = prop_dialog->widgets; widget; - widget = widget->next) { + for (widget = prop_dialog->widgets; widget; widget = widget->next) { w = widget->data; prop_name = g_object_get_data (G_OBJECT (w), "user"); @@ -433,26 +388,22 @@ prop_dialog_response (GtkWidget *dialog, gint response, for (props = part_get_properties (part); props; props = props->next) { prop = props->data; if (g_ascii_strcasecmp (prop->name, prop_name) == 0) { - if (prop->value) g_free (prop->value); + g_free (prop->value); prop->value = g_strdup (prop_value); } } g_free (prop_name); } - g_slist_free_full (props, g_object_unref); - g_list_free_full (widget, g_object_unref); update_canvas_labels (item); } -static void -edit_properties_point (PartItem *item) +static void edit_properties_point (PartItem *item) { GSList *properties; Part *part; - char *msg; GtkBuilder *gui; - GError *perror = NULL; + GError *error = NULL; GtkRadioButton *radio_v, *radio_c; GtkRadioButton *ac_r, *ac_m, *ac_i, *ac_p; GtkCheckButton *chk_db; @@ -460,38 +411,22 @@ edit_properties_point (PartItem *item) part = PART (sheet_item_get_data (SHEET_ITEM (item))); if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create part properties dialog.")); - return; - } - else - gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test( - OREGANO_UIDIR "/clamp-properties-dialog.ui", - 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_UIDIR "/clamp-properties-dialog.ui"); - oregano_error_with_title (_("Could not create part properties dialog."), msg); - g_free (msg); + oregano_error (_ ("Could not create part properties dialog.")); return; } + gtk_builder_set_translation_domain (gui, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/clamp-properties-dialog.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create part properties dialog."), msg); - g_error_free (perror); + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/clamp-properties-dialog.ui", &error) <= 0) { + oregano_error_with_title (_ ("Could not create part properties dialog."), error->message); + g_error_free (error); return; - } + } prop_dialog = g_new0 (PartPropDialog, 1); prop_dialog->part_item = item; - prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, - "clamp-properties-dialog")); + prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, "clamp-properties-dialog")); radio_v = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_v")); radio_c = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_c")); @@ -504,10 +439,9 @@ edit_properties_point (PartItem *item) ac_i = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_i")); chk_db = GTK_CHECK_BUTTON (gtk_builder_get_object (gui, "check_db")); - + // Setup GUI from properties - for (properties = part_get_properties (part); properties; - properties = properties->next) { + for (properties = part_get_properties (part); properties; properties = properties->next) { Property *prop; prop = properties->data; if (prop->name) { @@ -516,29 +450,21 @@ edit_properties_point (PartItem *item) if (!g_ascii_strcasecmp (prop->name, "type")) { if (!g_ascii_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); + 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_ascii_strcasecmp (prop->name, "ac_type")) { + } else if (!g_ascii_strcasecmp (prop->name, "ac_type")) { if (!g_ascii_strcasecmp (prop->value, "m")) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_m), TRUE); - } - else if (!g_ascii_strcasecmp (prop->value, "i")) { + } else if (!g_ascii_strcasecmp (prop->value, "i")) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_i), TRUE); - } - else if (!g_ascii_strcasecmp (prop->value, "p")) { + } else if (!g_ascii_strcasecmp (prop->value, "p")) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_p), TRUE); - } - else if (!g_ascii_strcasecmp (prop->value, "r")) { + } else if (!g_ascii_strcasecmp (prop->value, "r")) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_r), TRUE); } - } - else if (!g_ascii_strcasecmp (prop->name, "ac_db")) { + } else if (!g_ascii_strcasecmp (prop->name, "ac_db")) { if (!g_ascii_strcasecmp (prop->value, "true")) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chk_db), TRUE); } @@ -548,40 +474,33 @@ edit_properties_point (PartItem *item) gtk_dialog_run (prop_dialog->dialog); // Save properties from GUI - for (properties = part_get_properties (part); properties; - properties = properties->next) { + for (properties = part_get_properties (part); properties; properties = properties->next) { Property *prop; prop = properties->data; if (prop->name) { if (!g_ascii_strcasecmp (prop->name, "internal")) continue; - + if (!g_ascii_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 { + } else { prop->value = g_strdup ("i"); } - } - else if (!g_ascii_strcasecmp (prop->name, "ac_type")) { + } else if (!g_ascii_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))) { + } 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))) { + } 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))) { + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_r))) { prop->value = g_strdup ("r"); } - } - else if (!g_ascii_strcasecmp (prop->name, "ac_db")) { + } else if (!g_ascii_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"); @@ -590,20 +509,18 @@ edit_properties_point (PartItem *item) } } } - g_slist_free_full (properties, g_object_unref); gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); } -static void -edit_properties (SheetItem *object) +static void edit_properties (SheetItem *object) { GSList *properties; PartItem *item; Part *part; char *internal, *msg; GtkBuilder *gui; - GError *perror = NULL; - GtkTable *prop_table; + GError *error = NULL; + GtkGrid *prop_grid; GtkNotebook *notebook; gint response, y = 0; gboolean has_model; @@ -630,30 +547,15 @@ edit_properties (SheetItem *object) g_free (internal); if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create part properties dialog.")); + oregano_error (_ ("Could not create part properties dialog.")); return; - } - else - gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test( - OREGANO_UIDIR "/part-properties-dialog.ui", 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_UIDIR "/part-properties-dialog.ui"); - oregano_error_with_title (_("Could not create part properties dialog."), - msg); - g_free (msg); - return; - } + } else + gtk_builder_set_translation_domain (gui, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/part-properties-dialog.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create part properties dialog."), - msg); - g_error_free (perror); + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/part-properties-dialog.ui", &error) <= 0) { + msg = error->message; + oregano_error_with_title (_ ("Could not create part properties dialog."), msg); + g_error_free (error); return; } @@ -661,64 +563,57 @@ edit_properties (SheetItem *object) prop_dialog->part_item = item; - prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, - "part-properties-dialog")); + prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, "part-properties-dialog")); - prop_table = GTK_TABLE (gtk_builder_get_object (gui, "prop_table")); - notebook = GTK_NOTEBOOK (gtk_builder_get_object (gui, "notebook")); + prop_grid = GTK_GRID (gtk_builder_get_object (gui, "prop_grid")); + notebook = GTK_NOTEBOOK (gtk_builder_get_object (gui, "notebook")); - g_signal_connect (prop_dialog->dialog, "destroy", - G_CALLBACK (prop_dialog_destroy), prop_dialog); + g_signal_connect (prop_dialog->dialog, "destroy", G_CALLBACK (prop_dialog_destroy), + prop_dialog); prop_dialog->widgets = NULL; has_model = FALSE; - for (properties = part_get_properties (part); properties; - properties = properties->next) { + 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; - + gchar *temp = NULL; + if (!g_ascii_strcasecmp (prop->name, "internal")) continue; - if (!g_ascii_strcasecmp (prop->name, "model")) { + if (!g_ascii_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"); + 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); - + gtk_entry_set_text (GTK_ENTRY (entry), prop->value); + g_object_set_data (G_OBJECT (entry), "user", g_strdup (prop->name)); + + gtk_grid_attach (prop_grid, label, 0, y, 1, 1); + + gtk_grid_attach (prop_grid, entry, 1, y, 1, 1); + y++; gtk_widget_show (label); gtk_widget_show (entry); @@ -728,9 +623,8 @@ edit_properties (SheetItem *object) } if (!has_model) { - gtk_notebook_remove_page (notebook, 1); - } - else { + gtk_notebook_remove_page (notebook, 1); + } else { GtkTextBuffer *txtbuffer; GtkTextView *txtmodel; gchar *filename, *str; @@ -743,8 +637,7 @@ edit_properties (SheetItem *object) if (g_file_get_contents (filename, &str, NULL, &read_error)) { gtk_text_buffer_set_text (txtbuffer, str, -1); g_free (str); - } - else { + } else { gtk_text_buffer_set_text (txtbuffer, read_error->message, -1); g_error_free (read_error); } @@ -761,290 +654,201 @@ edit_properties (SheetItem *object) prop_dialog_response (GTK_WIDGET (prop_dialog->dialog), response, prop_dialog); - g_slist_free_full (properties, g_object_unref); gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); } -static void -part_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +inline static GooCanvasAnchorType angle_to_anchor (int angle) { - cairo_matrix_t affine; - GSList *label_items; GooCanvasAnchorType anchor; - GooCanvasGroup *group; - GooCanvasItem *canvas_item; - PartItem *item; - PartItemPriv *priv; - Part *part; - int index = 0; - int angle_anchor; - - g_return_if_fail (sheet_item != NULL); - g_return_if_fail (IS_PART_ITEM (sheet_item)); - - item = PART_ITEM (sheet_item); - group = GOO_CANVAS_GROUP (item); - part = PART (data); - - priv = item->priv; - if (angle != 0) - cairo_matrix_init_rotate (&affine, (double) angle * M_PI / 180); - else - // angle == 0: Nothing has changed, therefore do nothing - cairo_matrix_init_identity (&affine); - - for (index = 0; index < group->items->len; index++) { - canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); - - goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), - NULL); - goo_canvas_item_set_transform (GOO_CANVAS_ITEM (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 + // canvas doesn't know how to rotate text and since we rotate the // label_group instead of the labels directly. - angle_anchor = part_get_rotation (part); - switch (angle_anchor) { - case 90: + + while (angle < 0) + angle += 360; + angle %= 360; + + if (90 - 45 < angle && angle < 90 + 45) { anchor = GOO_CANVAS_ANCHOR_NORTH_WEST; - break; - case 180: + } else if (180 - 45 < angle && angle < 180 + 45) { anchor = GOO_CANVAS_ANCHOR_NORTH_EAST; - break; - case 270: + } else if (270 - 45 < angle && angle < 270 + 45) { anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST; - break; - default: + } else /* if (360-45 < angle && angle < 0+45) */ { anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; - break; } - for (label_items = priv->label_items; label_items; - label_items = label_items->next) { - gdouble x, y; - g_object_set (label_items->data, - "anchor", anchor, - NULL); - g_object_get (label_items->data, - "x", &x, - "y", &y, - NULL); - - goo_canvas_item_set_transform (label_items->data, - NULL); - // A bias (1.0, -2.0) is introduced due to ???? - goo_canvas_item_rotate (label_items->data, -angle, x + 1.0, y - 2.0); - } - - for (label_items = priv->label_nodes; label_items; - label_items = label_items->next) { - gdouble x, y; - g_object_set (label_items->data, - "anchor", anchor, - NULL); - g_object_get (label_items->data, - "x", &x, - "y", &y, - NULL); - - goo_canvas_item_set_transform (label_items->data, - NULL); - // A bias (1.0, -2.0) is introduced due to ???? - goo_canvas_item_rotate (label_items->data, -angle, x + 1.0, y - 2.0); - } - - // Invalidate the bounding box cache. - priv->cache_valid = FALSE; - - g_slist_free_full (label_items, g_object_unref); + return anchor; } -static void -part_flipped_callback (ItemData *data, gboolean horizontal, - SheetItem *sheet_item) +/** + * whenever the model changes, this one gets called to update the view + * representation + * @attention this recalculates the matrix every time, this makes sure no errors + * stack up + * @attention further reading on matrix manipulations + * @attention http://www.cairographics.org/matrix_transform/ + * @param data the model item, a bare C struct derived from ItemData + * @param sheet_item the view item, derived from goo_canvas_group/item + */ +static void part_changed_callback (ItemData *data, SheetItem *sheet_item) { - GSList *label; - GooCanvasAnchorType anchor; - GooCanvasItem *canvas_item; - GooCanvasGroup *group; - PartItem *item; - PartItemPriv *priv; - Part *part; - int index = 0; - gdouble scale_h = 1.0, scale_v = 1.0, x, y; - GooCanvasBounds bounds_before, bounds_after; - g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_PART_ITEM (sheet_item)); - item = PART_ITEM (sheet_item); - part = PART (data); - priv = item->priv; - group = GOO_CANVAS_GROUP (item); + // TODO add static vars in order to skip the redraw if nothing changed + // TODO may happen once in a while and the check is really cheap + PartItem *item = PART_ITEM (sheet_item); + PartItemPriv *priv = item->priv; - if (horizontal) - scale_v = -1.0; - else - scale_h = -1.0; - - // Get the group bounds before the flip - goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (sheet_item), - &bounds_before); - - for (index = 0; index < group->items->len; index++) { - canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); - goo_canvas_item_scale (canvas_item, scale_h, scale_v); + // init the states + + cairo_matrix_t morph, inv; + cairo_status_t done; + + inv = *(item_data_get_rotate (data)); // copy + cairo_matrix_multiply (&morph, &inv, item_data_get_translate (data)); + + done = cairo_matrix_invert (&inv); + if (done != CAIRO_STATUS_SUCCESS) { + g_warning ("Failed to invert matrix. This should never happen. Ever!"); + return; } + // no translations + inv.y0 = inv.x0 = 0.; - // Get the group bounds after the flip - goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (sheet_item), - &bounds_after); - - // Translation to the flip translation - x = bounds_after.x1 - bounds_before.x1; - y = bounds_after.y1 - bounds_before.y1; - goo_canvas_item_translate (GOO_CANVAS_ITEM (sheet_item), -x, -y); - - anchor = part_item_get_anchor_from_part (part); - switch (anchor) { - case GOO_CANVAS_ANCHOR_NORTH_WEST: - anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST; - break; - case GOO_CANVAS_ANCHOR_NORTH_EAST: - anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; - break; - case GOO_CANVAS_ANCHOR_SOUTH_EAST: - anchor = GOO_CANVAS_ANCHOR_NORTH_WEST; - break; - default: - anchor = GOO_CANVAS_ANCHOR_NORTH_EAST; + goo_canvas_item_set_transform (GOO_CANVAS_ITEM (sheet_item), &(morph)); + + priv->cache_valid = FALSE; + return; /* FIXME */ +#if 0 + GooCanvasGroup *group = GOO_CANVAS_GROUP (item); + + // rotate all items in the canvas group + for (int index = 0; index < group->items->len; index++) { + GooCanvasItem *canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); + goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), &morph); } - - for (label = priv->label_items; label; label = label->next) { - g_object_set (label->data, + + // revert the rotation of all labels and change their anchor to not overlap too badly + // this assures that the text is always horizontal and properly aligned + GooCanvasAnchorType anchor = angle_to_anchor (rotation); + + for (GSList *iter = priv->label_items; iter; iter = iter->next) { + g_object_set (iter->data, "anchor", anchor, NULL); - g_object_get (label->data, - "x", &x, - "y", &y, - NULL); - - goo_canvas_item_scale (label->data, scale_h, scale_v); - if (horizontal) - goo_canvas_item_translate (label->data, 0, -2 * y); - else - goo_canvas_item_translate (label->data, -2 * x, 0); - } - for (label = priv->label_nodes; label; label = label->next) { - g_object_set (label->data, - "anchor", anchor, - NULL); - g_object_get (label->data, - "x", &x, - "y", &y, + goo_canvas_item_set_transform (iter->data, &inv); + + } + // same for label nodes + for (GSList *iter = priv->label_nodes; iter; iter = iter->next) { + g_object_set (iter->data, + "anchor", anchor, NULL); - - goo_canvas_item_scale (label->data, scale_h, scale_v); - if (horizontal) - goo_canvas_item_translate (label->data, 0, -2 * y); - else - goo_canvas_item_translate (label->data, -2 * x, 0); + + goo_canvas_item_set_transform (iter->data, &inv); } - + + // Invalidate the bounding box cache. priv->cache_valid = FALSE; +#endif +} - g_slist_free_full (label, g_object_unref); +/** + * a part got rotated + * + * @angle the angle the item is rotated towards the default (0) rotation + * + */ +static void part_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +{ + // g_warning ("ROTATED callback called - LEGACY\n"); } -void -part_item_signal_connect_floating (PartItem *item) +/** + * handles the update of the canvas item when a part gets flipped (within the + * backend alias model) + * @data the part in form of a ItemData pointer + * @direction the new flip state + * @sheet_item the corresponding sheet_item to the model item @data + */ +static void part_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item) +{ + // g_warning ("FLIPPED callback called - LEGACY\n"); +} + +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); + g_signal_connect (G_OBJECT (item), "double_clicked", G_CALLBACK (edit_properties), item); } -static void -selection_changed (PartItem *item, gboolean select, gpointer user_data) +static void selection_changed (PartItem *item, gboolean select, gpointer user_data) { g_object_ref (G_OBJECT (item)); if (select) - g_idle_add ((gpointer) select_idle_callback, item); + g_idle_add ((gpointer)select_idle_callback, item); else - g_idle_add ((gpointer) deselect_idle_callback, item); + g_idle_add ((gpointer)deselect_idle_callback, item); } -static int -select_idle_callback (PartItem *item) +static int select_idle_callback (PartItem *item) { GooCanvasItem *canvas_item = NULL; int index; - + g_return_val_if_fail (item != NULL, FALSE); - for (index = 0; index < GOO_CANVAS_GROUP (item)->items->len; index++) { + for (index = 0; index < GOO_CANVAS_GROUP (item)->items->len; index++) { canvas_item = GOO_CANVAS_ITEM (GOO_CANVAS_GROUP (item)->items->pdata[index]); - g_object_set (canvas_item, - "stroke-color", SELECTED_COLOR, - NULL); + g_object_set (canvas_item, "stroke-color", SELECTED_COLOR, NULL); } g_object_unref (G_OBJECT (item)); return FALSE; } -static int -deselect_idle_callback (PartItem *item) +static int deselect_idle_callback (PartItem *item) { GooCanvasItem *canvas_item = NULL; int index; for (index = 0; index < GOO_CANVAS_GROUP (item)->items->len; index++) { canvas_item = GOO_CANVAS_ITEM (GOO_CANVAS_GROUP (item)->items->pdata[index]); - + if (GOO_IS_CANVAS_TEXT (canvas_item)) { - g_object_set (canvas_item, - "stroke-color", LABEL_COLOR, - NULL); - } - else { - g_object_set (canvas_item, - "stroke-color", NORMAL_COLOR, - NULL); + g_object_set (canvas_item, "stroke-color", LABEL_COLOR, NULL); + } else { + g_object_set (canvas_item, "stroke-color", NORMAL_COLOR, NULL); } } g_object_unref (G_OBJECT (item)); return FALSE; } -static gboolean -is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2) { PartItem *item; - SheetPos bbox_start, bbox_end; + Coords 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; + 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) +static void show_labels (SheetItem *sheet_item, gboolean show) { PartItem *item; PartItemPriv *priv; @@ -1056,27 +860,25 @@ show_labels (SheetItem *sheet_item, gboolean show) priv = item->priv; if (show) - g_object_set (priv->label_group, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); + g_object_set (priv->label_group, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); else - g_object_set (priv->label_group, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + g_object_set (priv->label_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } // 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) +inline static void get_cached_bounds (PartItem *item, Coords *p1, Coords *p2) { PartItemPriv *priv; priv = item->priv; - if (!priv->cache_valid) { - SheetPos start_pos, end_pos; + if (G_LIKELY (priv->cache_valid)) { + *p1 = priv->bbox_start; + *p2 = priv->bbox_end; + } else { + Coords start_pos, end_pos; GooCanvasBounds bounds; - + goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds); start_pos.x = bounds.x1; @@ -1084,17 +886,13 @@ get_cached_bounds (PartItem *item, SheetPos *p1, SheetPos *p2) end_pos.x = bounds.x2; end_pos.y = bounds.y2; - priv->bbox_start = start_pos; - priv->bbox_end = end_pos; + *p1 = priv->bbox_start = start_pos; + *p2 = 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 (Sheet *sheet, ItemData *data) +static void part_item_paste (Sheet *sheet, ItemData *data) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -1104,8 +902,7 @@ part_item_paste (Sheet *sheet, ItemData *data) sheet_add_ghost_item (sheet, data); } -PartItem * -part_item_new (Sheet *sheet, Part *part) +PartItem *part_item_new (Sheet *sheet, Part *part) { Library *library; LibraryPart *library_part; @@ -1117,7 +914,7 @@ part_item_new (Sheet *sheet, Part *part) library = priv->library; library_part = library_get_part (library, priv->name); - // Create the PartItem canvas item. + // Create the PartItem canvas item item = part_item_canvas_new (sheet, part); create_canvas_items (GOO_CANVAS_GROUP (item), library_part); create_canvas_labels (item, part); @@ -1126,9 +923,7 @@ part_item_new (Sheet *sheet, Part *part) return item; } -void -part_item_create_canvas_items_for_preview (GooCanvasGroup *group, - LibraryPart *library_part) +void part_item_create_canvas_items_for_preview (GooCanvasGroup *group, LibraryPart *library_part) { g_return_if_fail (group != NULL); g_return_if_fail (library_part != NULL); @@ -1136,99 +931,75 @@ part_item_create_canvas_items_for_preview (GooCanvasGroup *group, create_canvas_items (group, library_part); } -static void -create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part) +static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part) { - GooCanvasItem *item; + GooCanvasItem *item; GooCanvasPoints *points; - GSList *objects; - LibrarySymbol *symbol; - SymbolObject *object; - gdouble height, width; - GooCanvasBounds bounds, group_bounds = {0,0,0,0}; - + GSList *objects; + LibrarySymbol *symbol; + SymbolObject *object; + gdouble height, width; + GooCanvasBounds bounds, group_bounds = {0, 0, 0, 0}; g_return_if_fail (group != NULL); g_return_if_fail (library_part != NULL); symbol = library_get_symbol (library_part->symbol_name); - if (symbol == NULL) { + 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); + 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 = goo_canvas_polyline_new (GOO_CANVAS_ITEM (group), - FALSE, - 0, - "points", points, - "stroke-color", NORMAL_COLOR, - "line-width", 0.5, - NULL); - if (object->u.uline.spline) { - g_object_set (item, - "smooth", TRUE, - "spline_steps", 5, - NULL); - } - break; - case SYMBOL_OBJECT_ARC: - item = goo_canvas_ellipse_new (GOO_CANVAS_ITEM (group), - (object->u.arc.x2 + object->u.arc.x1) / 2.0, - (object->u.arc.y1 + object->u.arc.y2) / 2.0, - (object->u.arc.x2 - object->u.arc.x1) / 2.0, - (object->u.arc.y1 - object->u.arc.y2) / 2.0, - "stroke-color", NORMAL_COLOR, - "line_width", 1.0, - NULL); - break; - case SYMBOL_OBJECT_TEXT: - item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), - object->u.text.str, - (double) object->u.text.x, - (double) object->u.text.y, - -1, - GOO_CANVAS_ANCHOR_NORTH_EAST, - "fill_color", LABEL_COLOR, - "font", "Sans 8", - NULL); + case SYMBOL_OBJECT_LINE: + points = object->u.uline.line; + item = goo_canvas_polyline_new (GOO_CANVAS_ITEM (group), FALSE, 0, "points", points, + "stroke-color", NORMAL_COLOR, "line-width", 0.5, NULL); + if (object->u.uline.spline) { + g_object_set (item, "smooth", TRUE, "spline_steps", 5, NULL); + } break; - default: - g_warning ("Unknown symbol object.\n"); - continue; + case SYMBOL_OBJECT_ARC: + item = goo_canvas_ellipse_new (GOO_CANVAS_ITEM (group), + (object->u.arc.x2 + object->u.arc.x1) / 2.0, + (object->u.arc.y1 + object->u.arc.y2) / 2.0, + (object->u.arc.x2 - object->u.arc.x1) / 2.0, + (object->u.arc.y1 - object->u.arc.y2) / 2.0, + "stroke-color", NORMAL_COLOR, "line_width", 1.0, NULL); + break; + case SYMBOL_OBJECT_TEXT: + item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), object->u.text.str, + (double)object->u.text.x, (double)object->u.text.y, -1, + GOO_CANVAS_ANCHOR_NORTH_EAST, "fill_color", LABEL_COLOR, + "font", "Sans 8", NULL); + break; + default: + g_warning ("Unknown symbol object.\n"); + continue; } goo_canvas_item_get_bounds (item, &bounds); - if (group_bounds.x1 > bounds.x1) group_bounds.x1 = bounds.x1; - if (group_bounds.x2 < bounds.x2) group_bounds.x2 = bounds.x2; - if (group_bounds.y1 > bounds.y1) group_bounds.y1 = bounds.y1; - if (group_bounds.y2 < bounds.y2) group_bounds.y2 = bounds.y2; - + if (group_bounds.x1 > bounds.x1) + group_bounds.x1 = bounds.x1; + if (group_bounds.x2 < bounds.x2) + group_bounds.x2 = bounds.x2; + if (group_bounds.y1 > bounds.y1) + group_bounds.y1 = bounds.y1; + if (group_bounds.y2 < bounds.y2) + group_bounds.y2 = bounds.y2; } - - g_object_get (group, - "width", &width, - "height", &height, - NULL); + + g_object_get (group, "width", &width, "height", &height, NULL); width = group_bounds.x2 - group_bounds.x1; height = group_bounds.y2 - group_bounds.y1; - - g_object_set (group, - "width", width, - "height", height, - NULL); - - g_slist_free_full (objects, g_object_unref); + + g_object_set (group, "width", width, "height", height, NULL); } -static void -create_canvas_labels (PartItem *item, Part *part) +static void create_canvas_labels (PartItem *item, Part *part) { GooCanvasItem *canvas_item; GSList *list, *item_list; @@ -1248,37 +1019,28 @@ create_canvas_labels (PartItem *item, Part *part) text = part_property_expand_macros (part, label->text); - canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), - text, - (double) label->pos.x, - (double) label->pos.y, - 0, - GOO_CANVAS_ANCHOR_SOUTH_WEST, - "fill_color", LABEL_COLOR, - "font", "Sans 8", - NULL); + canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), text, (double)label->pos.x, + (double)label->pos.y, 0, GOO_CANVAS_ANCHOR_SOUTH_WEST, + "fill_color", LABEL_COLOR, "font", "Sans 8", NULL); item_list = g_slist_prepend (item_list, canvas_item); g_free (text); } - g_slist_free_full (list, g_object_unref); 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) +static void create_canvas_label_nodes (PartItem *item, Part *part) { GooCanvasItem *canvas_item; GSList *item_list; GooCanvasItem *group; Pin *pins; int num_pins, i; - SheetPos p1, p2; + Coords p1, p2; GooCanvasAnchorType anchor; - + g_return_if_fail (item != NULL); g_return_if_fail (IS_PART_ITEM (item)); g_return_if_fail (part != NULL); @@ -1292,20 +1054,20 @@ create_canvas_label_nodes (PartItem *item, Part *part) get_cached_bounds (item, &p1, &p2); switch (part_get_rotation (part)) { - case 0: - anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; + case 0: + anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; break; - case 90: - anchor = GOO_CANVAS_ANCHOR_NORTH_WEST; + case 90: + anchor = GOO_CANVAS_ANCHOR_NORTH_WEST; break; - case 180: - anchor = GOO_CANVAS_ANCHOR_NORTH_EAST; + case 180: + anchor = GOO_CANVAS_ANCHOR_NORTH_EAST; break; - case 270: - anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST; + case 270: + anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST; break; - default: - anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; + default: + anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; } for (i = 0; i < num_pins; i++) { @@ -1315,17 +1077,11 @@ create_canvas_label_nodes (PartItem *item, Part *part) y = pins[i].offset.y; text = g_strdup_printf ("%d", pins[i].node_nr); - canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), - text, - (double) x, - (double) y, - 0, - anchor, - "fill_color", "black", - "font", "Sans 8", - NULL); + canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), text, (double)x, (double)y, 0, + anchor, "fill_color", "black", "font", "Sans 8", NULL); // Shift slightly the label for a Voltmeter - if (i == 0) goo_canvas_item_translate (canvas_item, -15.0, -10.0); + if (i == 0) + goo_canvas_item_translate (canvas_item, -15.0, -10.0); item_list = g_slist_prepend (item_list, canvas_item); g_free (text); @@ -1334,79 +1090,41 @@ create_canvas_label_nodes (PartItem *item, Part *part) 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) +static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item) {} + +static void part_item_place (SheetItem *item, Sheet *sheet) { - 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)); + g_signal_connect (G_OBJECT (item), "button_press_event", G_CALLBACK (sheet_item_event), sheet); - if (pos == NULL) - return; + g_signal_connect (G_OBJECT (item), "button_release_event", G_CALLBACK (sheet_item_event), + sheet); - part_item = PART_ITEM (item); - - // Move the canvas item and invalidate the bbox cache. - goo_canvas_item_set_transform (GOO_CANVAS_ITEM (item), - NULL); - goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (item), - pos->x, - pos->y, - 1.0, - 0.0); - - part_item->priv->cache_valid = FALSE; -} + g_signal_connect (G_OBJECT (item), "motion_notify_event", G_CALLBACK (sheet_item_event), sheet); -static void -part_item_place (SheetItem *item, Sheet *sheet) -{ - g_signal_connect (G_OBJECT (item), "button_press_event", - G_CALLBACK (sheet_item_event), sheet); - - g_signal_connect (G_OBJECT (item), "button_release_event", - G_CALLBACK (sheet_item_event), sheet); - - g_signal_connect (G_OBJECT (item), "motion_notify_event", - G_CALLBACK (sheet_item_event), sheet); - - g_signal_connect (G_OBJECT (item), "key_press_event", - G_CALLBACK (sheet_item_event), sheet); - - g_signal_connect (G_OBJECT (item), "double_clicked", - G_CALLBACK (edit_properties), item); + g_signal_connect (G_OBJECT (item), "key_press_event", G_CALLBACK (sheet_item_event), sheet); + + g_signal_connect (G_OBJECT (item), "double_clicked", G_CALLBACK (edit_properties), item); } -static void -part_item_place_ghost (SheetItem *item, Sheet *sheet) +static void part_item_place_ghost (SheetItem *item, Sheet *sheet) { -// part_item_signal_connect_placed (PART_ITEM (item)); + // part_item_signal_connect_placed (PART_ITEM (item)); } -void -part_item_show_node_labels (PartItem *part, gboolean show) +void part_item_show_node_labels (PartItem *part, gboolean show) { PartItemPriv *priv; priv = part->priv; if (show) - g_object_set (priv->node_group, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); + g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); else - g_object_set (priv->node_group, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } -static GooCanvasAnchorType -part_item_get_anchor_from_part (Part *part) +static GooCanvasAnchorType part_item_get_anchor_from_part (Part *part) { int anchor_h, anchor_v; int angle; @@ -1416,19 +1134,19 @@ part_item_get_anchor_from_part (Part *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; + 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) { diff --git a/src/sheet/part-item.h b/src/sheet/part-item.h index cf835ac..ddb471e 100644 --- a/src/sheet/part-item.h +++ b/src/sheet/part-item.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __PART_ITEM_H @@ -42,26 +44,27 @@ typedef struct _PartItemPriv PartItemPriv; #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 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 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)) -struct _PartItem { - SheetItem parent_object; +struct _PartItem +{ + SheetItem parent_object; PartItemPriv *priv; }; -struct _PartItemClass { +struct _PartItemClass +{ SheetItemClass parent_class; }; -GType part_item_get_type (void); +GType part_item_get_type (void); PartItem *part_item_new (Sheet *sheet, Part *part); -void part_item_create_canvas_items_for_preview (GooCanvasGroup *group, - LibraryPart *library_part); -void part_item_update_node_label (PartItem *part); -void part_item_show_node_labels (PartItem *part, gboolean b); +void part_item_create_canvas_items_for_preview (GooCanvasGroup *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/plot-add-function.c b/src/sheet/plot-add-function.c index 4f8d43d..853aa7c 100644 --- a/src/sheet/plot-add-function.c +++ b/src/sheet/plot-add-function.c @@ -3,15 +3,17 @@ * * * Authors: - * Ricardo Markiewicz <rmarkie@fi.uba.ar> - * Andres de Barbara <adebarbara@fi.uba.ar> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * - * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,8 +27,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> @@ -34,42 +36,29 @@ #include "plot-add-function.h" #include "dialogs.h" +#include "simulation.h" -void -plot_add_function_show (OreganoEngine *engine, SimulationData *current) +void plot_add_function_show (OreganoEngine *engine, SimulationData *current) { GtkBuilder *gui; GError *perror = NULL; GtkDialog *dialog; - gchar *msg; GtkComboBoxText *op1, *op2, *functiontype; int i; gint result = 0; GtkWidget *warning; GtkWidget *container_temp; - + SimulationFunction *func = g_new0 (SimulationFunction, 1); if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create plot window.")); - return; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/plot-add-function.ui", - 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_UIDIR "/plot-add-function.ui"); - oregano_error_with_title (_("Could not create plot window."), msg); - g_free (msg); + oregano_error (_ ("Could not create plot window.")); return; } + gtk_builder_set_translation_domain (gui, NULL); - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/plot-add-function.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create plot window."), msg); + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/plot-add-function.ui", &perror) <= 0) { + oregano_error_with_title (_ ("Could not create plot window."), perror->message); g_error_free (perror); return; } @@ -79,7 +68,7 @@ plot_add_function_show (OreganoEngine *engine, SimulationData *current) op1 = GTK_COMBO_BOX_TEXT (gtk_combo_box_text_new ()); gtk_container_add (GTK_CONTAINER (container_temp), GTK_WIDGET (op1)); gtk_widget_show (GTK_WIDGET (op1)); - + container_temp = GTK_WIDGET (gtk_builder_get_object (gui, "op2_alignment")); op2 = GTK_COMBO_BOX_TEXT (gtk_combo_box_text_new ()); gtk_container_add (GTK_CONTAINER (container_temp), GTK_WIDGET (op2)); @@ -90,41 +79,39 @@ plot_add_function_show (OreganoEngine *engine, SimulationData *current) gtk_container_add (GTK_CONTAINER (container_temp), GTK_WIDGET (functiontype)); gtk_widget_show (GTK_WIDGET (functiontype)); - gtk_combo_box_text_append_text (functiontype, _("Substraction")); - gtk_combo_box_text_append_text (functiontype, _("Division")); + for (const gchar **ptr = SimulationFunctionTypeString; *ptr != NULL; ptr++) { + gtk_combo_box_text_append_text(functiontype, *ptr); + } for (i = 1; i < current->n_variables; i++) { - if (current->type != DC_TRANSFER) { + if (current->type != ANALYSIS_TYPE_DC_TRANSFER) { if (strchr (current->var_names[i], '#') == NULL) { gtk_combo_box_text_append_text (op1, current->var_names[i]); gtk_combo_box_text_append_text (op2, current->var_names[i]); } - } - else { + } else { gtk_combo_box_text_append_text (op1, current->var_names[i]); gtk_combo_box_text_append_text (op2, current->var_names[i]); } } - gtk_combo_box_set_active (GTK_COMBO_BOX (op1),0); - gtk_combo_box_set_active (GTK_COMBO_BOX (op2),1); - gtk_combo_box_set_active (GTK_COMBO_BOX (functiontype),0); + gtk_combo_box_set_active (GTK_COMBO_BOX (op1), 0); + gtk_combo_box_set_active (GTK_COMBO_BOX (op2), 1); + gtk_combo_box_set_active (GTK_COMBO_BOX (functiontype), 0); result = gtk_dialog_run (GTK_DIALOG (dialog)); - + if ((result == GTK_RESPONSE_OK) && ((gtk_combo_box_get_active (GTK_COMBO_BOX (op1)) == -1) || - (gtk_combo_box_get_active (GTK_COMBO_BOX (op2)) == -1) || - (gtk_combo_box_get_active (GTK_COMBO_BOX (functiontype)) == -1))) - { + (gtk_combo_box_get_active (GTK_COMBO_BOX (op2)) == -1) || + (gtk_combo_box_get_active (GTK_COMBO_BOX (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) { + 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)); @@ -132,17 +119,19 @@ plot_add_function_show (OreganoEngine *engine, SimulationData *current) } } - if ((result == GTK_RESPONSE_OK) && - ((gtk_combo_box_get_active (GTK_COMBO_BOX (op1)) != -1) && - (gtk_combo_box_get_active (GTK_COMBO_BOX (op2)) != -1) && - (gtk_combo_box_get_active (GTK_COMBO_BOX (functiontype)) != -1))) { - + if ((result == GTK_RESPONSE_OK) && + ((gtk_combo_box_get_active (GTK_COMBO_BOX (op1)) != -1) && + (gtk_combo_box_get_active (GTK_COMBO_BOX (op2)) != -1) && + (gtk_combo_box_get_active (GTK_COMBO_BOX (functiontype)) != -1))) { + + func->type = gtk_combo_box_get_active (GTK_COMBO_BOX (functiontype)); + for (i = 1; i < current->n_variables; i++) { if (g_strcmp0 (current->var_names[i], gtk_combo_box_text_get_active_text (op1)) == 0) func->first = i; if (g_strcmp0 (current->var_names[i], gtk_combo_box_text_get_active_text (op2)) == 0) func->second = i; - } + } current->functions = g_list_append (current->functions, func); } diff --git a/src/sheet/plot-add-function.h b/src/sheet/plot-add-function.h index 35cac1a..1d0961b 100644 --- a/src/sheet/plot-add-function.h +++ b/src/sheet/plot-add-function.h @@ -7,7 +7,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber @@ -24,16 +24,17 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#ifndef __PLOT_ADD__H +#ifndef __PLOT_ADD_H #define __PLOT_ADD_H #include "simulation.h" #include "engine.h" void plot_add_function_show (OreganoEngine *engine, SimulationData *current); +extern const char *SimulationFunctionTypeString[]; #endif diff --git a/src/sheet/rubberband.c b/src/sheet/rubberband.c new file mode 100644 index 0000000..6332dfa --- /dev/null +++ b/src/sheet/rubberband.c @@ -0,0 +1,255 @@ +/* + * rubberband.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> + * + * Description: Handles the user interaction when doing area/rubberband + *selections. + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <math.h> + +#include <gdk/gdkkeysyms.h> + +#include "rubberband.h" +#include "sheet-private.h" + +#include "debug.h" + +inline static cairo_pattern_t *create_stipple (const char *color_name, guchar stipple_data[]) +{ + cairo_surface_t *surface; + cairo_pattern_t *pattern; + GdkRGBA color; + int stride; + const int width = 8; + const int height = 8; + + gdk_rgba_parse (&color, color_name); + /* stipple_data[2] = stipple_data[14] = color.red >> 8; + stipple_data[1] = stipple_data[13] = color.green >> 8; + stipple_data[0] = stipple_data[12] = color.blue >> 8; + */ + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); + g_assert (stride > 0); + NG_DEBUG ("stride = %i", stride); + surface = cairo_image_surface_create_for_data (stipple_data, CAIRO_FORMAT_ARGB32, width, height, + stride); + pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + + return pattern; +} + +#define COLOR_A 0x3093BA52 +#define COLOR_B 0x30FFFFFF +#define PREMULTIPLY(argb) \ + ((argb & 0xFF << 24) | \ + ((((argb & 0xFF << 16) >> 16) * ((argb & 0xFF << 24) >> 24) / 0xFF) << 16) | \ + ((((argb & 0xFF << 8) >> 8) * ((argb & 0xFF << 24) >> 24) / 0xFF) << 8) | \ + ((((argb & 0xFF << 0) >> 0) * ((argb & 0xFF << 24) >> 24) / 0xFF) << 0)) +#define COLOR_A_PRE PREMULTIPLY (COLOR_A) +#define COLOR_B_PRE PREMULTIPLY (COLOR_B) + +RubberbandInfo *rubberband_info_new (Sheet *sheet) +{ + RubberbandInfo *rubberband_info; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + NG_DEBUG ("0x%x A", COLOR_A); + NG_DEBUG ("0x%x B", COLOR_B); + NG_DEBUG ("0x%x A PRE", COLOR_A_PRE); + NG_DEBUG ("0x%x B PRE", COLOR_B_PRE); + static guint32 stipple_data[8 * 8] = { + COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, + COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, + COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, + COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, + COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, + + COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, + COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, + COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, + COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, COLOR_A_PRE, + COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE, COLOR_B_PRE}; + + /* the stipple patten should look like that + * 1 1 1 0 0 0 0 1 + * 1 1 0 0 0 0 1 1 + * 1 0 0 0 0 1 1 1 + * 0 0 0 0 1 1 1 1 + * + * 0 0 0 1 1 1 1 0 + * 0 0 1 1 1 1 0 0 + * 0 1 1 1 1 0 0 0 + * 1 1 1 1 0 0 0 0 + */ + rubberband_info = g_new (RubberbandInfo, 1); + rubberband_info->state = RUBBERBAND_START; + + pattern = create_stipple ("lightgrey", (guchar *)stipple_data); + + // scale 5x, see + // http://cairographics.org/manual/cairo-cairo-pattern-t.html#cairo-pattern-t + cairo_matrix_init_scale (&matrix, 1.0, 1.0); + cairo_pattern_set_matrix (pattern, &matrix); + + rubberband_info->rectangle = GOO_CANVAS_RECT (goo_canvas_rect_new ( + GOO_CANVAS_ITEM (sheet->object_group), 10.0, 10.0, 10.0, 10.0, "stroke-color", "black", + "line-width", 0.2, "fill-pattern", pattern, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL)); + cairo_pattern_destroy (pattern); + return rubberband_info; +} + +void rubberband_info_destroy (RubberbandInfo *rubberband_info) +{ + g_return_if_fail (rubberband_info != NULL); + goo_canvas_item_remove (GOO_CANVAS_ITEM (rubberband_info->rectangle)); + g_free (rubberband_info); +} + +gboolean rubberband_start (Sheet *sheet, GdkEvent *event) +{ + GList *list; + double x, y; + RubberbandInfo *rubberband_info; + + g_assert (event->type == GDK_BUTTON_PRESS); + x = event->button.x; + y = event->button.y; + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &x, &y); + + rubberband_info = sheet->priv->rubberband_info; + rubberband_info->start.x = x; + rubberband_info->start.y = y; + rubberband_info->end.x = x; + rubberband_info->end.y = y; + rubberband_info->state = RUBBERBAND_ACTIVE; + + // FIXME TODO recheck + g_assert (rubberband_info->rectangle != NULL); + g_object_set (rubberband_info->rectangle, "x", x, "y", y, "width", 0., "height", 0., + "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); +#if 1 + // Mark all the selected objects to preserve their selected state + // if SHIFT is pressed while rubberbanding. + if (event->button.state & GDK_SHIFT_MASK) { + for (list = sheet->priv->selected_objects; list; list = list->next) + sheet_item_set_preserve_selection (SHEET_ITEM (list->data), TRUE); + + sheet->priv->preserve_selection_items = g_list_copy (sheet->priv->selected_objects); + } +#endif + + sheet_pointer_grab (sheet, event); + return TRUE; +} + +gboolean rubberband_update (Sheet *sheet, GdkEvent *event) +{ + GList *iter; + Coords cur, cmin, cmax; + double dx, dy; // TODO maybe keep track of subpixel changes, make em + // global/part of the rubberband_info struct and reset on + // finish + double width, height, width_ng, height_ng; + RubberbandInfo *rubberband_info; + + rubberband_info = sheet->priv->rubberband_info; + + g_assert (event->type == GDK_MOTION_NOTIFY); + cur.x = event->motion.x; + cur.y = event->motion.y; + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &cur.x, &cur.y); + + width = fabs (rubberband_info->end.x - rubberband_info->start.x); + height = fabs (rubberband_info->end.y - rubberband_info->start.y); + + width_ng = fabs (cur.x - rubberband_info->start.x); + height_ng = fabs (cur.y - rubberband_info->start.y); + + dx = fabs (width_ng - width); + dy = fabs (height_ng - height); + NG_DEBUG ("motion :: dx=%lf, dy=%lf :: x=%lf, y=%lf :: w_ng=%lf, h_ng=%lf", dx, dy, cur.x, + cur.y, width_ng, height_ng); + + // TODO FIXME scroll window if needed (use + // http://developer.gnome.org/goocanvas/stable/GooCanvas.html#goo-canvas-scroll-to) + + if (dx > 0.1 || dy > 0.1) { // a 0.1 change in pixel coords would be the least + // visible, silently ignore everything else + rubberband_info->end.x = cur.x; + rubberband_info->end.y = cur.y; + cmin.x = MIN (rubberband_info->start.x, rubberband_info->end.x); + cmin.y = MIN (rubberband_info->start.y, rubberband_info->end.y); + cmax.x = cmin.x + width_ng; + cmax.y = cmin.y + height_ng; +#if 1 + for (iter = sheet->priv->items; iter; iter = iter->next) { + sheet_item_select_in_area (iter->data, &cmin, &cmax); + } +#endif + + g_object_set (GOO_CANVAS_ITEM (rubberband_info->rectangle), "x", cmin.x, "y", cmin.y, + "width", width_ng, "height", height_ng, "visibility", GOO_CANVAS_ITEM_VISIBLE, + NULL); + goo_canvas_item_raise (GOO_CANVAS_ITEM (rubberband_info->rectangle), NULL); + } + return TRUE; +} + +gboolean rubberband_finish (Sheet *sheet, GdkEvent *event) +{ + RubberbandInfo *rubberband_info; + + rubberband_info = sheet->priv->rubberband_info; +#if 1 + GList *iter = NULL; + if (sheet->priv->preserve_selection_items) { + for (iter = sheet->priv->preserve_selection_items; iter; iter = iter->next) + sheet_item_set_preserve_selection (SHEET_ITEM (iter->data), FALSE); + + g_list_free (sheet->priv->preserve_selection_items); + sheet->priv->preserve_selection_items = NULL; + } +#endif + + sheet_pointer_ungrab (sheet, event); + + g_object_set (rubberband_info->rectangle, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + + rubberband_info->state = RUBBERBAND_START; + return TRUE; +} diff --git a/src/sheet/rubberband.h b/src/sheet/rubberband.h new file mode 100644 index 0000000..dc9b165 --- /dev/null +++ b/src/sheet/rubberband.h @@ -0,0 +1,66 @@ +/* + * rubberband.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * + * Description: Handles the user interaction when doing area/rubberband + *selections. + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __INPUT_CONTEXT_RUBBERBAND_H +#define __INPUT_CONTEXT_RUBBERBAND_H + +#include <goocanvas.h> +#include <glib.h> + +#include "coords.h" + +typedef enum { RUBBERBAND_DISABLED, RUBBERBAND_START, RUBBERBAND_ACTIVE } RubberbandState; + +typedef struct _RubberbandInfo RubberbandInfo; + +#include "sheet.h" + +struct _RubberbandInfo +{ + RubberbandState state; + Coords start; + Coords end; + GooCanvasRect *rectangle; +}; + +RubberbandInfo *rubberband_info_new (Sheet *sheet); +void rubberband_info_destroy (RubberbandInfo *rubberband); +gboolean rubberband_start (Sheet *sheet, GdkEvent *event); +gboolean rubberband_update (Sheet *sheet, GdkEvent *event); +gboolean rubberband_finish (Sheet *sheet, GdkEvent *event); + +#endif /* __INPUT_CONTEXT_RUBBERBAND_H */ diff --git a/src/sheet/sheet-item-factory.c b/src/sheet/sheet-item-factory.c index 9a0041f..4d48e8f 100644 --- a/src/sheet/sheet-item-factory.c +++ b/src/sheet/sheet-item-factory.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "sheet-item-factory.h" @@ -35,13 +35,12 @@ #include "part-item.h" #include "textbox-item.h" -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.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 (Sheet *sheet, ItemData *data) +SheetItem *sheet_item_factory_create_sheet_item (Sheet *sheet, ItemData *data) { SheetItem *item; @@ -54,19 +53,17 @@ sheet_item_factory_create_sheet_item (Sheet *sheet, ItemData *data) // Pick the right model. if (IS_PART (data)) { - NG_DEBUG ("sheet_item_factory_create_sheet_item part\n\n"); item = SHEET_ITEM (part_item_new (sheet, PART (data))); - } - else if (IS_WIRE (data)) { - NG_DEBUG ("sheet_item_factory_create_sheet_item wire\n\n"); + NG_DEBUG ("part %p", item); + } else if (IS_WIRE (data)) { item = SHEET_ITEM (wire_item_new (sheet, WIRE (data))); - } - else if (IS_TEXTBOX (data)) { - NG_DEBUG ("sheet_item_factory_create_sheet_item text\n\n"); + NG_DEBUG ("wire %p", item); + } else if (IS_TEXTBOX (data)) { item = SHEET_ITEM (textbox_item_new (sheet, TEXTBOX (data))); - } - else + NG_DEBUG ("text %p", item); + } else { g_warning ("Unknown Item type."); + } return item; } diff --git a/src/sheet/sheet-item-factory.h b/src/sheet/sheet-item-factory.h index 12579d9..9ba3990 100644 --- a/src/sheet/sheet-item-factory.h +++ b/src/sheet/sheet-item-factory.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SHEET_ITEM_FACTORY_H @@ -35,7 +35,6 @@ #include "sheet-item.h" -SheetItem *sheet_item_factory_create_sheet_item (Sheet *sheet, - ItemData *data); +SheetItem *sheet_item_factory_create_sheet_item (Sheet *sheet, ItemData *data); #endif /* __SHEET_ITEM_FACTORY_H */ diff --git a/src/sheet/sheet-item.c b/src/sheet/sheet-item.c index f0c4a12..c61a056 100644 --- a/src/sheet/sheet-item.c +++ b/src/sheet/sheet-item.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,10 +30,13 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ +// FIXME +#define FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK 1 + #include <glib/gi18n.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> @@ -39,29 +46,29 @@ #include "sheet-private.h" #include "sheet-item.h" #include "stock.h" -#include "config.h" #include "clipboard.h" +#include "options.h" -static void sheet_item_class_init (SheetItemClass * klass); +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_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_finalize (GObject *object); -static void sheet_item_run_menu (SheetItem *item, Sheet *sheet, - GdkEventButton *event); +static void sheet_item_run_menu (SheetItem *item, Sheet *sheet, GdkEventButton *event); static void sheet_item_reparent (SheetItem *item, GooCanvasGroup *group); static void sheet_item_dispose (GObject *object); -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" -struct _SheetItemPriv { - guint selected : 1; - guint preserve_selection : 1; - ItemData * data; - GtkActionGroup * action_group; - GtkUIManager * ui_manager; +struct _SheetItemPriv +{ + guint selected : 1; + guint preserve_selection : 1; + ItemData *data; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; }; enum { @@ -77,56 +84,44 @@ enum { ARG_ACTION_GROUP }; -enum { - MOVED, - PLACED, - SELECTION_CHANGED, - MOUSE_OVER, - DOUBLE_CLICKED, - LAST_SIGNAL -}; +enum { MOVED, PLACED, SELECTION_CHANGED, MOUSE_OVER, DOUBLE_CLICKED, LAST_SIGNAL }; -static guint so_signals[LAST_SIGNAL] = { 0 }; +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 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} -}; + {"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}}; G_DEFINE_TYPE (SheetItem, sheet_item, GOO_TYPE_CANVAS_GROUP) -static void -sheet_item_dispose (GObject *object) +static void sheet_item_dispose (GObject *object) { G_OBJECT_CLASS (sheet_item_parent_class)->dispose (object); } -static void -sheet_item_class_init (SheetItemClass *sheet_item_class) +static void sheet_item_class_init (SheetItemClass *sheet_item_class) { GObjectClass *object_class; @@ -135,26 +130,27 @@ sheet_item_class_init (SheetItemClass *sheet_item_class) object_class->dispose = sheet_item_dispose; object_class->set_property = sheet_item_set_property; object_class->get_property = sheet_item_get_property; - + sheet_item_parent_class = g_type_class_peek_parent (sheet_item_class); - + // Override from GooCanvasGroup - g_object_class_override_property (object_class, ARG_X, "x"); - g_object_class_override_property (object_class, ARG_Y, "y"); - g_object_class_override_property (object_class, ARG_WIDTH, "width"); - g_object_class_override_property (object_class, ARG_HEIGHT, "height"); - - 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_override_property (object_class, ARG_X, "x"); + g_object_class_override_property (object_class, ARG_Y, "y"); + g_object_class_override_property (object_class, ARG_WIDTH, "width"); + g_object_class_override_property (object_class, ARG_HEIGHT, "height"); + + 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) ); + g_param_spec_pointer ("action_group", + "SheetItem::action_group", + "action group", G_PARAM_READWRITE)); sheet_item_class->is_in_area = NULL; sheet_item_class->show_labels = NULL; @@ -164,54 +160,30 @@ sheet_item_class_init (SheetItemClass *sheet_item_class) 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); + 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) +static void sheet_item_init (SheetItem *item) { GError *error = NULL; @@ -224,149 +196,136 @@ sheet_item_init (SheetItem *item) 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); + 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_warning ("building menus failed: %s", error->message); g_error_free (error); } } -static void -sheet_item_set_property (GObject *object, guint prop_id, const GValue *value, - GParamSpec *spec) +static void sheet_item_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) { - GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; - SheetItem *sheet_item; - SheetPos pos; + GooCanvasItemSimple *simple = (GooCanvasItemSimple *)object; + SheetItem *sheet_item; + Coords pos; sheet_item = SHEET_ITEM (object); - + switch (prop_id) { - case ARG_X: - sheet_item->x = g_value_get_double (value); - break; - case ARG_Y: - sheet_item->y = g_value_get_double (value); + case ARG_X: + sheet_item->x = g_value_get_double (value); break; - case ARG_WIDTH: - sheet_item->width = g_value_get_double (value); + case ARG_Y: + sheet_item->y = g_value_get_double (value); break; - case ARG_HEIGHT: - sheet_item->height = g_value_get_double (value); - break; - case ARG_DATA: - if (sheet_item->priv->data) { - g_warning (_("Cannot set SheetItem after creation.")); - break; - } - sheet_item->priv->data = g_value_get_pointer (value); - item_data_get_pos (sheet_item->priv->data, &pos); - sheet_item->x = pos.x; - sheet_item->y = pos.y; + case ARG_WIDTH: + sheet_item->width = g_value_get_double (value); + break; + case ARG_HEIGHT: + sheet_item->height = g_value_get_double (value); + break; + case ARG_DATA: + if (sheet_item->priv->data) { + g_warning (_ ("Cannot set SheetItem after creation.")); 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->data = g_value_get_pointer (value); + item_data_get_pos (sheet_item->priv->data, &pos); + sheet_item->x = pos.x; + sheet_item->y = pos.y; + 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); - break; - default: - break; + break; + default: + break; } goo_canvas_item_simple_changed (simple, TRUE); } -static void -sheet_item_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +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_X: - g_value_set_double (value, sheet_item->x); - break; - case ARG_Y: - g_value_set_double (value, sheet_item->y); - break; - case ARG_WIDTH: - g_value_set_double (value, sheet_item->width); - break; - case ARG_HEIGHT: - g_value_set_double (value, sheet_item->height); - break; - 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; + case ARG_X: + g_value_set_double (value, sheet_item->x); + break; + case ARG_Y: + g_value_set_double (value, sheet_item->y); + break; + case ARG_WIDTH: + g_value_set_double (value, sheet_item->width); + break; + case ARG_HEIGHT: + g_value_set_double (value, sheet_item->height); + break; + 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_finalize (GObject *object) +static void sheet_item_finalize (GObject *object) { - GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; - SheetItem *sheet_item; + SheetItem *sheet_item = SHEET_ITEM (object); + + g_free (sheet_item->priv); + sheet_item->priv = NULL; - sheet_item = SHEET_ITEM (object); - if (simple->simple_data) { - g_free (sheet_item->priv); - sheet_item->priv = NULL; - } G_OBJECT_CLASS (sheet_item_parent_class)->finalize (object); } -static void -sheet_item_run_menu (SheetItem *item, Sheet *sheet, GdkEventButton *event) +static void sheet_item_run_menu (SheetItem *item, Sheet *sheet, GdkEventButton *event) { GtkWidget *menu; menu = gtk_ui_manager_get_widget (item->priv->ui_manager, "/ItemMenu"); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sheet, event->button, - event->time); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sheet, event->button, event->time); } - // Event handler for a SheetItem -gboolean -sheet_item_event (GooCanvasItem *sheet_item, - GooCanvasItem *sheet_target_item, - GdkEvent *event, Sheet *sheet) +gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_target_item, + GdkEvent *event, Sheet *sheet) { // Remember the last position of the mouse cursor. - static double last_x, last_y; GooCanvas *canvas; SheetPriv *priv; GList *list; - // 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; - - + + static Coords last, current, snapped; + // snapped : Mouse cursor position in window coordinates, snapped to the grid + // spacing. + // delta : Move the selected item(s) by this movement. + Coords delta, after; + g_return_val_if_fail (sheet_item != NULL, FALSE); g_return_val_if_fail (sheet != NULL, FALSE); priv = sheet->priv; canvas = GOO_CANVAS (sheet); - + switch (event->type) { case GDK_BUTTON_PRESS: // Grab focus to sheet for correct use of events @@ -375,7 +334,7 @@ sheet_item_event (GooCanvasItem *sheet_item, case 1: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); sheet->state = SHEET_STATE_DRAG_START; - sheet_get_pointer (sheet, &last_x, &last_y); + g_assert (sheet_get_pointer (sheet, &last.x, &last.y)); break; case 3: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); @@ -385,13 +344,12 @@ sheet_item_event (GooCanvasItem *sheet_item, // Bring up a context menu for right button clicks. if (!SHEET_ITEM (sheet_item)->priv->selected && - !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) - sheet_select_all (sheet, FALSE); + !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) + sheet_select_all (sheet, FALSE); sheet_item_select (SHEET_ITEM (sheet_item), TRUE); - sheet_item_run_menu (SHEET_ITEM (sheet_item), sheet, - (GdkEventButton *) event); + sheet_item_run_menu (SHEET_ITEM (sheet_item), sheet, (GdkEventButton *)event); break; default: return FALSE; @@ -423,8 +381,7 @@ sheet_item_event (GooCanvasItem *sheet_item, case GDK_BUTTON_RELEASE: switch (event->button.button) { case 1: - if (sheet->state != SHEET_STATE_DRAG && - sheet->state != SHEET_STATE_DRAG_START) + if (sheet->state != SHEET_STATE_DRAG && sheet->state != SHEET_STATE_DRAG_START) return TRUE; g_signal_stop_emission_by_name (sheet_item, "button-release-event"); @@ -442,152 +399,143 @@ sheet_item_event (GooCanvasItem *sheet_item, } // Get the mouse motion - sheet_get_pointer (sheet, &snapped_x, &snapped_y); - snapped_x -= last_x; - snapped_y -= last_y; + g_assert (sheet_get_pointer (sheet, &snapped.x, &snapped.y)); + delta = coords_sub (&snapped, &last); sheet->state = SHEET_STATE_NONE; - goo_canvas_pointer_ungrab (canvas, GOO_CANVAS_ITEM (sheet_item), - event->button.time); + goo_canvas_pointer_ungrab (canvas, GOO_CANVAS_ITEM (sheet_item), event->button.time); // Reparent the selected objects to the normal group // to have correct behaviour for (list = priv->selected_objects; list; list = list->next) { - sheet_item_reparent (SHEET_ITEM (list->data), - sheet->object_group); - } + sheet_item_reparent (SHEET_ITEM (list->data), sheet->object_group); + } for (list = priv->selected_objects; list; list = list->next) { - ItemData *item_data; - SheetPos pos; - - item_data = SHEET_ITEM (list->data)->priv->data; - pos.x = snapped_x; - pos.y = snapped_y; - item_data_move (item_data, &pos); - item_data_register (item_data); - } - g_list_free_full (list, g_object_unref); - + ItemData *item_data; + + item_data = SHEET_ITEM (list->data)->priv->data; + item_data_move (item_data, &delta); + item_data_get_pos (item_data, &after); + snap_to_grid (sheet->grid, &after.x, &after.y); + item_data_set_pos (item_data, &after); + item_data_register (item_data); + } break; } - + case GDK_KEY_PRESS: switch (event->key.keyval) { - case GDK_KEY_r: - sheet_rotate_selection (sheet); - { - gdouble x, y; - GooCanvasBounds bounds; - - sheet_get_pointer (sheet, &x, &y); - - // Center the objects around the mouse pointer. - goo_canvas_item_get_bounds ( - GOO_CANVAS_ITEM (priv->floating_group), &bounds); - - dx = x - (bounds.x1 + bounds.x2) / 2; - dy = y - (bounds.y1 + bounds.y2) / 2; - snap_to_grid (sheet->grid, &dx, &dy); - - goo_canvas_item_translate ( - GOO_CANVAS_ITEM (priv->floating_group), dx, dy); - - last_x = snapped_x; - last_y = snapped_y; - } - break; - default: - return FALSE; + case GDK_KEY_r: { +#ifndef FIXME_STILL_MINI_OFFSET + Coords bbdelta; + GooCanvasBounds bounds; + + // Center the objects around the mouse pointer. + goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->selected_group), &bounds); + + bbdelta.x = (bounds.x2 - bounds.x1) / 2.; + bbdelta.y = (bounds.y2 - bounds.y1) / 2.; +#endif + sheet_rotate_selection (sheet, 90); +#ifndef FIXME_STILL_MINI_OFFSET + // Center the objects around the mouse pointer. + goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->selected_group), &bounds); + + bbdelta.x -= (bounds.x2 - bounds.x1) / 2.; + bbdelta.y -= (bounds.y2 - bounds.y1) / 2.; + + snap_to_grid (sheet->grid, &bbdelta.x, &bbdelta.y); + + goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), bbdelta.x, + bbdelta.y); +#endif + } break; + default: + return FALSE; } return TRUE; case GDK_MOTION_NOTIFY: - if (sheet->state != SHEET_STATE_DRAG && - sheet->state != SHEET_STATE_DRAG_START) + 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 (IS_SHEET_ITEM (sheet_item) && - (!SHEET_ITEM (sheet_item)->priv->selected)) { + + // Update the selection if needed. + if (IS_SHEET_ITEM (sheet_item) && (!SHEET_ITEM (sheet_item)->priv->selected)) { if (!(event->button.state & GDK_SHIFT_MASK)) { sheet_select_all (sheet, FALSE); } sheet_item_select (SHEET_ITEM (sheet_item), TRUE); } - // Reparent the selected objects so that we can move them + // 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); + sheet_item_reparent (SHEET_ITEM (list->data), priv->selected_group); } - g_list_free_full (list, g_object_unref); - goo_canvas_pointer_grab (canvas, GOO_CANVAS_ITEM (sheet_item), - GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, - NULL, - event->button.time); + GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, + event->button.time); } // Set last_x & last_y to the pointer position - sheet_get_pointer (sheet, &snapped_x, &snapped_y); - - dx = snapped_x - last_x; - dy = snapped_y - last_y; - - // 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; - } + sheet_get_pointer (sheet, &snapped.x, &snapped.y); - // 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; - */ - - goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->selected_group), - NULL); - goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), - dx, dy); + delta = coords_sub (&snapped, &last); + +// 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 !FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK + last = snapped; +#else + goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->selected_group), NULL); +#endif + goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), delta.x, delta.y); return TRUE; - + default: return FALSE; } return TRUE; } -// Cancel the placement of floating items and remove them. -void -sheet_item_cancel_floating (Sheet *sheet) +// Cancel the placement of floating items and remove them. +void sheet_item_cancel_floating (Sheet *sheet) { GooCanvasGroup *group; GList *list; @@ -595,57 +543,48 @@ sheet_item_cancel_floating (Sheet *sheet) g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - group = GOO_CANVAS_GROUP (sheet->priv->floating_group); if (group == NULL) return; - if (sheet->state != SHEET_STATE_FLOAT && - sheet->state != SHEET_STATE_FLOAT_START) + if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START) return; if (g_signal_handler_is_connected (sheet, sheet->priv->float_handler_id)) g_signal_handler_disconnect (sheet, sheet->priv->float_handler_id); - g_object_unref (G_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) { - g_object_unref (G_OBJECT (list->data)); - } + // TODO verfiy that the following has no nasty sideffects + for (list = sheet->priv->floating_objects; list; list = list->next) { + goo_canvas_item_remove (list->data); // remove from canvas and free } + g_list_free (sheet->priv->floating_objects); + sheet->priv->floating_objects = NULL; + goo_canvas_item_remove (GOO_CANVAS_ITEM (group)); // Create a new empty group to prepare next floating group sheet->priv->floating_group = GOO_CANVAS_GROUP ( - goo_canvas_group_new (GOO_CANVAS_ITEM (sheet->object_group), - "x", 0.0, - "y", 0.0, - NULL)); + goo_canvas_group_new (GOO_CANVAS_ITEM (sheet->object_group), "x", 0.0, "y", 0.0, NULL)); + // sheet_clear_ghosts (sheet); sheet->priv->float_handler_id = 0; sheet->state = SHEET_STATE_NONE; - sheet_clear_ghosts (sheet); } // Event handler for a "floating" group of objects. -int -sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) +int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) { SheetPriv *priv; GList *list; - static SheetPos pos; - static int control_key_down = 0; + static gboolean keep = FALSE; - // Remember the last position of the mouse cursor. - static double last_x, last_y; + // Remember the start position of the mouse cursor. + static Coords last = {0., 0.}; // Mouse cursor position in window coordinates, snapped to the grid spacing. - double snapped_x, snapped_y; + static Coords snapped = {0., 0.}; // Move the selected item(s) by this movement. - double dx, dy; + Coords delta = {0., 0.}; g_return_val_if_fail (sheet != NULL, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); @@ -669,59 +608,50 @@ sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) return FALSE; case 1: - control_key_down = event->button.state & GDK_CONTROL_MASK; + // do not free the floating items, but use them like a stamp + keep = event->button.state & GDK_CONTROL_MASK; // Continue adding if CTRL is pressed - if (!control_key_down) { + if (!keep) { sheet->state = SHEET_STATE_NONE; g_signal_stop_emission_by_name (sheet, "event"); - if (g_signal_handler_is_connected (sheet, - sheet->priv->float_handler_id)) - g_signal_handler_disconnect (sheet, - sheet->priv->float_handler_id); + if (g_signal_handler_is_connected (sheet, sheet->priv->float_handler_id)) + g_signal_handler_disconnect (sheet, sheet->priv->float_handler_id); sheet->priv->float_handler_id = 0; } - - // Get pointer position independantly of the zoom - sheet_get_pointer (sheet, &pos.x, &pos.y); - + // FIXME assert that `Coords current` has been set by now! for (list = priv->floating_objects; list; list = list->next) { SheetItem *floating_item; ItemData *floating_data; - + // Create a real item. floating_item = list->data; - if (!control_key_down) { + if (!keep) { floating_data = sheet_item_get_data (floating_item); - g_object_set (floating_item, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); - } - else + g_object_set (floating_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + } else { + // FIXME the bounding box of the clone is wrong floating_data = item_data_clone (sheet_item_get_data (floating_item)); + } + + NG_DEBUG ("Item Data Pos will be %lf %lf", snapped.x, snapped.y) + + item_data_set_pos (floating_data, &snapped); + + schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), floating_data); - g_object_ref (G_OBJECT (floating_data)); - item_data_set_pos (floating_data, &pos); - schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), - floating_data); - if (!control_key_down) + if (!keep) g_object_unref (G_OBJECT (floating_item)); } - g_list_free_full (list, g_object_unref); - if (!control_key_down) { - g_list_free (sheet->priv->floating_objects); - sheet->priv->floating_objects = NULL; - } - else - g_object_set (G_OBJECT (sheet->priv->floating_group), - "x", pos.x, - "y", pos.y, + if (keep) { + g_object_set (G_OBJECT (priv->floating_group), "x", snapped.x, "y", snapped.y, NULL); - - pos.x = 0.0; - pos.y = 0.0; + } else { + g_list_free (priv->floating_objects); + priv->floating_objects = NULL; + } break; case 3: @@ -738,76 +668,86 @@ sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) return TRUE; case GDK_MOTION_NOTIFY: - if (sheet->state != SHEET_STATE_FLOAT && - sheet->state != SHEET_STATE_FLOAT_START) +// keep track of the position, as `sheet_get_pointer*()` does not work +// in other events than MOTION_NOTIFY +#if 0 + { + Coords tmp; + last = current; + if (sheet_get_pointer (sheet, &tmp.x, &tmp.y)) { + snapped_current = current = tmp; + snap_to_grid (sheet->grid, &snapped_current.x, &snapped_current.y); + } + } +#endif + if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START) return FALSE; g_signal_stop_emission_by_name (sheet, "event"); + // Get pointer position independantly of the zoom + if (sheet->state == SHEET_STATE_FLOAT_START) { sheet->state = SHEET_STATE_FLOAT; - - // Reparent the selected objects so that we can move them + last.x = last.y = 0.; + // 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); - + sheet_item_reparent (SHEET_ITEM (list->data), priv->floating_group); // Set the floating item visible - g_object_set (G_OBJECT (list->data), - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); - } - last_x = 0.0; - last_y = 0.0; + g_object_set (G_OBJECT (list->data), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + } +#if 0 + GooCanvasBounds box; + goo_canvas_item_get_bounds (priv->floating_group, &box); +#endif + NG_DEBUG ("\n\n\nFLOAT ### START\n\n\n\n"); } - // Get pointer position independantly of the zoom - sheet_get_pointer (sheet, &snapped_x, &snapped_y); - - // Calculate which amount to move the selected objects by. - dx = snapped_x - last_x; - dy = snapped_y - last_y; - - last_x = snapped_x; - last_y = snapped_y; - - for (list = priv->floating_objects; list; list = list->next) { - goo_canvas_item_translate (GOO_CANVAS_ITEM (list->data), - dx, dy); - } - g_list_free_full (list, g_object_unref); + sheet_get_pointer_snapped (sheet, &snapped.x, &snapped.y); + + delta = coords_sub (&snapped, &last); + NG_DEBUG ("drag floating current sx=%lf sy=%lf \n", snapped.x, snapped.y); + NG_DEBUG ("drag floating last lx=%lf ly=%lf \n", last.x, last.y); + NG_DEBUG ("drag floating delta -> dx=%lf dy=%lf \n", delta.x, delta.y); + +#if !FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK + last = snapped; +#else + goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->floating_group), NULL); +#endif + goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), delta.x, delta.y); + break; case GDK_KEY_PRESS: switch (event->key.keyval) { - case GDK_KEY_r: - case GDK_KEY_R: - sheet_rotate_ghosts (sheet); - { - gdouble x, y; - GooCanvasBounds bounds; - - sheet_get_pointer (sheet, &x, &y); - - // Center the objects around the mouse pointer. - goo_canvas_item_get_bounds ( - GOO_CANVAS_ITEM (priv->floating_group), &bounds); - - snapped_x = x - (bounds.x1 + bounds.x2) / 2; - snapped_y = y - (bounds.y1 + bounds.y2) / 2; - snap_to_grid (sheet->grid, &snapped_x, &snapped_y); - - goo_canvas_item_translate ( - GOO_CANVAS_ITEM (priv->floating_group), snapped_x, - snapped_y); - - last_x = snapped_x; - last_y = snapped_y; - } - break; - default: - return FALSE; + case GDK_KEY_r: + case GDK_KEY_R: { + Coords bbdelta; + GooCanvasBounds bounds; + + // Center the objects around the mouse pointer. + goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); + + bbdelta.x = (bounds.x2 - bounds.x1) / 2.; + bbdelta.y = (bounds.y2 - bounds.y1) / 2.; + + sheet_rotate_ghosts (sheet); + + // Center the objects around the mouse pointer. + goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); + + bbdelta.x -= (bounds.x2 - bounds.x1) / 2.; + bbdelta.y -= (bounds.y2 - bounds.y1) / 2.; + + snap_to_grid (sheet->grid, &bbdelta.x, &bbdelta.y); + + goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), bbdelta.x, + bbdelta.y); + } break; + default: + return FALSE; } default: return FALSE; @@ -815,14 +755,12 @@ sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) return TRUE; } -gboolean -sheet_item_select (SheetItem *item, gboolean select) +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)) { + if ((item->priv->selected && select) || (!item->priv->selected && !select)) { return FALSE; } @@ -832,8 +770,7 @@ sheet_item_select (SheetItem *item, gboolean select) return TRUE; } -void -sheet_item_select_in_area (SheetItem *item, SheetPos *p1, SheetPos *p2) +void sheet_item_select_in_area (SheetItem *item, Coords *p1, Coords *p2) { SheetItemClass *sheet_item_class; gboolean in_area; @@ -848,28 +785,24 @@ sheet_item_select_in_area (SheetItem *item, SheetPos *p1, SheetPos *p2) if (in_area && !item->priv->selected) sheet_item_select (item, TRUE); - else if (!in_area && item->priv->selected && - !item->priv->preserve_selection) + else if (!in_area && item->priv->selected && !item->priv->preserve_selection) sheet_item_select (item, FALSE); } // Reparent a sheet object without moving it on the sheet. -void -sheet_item_reparent (SheetItem *item, GooCanvasGroup *group) +void sheet_item_reparent (SheetItem *item, GooCanvasGroup *group) { g_return_if_fail (item != NULL); g_return_if_fail (IS_SHEET_ITEM (item)); g_return_if_fail (group != NULL); g_object_ref (item); - goo_canvas_item_remove (GOO_CANVAS_ITEM (item)); - goo_canvas_item_add_child (GOO_CANVAS_ITEM (group), - GOO_CANVAS_ITEM (item), - -1); + goo_canvas_item_remove (GOO_CANVAS_ITEM (item)); + goo_canvas_item_add_child (GOO_CANVAS_ITEM (group), GOO_CANVAS_ITEM (item), -1); + g_object_unref (item); } -void -sheet_item_edit_properties (SheetItem *item) +void sheet_item_edit_properties (SheetItem *item) { SheetItemClass *sheet_item_class; @@ -882,8 +815,7 @@ sheet_item_edit_properties (SheetItem *item) sheet_item_class->edit_properties (item); } -void -sheet_item_rotate (SheetItem *sheet_item, int angle, SheetPos *center) +void sheet_item_rotate (SheetItem *sheet_item, int angle, Coords *center) { g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_SHEET_ITEM (sheet_item)); @@ -891,8 +823,7 @@ sheet_item_rotate (SheetItem *sheet_item, int angle, SheetPos *center) item_data_rotate (sheet_item->priv->data, angle, center); } -void -sheet_item_paste (Sheet *sheet, ClipboardData *data) +void sheet_item_paste (Sheet *sheet, ClipboardData *data) { SheetItemClass *item_class; ItemDataClass *id_class; @@ -916,8 +847,7 @@ sheet_item_paste (Sheet *sheet, ClipboardData *data) } } -ItemData * -sheet_item_get_data (SheetItem *item) +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); @@ -925,8 +855,7 @@ sheet_item_get_data (SheetItem *item) return item->priv->data; } -Sheet * -sheet_item_get_sheet (SheetItem *item) +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); @@ -934,8 +863,7 @@ sheet_item_get_sheet (SheetItem *item) return SHEET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (item))); } -gboolean -sheet_item_get_selected (SheetItem *item) +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); @@ -943,8 +871,7 @@ sheet_item_get_selected (SheetItem *item) return item->priv->selected; } -gboolean -sheet_item_get_preserve_selection (SheetItem *item) +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); @@ -952,8 +879,7 @@ sheet_item_get_preserve_selection (SheetItem *item) return item->priv->preserve_selection; } -void -sheet_item_set_preserve_selection (SheetItem *item, gboolean set) +void sheet_item_set_preserve_selection (SheetItem *item, gboolean set) { g_return_if_fail (item != NULL); g_return_if_fail (IS_SHEET_ITEM (item)); @@ -961,8 +887,7 @@ sheet_item_set_preserve_selection (SheetItem *item, gboolean set) item->priv->preserve_selection = set; } -void -sheet_item_place (SheetItem *item, Sheet *sheet) +void sheet_item_place (SheetItem *item, Sheet *sheet) { SheetItemClass *sheet_item_class; @@ -975,8 +900,7 @@ sheet_item_place (SheetItem *item, Sheet *sheet) sheet_item_class->place (item, sheet); } -void -sheet_item_place_ghost (SheetItem *item, Sheet *sheet) +void sheet_item_place_ghost (SheetItem *item, Sheet *sheet) { SheetItemClass *sheet_item_class; @@ -989,19 +913,14 @@ sheet_item_place_ghost (SheetItem *item, Sheet *sheet) sheet_item_class->place_ghost (item, sheet); } -void -sheet_item_add_menu (SheetItem *item, const char *menu, - const GtkActionEntry *action_entries, int nb_entries) +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)) { + 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); } diff --git a/src/sheet/sheet-item.h b/src/sheet/sheet-item.h index 7814199..df91798 100644 --- a/src/sheet/sheet-item.h +++ b/src/sheet/sheet-item.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SHEET_ITEM_H #define __SHEET_ITEM_H @@ -37,12 +37,13 @@ #include <goocanvas.h> #include "sheet.h" -#include "sheet-pos.h" +#include "coords.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_CHECK_INSTANCE_TYPE(obj, TYPE_SHEET_ITEM)) +#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_CHECK_INSTANCE_TYPE (obj, TYPE_SHEET_ITEM)) typedef struct _SheetItemClass SheetItemClass; typedef struct _SheetItemPriv SheetItemPriv; @@ -50,55 +51,53 @@ typedef struct _SheetItemPriv SheetItemPriv; #include "sheet.h" #include "clipboard.h" -struct _SheetItem { +struct _SheetItem +{ GooCanvasGroup canvas_group; - gdouble width; - gdouble height; - gdouble x; - gdouble y; + gdouble width; + gdouble height; + gdouble x; // left + gdouble y; // top SheetItemPriv *priv; }; -struct _SheetItemClass { +struct _SheetItemClass +{ GooCanvasGroupClass 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) (Sheet *sheet, ItemData *data); - void (*place) (SheetItem *item, Sheet *sheet); - void (*place_ghost) (SheetItem *item, Sheet *sheet); + gboolean (*is_in_area)(SheetItem *item, Coords *p1, Coords *p2); + void (*show_labels)(SheetItem *sheet_item, gboolean show); + void (*edit_properties)(SheetItem *item); + void (*paste)(Sheet *sheet, ItemData *data); + void (*place)(SheetItem *item, Sheet *sheet); + void (*place_ghost)(SheetItem *item, Sheet *sheet); // Signal handlers. - void (*moved) (SheetItem *item); - void (*selection_changed)(SheetItem *item); - void (*mouse_over) (SheetItem *item); + 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 (Sheet *sheet, gboolean select); -gboolean sheet_item_select (SheetItem *item, gboolean select); -Sheet * sheet_item_get_sheet (SheetItem *item); -gboolean sheet_item_event (GooCanvasItem *sheet_item, - GooCanvasItem *sheet_target_item, GdkEvent *event, - Sheet *sheet); -int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event); -void sheet_item_cancel_floating (Sheet *sheet); -void sheet_item_edit_properties (SheetItem *item); -ItemData * sheet_item_get_data (SheetItem *item); -void sheet_item_paste (Sheet *sheet, - 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, Sheet *sheet); -void sheet_item_place_ghost (SheetItem *item, Sheet *sheet); -void sheet_item_add_menu (SheetItem *item, const char *menu, - const GtkActionEntry *action_entries, int nb_entries); +GType sheet_item_get_type (void); +void sheet_item_select_all (Sheet *sheet, gboolean select); +gboolean sheet_item_select (SheetItem *item, gboolean select); +Sheet *sheet_item_get_sheet (SheetItem *item); +gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_target_item, + GdkEvent *event, Sheet *sheet); +int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event); +void sheet_item_cancel_floating (Sheet *sheet); +void sheet_item_edit_properties (SheetItem *item); +ItemData *sheet_item_get_data (SheetItem *item); +void sheet_item_paste (Sheet *sheet, ClipboardData *data); +void sheet_item_rotate (SheetItem *sheet_item, int angle, Coords *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, Coords *p1, Coords *p2); +void sheet_item_place (SheetItem *item, Sheet *sheet); +void sheet_item_place_ghost (SheetItem *item, Sheet *sheet); +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 index 8f53796..5d4e8de 100644 --- a/src/sheet/sheet-private.h +++ b/src/sheet/sheet-private.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SHEET_PRIVATE_H @@ -38,47 +38,37 @@ #include "sheet.h" #include "create-wire.h" +#include "rubberband.h" -typedef enum { - RUBBER_NO = 0, - RUBBER_YES, - RUBBER_START -} RubberState; - -typedef struct { - RubberState state; - int timeout_id; - int click_start_state; - GooCanvasItem *rectangle; - double start_x, start_y; -} RubberbandInfo; - -struct _SheetPriv { - // Keeps the current signal handler for wire creation. - int wire_handler_id; +struct _SheetPriv +{ + // Keeps the current signal handler for wire creation + int wire_handler_id; // Keeps the signal handler for floating objects. - int float_handler_id; + int float_handler_id; + + double zoom; + gulong width; + gulong height; - double zoom; - gulong width; - gulong height; + GooCanvasGroup *selected_group; + GooCanvasGroup *floating_group; + GList *selected_objects; + GList *floating_objects; - GooCanvasGroup *selected_group; - GooCanvasGroup *floating_group; - GList *selected_objects; - GList *floating_objects; + GList *items; + RubberbandInfo *rubberband_info; + GList *preserve_selection_items; + GooCanvasClass *sheet_parent_class; - GList *items; - RubberbandInfo *rubberband; - GList *preserve_selection_items; - GooCanvasClass *sheet_parent_class; + GHashTable *voltmeter_nodes; - GList *voltmeter_items; // List of GooCanvasItem - GHashTable *voltmeter_nodes; + CreateWireInfo *create_wire_info; // Wire context for each schematic - CreateWireContext *create_wire_context; // Wire context for each schematic + GHashTable *node_dots; - GHashTable *node_dots; + guint8 keyboard_grabbed : 1; + guint8 pointer_grabbed : 1; }; #endif diff --git a/src/sheet/sheet.c b/src/sheet/sheet.c index 7d3d0e0..8213430 100644 --- a/src/sheet/sheet.c +++ b/src/sheet/sheet.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - - * Web page: https://github.com/marc-lorber/oregano + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +30,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gdk/gdk.h> @@ -45,49 +49,39 @@ #include "grid.h" #include "sheet-item-factory.h" #include "schematic-view.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_set_zoom (const Sheet *sheet, double zoom); -static GList * sheet_preserve_selection (Sheet *sheet); -static void rotate_items (Sheet *sheet, GList *items); -static void flip_items (Sheet *sheet, GList *items, gboolean horizontal); -static void node_dot_added_callback (Schematic *schematic, SheetPos *pos, - Sheet *sheet); -static void node_dot_removed_callback (Schematic *schematic, SheetPos *pos, - Sheet *sheet); -static void sheet_finalize (GObject *object); -static int dot_equal (gconstpointer a, gconstpointer b); -static guint dot_hash (gconstpointer key); - +#include "options.h" +#include "rubberband.h" +#include "create-wire.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_set_zoom (Sheet *sheet, const double zoom); +static GList *sheet_preserve_selection (Sheet *sheet); +static void rotate_items (Sheet *sheet, GList *items, gint angle); +static void move_items (Sheet *sheet, GList *items, const Coords *delta); +static void flip_items (Sheet *sheet, GList *items, IDFlip direction); +static void node_dot_added_callback (Schematic *schematic, Coords *pos, Sheet *sheet); +static void node_dot_removed_callback (Schematic *schematic, Coords *pos, Sheet *sheet); +static void sheet_finalize (GObject *object); +static int dot_equal (gconstpointer a, gconstpointer b); +static guint dot_hash (gconstpointer key); #define ZOOM_MIN 0.35 #define ZOOM_MAX 3 -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) +#include "debug.h" -enum { - SELECTION_CHANGED, - BUTTON_PRESS, - CONTEXT_CLICK, - CANCEL, - LAST_SIGNAL -}; -static guint signals[LAST_SIGNAL] = { 0 }; +enum { SELECTION_CHANGED, BUTTON_PRESS, CONTEXT_CLICK, CANCEL, LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = {0}; -enum { - ARG_0, - ARG_ZOOM -}; +enum { ARG_0, ARG_ZOOM }; G_DEFINE_TYPE (Sheet, sheet, GOO_TYPE_CANVAS) -static void -sheet_class_init (SheetClass *sheet_class) +static void sheet_class_init (SheetClass *sheet_class) { GObjectClass *object_class; @@ -99,246 +93,420 @@ sheet_class_init (SheetClass *sheet_class) sheet_parent_class = g_type_class_peek (GOO_TYPE_CANVAS); 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)); - - // 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, - 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); -} + g_param_spec_double ("zoom", "Sheet::zoom", "the zoom factor", + 0.01f, 10.0f, 1.0f, G_PARAM_READWRITE)); -static -cairo_pattern_t* -create_stipple (const char *color_name, guchar stipple_data[16]) -{ - cairo_surface_t *surface; - cairo_pattern_t *pattern; - GdkColor color; - - gdk_color_parse (color_name, &color); - stipple_data[2] = stipple_data[14] = color.red >> 8; - stipple_data[1] = stipple_data[13] = color.green >> 8; - stipple_data[0] = stipple_data[12] = color.blue >> 8; - surface = cairo_image_surface_create_for_data (stipple_data, - CAIRO_FORMAT_ARGB32, - 2, 2, 8); - pattern = cairo_pattern_create_for_surface (surface); - cairo_surface_destroy (surface); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - - return pattern; + // 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, 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); } -static void -sheet_init (Sheet *sheet) +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->floating_objects = NULL; + sheet->priv->selected_objects = NULL; sheet->priv->wire_handler_id = 0; sheet->priv->float_handler_id = 0; - + sheet->priv->items = NULL; - sheet->priv->rubberband = g_new0 (RubberbandInfo, 1); - sheet->priv->rubberband->state = RUBBER_NO; + sheet->priv->rubberband_info = NULL; + sheet->priv->create_wire_info = NULL; sheet->priv->preserve_selection_items = NULL; sheet->priv->sheet_parent_class = g_type_class_ref (GOO_TYPE_CANVAS); - sheet->priv->voltmeter_items = NULL; - sheet->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash, - g_str_equal, g_free, g_free); + sheet->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); sheet->state = SHEET_STATE_NONE; } -static void -sheet_finalize (GObject *object) -{ +static void sheet_finalize (GObject *object) +{ Sheet *sheet = SHEET (object); if (sheet->priv) { - g_hash_table_destroy (sheet->priv->node_dots); + g_list_free (sheet->priv->selected_objects); + g_list_free (sheet->priv->floating_objects); + g_list_free (sheet->priv->items); + g_list_free (sheet->priv->preserve_selection_items); + + if (sheet->priv->voltmeter_nodes) + g_hash_table_destroy (sheet->priv->voltmeter_nodes); + + if (sheet->priv->node_dots) + g_hash_table_destroy (sheet->priv->node_dots); + + g_free (sheet->priv->rubberband_info); + g_free (sheet->priv->create_wire_info); + g_free (sheet->priv); } + + if (sheet->grid) + g_object_unref (G_OBJECT (sheet->grid)); + if (G_OBJECT_CLASS (sheet_parent_class)->finalize) - (* G_OBJECT_CLASS (sheet_parent_class)->finalize) (object); + (*G_OBJECT_CLASS (sheet_parent_class)->finalize)(object); } -void -sheet_get_pointer (Sheet *sheet, gdouble *x, gdouble *y) +/** + * position within the sheet in pixel coordinates + * + * coordinates are clamped to grid if grid is enabled + * see snap_to_grid + * zero point : top left corner of the window (not widget!) + * + * @param x horizontal, left to right + * @param y vertical, top to bottom + * @returns wether the position could be detected properly + * + * @attention never call in event handlers! + */ +gboolean sheet_get_pointer_pixel (Sheet *sheet, gdouble *x, gdouble *y) { - GtkWidget *widget; - GtkAdjustment *hadjustment; - GtkAdjustment *vadjustment; - gdouble value, x1, y1; + GtkAdjustment *hadj = NULL, *vadj = NULL; + gdouble x1, y1; gint _x, _y; + GdkDevice *device_pointer; + GdkRectangle allocation; + GdkDisplay *display; + GdkWindow *window; +#if GTK_CHECK_VERSION (3,20,0) + GdkSeat *seat; +#else + GdkDeviceManager *device_manager; +#endif + + // deprecated gtk_widget_get_pointer (GTK_WIDGET (sheet), &_x, &_y); + // replaced by a code copied from evince + + if (G_UNLIKELY (!sheet || !gtk_widget_get_realized (GTK_WIDGET (sheet)))) { + NG_DEBUG ("Widget is not realized."); + return FALSE; + } + + display = gtk_widget_get_display (GTK_WIDGET (sheet)); + +#if GTK_CHECK_VERSION (3,20,0) + seat = gdk_display_get_default_seat (display); + device_pointer = gdk_seat_get_pointer (seat); +#else + device_manager = gdk_display_get_device_manager (display); + + // gdk_device_manager_get_client_pointer + // shall not be used within events + device_pointer = gdk_device_manager_get_client_pointer (device_manager); +#endif + + window = gtk_widget_get_window (GTK_WIDGET (sheet)); + + if (!window) { + NG_DEBUG ("Window is not realized."); + return FALSE; + } + // even though above is all defined the below will always return NUL for + // unknown reason and _x and _y are populated as expected + gdk_window_get_device_position (window, device_pointer, &_x, &_y, NULL); +#if 0 + if (!window) { //fails always + NG_DEBUG ("Window does not seem to be realized yet?"); + return FALSE; + } +#else + #if GTK_CHECK_VERSION (3,20,0) + NG_DEBUG ("\n%p %p %p %p %i %i\n\n", display, seat, device_pointer, window, _x, _y); + #else + NG_DEBUG ("\n%p %p %p %p %i %i\n\n", display, device_manager, device_pointer, window, _x, _y); + #endif +#endif + + gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); + + _x -= allocation.x; + _y -= allocation.y; + + x1 = (gdouble)_x; + y1 = (gdouble)_y; + + if (!sheet_get_adjustments (sheet, &hadj, &vadj)) + return FALSE; + + x1 += gtk_adjustment_get_value (hadj); + y1 += gtk_adjustment_get_value (vadj); - gtk_widget_get_pointer (GTK_WIDGET (sheet), &_x, &_y); - x1 = (gdouble) _x; - y1 = (gdouble) _y; - - widget = gtk_widget_get_parent (GTK_WIDGET (sheet)); - hadjustment = gtk_scrolled_window_get_hadjustment ( - GTK_SCROLLED_WINDOW (widget)); - value = gtk_adjustment_get_value (hadjustment); - - x1 += value; - vadjustment = gtk_scrolled_window_get_vadjustment ( - GTK_SCROLLED_WINDOW (widget)); - value = gtk_adjustment_get_value (vadjustment); - y1 += value; *x = x1; *y = y1; + return TRUE; +} + +/** + * get the pointer position in goocanvas coordinates + * + * @attention shall not be called in event callbacks, + * except for GDK_MOTION_... where it is useless since + * the event itself contains the cursor position + */ +gboolean sheet_get_pointer (Sheet *sheet, gdouble *x, gdouble *y) +{ + if (!sheet_get_pointer_pixel (sheet, x, y)) + return FALSE; + goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), x, y); + return TRUE; +} + +gboolean sheet_get_pointer_snapped (Sheet *sheet, gdouble *x, gdouble *y) +{ + if (!sheet_get_pointer_pixel (sheet, x, y)) + return FALSE; goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), x, y); snap_to_grid (sheet->grid, x, y); + return TRUE; } -void -sheet_get_zoom (const Sheet *sheet, gdouble *zoom) +void sheet_get_zoom (const Sheet *sheet, gdouble *zoom) { + g_return_if_fail (sheet); + g_return_if_fail (IS_SHEET (sheet)); + *zoom = sheet->priv->zoom; } -static void -sheet_set_zoom (const Sheet *sheet, double zoom) +static void sheet_set_zoom (Sheet *sheet, const double zoom) { - goo_canvas_set_scale (GOO_CANVAS (sheet), zoom); + g_return_if_fail (sheet); + g_return_if_fail (IS_SHEET (sheet)); + sheet->priv->zoom = zoom; } -void -sheet_change_zoom (const Sheet *sheet, gdouble rate) +/* + * \brief gets the sheets parent adjustments + * + * @returns TRUE on success + */ +gboolean sheet_get_adjustments (const Sheet *sheet, GtkAdjustment **hadj, GtkAdjustment **vadj) +{ + GtkWidget *parent; + GtkScrolledWindow *scrolled; + + if (G_UNLIKELY (!sheet)) + return FALSE; + if (G_UNLIKELY (!vadj || !hadj)) + return FALSE; + + parent = gtk_widget_get_parent (GTK_WIDGET (sheet)); + if (G_UNLIKELY (!parent || !GTK_IS_SCROLLED_WINDOW (parent))) + return FALSE; + scrolled = GTK_SCROLLED_WINDOW (parent); + + *hadj = gtk_scrolled_window_get_hadjustment (scrolled); + if (G_UNLIKELY (!*hadj || !GTK_IS_ADJUSTMENT (*hadj))) + return FALSE; + + *vadj = gtk_scrolled_window_get_vadjustment (scrolled); + if (G_UNLIKELY (!*vadj || !GTK_IS_ADJUSTMENT (*vadj))) + return FALSE; + + return TRUE; +} + +/** + * \brief change the zoom by factor (zoom step) + * + * zoom origin when zooming in is the cursor + * zoom origin when zooming out is the center of the current viewport + * + * @param sheet + * @param factor values should be in the range of [0.5 .. 2] + */ +void sheet_zoom_step (Sheet *sheet, const gdouble factor) { - gdouble scale; - - sheet->priv->zoom *= rate; - scale = goo_canvas_get_scale (GOO_CANVAS (sheet)); - scale = scale * rate; - goo_canvas_set_scale (GOO_CANVAS (sheet), scale); + double zoom; + + g_return_if_fail (sheet); + g_return_if_fail (IS_SHEET (sheet)); + + sheet_get_zoom (sheet, &zoom); + sheet_set_zoom (sheet, zoom * factor); + + Coords adju, r, pointer, delta, center, pagesize; + GtkAdjustment *hadj = NULL, *vadj = NULL; + GooCanvas *canvas; + gboolean b = FALSE; + + canvas = GOO_CANVAS (sheet); + + // if we scroll out, just use the center as focus + // mouse curser centered scroll out "feels" awkward + if (factor < 1.) { + goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); + return; + } + + // get pointer position in pixels + // just skip the correction if we can not get the pointer + if (!sheet_get_pointer_pixel (sheet, &pointer.x, &pointer.y)) { + goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); + g_warning ("Failed to get cursor position."); + return; + } + + // top left corner in pixels + b = sheet_get_adjustments (sheet, &hadj, &vadj); + if (b) { + adju.x = gtk_adjustment_get_value (hadj); + adju.y = gtk_adjustment_get_value (vadj); + // get the page size in pixels + pagesize.x = gtk_adjustment_get_page_size (hadj); + pagesize.y = gtk_adjustment_get_page_size (vadj); + } else { + // FIXME untested codepath, check for variable space conversion + // FIXME Pixel vs GooUnits + gdouble left, right, top, bottom; + goo_canvas_get_bounds (canvas, &left, &top, &right, &bottom); + pagesize.x = bottom - top; + pagesize.y = right - left; + adju.x = adju.y = 0.; + } + + // calculate the center of the widget in pixels + center.x = adju.x + pagesize.x / 2.; + center.y = adju.y + pagesize.y / 2.; + + // calculate the delta between the center and the pointer in pixels + // this is required as the center is the zoom target + delta.x = pointer.x - center.x; + delta.y = pointer.y - center.y; + + // increase the top left position in pixels by our calculated delta + adju.x += delta.x; + adju.y += delta.y; + + // convert to canvas coords + goo_canvas_convert_from_pixels (canvas, &adju.x, &adju.y); + + // the center of the canvas is now our cursor position + goo_canvas_scroll_to (canvas, adju.x, adju.y); + + // calculate a correction term + // for the case that we can not scroll the pane far enough to + // compensate the whole off-due-to-wrong-center-error + + if (b) { + r.x = gtk_adjustment_get_value (hadj); + r.y = gtk_adjustment_get_value (vadj); + goo_canvas_convert_from_pixels (canvas, &r.x, &r.y); + // the correction term in goo coordinates, to be subtracted from the + // backscroll distance + r.x -= adju.x; + r.y -= adju.y; + } else { + r.x = r.y = 0.; + } + + // no the center is our cursor position and we can safely call scale + goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); + + // top left corner in pixels after scaling + if (b) { + adju.x = gtk_adjustment_get_value (hadj); + adju.y = gtk_adjustment_get_value (vadj); + } else { + adju.x = adju.y = 0.; + } + + // gtk_adjustment_get_page_size is constant before and after scale + adju.x -= (delta.x) / sheet->priv->zoom; + adju.y -= (delta.y) / sheet->priv->zoom; + goo_canvas_convert_from_pixels (canvas, &adju.x, &adju.y); + + goo_canvas_scroll_to (canvas, adju.x - r.x, adju.y - r.y); + + gtk_widget_queue_draw (GTK_WIDGET (canvas)); } -// This function defines the drawing sheet on which schematic will be drawn -GtkWidget * -sheet_new (int width, int height) +/** + * \brief defines the drawing widget on which the actual schematic will be drawn + * + * @param width width of the content area + * @param height height of the content area + */ +GtkWidget *sheet_new (const gdouble width, const gdouble height) { GooCanvas *sheet_canvas; GooCanvasGroup *sheet_group; GooCanvasPoints *points; Sheet *sheet; GtkWidget *sheet_widget; - GooCanvasItem *root; - + GooCanvasItem *root; + // Creation of the Canvas sheet = SHEET (g_object_new (TYPE_SHEET, NULL)); sheet_canvas = GOO_CANVAS (sheet); - g_object_set (G_OBJECT (sheet_canvas), - "bounds-from-origin", FALSE, - "bounds-padding", 4.0, - "background-color-rgb", 0xFFFFFF, - NULL); - - root = goo_canvas_get_root_item (sheet_canvas); - - sheet_group = GOO_CANVAS_GROUP (goo_canvas_group_new ( - root, - NULL)); - sheet_widget = GTK_WIDGET (sheet); + g_object_set (G_OBJECT (sheet_canvas), "bounds-from-origin", FALSE, "bounds-padding", 4.0, + "background-color-rgb", 0xFFFFFF, NULL); - goo_canvas_set_bounds (GOO_CANVAS (sheet_canvas), 0, 0, - width + 20, height + 20); + root = goo_canvas_get_root_item (sheet_canvas); - // Define vicinity around GooCanvasItem - //sheet_canvas->close_enough = 6.0; + sheet_group = GOO_CANVAS_GROUP (goo_canvas_group_new (root, NULL)); + sheet_widget = GTK_WIDGET (sheet); + + goo_canvas_set_bounds (GOO_CANVAS (sheet_canvas), 0., 0., width + 20., height + 20.); sheet->priv->width = width; sheet->priv->height = height; // Create the dot grid. - sheet->grid = grid_create (GOO_CANVAS_ITEM (sheet_group), - width, - height); + sheet->grid = grid_new (GOO_CANVAS_ITEM (sheet_group), width, height); // Everything outside the sheet should be gray. // top // - goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), - 0.0, - 0.0, - (double) width + 20.0, - 20.0, - "fill_color", "gray", - "line-width", 0.0, - NULL); - - goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), - 0.0, - (double) height, - (double) width + 20.0, - (double) height + 20.0, - "fill_color", "gray", - "line-width", 0.0, - NULL); + goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), 0.0, 0.0, width + 20.0, 20.0, "fill_color", + "gray", "line-width", 0.0, NULL); - // right // - goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), - 0.0, - 0.0, - 20.0, - (double) height + 20.0, - "fill_color", "gray", - "line-width", 0.0, - NULL); - - goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), - (double) width, - 0.0, - (double) width + 20.0, - (double) height + 20.0, - "fill_color", "gray", - "line-width", 0.0, - NULL); + goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), 0.0, height, width + 20.0, height + 20.0, + "fill_color", "gray", "line-width", 0.0, NULL); + // right // + goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), 0.0, 0.0, 20.0, height + 20.0, "fill_color", + "gray", "line-width", 0.0, NULL); + + goo_canvas_rect_new (GOO_CANVAS_ITEM (sheet_group), width, 0.0, width + 20.0, height + 20.0, + "fill_color", "gray", "line-width", 0.0, NULL); + + if (oregano_options_debug_directions ()) { + goo_canvas_polyline_new_line (GOO_CANVAS_ITEM (sheet_group), 10.0, 10.0, 50.0, 10.0, + "stroke-color", "green", "line-width", 2.0, "end-arrow", TRUE, + NULL); + goo_canvas_text_new (GOO_CANVAS_ITEM (sheet_group), "x", 90.0, 10.0, -1.0, + GOO_CANVAS_ANCHOR_WEST, "fill-color", "green", NULL); + + goo_canvas_polyline_new_line (GOO_CANVAS_ITEM (sheet_group), 10.0, 10.0, 10.0, 50.0, + "stroke-color", "red", "line-width", 2.0, "end-arrow", TRUE, + NULL); + goo_canvas_text_new (GOO_CANVAS_ITEM (sheet_group), "y", 10.0, 90.0, -1.0, + GOO_CANVAS_ANCHOR_CENTER, "fill-color", "red", NULL); + } // Draw a thin black border around the sheet. points = goo_canvas_points_new (5); points->coords[0] = 20.0; @@ -351,57 +519,94 @@ sheet_new (int width, int height) points->coords[7] = height; points->coords[8] = 20.0; points->coords[9] = 20.0; - - goo_canvas_polyline_new (GOO_CANVAS_ITEM (sheet_group), - FALSE, 0, - "line-width", 1.0, - "points", points, - NULL); + + goo_canvas_polyline_new (GOO_CANVAS_ITEM (sheet_group), FALSE, 0, "line-width", 1.0, "points", + points, NULL); goo_canvas_points_unref (points); // Finally, create the object group that holds all objects. - sheet->object_group = GOO_CANVAS_GROUP (goo_canvas_group_new ( - root, - "x", 0.0, - "y", 0.0, - NULL)); - - sheet->priv->selected_group = GOO_CANVAS_GROUP (goo_canvas_group_new ( - GOO_CANVAS_ITEM (sheet->object_group), - "x", 0.0, - "y", 0.0, - NULL)); - - sheet->priv->floating_group = GOO_CANVAS_GROUP (goo_canvas_group_new ( - GOO_CANVAS_ITEM (sheet->object_group), - "x", 0.0, - "y", 0.0, - NULL)); - - // Hash table that keeps maps coordinate to a specific dot. - sheet->priv->node_dots = g_hash_table_new_full (dot_hash, dot_equal, g_free, - NULL); - + sheet->object_group = GOO_CANVAS_GROUP (goo_canvas_group_new (root, "x", 0.0, "y", 0.0, NULL)); + NG_DEBUG ("root group %p", sheet->object_group); + + sheet->priv->selected_group = GOO_CANVAS_GROUP ( + goo_canvas_group_new (GOO_CANVAS_ITEM (sheet->object_group), "x", 0.0, "y", 0.0, NULL)); + NG_DEBUG ("selected group %p", sheet->priv->selected_group); + + sheet->priv->floating_group = GOO_CANVAS_GROUP ( + goo_canvas_group_new (GOO_CANVAS_ITEM (sheet->object_group), "x", 0.0, "y", 0.0, NULL)); + NG_DEBUG ("floating group %p", sheet->priv->floating_group); + + // Hash table that maps coordinates to a specific dot. + sheet->priv->node_dots = g_hash_table_new_full (dot_hash, dot_equal, g_free, NULL); + + // this requires object_group to be setup properly + sheet->priv->rubberband_info = rubberband_info_new (sheet); + sheet->priv->create_wire_info = create_wire_info_new (sheet); + return sheet_widget; } -static void -sheet_set_property (GObject *object, - guint prop_id, const GValue *value, GParamSpec *spec) +/* + * Replace the current sheet with a new one (eventually + * with a different size, i.e. with a different width + * and/or height). + */ +gboolean sheet_replace (SchematicView *sv) { - const Sheet *sheet = SHEET (object); + g_return_val_if_fail (sv != NULL, FALSE); + g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), FALSE); + + Schematic *sm = schematic_view_get_schematic (sv); + Sheet *old_sheet = schematic_view_get_sheet (sv); + Sheet *sheet; + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (old_sheet)); + gdouble zoom; + + g_return_val_if_fail (old_sheet != NULL, FALSE); + g_return_val_if_fail (IS_SHEET (old_sheet), FALSE); + + sheet_get_zoom (old_sheet, &zoom); + sheet = SHEET (sheet_new ((double) schematic_get_width (sm) + SHEET_BORDER, (double) schematic_get_height (sm) + SHEET_BORDER)); + if (!sheet) + return FALSE; + + sheet_set_zoom (sheet, zoom); + goo_canvas_set_scale (GOO_CANVAS (sheet), zoom); + + g_signal_connect (G_OBJECT (sheet), "event", G_CALLBACK (sheet_event_callback), + sheet); + + schematic_view_set_sheet (sv, sheet); + + rubberband_info_destroy (old_sheet->priv->rubberband_info); + old_sheet->priv->rubberband_info = NULL; + + create_wire_info_destroy (old_sheet->priv->create_wire_info); + old_sheet->priv->create_wire_info = NULL; + + gtk_widget_destroy (GTK_WIDGET (old_sheet)); + + gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (sheet)); + + gtk_widget_grab_focus (GTK_WIDGET (sheet)); + + return TRUE; +} + +static void sheet_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) +{ + Sheet *sheet = SHEET (object); switch (prop_id) { case ARG_ZOOM: - sheet_set_zoom (sheet, g_value_get_double (value)); + sheet_set_zoom (sheet, (const double) g_value_get_double (value)); break; } } -static void -sheet_get_property (GObject *object, - guint prop_id, GValue *value, GParamSpec *spec) +static void sheet_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) { const Sheet *sheet = SHEET (object); @@ -416,84 +621,82 @@ sheet_get_property (GObject *object, } } -void -sheet_scroll (const Sheet *sheet, int delta_x, int delta_y) +/* + * scroll to <dx,dy> in pixels relative to the current coords + * note that pixels are _not_ affected by zoom + */ +void sheet_scroll_pixel (const Sheet *sheet, int delta_x, int delta_y) { - GtkAdjustment *hadj, *vadj; + GtkAdjustment *hadj = NULL, *vadj = NULL; GtkAllocation allocation; gfloat vnew, hnew; gfloat hmax, vmax; + gfloat x1, y1; const SheetPriv *priv = sheet->priv; - hadj = gtk_container_get_focus_hadjustment (GTK_CONTAINER (sheet)); - vadj = gtk_container_get_focus_vadjustment (GTK_CONTAINER (sheet)); + if (sheet_get_adjustments (sheet, &hadj, &vadj)) { + x1 = gtk_adjustment_get_value (hadj); + y1 = gtk_adjustment_get_value (vadj); + } else { + x1 = y1 = 0.f; + } gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); if (priv->width > allocation.width) - hmax = (gfloat) (priv->width - allocation.width); + hmax = (gfloat)(priv->width - allocation.width); else - hmax = 0.0; + hmax = 0.f; if (priv->height > allocation.height) - vmax = (gfloat) (priv->height - allocation.height); + vmax = (gfloat)(priv->height - allocation.height); else - vmax = 0.0; + vmax = 0.f; - hnew = CLAMP (gtk_adjustment_get_value (hadj) + (gfloat) delta_x, 0.0, hmax); - vnew = CLAMP (gtk_adjustment_get_value (vadj) + (gfloat) delta_y, 0.0, vmax); + hnew = CLAMP (x1 + (gfloat)delta_x, 0.f, hmax); + vnew = CLAMP (y1 + (gfloat)delta_y, 0.f, vmax); - if (hnew != gtk_adjustment_get_value (hadj)) { + if (hadj && hnew != x1) { gtk_adjustment_set_value (hadj, hnew); g_signal_emit_by_name (G_OBJECT (hadj), "value_changed"); } - if (vnew != gtk_adjustment_get_value (vadj)) { + if (vadj && vnew != y1) { gtk_adjustment_set_value (vadj, vnew); g_signal_emit_by_name (G_OBJECT (vadj), "value_changed"); } } -void -sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height) +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_remove_selected_object (const Sheet *sheet, SheetItem *item) +void sheet_remove_selected_object (const Sheet *sheet, SheetItem *item) { - sheet->priv->selected_objects = - g_list_remove (sheet->priv->selected_objects, item); + sheet->priv->selected_objects = g_list_remove (sheet->priv->selected_objects, item); } -void -sheet_prepend_selected_object (Sheet *sheet, SheetItem *item) +void sheet_prepend_selected_object (Sheet *sheet, SheetItem *item) { - sheet->priv->selected_objects = - g_list_prepend (sheet->priv->selected_objects, item); + sheet->priv->selected_objects = g_list_prepend (sheet->priv->selected_objects, item); } -void -sheet_remove_floating_object (const Sheet *sheet, SheetItem *item) +void sheet_remove_floating_object (const Sheet *sheet, SheetItem *item) { - sheet->priv->floating_objects = - g_list_remove (sheet->priv->floating_objects, item); + sheet->priv->floating_objects = g_list_remove (sheet->priv->floating_objects, item); } -void -sheet_prepend_floating_object (Sheet *sheet, SheetItem *item) +void sheet_prepend_floating_object (Sheet *sheet, SheetItem *item) { - sheet->priv->floating_objects = - g_list_prepend (sheet->priv->floating_objects, item); + sheet->priv->floating_objects = g_list_prepend (sheet->priv->floating_objects, item); } -void -sheet_connect_part_item_to_floating_group (Sheet *sheet, gpointer *sv) +void sheet_connect_part_item_to_floating_group (Sheet *sheet, gpointer *sv) { - g_return_if_fail (sheet != NULL); + g_return_if_fail (sheet); g_return_if_fail (IS_SHEET (sheet)); - g_return_if_fail (sv != NULL); + g_return_if_fail (sv); g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); sheet->state = SHEET_STATE_FLOAT_START; @@ -501,29 +704,25 @@ sheet_connect_part_item_to_floating_group (Sheet *sheet, gpointer *sv) 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); + sheet->priv->float_handler_id = + g_signal_connect (G_OBJECT (sheet), "event", G_CALLBACK (sheet_item_floating_event), sv); } -void -sheet_show_node_labels (Sheet *sheet, gboolean show) +void sheet_show_node_labels (Sheet *sheet, gboolean show) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - GList *item; + GList *item = NULL; - for (item = sheet->priv->items; item; item=item->next) { - if (IS_PART_ITEM (item->data)) - if (part_get_num_pins (PART (sheet_item_get_data (SHEET_ITEM(item->data))))==1) + for (item = sheet->priv->items; item; item = item->next) { + if (IS_PART_ITEM (item->data)) + if (part_get_num_pins (PART (sheet_item_get_data (SHEET_ITEM (item->data)))) == 1) part_item_show_node_labels (PART_ITEM (item->data), show); - } - g_list_free_full (item, g_object_unref); + } } -void -sheet_add_item (Sheet *sheet, SheetItem *item) +void sheet_add_item (Sheet *sheet, SheetItem *item) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -533,312 +732,185 @@ sheet_add_item (Sheet *sheet, SheetItem *item) sheet->priv->items = g_list_prepend (sheet->priv->items, item); } -int -sheet_rubberband_timeout_cb (Sheet *sheet) -{ - static double width_old = 0, height_old = 0; - double x, y; - double height, width; - - double dx, dy; - GList *list; - SheetPos p1, p2; - - // Obtains the current pointer position and modifier state. - // The position is given in coordinates relative to window. - sheet_get_pointer (sheet, &x, &y); - - if (x < sheet->priv->rubberband->start_x) { - width = sheet->priv->rubberband->start_x - x; - } - else { - double tmp = x; - x = sheet->priv->rubberband->start_x; - width = tmp - sheet->priv->rubberband->start_x; - } - - if (y < sheet->priv->rubberband->start_y) { - height = sheet->priv->rubberband->start_y - y; - } - else { - double tmp = y; - y = sheet->priv->rubberband->start_y; - height = tmp - sheet->priv->rubberband->start_y; - } - - p1.x = x; - p1.y = y; - p2.x = x + width; - p2.y = y + height; - - // Scroll the sheet if needed. - // Need FIX - /*{ - int width, height; - int dx = 0, dy = 0; - GtkAllocation allocation; - - sheet_get_pointer (sheet, &x, &y); - - gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); - width = allocation.width; - height = 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); - }*/ - - // Modify the rubberband rectangle if needed - dx = fabs (width - width_old); - dy = fabs (height - height_old); - if (dx > 1.0 || dy > 1.0) { - // Save old state - width_old = width; - height_old = height; - - for (list = sheet->priv->items; list; list = list->next) { - sheet_item_select_in_area (list->data, &p1, &p2); - } - - g_object_set (sheet->priv->rubberband->rectangle, - "x", (double) x, - "y", (double) y, - "width", width, - "height", height, - NULL); - } - g_list_free_full (list, g_object_unref); - return TRUE; -} - -void -sheet_stop_rubberband (Sheet *sheet, GdkEventButton *event) -{ - GList *list; - - g_source_remove (sheet->priv->rubberband->timeout_id); - sheet->priv->rubberband->state = RUBBER_NO; - - if (sheet->priv->preserve_selection_items != NULL) { - for (list = sheet->priv->preserve_selection_items; list; list = list->next) - sheet_item_set_preserve_selection (SHEET_ITEM (list->data), FALSE); - - g_list_free (sheet->priv->preserve_selection_items); - sheet->priv->preserve_selection_items = NULL; - } - - goo_canvas_pointer_ungrab (GOO_CANVAS (sheet), - GOO_CANVAS_ITEM (sheet->grid), event->time); - - goo_canvas_item_remove (GOO_CANVAS_ITEM (sheet->priv->rubberband->rectangle)); - g_list_free_full (list, g_object_unref); -} - -void -sheet_setup_rubberband (Sheet *sheet, GdkEventButton *event) -{ - double x, y; - cairo_pattern_t *pattern; - static guchar stipple_data[16] = - {0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255 }; - - 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. - goo_canvas_convert_from_pixels (GOO_CANVAS (sheet), &x, &y); - - sheet->priv->rubberband->start_x = x; - sheet->priv->rubberband->start_y = y; - - sheet->priv->rubberband->state = RUBBER_YES; - sheet->priv->rubberband->click_start_state = event->state; - - pattern = create_stipple ("lightgrey", stipple_data); - - sheet->priv->rubberband->rectangle = goo_canvas_rect_new ( - GOO_CANVAS_ITEM (sheet->object_group), - x, y, 0.0, 0.0, - "stroke-color", "black", - "line-width", 0.2, - "fill-pattern", pattern, - NULL); - - goo_canvas_pointer_grab (GOO_CANVAS (sheet), GOO_CANVAS_ITEM (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) { - sheet->priv->preserve_selection_items = - g_list_copy (sheet_preserve_selection (sheet)); - } - - sheet->priv->rubberband->timeout_id = g_timeout_add ( - 10, - (gpointer) sheet_rubberband_timeout_cb, - (gpointer) sheet); -} - -GList * -sheet_preserve_selection (Sheet *sheet) +/** + * save the selection + * @attention not stackable + */ +GList *sheet_preserve_selection (Sheet *sheet) { g_return_val_if_fail (sheet != NULL, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); - - GList *list; + + GList *list = NULL; for (list = sheet->priv->selected_objects; list; list = list->next) { sheet_item_set_preserve_selection (SHEET_ITEM (list->data), TRUE); } // Return the list so that we can remove the preserve_selection // flags later. - g_list_free_full (list, g_object_unref); return sheet->priv->selected_objects; } -int -sheet_event_callback (GtkWidget *widget, GdkEvent *event, Sheet *sheet) +int sheet_event_callback (GtkWidget *widget, GdkEvent *event, Sheet *sheet) { + GtkWidgetClass *wklass = GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class); 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->priv->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->priv->sheet_parent_class) - ->button_press_event) (widget, (GdkEventButton *) event)) { - return TRUE; - } + case GDK_3BUTTON_PRESS: + // We do not care about triple clicks on the sheet. + return FALSE; - if (event->button.button == 3) { - run_context_menu ( - schematic_view_get_schematicview_from_sheet (sheet), - (GdkEventButton *) event); - return TRUE; - } + 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. + return wklass->button_press_event (widget, (GdkEventButton *)event); - if (event->button.button == 1) { - if (!(event->button.state & GDK_SHIFT_MASK)) - sheet_select_all (sheet, 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; + } - sheet_setup_rubberband (sheet, (GdkEventButton *) event); - return TRUE; - } - break; - case GDK_BUTTON_RELEASE: - if (event->button.button == 4 || event->button.button == 5) - return TRUE; + if (wklass->button_press_event (widget, (GdkEventButton *)event)) { + return TRUE; + } - if (event->button.button == 1 && - sheet->priv->rubberband->state == RUBBER_YES) { - sheet_stop_rubberband (sheet, (GdkEventButton *) event); - return TRUE; - } + if (event->button.button == 3) { + run_context_menu (schematic_view_get_schematicview_from_sheet (sheet), + (GdkEventButton *)event); + return TRUE; + } - if (GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class)->button_release_event != NULL) { - return GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class) - ->button_release_event (widget, (GdkEventButton *) event); - } + if (event->button.button == 1) { + if (!(event->button.state & GDK_SHIFT_MASK)) + sheet_select_all (sheet, FALSE); - break; + rubberband_start (sheet, event); + return TRUE; + } + break; + + case GDK_BUTTON_RELEASE: + if (event->button.button == 4 || event->button.button == 5) + return TRUE; + + if (event->button.button == 1 && sheet->priv->rubberband_info->state == RUBBERBAND_ACTIVE) { + rubberband_finish (sheet, event); + return TRUE; + } + if (wklass->button_release_event != NULL) { + return wklass->button_release_event (widget, (GdkEventButton *)event); + } + break; - case GDK_SCROLL: - if (((GdkEventScroll *)event)->direction == GDK_SCROLL_UP) { + case GDK_SCROLL: { + GdkEventScroll *scr_event = (GdkEventScroll *)event; + if (scr_event->state & GDK_SHIFT_MASK) { + // Scroll horizontally + if (scr_event->direction == GDK_SCROLL_UP) + sheet_scroll_pixel (sheet, -30, 0); + else if (scr_event->direction == GDK_SCROLL_DOWN) + sheet_scroll_pixel (sheet, 30, 0); + + } else if (scr_event->state & GDK_CONTROL_MASK) { + // Scroll vertically + if (scr_event->direction == GDK_SCROLL_UP) + sheet_scroll_pixel (sheet, 0, -30); + else if (scr_event->direction == GDK_SCROLL_DOWN) + sheet_scroll_pixel (sheet, 0, 30); + + } else { + // Zoom + if (scr_event->direction == GDK_SCROLL_UP) { double zoom; sheet_get_zoom (sheet, &zoom); if (zoom < ZOOM_MAX) - sheet_change_zoom (sheet, 1.1); - } - else if (((GdkEventScroll *)event)->direction == GDK_SCROLL_DOWN) { - double zoom; + sheet_zoom_step (sheet, 1.1); + } else if (scr_event->direction == GDK_SCROLL_DOWN) { + double zoom; sheet_get_zoom (sheet, &zoom); if (zoom > ZOOM_MIN) - sheet_change_zoom (sheet, 0.9); - } - break; - case GDK_MOTION_NOTIFY: - if (GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class) - ->motion_notify_event != NULL) { - return GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class) - ->motion_notify_event (widget, (GdkEventMotion *) event); + sheet_zoom_step (sheet, 0.9); } - case GDK_ENTER_NOTIFY: - GTK_WIDGET_CLASS (sheet->priv->sheet_parent_class) - ->enter_notify_event (widget, (GdkEventCrossing *) event); - case GDK_KEY_PRESS: - switch (event->key.keyval) { - case GDK_KEY_R: - case GDK_KEY_r: - if (sheet->state == SHEET_STATE_NONE) - sheet_rotate_selection (sheet); - break; - case GDK_KEY_Home: - case GDK_KEY_End: - break; - case GDK_KEY_Left: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, -20, 0); - break; - case GDK_KEY_Up: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, 0, -20); - break; - case GDK_KEY_Right: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, 20, 0); - break; - case GDK_KEY_Down: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, 0, 20); - break; - case GDK_KEY_Page_Up: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, 0, -120); - break; - case GDK_KEY_Page_Down: - if (event->key.state & GDK_MOD1_MASK) - sheet_scroll (sheet, 0, 120); - break; - case GDK_KEY_Escape: - g_signal_emit_by_name (G_OBJECT (sheet), "cancel"); - break; - case GDK_KEY_Delete: - sheet_delete_selection (sheet); - break; - default: - return FALSE; + } + } break; + + case GDK_MOTION_NOTIFY: + if (sheet->priv->rubberband_info->state == RUBBERBAND_ACTIVE) { + rubberband_update (sheet, event); + return TRUE; + } + if (wklass->motion_notify_event != NULL) { + return wklass->motion_notify_event (widget, (GdkEventMotion *)event); + } + break; + + case GDK_ENTER_NOTIFY: + return wklass->enter_notify_event (widget, (GdkEventCrossing *)event); + + case GDK_KEY_PRESS: + switch (event->key.keyval) { + case GDK_KEY_R: + case GDK_KEY_r: + if (sheet->state == SHEET_STATE_NONE) + sheet_rotate_selection (sheet, 90); + break; + case GDK_KEY_L: + case GDK_KEY_l: + if (sheet->state == SHEET_STATE_NONE) + sheet_rotate_selection (sheet, -90); + break; + case GDK_KEY_Home: + case GDK_KEY_End: + break; + case GDK_KEY_Left: + if (event->key.state & GDK_MOD1_MASK) { + sheet_scroll_pixel (sheet, -20, 0); + } else { + sheet_move_selection (sheet, -20., 0.); + } + break; + case GDK_KEY_Up: + if (event->key.state & GDK_MOD1_MASK) { + sheet_scroll_pixel (sheet, 0, -20); + } else { + sheet_move_selection (sheet, 0., -20.); + } + break; + case GDK_KEY_Right: + if (event->key.state & GDK_MOD1_MASK) { + sheet_scroll_pixel (sheet, 20, 0); + } else { + sheet_move_selection (sheet, +20., 0.); + } + break; + case GDK_KEY_Down: + if (event->key.state & GDK_MOD1_MASK) { + sheet_scroll_pixel (sheet, 0, 20); + } else { + sheet_move_selection (sheet, 0., +20.); } + break; + case GDK_KEY_Escape: + g_signal_emit_by_name (G_OBJECT (sheet), "cancel"); + break; + case GDK_KEY_Delete: + sheet_delete_selection (sheet); + break; default: return FALSE; + } + default: + return FALSE; } return TRUE; } -void -sheet_select_all (Sheet *sheet, gboolean select) +/** + * select all items on the sheet + */ +void sheet_select_all (Sheet *sheet, gboolean select) { GList *list; @@ -850,68 +922,104 @@ sheet_select_all (Sheet *sheet, gboolean select) if (!select) sheet_release_selected_objects (sheet); - g_list_free_full (list, g_object_unref); } -void -sheet_rotate_selection (Sheet *sheet) +/** + * rotate the currently selected on the sheet + */ +void sheet_rotate_selection (Sheet *sheet, gint angle) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (IS_SHEET (sheet)); + + if (sheet->priv->selected_objects != NULL) + rotate_items (sheet, sheet->priv->selected_objects, angle); +} + +/** + * move the currently selected on the sheet + */ +void sheet_move_selection (Sheet *sheet, gdouble x, gdouble y) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); + const Coords delta = {x, y}; if (sheet->priv->selected_objects != NULL) - rotate_items (sheet, sheet->priv->selected_objects); + move_items (sheet, sheet->priv->selected_objects, &delta); } -void -sheet_rotate_ghosts (Sheet *sheet) +/** + * rotate floating items + */ +void sheet_rotate_ghosts (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); if (sheet->priv->floating_objects != NULL) - rotate_items (sheet, sheet->priv->floating_objects); + rotate_items (sheet, sheet->priv->floating_objects, 90); } -static void -rotate_items (Sheet *sheet, GList *items) +static void rotate_items (Sheet *sheet, GList *items, gint angle) { GList *list, *item_data_list; - SheetPos center, b1, b2; + Coords center, b1, b2; 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 = 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; + center = coords_average (&b1, &b2); snap_to_grid (sheet->grid, ¢er.x, ¢er.y); for (list = item_data_list; list; list = list->next) { ItemData *item_data = list->data; + if (item_data == NULL) + continue; if (sheet->state == SHEET_STATE_NONE) item_data_unregister (item_data); - item_data_rotate (item_data, 90, ¢er); + item_data_rotate (item_data, angle, ¢er); if (sheet->state == SHEET_STATE_NONE) item_data_register (item_data); } g_list_free (item_data_list); - g_list_free_full (list, g_object_unref); } -void -sheet_delete_selection (Sheet *sheet) +static void move_items (Sheet *sheet, GList *items, const Coords *trans) { - GList *list, *copy; + GList *list; + + for (list = items; list; list = list->next) { + g_assert (list->data != NULL); + ItemData *item_data = sheet_item_get_data (list->data); + g_assert (item_data != NULL); + + if (sheet->state == SHEET_STATE_NONE) + item_data_unregister (item_data); + + item_data_move (item_data, trans); + + if (sheet->state == SHEET_STATE_NONE) + item_data_register (item_data); + } +} + +/** + * remove the currently selected items from the sheet + * (especially their goocanvas representators) + */ +void sheet_delete_selection (Sheet *sheet) +{ + GList *copy, *iter; g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -920,47 +1028,53 @@ sheet_delete_selection (Sheet *sheet) return; copy = g_list_copy (sheet->priv->selected_objects); - for (list = copy; list; list = list->next) { - sheet_remove_item_in_sheet (SHEET_ITEM (list->data), sheet); - goo_canvas_item_remove (GOO_CANVAS_ITEM (list->data)); + + for (iter = copy; iter; iter = iter->next) { + sheet_remove_item_in_sheet (SHEET_ITEM (iter->data), sheet); + goo_canvas_item_remove (GOO_CANVAS_ITEM (iter->data)); + g_object_unref (iter->data); } + g_list_free (copy); + // function <sheet_remove_item_in_sheet> + // requires selected_objects, items, floating_objects + // to be not NULL! g_list_free (sheet->priv->selected_objects); sheet->priv->selected_objects = NULL; - - g_list_free (copy); - g_list_free_full (list, g_object_unref); } -void -sheet_release_selected_objects (Sheet *sheet) +/** + * removes all canvas items in the selected canvas group from their parents + * but does NOT delete them + */ +void sheet_release_selected_objects (Sheet *sheet) { GList *list, *copy; GooCanvasGroup *group; - gint item_nbr; - + gint item_nbr; + g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); group = sheet->priv->selected_group; copy = g_list_copy (sheet->priv->selected_objects); - + // Remove all the selected_objects into selected_group for (list = copy; list; list = list->next) { - item_nbr = goo_canvas_item_find_child (GOO_CANVAS_ITEM (group), - GOO_CANVAS_ITEM (list->data)); - goo_canvas_item_remove_child (GOO_CANVAS_ITEM (group), - item_nbr); + item_nbr = + goo_canvas_item_find_child (GOO_CANVAS_ITEM (group), GOO_CANVAS_ITEM (list->data)); + goo_canvas_item_remove_child (GOO_CANVAS_ITEM (group), item_nbr); } g_list_free (copy); - + g_list_free (sheet->priv->selected_objects); - g_list_free_full (list, g_object_unref); sheet->priv->selected_objects = NULL; } -GList * -sheet_get_selection (Sheet *sheet) +/** + * @returns [transfer-none] the list of selected objects + */ +GList *sheet_get_selection (Sheet *sheet) { g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); @@ -968,8 +1082,10 @@ sheet_get_selection (Sheet *sheet) return sheet->priv->selected_objects; } -void -sheet_update_parts (Sheet *sheet) +/** + * update the node lables of all parts in the current sheet + */ +void sheet_update_parts (Sheet *sheet) { GList *list; @@ -980,93 +1096,83 @@ sheet_update_parts (Sheet *sheet) if (IS_PART_ITEM (list->data)) part_item_update_node_label (PART_ITEM (list->data)); } - g_list_free_full (list, g_object_unref); } -void -sheet_flip_selection (Sheet *sheet, gboolean horizontal) +/** + * flip currently selected items + */ +void sheet_flip_selection (Sheet *sheet, IDFlip direction) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); if (sheet->priv->selected_objects != NULL) - flip_items (sheet, sheet->priv->selected_objects, horizontal); + flip_items (sheet, sheet->priv->selected_objects, direction); } -void -sheet_flip_ghosts (Sheet *sheet, gboolean horizontal) +/** + * flip currently floating items + */ +void sheet_flip_ghosts (Sheet *sheet, IDFlip direction) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); if (sheet->priv->floating_objects != NULL) - flip_items (sheet, sheet->priv->floating_objects, horizontal); + flip_items (sheet, sheet->priv->floating_objects, direction); } -static void -flip_items (Sheet *sheet, GList *items, gboolean horizontal) +static void flip_items (Sheet *sheet, GList *items, IDFlip direction) { - GList *list, *item_data_list; - SheetPos center, b1, b2; - SheetPos after; + GList *iter, *item_data_list; + Coords center, b1, b2; + Coords after; 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)); + for (iter = items; iter; iter = iter->next) { + item_data_list = g_list_prepend (item_data_list, sheet_item_get_data (iter->data)); } item_data_list_get_absolute_bbox (item_data_list, &b1, &b2); - center.x = (b2.x + b1.x) / 2; - center.y = (b2.y + b1.y) / 2; + // FIXME center is currently not used by item_data_flip (part.c implentation) + center.x = b2.x / 2 + b1.x / 2; + center.y = b2.y / 2 + b1.y / 2; - for (list = item_data_list; list; list = list->next) { - ItemData *item_data = list->data; + // FIXME - registering an item after flipping it still creates an offset as + // the position is still 0 + for (iter = item_data_list; iter; iter = iter->next) { + ItemData *item_data = iter->data; if (sheet->state == SHEET_STATE_NONE) item_data_unregister (item_data); - item_data_flip (item_data, horizontal, ¢er); + item_data_flip (item_data, direction, ¢er); // Make sure we snap to grid. item_data_get_pos (item_data, &after); snap_to_grid (sheet->grid, &after.x, &after.y); - item_data_move (item_data, &after); + item_data_set_pos (item_data, &after); if (sheet->state == SHEET_STATE_NONE) item_data_register (item_data); } g_list_free (item_data_list); - g_list_free_full (list, g_object_unref); } -void -sheet_clear_op_values (Sheet *sheet) +void sheet_clear_op_values (Sheet *sheet) { - GList *list; - g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - for (list = sheet->priv->voltmeter_items; list; list = list->next) { - g_object_unref (G_OBJECT (list->data)); - } - - g_list_free (sheet->priv->voltmeter_items); - sheet->priv->voltmeter_items = NULL; - g_hash_table_destroy (sheet->priv->voltmeter_nodes); - sheet->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash, - g_str_equal, g_free, NULL); - g_list_free_full (list, g_object_unref); + sheet->priv->voltmeter_nodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } -void -sheet_provide_object_properties (Sheet *sheet) +void sheet_provide_object_properties (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -1075,15 +1181,18 @@ sheet_provide_object_properties (Sheet *sheet) sheet_item_edit_properties (sheet->priv->selected_objects->data); } -void -sheet_clear_ghosts (Sheet *sheet) +/** + * get rid of floating items (delete them) + */ +void sheet_clear_ghosts (Sheet *sheet) { GList *copy, *list; - + g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - if (sheet->priv->floating_objects == NULL) return; + if (sheet->priv->floating_objects == NULL) + return; g_assert (sheet->state != SHEET_STATE_FLOAT); copy = g_list_copy (sheet->priv->floating_objects); @@ -1094,13 +1203,14 @@ sheet_clear_ghosts (Sheet *sheet) } g_list_free (copy); - + sheet->priv->floating_objects = NULL; - g_list_free_full (list, g_object_unref); } -guint -sheet_get_selected_objects_length (Sheet *sheet) +/** + * count selected objects O(n) + */ +guint sheet_get_selected_objects_length (Sheet *sheet) { g_return_val_if_fail ((sheet != NULL), 0); g_return_val_if_fail (IS_SHEET (sheet), 0); @@ -1108,8 +1218,10 @@ sheet_get_selected_objects_length (Sheet *sheet) return g_list_length (sheet->priv->selected_objects); } -GList * -sheet_get_floating_objects (Sheet *sheet) +/** + * @returns a shallow copy of the floating items list, free with `g_list_free` + */ +GList *sheet_get_floating_objects (Sheet *sheet) { g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); @@ -1117,8 +1229,12 @@ sheet_get_floating_objects (Sheet *sheet) return g_list_copy (sheet->priv->floating_objects); } -void -sheet_add_ghost_item (Sheet *sheet, ItemData *data) +/** + * add a recently created item (no view representation yet!) + * to the group of floating items and add a SheetItem via + * `sheet_item_factory_create_sheet_item()` + */ +void sheet_add_ghost_item (Sheet *sheet, ItemData *data) { SheetItem *item; @@ -1127,90 +1243,77 @@ sheet_add_ghost_item (Sheet *sheet, ItemData *data) item = sheet_item_factory_create_sheet_item (sheet, data); - g_object_set (G_OBJECT (item), - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + g_object_set (G_OBJECT (item), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); sheet_prepend_floating_object (sheet, item); } -void -sheet_stop_create_wire (Sheet *sheet) +void sheet_stop_create_wire (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - - if (sheet->priv->create_wire_context) { - create_wire_exit (sheet->priv->create_wire_context); - sheet->priv->create_wire_context = NULL; - } + + create_wire_cleanup (sheet); } -void -sheet_initiate_create_wire (Sheet *sheet) +void sheet_initiate_create_wire (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - sheet->priv->create_wire_context = create_wire_initiate (sheet); + create_wire_setup (sheet); } -static void -node_dot_added_callback (Schematic *schematic, SheetPos *pos, Sheet *sheet) +static void node_dot_added_callback (Schematic *schematic, Coords *pos, Sheet *sheet) { NodeItem *node_item; - - SheetPos *key; + Coords *key; g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); node_item = g_hash_table_lookup (sheet->priv->node_dots, pos); if (node_item == NULL) { - node_item = NODE_ITEM (g_object_new (TYPE_NODE_ITEM, NULL)); - g_object_set (node_item, - "parent", goo_canvas_get_root_item (GOO_CANVAS (sheet)), - "x", pos->x, - "y", pos->y, - NULL); + node_item = NODE_ITEM (g_object_new (TYPE_NODE_ITEM, NULL)); + g_object_set (node_item, "parent", goo_canvas_get_root_item (GOO_CANVAS (sheet)), "x", + pos->x, "y", pos->y, NULL); } node_item_show_dot (node_item, TRUE); - key = g_new0 (SheetPos, 1); + key = g_new0 (Coords, 1); key->x = pos->x; key->y = pos->y; g_hash_table_insert (sheet->priv->node_dots, key, node_item); } -static void -node_dot_removed_callback (Schematic *schematic, SheetPos *pos, Sheet *sheet) +static void node_dot_removed_callback (Schematic *schematic, Coords *pos, Sheet *sheet) { GooCanvasItem *node_item; - SheetPos * orig_key; + Coords *orig_key; gboolean found; g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - found = g_hash_table_lookup_extended ( - sheet->priv->node_dots, - pos, - (gpointer) &orig_key, - (gpointer) &node_item); + found = g_hash_table_lookup_extended (sheet->priv->node_dots, pos, (gpointer)&orig_key, + (gpointer)&node_item); if (found) { goo_canvas_item_remove (GOO_CANVAS_ITEM (node_item)); g_hash_table_remove (sheet->priv->node_dots, pos); - } - else + } else { g_warning ("No dot to remove!"); + } } -static guint -dot_hash (gconstpointer key) +/** + * hash function for dots (for their position actually) + * good enough to get some spread and very lightweight + */ +static guint dot_hash (gconstpointer key) { - SheetPos *sp = (SheetPos *) key; + Coords *sp = (Coords *)key; int x, y; x = (int)rint (sp->x) % 256; @@ -1221,16 +1324,15 @@ dot_hash (gconstpointer key) #define HASH_EPSILON 1e-2 -static int -dot_equal (gconstpointer a, gconstpointer b) +static int dot_equal (gconstpointer a, gconstpointer b) { - SheetPos *spa, *spb; + Coords *spa, *spb; - g_return_val_if_fail (a!=NULL, 0); - g_return_val_if_fail (b!=NULL, 0); + g_return_val_if_fail (a != NULL, 0); + g_return_val_if_fail (b != NULL, 0); - spa = (SheetPos *) a; - spb = (SheetPos *) b; + spa = (Coords *)a; + spb = (Coords *)b; if (fabs (spa->y - spb->y) > HASH_EPSILON) return 0; @@ -1241,44 +1343,151 @@ dot_equal (gconstpointer a, gconstpointer b) return 1; } -void -sheet_connect_node_dots_to_signals (Sheet *sheet) +void sheet_connect_node_dots_to_signals (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - GList *list; + GList *iter, *list; Schematic *sm; sm = schematic_view_get_schematic_from_sheet (sheet); - list = schematic_get_items (sm); - g_signal_connect_object (G_OBJECT (sm), "node_dot_added", - G_CALLBACK (node_dot_added_callback), G_OBJECT (sheet), 0); + g_signal_connect_object (G_OBJECT (sm), "node_dot_added", G_CALLBACK (node_dot_added_callback), + G_OBJECT (sheet), 0); g_signal_connect_object (G_OBJECT (sm), "node_dot_removed", - G_CALLBACK (node_dot_removed_callback), G_OBJECT (sheet), 0); + G_CALLBACK (node_dot_removed_callback), G_OBJECT (sheet), 0); list = node_store_get_node_positions (schematic_get_store (sm)); - for (; list; list = list->next) - node_dot_added_callback (sm, list->data, sheet); - - g_list_free_full (list, g_object_unref); + for (iter = list; iter; iter = iter->next) + node_dot_added_callback (sm, iter->data, sheet); } -void -sheet_remove_item_in_sheet (SheetItem *item, Sheet *sheet) +/** + * remove a single item from the sheet + */ +void sheet_remove_item_in_sheet (SheetItem *item, Sheet *sheet) { g_return_if_fail (sheet != NULL); - g_return_if_fail (IS_SHEET (sheet)); - g_return_if_fail (item != NULL); - g_return_if_fail (IS_SHEET_ITEM (item)); + g_return_if_fail (IS_SHEET (sheet)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + + sheet->priv->items = g_list_remove (sheet->priv->items, item); - sheet->priv->items = g_list_remove (sheet->priv->items, item); + // Remove the object from the selected-list before destroying. + sheet_remove_selected_object (sheet, item); + sheet_remove_floating_object (sheet, item); - // Remove the object from the selected-list before destroying. - sheet_remove_selected_object (sheet, item); - sheet_remove_floating_object (sheet, item); + ItemData *data = sheet_item_get_data (item); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + // properly unregister the itemdata from the sheet + item_data_unregister (data); // Destroy the item-data (model) associated to the sheet-item - g_object_unref (sheet_item_get_data (item)); + g_object_unref (data); +} + +inline static guint32 extract_time (GdkEvent *event) +{ + if (event) { + switch (event->type) { + /* only added relevant events */ + case GDK_MOTION_NOTIFY: + return ((GdkEventMotion *)event)->time; + case GDK_3BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_BUTTON_PRESS: + return ((GdkEventButton *)event)->time; + case GDK_KEY_PRESS: + return ((GdkEventKey *)event)->time; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + return ((GdkEventCrossing *)event)->time; + case GDK_PROPERTY_NOTIFY: + return ((GdkEventProperty *)event)->time; + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DRAG_STATUS: + case GDK_DROP_START: + case GDK_DROP_FINISHED: + return ((GdkEventDND *)event)->time; + default: + return 0; + } + } + return 0; +} + +/** + * helpful for debugging to not grab all input forever + * if oregano segfaults while running inside + * a gdb session + */ +#ifndef DEBUG_DISABLE_GRABBING +#define DEBUG_DISABLE_GRABBING 0 +#endif + +gboolean sheet_pointer_grab (Sheet *sheet, GdkEvent *event) +{ + g_return_val_if_fail (sheet, FALSE); + g_return_val_if_fail (IS_SHEET (sheet), FALSE); +#if !DEBUG_DISABLE_GRABBING + if (sheet->priv->pointer_grabbed == 0 && + goo_canvas_pointer_grab (GOO_CANVAS (sheet), GOO_CANVAS_ITEM (sheet->grid), + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, extract_time (event)) == GDK_GRAB_SUCCESS) { + sheet->priv->pointer_grabbed = 1; + } + return (sheet->priv->pointer_grabbed == 1); +#else + return TRUE; +#endif +} + +void sheet_pointer_ungrab (Sheet *sheet, GdkEvent *event) +{ + g_return_if_fail (sheet); + g_return_if_fail (IS_SHEET (sheet)); +#if !DEBUG_DISABLE_GRABBING + if (sheet->priv->pointer_grabbed) { + sheet->priv->pointer_grabbed = FALSE; + goo_canvas_pointer_ungrab (GOO_CANVAS (sheet), GOO_CANVAS_ITEM (sheet->grid), + extract_time (event)); + } +#endif +} + +gboolean sheet_keyboard_grab (Sheet *sheet, GdkEvent *event) +{ + g_return_val_if_fail (sheet, FALSE); + g_return_val_if_fail (IS_SHEET (sheet), FALSE); +#if !DEBUG_DISABLE_GRABBING + if (sheet->priv->keyboard_grabbed == FALSE && + goo_canvas_keyboard_grab (GOO_CANVAS (sheet), GOO_CANVAS_ITEM (sheet->grid), + TRUE, /*do not reroute signals through sheet->grid*/ + extract_time (event)) == GDK_GRAB_SUCCESS) { + sheet->priv->keyboard_grabbed = TRUE; + } + return (sheet->priv->keyboard_grabbed == TRUE); +#else + return TRUE; +#endif +} + +void sheet_keyboard_ungrab (Sheet *sheet, GdkEvent *event) +{ + g_return_if_fail (sheet); + g_return_if_fail (IS_SHEET (sheet)); +#if !DEBUG_DISABLE_GRABBING + if (sheet->priv->keyboard_grabbed) { + sheet->priv->keyboard_grabbed = FALSE; + goo_canvas_keyboard_ungrab (GOO_CANVAS (sheet), GOO_CANVAS_ITEM (sheet->grid), + extract_time (event)); + } +#endif } diff --git a/src/sheet/sheet.h b/src/sheet/sheet.h index 8e8ff43..331c044 100644 --- a/src/sheet/sheet.h +++ b/src/sheet/sheet.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SHEET_H @@ -37,17 +39,24 @@ #include <gtk/gtk.h> #include "grid.h" + #include "item-data.h" -typedef struct _Sheet Sheet; -typedef struct _SheetPriv SheetPriv; +// typedef to avoid circular dependency +typedef struct _SchematicView SchematicView; + +// Allow about 200 pixels for the border of the sheet +#define SHEET_BORDER 200 + +typedef struct _Sheet Sheet; +typedef struct _SheetPriv SheetPriv; typedef struct _SheetClass SheetClass; -typedef struct _SheetItem SheetItem; +typedef struct _SheetItem SheetItem; -#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)) +#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, @@ -61,68 +70,76 @@ typedef enum { SHEET_STATE_TEXTBOX_CREATING } SheetState; -struct _Sheet { - GooCanvas parent_canvas; - SheetState state; - GooCanvasGroup *object_group; - Grid *grid; - SheetPriv *priv; +struct _Sheet +{ + GooCanvas parent_canvas; + SheetState state; + GooCanvasGroup *object_group; + + Grid *grid; + SheetPriv *priv; }; -struct _SheetClass { - GooCanvasClass parent_class; +struct _SheetClass +{ + GooCanvasClass 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 (*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); }; -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); -gpointer sheet_get_first_selected_item (const Sheet *sheet); -void sheet_change_zoom (const Sheet *sheet, double rate); -void sheet_get_zoom (const Sheet *sheet, gdouble *zoom); -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); -void sheet_remove_selected_object (const Sheet *sheet, SheetItem *item); -void sheet_prepend_selected_object (Sheet *sheet, SheetItem *item); -void sheet_remove_floating_object (const Sheet *sheet, SheetItem *item); -void sheet_prepend_floating_object (Sheet *sheet, SheetItem *item); -void sheet_connect_part_item_to_floating_group (Sheet *sheet, - gpointer *sv); -void sheet_show_node_labels (Sheet *sheet, gboolean show); -void sheet_add_item (Sheet *sheet, SheetItem *item); -void sheet_stop_rubberband (Sheet *sheet, GdkEventButton *event); -void sheet_setup_rubberband (Sheet *sheet, GdkEventButton *event); -int sheet_event_callback (GtkWidget *widget, GdkEvent *event, - Sheet *sheet); -void sheet_select_all (Sheet *sheet, gboolean select); -void sheet_rotate_selection (Sheet *sheet); -void sheet_delete_selection (Sheet *sheet); -void sheet_release_selected_objects (Sheet *sheet); -GList * sheet_get_selection (Sheet *sheet); -void sheet_update_parts (Sheet *sheet); -void sheet_destroy_sheet_item (SheetItem *item, Sheet *sheet); -void sheet_rotate_ghosts (Sheet *sheet); -void sheet_flip_selection (Sheet *sheet, gboolean horizontal); -void sheet_flip_ghosts (Sheet *sheet, gboolean horizontal); -void sheet_clear_op_values (Sheet *sheet); -void sheet_provide_object_properties (Sheet *sheet); -void sheet_clear_ghosts (Sheet *sheet); -guint sheet_get_selected_objects_length (Sheet *sheet); -GList * sheet_get_floating_objects (Sheet *sheet); -void sheet_add_ghost_item (Sheet *sheet, ItemData *data); -GList * sheet_get_items (const Sheet *sheet); -void sheet_stop_create_wire (Sheet *sheet); -void sheet_initiate_create_wire (Sheet *sheet); -void sheet_connect_node_dots_to_signals (Sheet *sheet); -void sheet_remove_item_in_sheet (SheetItem *item, Sheet *sheet); -void sheet_get_pointer (Sheet *sheet, gdouble *x, gdouble *y); - +GType sheet_get_type (void); +GtkWidget *sheet_new (const gdouble width, const gdouble height); +gboolean sheet_replace (SchematicView *sv); +void sheet_scroll_pixel (const Sheet *sheet, int dx, int dy); +void sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height); +gpointer sheet_get_first_selected_item (const Sheet *sheet); +void sheet_zoom_step (Sheet *sheet, const double rate); +void sheet_get_zoom (const Sheet *sheet, gdouble *zoom); +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); +void sheet_remove_selected_object (const Sheet *sheet, SheetItem *item); +void sheet_prepend_selected_object (Sheet *sheet, SheetItem *item); +void sheet_remove_floating_object (const Sheet *sheet, SheetItem *item); +void sheet_prepend_floating_object (Sheet *sheet, SheetItem *item); +void sheet_connect_part_item_to_floating_group (Sheet *sheet, gpointer *sv); +void sheet_show_node_labels (Sheet *sheet, gboolean show); +void sheet_add_item (Sheet *sheet, SheetItem *item); +void sheet_stop_rubberband (Sheet *sheet, GdkEventButton *event); +void sheet_setup_rubberband (Sheet *sheet, GdkEventButton *event); +int sheet_event_callback (GtkWidget *widget, GdkEvent *event, Sheet *sheet); +void sheet_select_all (Sheet *sheet, gboolean select); +void sheet_rotate_selection (Sheet *sheet, gint angle); +void sheet_move_selection (Sheet *sheet, gdouble dx, gdouble dy); +void sheet_delete_selection (Sheet *sheet); +void sheet_release_selected_objects (Sheet *sheet); +GList *sheet_get_selection (Sheet *sheet); +void sheet_update_parts (Sheet *sheet); +void sheet_destroy_sheet_item (SheetItem *item, Sheet *sheet); +void sheet_rotate_ghosts (Sheet *sheet); +void sheet_flip_selection (Sheet *sheet, IDFlip direction); +void sheet_flip_ghosts (Sheet *sheet, IDFlip direction); +void sheet_clear_op_values (Sheet *sheet); +void sheet_provide_object_properties (Sheet *sheet); +void sheet_clear_ghosts (Sheet *sheet); +guint sheet_get_selected_objects_length (Sheet *sheet); +GList *sheet_get_floating_objects (Sheet *sheet); +void sheet_add_ghost_item (Sheet *sheet, ItemData *data); +GList *sheet_get_items (const Sheet *sheet); +void sheet_stop_create_wire (Sheet *sheet); +void sheet_initiate_create_wire (Sheet *sheet); +void sheet_connect_node_dots_to_signals (Sheet *sheet); +void sheet_remove_item_in_sheet (SheetItem *item, Sheet *sheet); +gboolean sheet_get_pointer_pixel (Sheet *sheet, gdouble *x, gdouble *y); +gboolean sheet_get_pointer (Sheet *sheet, gdouble *x, gdouble *y); +gboolean sheet_get_pointer_snapped (Sheet *sheet, gdouble *x, gdouble *y); +gboolean sheet_pointer_grab (Sheet *sheet, GdkEvent *event); +void sheet_pointer_ungrab (Sheet *sheet, GdkEvent *event); +gboolean sheet_keyboard_grab (Sheet *sheet, GdkEvent *event); +void sheet_keyboard_ungrab (Sheet *sheet, GdkEvent *event); +gboolean sheet_get_adjustments (const Sheet *sheet, GtkAdjustment **hadj, GtkAdjustment **vadj); #endif diff --git a/src/sheet/textbox-item.c b/src/sheet/textbox-item.c index b53fcd0..ed618ef 100644 --- a/src/sheet/textbox-item.c +++ b/src/sheet/textbox-item.c @@ -2,17 +2,19 @@ * textbox-item.c * * - * Author: + * Authors: * Richard Hult <rhult@hem.passagen.se> * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,15 +28,15 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <string.h> #include <glib/gi18n.h> #include "cursors.h" -#include "sheet-pos.h" +#include "coords.h" #include "textbox-item.h" #include "textbox.h" #include "dialogs.h" @@ -43,93 +45,81 @@ #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_finalize (GObject *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_item_paste (Sheet *sheet, 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, Sheet *sheet); -static void textbox_item_place_ghost (SheetItem *item, Sheet *sheet); -static void edit_textbox (SheetItem *sheet_item); -static void edit_cmd (GtkWidget *widget, Sheet *sheet); - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -typedef struct { +static void textbox_item_class_init (TextboxItemClass *klass); +static void textbox_item_init (TextboxItem *item); +static void textbox_item_finalize (GObject *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, IDFlip direction, SheetItem *sheet_item); +static void textbox_moved_callback (ItemData *data, Coords *pos, SheetItem *item); +static void textbox_text_changed_callback (ItemData *data, gchar *new_text, SheetItem *item); +static void textbox_item_paste (Sheet *sheet, 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, Coords *p1, Coords *p2); +inline static void get_cached_bounds (TextboxItem *item, Coords *p1, Coords *p2); +static void textbox_item_place (SheetItem *item, Sheet *sheet); +static void textbox_item_place_ghost (SheetItem *item, Sheet *sheet); +static void edit_textbox (SheetItem *sheet_item); +static void edit_cmd (GtkWidget *widget, Sheet *sheet); + +#include "debug.h" + +typedef struct +{ GtkDialog *dialog; GtkEntry *entry; } TextboxPropDialog; static TextboxPropDialog *prop_dialog = NULL; -static const char *textbox_item_context_menu = -"<ui>" -" <popup name='ItemMenu'>" -" <menuitem action='EditText'/>" -" </popup>" -"</ui>"; +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)} -}; +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 -}; +enum { TEXTBOX_ITEM_ARG_0, TEXTBOX_ITEM_ARG_NAME }; -struct _TextboxItemPriv { +struct _TextboxItemPriv +{ guint cache_valid : 1; guint highlight : 1; GooCanvasItem *text_canvas_item; // Cached bounding box. This is used to make // the rubberband selection a bit faster. - SheetPos bbox_start; - SheetPos bbox_end; + Coords bbox_start; + Coords bbox_end; }; G_DEFINE_TYPE (TextboxItem, textbox_item, TYPE_SHEET_ITEM) -static void -textbox_item_class_init (TextboxItemClass *textbox_item_class) +static void textbox_item_class_init (TextboxItemClass *textbox_item_class) { GObjectClass *object_class; SheetItemClass *sheet_item_class; object_class = G_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); + textbox_item_parent_class = g_type_class_peek_parent (textbox_item_class); object_class->finalize = textbox_item_finalize; 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->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) +static void textbox_item_init (TextboxItem *item) { TextboxItemPriv *priv; @@ -139,26 +129,24 @@ textbox_item_init (TextboxItem *item) 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)); + sheet_item_add_menu (SHEET_ITEM (item), textbox_item_context_menu, action_entries, + G_N_ELEMENTS (action_entries)); } -static void -textbox_item_finalize (GObject *object) +static void textbox_item_finalize (GObject *object) { TextboxItem *item; item = TEXTBOX_ITEM (object); - if (item->priv) - g_free (item->priv); - + g_free (item->priv); + item->priv = NULL; + G_OBJECT_CLASS (textbox_item_parent_class)->finalize (object); } // "moved" signal handler. Invalidates the bounding box cache. -static void -textbox_item_moved (SheetItem *object) +static void textbox_item_moved (SheetItem *object) { TextboxItem *item; TextboxItemPriv *priv; @@ -169,83 +157,65 @@ textbox_item_moved (SheetItem *object) priv->cache_valid = FALSE; } -TextboxItem * -textbox_item_new (Sheet *sheet, Textbox *textbox) +TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox) { GooCanvasItem *item; TextboxItem *textbox_item; TextboxItemPriv *priv; - SheetPos pos; + Coords pos; ItemData *item_data; 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 = g_object_new (TYPE_TEXTBOX_ITEM, NULL); - - g_object_set (item, - "parent", sheet->object_group, - NULL); - + + g_object_set (item, "parent", sheet->object_group, NULL); + textbox_item = TEXTBOX_ITEM (item); - g_object_set (textbox_item, - "data", textbox, - NULL); + g_object_set (textbox_item, "data", textbox, NULL); priv = textbox_item->priv; - priv->text_canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (textbox_item), - textbox_get_text (textbox), 0.0, 0.0, -1, GOO_CANVAS_ANCHOR_SW, - "font", TEXTBOX_FONT, - "fill-color", NORMAL_COLOR, - NULL); + priv->text_canvas_item = goo_canvas_text_new ( + GOO_CANVAS_ITEM (textbox_item), textbox_get_text (textbox), 0.0, 0.0, -1, + GOO_CANVAS_ANCHOR_SW, "font", TEXTBOX_FONT, "fill-color", NORMAL_COLOR, NULL); item_data = ITEM_DATA (textbox); - item_data->rotated_handler_id = g_signal_connect_object (G_OBJECT (textbox), - "rotated", - G_CALLBACK (textbox_rotated_callback), - G_OBJECT (textbox_item), - 0); - item_data->flipped_handler_id = g_signal_connect_object (G_OBJECT (textbox), - "flipped", - G_CALLBACK (textbox_flipped_callback), - G_OBJECT (textbox_item), - 0); - item_data->moved_handler_id = g_signal_connect_object (G_OBJECT (textbox), - "moved", - G_CALLBACK (textbox_moved_callback), - G_OBJECT (textbox_item), - 0); - textbox->text_changed_handler_id = g_signal_connect_object (G_OBJECT (textbox), - "text_changed", - G_CALLBACK (textbox_text_changed_callback), - G_OBJECT (textbox_item), - 0); + item_data->rotated_handler_id = + g_signal_connect_object (G_OBJECT (textbox), "rotated", + G_CALLBACK (textbox_rotated_callback), G_OBJECT (textbox_item), 0); + item_data->flipped_handler_id = + g_signal_connect_object (G_OBJECT (textbox), "flipped", + G_CALLBACK (textbox_flipped_callback), G_OBJECT (textbox_item), 0); + item_data->moved_handler_id = + g_signal_connect_object (G_OBJECT (textbox), "moved", G_CALLBACK (textbox_moved_callback), + G_OBJECT (textbox_item), 0); + textbox->text_changed_handler_id = g_signal_connect_object ( + G_OBJECT (textbox), "text_changed", G_CALLBACK (textbox_text_changed_callback), + G_OBJECT (textbox_item), 0); textbox_update_bbox (textbox); return textbox_item; } -void -textbox_item_signal_connect_placed (TextboxItem *textbox_item, - Sheet *sheet) +void textbox_item_signal_connect_placed (TextboxItem *textbox_item, Sheet *sheet) { - g_signal_connect (G_OBJECT (textbox_item), "button_press_event", - G_CALLBACK (sheet_item_event), sheet); + g_signal_connect (G_OBJECT (textbox_item), "button_press_event", G_CALLBACK (sheet_item_event), + sheet); g_signal_connect (G_OBJECT (textbox_item), "button_release_event", - G_CALLBACK (sheet_item_event), sheet); - - g_signal_connect (G_OBJECT (textbox_item), "key_press_event", - G_CALLBACK (sheet_item_event), sheet); + G_CALLBACK (sheet_item_event), sheet); + + g_signal_connect (G_OBJECT (textbox_item), "key_press_event", G_CALLBACK (sheet_item_event), + sheet); } -static void -textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +static void textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) { TextboxItem *item; @@ -257,13 +227,11 @@ textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) item->priv->cache_valid = FALSE; } -static void -textbox_flipped_callback (ItemData *data, - gboolean horizontal, SheetItem *sheet_item) +static void textbox_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item) { TextboxItem *item; - g_return_if_fail (sheet_item != NULL); + g_return_if_fail (sheet_item); g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item)); item = TEXTBOX_ITEM (sheet_item); @@ -271,59 +239,48 @@ textbox_flipped_callback (ItemData *data, item->priv->cache_valid = FALSE; } -static int -select_idle_callback (TextboxItem *item) +static int select_idle_callback (TextboxItem *item) { - SheetPos bbox_start, bbox_end; + Coords bbox_start, bbox_end; TextboxItemPriv *priv = item->priv; get_cached_bounds (item, &bbox_start, &bbox_end); - g_object_set (priv->text_canvas_item, - "fill_color", SELECTED_COLOR, - NULL); + g_object_set (priv->text_canvas_item, "fill_color", SELECTED_COLOR, NULL); priv->highlight = TRUE; return FALSE; } -static int -deselect_idle_callback (TextboxItem *item) +static int deselect_idle_callback (TextboxItem *item) { TextboxItemPriv *priv = item->priv; - g_object_set (priv->text_canvas_item, - "fill_color", NORMAL_COLOR, - NULL); + g_object_set (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) +static void selection_changed (TextboxItem *item, gboolean select, gpointer user_data) { if (select) - g_idle_add ((gpointer) select_idle_callback, item); + g_idle_add ((gpointer)select_idle_callback, item); else - g_idle_add ((gpointer) deselect_idle_callback, item); + g_idle_add ((gpointer)deselect_idle_callback, item); } -static gboolean -is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2) { TextboxItem *item; - SheetPos bbox_start, bbox_end; + Coords 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) + 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; @@ -331,27 +288,25 @@ is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) // 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) +inline static void get_cached_bounds (TextboxItem *item, Coords *p1, Coords *p2) { PangoFontDescription *font; - SheetPos pos; + Coords pos; TextboxItemPriv *priv; priv = item->priv; if (!priv->cache_valid) { - SheetPos start_pos, end_pos; + Coords start_pos, end_pos; font = pango_font_description_from_string (TEXTBOX_FONT); - item_data_get_pos (sheet_item_get_data (SHEET_ITEM (item)), - &pos); + 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; + 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; @@ -359,12 +314,11 @@ get_cached_bounds (TextboxItem *item, SheetPos *p1, SheetPos *p2) pango_font_description_free (font); } - memcpy (p1, &priv->bbox_start, sizeof (SheetPos)); - memcpy (p2, &priv->bbox_end, sizeof (SheetPos)); + memcpy (p1, &priv->bbox_start, sizeof(Coords)); + memcpy (p2, &priv->bbox_end, sizeof(Coords)); } -static void -textbox_item_paste (Sheet *sheet, ItemData *data) +static void textbox_item_paste (Sheet *sheet, ItemData *data) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -375,8 +329,7 @@ textbox_item_paste (Sheet *sheet, ItemData *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) +static void textbox_moved_callback (ItemData *data, Coords *pos, SheetItem *item) { TextboxItem *textbox_item; @@ -395,9 +348,7 @@ textbox_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item) textbox_item->priv->cache_valid = FALSE; } -static void -textbox_text_changed_callback (ItemData *data, - gchar *new_text, SheetItem *item) +static void textbox_text_changed_callback (ItemData *data, gchar *new_text, SheetItem *item) { TextboxItem *textbox_item; @@ -408,108 +359,96 @@ textbox_text_changed_callback (ItemData *data, textbox_item = TEXTBOX_ITEM (item); - g_object_set (textbox_item->priv->text_canvas_item, - "text", new_text, - NULL ); + g_object_set (textbox_item->priv->text_canvas_item, "text", new_text, NULL); goo_canvas_item_ensure_updated (GOO_CANVAS_ITEM (textbox_item)); } -static void -textbox_item_place (SheetItem *item, Sheet *sheet) +static void textbox_item_place (SheetItem *item, Sheet *sheet) { textbox_item_signal_connect_placed (TEXTBOX_ITEM (item), sheet); - g_signal_connect (G_OBJECT (item), "double_clicked", - G_CALLBACK (edit_textbox), item); + g_signal_connect (G_OBJECT (item), "double_clicked", G_CALLBACK (edit_textbox), item); } -static void -textbox_item_place_ghost (SheetItem *item, Sheet *sheet) +static void textbox_item_place_ghost (SheetItem *item, Sheet *sheet) { -// textbox_item_signal_connect_placed (TEXTBOX_ITEM (item)); + // textbox_item_signal_connect_placed (TEXTBOX_ITEM (item)); } -static gboolean -create_textbox_event (Sheet *sheet, GdkEvent *event) +static gboolean create_textbox_event (Sheet *sheet, GdkEvent *event) { switch (event->type) { - case GDK_3BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - return TRUE; + 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; + 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; + if (event->button.button == 1) { + if (sheet->state == SHEET_STATE_TEXTBOX_WAIT) + sheet->state = SHEET_STATE_TEXTBOX_START; - return TRUE; - } - else - return FALSE; + return TRUE; + } else + return FALSE; - case GDK_BUTTON_RELEASE: - if (event->button.button == 4 || event->button.button == 5) - 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; + if (sheet->state == SHEET_STATE_TEXTBOX_START) { + Textbox *textbox; + Coords pos; - sheet->state = SHEET_STATE_NONE; + sheet->state = SHEET_STATE_NONE; - sheet_get_pointer (sheet, &pos.x, &pos.y); - textbox = textbox_new (NULL); - - textbox_set_text (textbox, _("Label")); + sheet_get_pointer (sheet, &pos.x, &pos.y); + textbox = textbox_new (NULL); - schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), - ITEM_DATA (textbox)); - item_data_set_pos (ITEM_DATA (textbox), &pos); - - schematic_view_reset_tool ( - schematic_view_get_schematicview_from_sheet (sheet)); - g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), - G_CALLBACK (create_textbox_event), sheet); - } + textbox_set_text (textbox, _ ("Label")); - return TRUE; + item_data_set_pos (ITEM_DATA (textbox), &pos); + schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), + ITEM_DATA (textbox)); - default: - return FALSE; + schematic_view_reset_tool (schematic_view_get_schematicview_from_sheet (sheet)); + g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), + G_CALLBACK (create_textbox_event), sheet); } + return TRUE; + + default: + return FALSE; + } + return TRUE; } - void -textbox_item_cancel_listen (Sheet *sheet) +void textbox_item_cancel_listen (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); sheet->state = SHEET_STATE_NONE; - g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), - G_CALLBACK (create_textbox_event), sheet); + g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), G_CALLBACK (create_textbox_event), + sheet); } -void -textbox_item_listen (Sheet *sheet) +void textbox_item_listen (Sheet *sheet) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); // 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), sheet); + g_signal_connect (G_OBJECT (sheet), "event", G_CALLBACK (create_textbox_event), sheet); } // Go through the properties and commit the changes. -void -edit_dialog_ok (TextboxItem *item) +void edit_dialog_ok (TextboxItem *item) { const gchar *value; Textbox *textbox; @@ -522,52 +461,37 @@ edit_dialog_ok (TextboxItem *item) textbox_set_text (textbox, value); } -static void -edit_textbox (SheetItem *sheet_item) +static void edit_textbox (SheetItem *sheet_item) { TextboxItem *item; Textbox *textbox; - char *msg; const char *value; - GtkBuilder *gui; - GError *perror = NULL; + GtkBuilder *builder; + GError *e = NULL; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item)); - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create textbox properties dialog")); + if ((builder = gtk_builder_new ()) == NULL) { + oregano_error (_ ("Could not create textbox properties dialog")); return; - } - else gtk_builder_set_translation_domain (gui, NULL); + } + gtk_builder_set_translation_domain (builder, NULL); item = TEXTBOX_ITEM (sheet_item); textbox = TEXTBOX (sheet_item_get_data (sheet_item)); - if (!g_file_test (OREGANO_UIDIR "/textbox-properties-dialog.ui", - 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_UIDIR "/textbox-properties-dialog.ui"); - oregano_error (_("Could not create textbox properties dialog")); - g_free (msg); - return; - } - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/textbox-properties-dialog.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create textbox properties dialog"), msg); - g_error_free (perror); + if (!gtk_builder_add_from_file (builder, OREGANO_UIDIR "/textbox-properties-dialog.ui", &e)) { + oregano_error_with_title (_ ("Could not create textbox properties dialog."), e->message); + g_clear_error (&e); return; } prop_dialog = g_new0 (TextboxPropDialog, 1); - prop_dialog->dialog = GTK_DIALOG ( - gtk_builder_get_object (gui, "textbox-properties-dialog")); + prop_dialog->dialog = + GTK_DIALOG (gtk_builder_get_object (builder, "textbox-properties-dialog")); - prop_dialog->entry = GTK_ENTRY (gtk_builder_get_object (gui, "entry")); + prop_dialog->entry = GTK_ENTRY (gtk_builder_get_object (builder, "entry")); value = textbox_get_text (textbox); gtk_entry_set_text (GTK_ENTRY (prop_dialog->entry), value); @@ -575,18 +499,17 @@ edit_textbox (SheetItem *sheet_item) gtk_dialog_run (GTK_DIALOG (prop_dialog->dialog)); edit_dialog_ok (item); - // Clean / destroy the dialog gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); - prop_dialog = NULL; + g_free (prop_dialog); + g_object_unref (builder); } -static void -edit_cmd (GtkWidget *widget, Sheet *sheet) +static void edit_cmd (GtkWidget *widget, Sheet *sheet) { GList *list; list = sheet_get_selection (sheet); - if ((list != NULL) && IS_TEXTBOX_ITEM (list->data)) + if ((list != NULL) && IS_TEXTBOX_ITEM (list->data)) edit_textbox (list->data); g_list_free_full (list, g_object_unref); } diff --git a/src/sheet/textbox-item.h b/src/sheet/textbox-item.h index c4f05a7..43a5981 100644 --- a/src/sheet/textbox-item.h +++ b/src/sheet/textbox-item.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __TEXTBOX_ITEM_H @@ -39,11 +39,11 @@ #include "sheet-item.h" #include "textbox.h" - -#define TYPE_TEXTBOX_ITEM (textbox_item_get_type ()) -#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 ()) +#define TYPE_TEXTBOX_ITEM (textbox_item_get_type ()) +#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; @@ -54,20 +54,21 @@ typedef enum { TEXTBOX_DIR_DIAG = 3 } TextboxDir; -typedef struct { - SheetItem parent_object; - TextboxItemPriv * priv; +typedef struct +{ + SheetItem parent_object; + TextboxItemPriv *priv; } TextboxItem; -typedef struct { - SheetItemClass parent_class; +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, - Sheet *sheet); -void textbox_item_cancel_listen (Sheet *sheet); -void textbox_item_listen (Sheet *sheet); +GType textbox_item_get_type (void); +TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox); +void textbox_item_signal_connect_placed (TextboxItem *textbox_item, Sheet *sheet); +void textbox_item_cancel_listen (Sheet *sheet); +void textbox_item_listen (Sheet *sheet); #endif diff --git a/src/sheet/wire-item.c b/src/sheet/wire-item.c index 8d5e4e8..208710c 100644 --- a/src/sheet/wire-item.c +++ b/src/sheet/wire-item.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,21 +26,22 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> #include <string.h> #include "cursors.h" -#include "sheet-pos.h" +#include "coords.h" #include "wire-item.h" #include "node-store.h" #include "wire.h" #include "wire-private.h" #include "schematic.h" #include "schematic-view.h" +#include "options.h" #define NORMAL_COLOR "blue" #define SELECTED_COLOR "green" @@ -48,63 +49,55 @@ #define RESIZER_SIZE 4.0f -static void wire_item_class_init (WireItemClass *klass); -static void wire_item_init (WireItem *item); -static void wire_item_finalize (GObject *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 (Sheet *sheet, 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_boundingbox (WireItem *item, SheetPos *p1, SheetPos *p2); -static void mouse_over_wire_callback (WireItem *item, Sheet *sheet); -static void highlight_wire_callback (Wire *wire, WireItem *item); -static int unhighlight_wire (WireItem *item); -static void wire_item_place (SheetItem *item, Sheet *sheet); -static void wire_item_place_ghost (SheetItem *item, Sheet *sheet); -static void wire_item_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *spec); -static void wire_item_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *spec); - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -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. +static void wire_item_class_init (WireItemClass *klass); +static void wire_item_init (WireItem *item); +static void wire_item_dispose (GObject *object); +static void wire_item_finalize (GObject *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, IDFlip horizontal, SheetItem *sheet_item); +static void wire_moved_callback (ItemData *data, Coords *pos, SheetItem *item); +static void wire_changed_callback (Wire *, WireItem *item); +static void wire_item_paste (Sheet *sheet, 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, Coords *p1, Coords *p2); +inline static void get_boundingbox (WireItem *item, Coords *p1, Coords *p2); +static void mouse_over_wire_callback (WireItem *item, Sheet *sheet); +static void highlight_wire_callback (Wire *wire, WireItem *item); +static int unhighlight_wire (WireItem *item); +static void wire_item_place (SheetItem *item, Sheet *sheet); +static void wire_item_place_ghost (SheetItem *item, Sheet *sheet); +static void wire_item_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec); +static void wire_item_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec); + +#include "debug.h" + +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. - GooCanvasPolyline * line; - GooCanvasRect * resize1; // Resize box of the wire - GooCanvasRect * resize2; // Resize box of the wire + GooCanvasPolyline *line; + GooCanvasRect *resize1; // Resize box of the wire + GooCanvasRect *resize2; // Resize box of the wire // Cached bounding box. This is used to make // the rubberband selection a bit faster. - SheetPos bbox_start; - SheetPos bbox_end; + Coords bbox_start; + Coords bbox_end; }; G_DEFINE_TYPE (WireItem, wire_item, TYPE_SHEET_ITEM) -static void -wire_item_class_init (WireItemClass *wire_item_class) +static void wire_item_class_init (WireItemClass *wire_item_class) { GObjectClass *object_class; SheetItemClass *sheet_item_class; @@ -112,48 +105,46 @@ wire_item_class_init (WireItemClass *wire_item_class) object_class = G_OBJECT_CLASS (wire_item_class); sheet_item_class = SHEET_ITEM_CLASS (wire_item_class); wire_item_parent_class = g_type_class_peek_parent (wire_item_class); - + object_class->finalize = wire_item_finalize; + object_class->dispose = wire_item_dispose; object_class->set_property = wire_item_set_property; - object_class->get_property = wire_item_get_property; + object_class->get_property = wire_item_get_property; 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->selection_changed = selection_changed; sheet_item_class->place = wire_item_place; sheet_item_class->place_ghost = wire_item_place_ghost; } -static void -wire_item_set_property (GObject *object, guint property_id, const GValue *value, - GParamSpec *pspec) +static void wire_item_set_property (GObject *object, guint property_id, const GValue *value, + GParamSpec *pspec) { - g_return_if_fail (object != NULL); - g_return_if_fail (IS_WIRE_ITEM(object)); + g_return_if_fail (object != NULL); + g_return_if_fail (IS_WIRE_ITEM (object)); - switch (property_id) { - default: - g_warning ("PartItem: Invalid argument.\n"); - } + switch (property_id) { + default: + g_warning ("PartItem: Invalid argument.\n"); + } } -static void -wire_item_get_property (GObject *object, guint property_id, GValue *value, - GParamSpec *pspec) +static void wire_item_get_property (GObject *object, guint property_id, GValue *value, + GParamSpec *pspec) { - g_return_if_fail (object != NULL); - g_return_if_fail (IS_WIRE_ITEM (object)); - - switch (property_id) { - default: - pspec->value_type = G_TYPE_INVALID; - break; - } + g_return_if_fail (object != NULL); + g_return_if_fail (IS_WIRE_ITEM (object)); + + switch (property_id) { + default: + pspec->value_type = G_TYPE_INVALID; + break; + } } -static void -wire_item_init (WireItem *item) +static void wire_item_init (WireItem *item) { WireItemPriv *priv; @@ -162,45 +153,48 @@ wire_item_init (WireItem *item) priv->direction = WIRE_DIR_NONE; priv->highlight = FALSE; priv->cache_valid = FALSE; + priv->line = NULL; + priv->resize1 = NULL; + priv->resize2 = NULL; item->priv = priv; } -static void -wire_item_finalize (GObject *object) +static void wire_item_dispose (GObject *object) { WireItemPriv *priv; priv = WIRE_ITEM (object)->priv; - if (priv != NULL) { - g_free (priv); - } - - G_OBJECT_CLASS (wire_item_parent_class)->finalize (object); + G_OBJECT_CLASS (wire_item_parent_class)->dispose (object); } -// "moved" signal handler. Invalidates the bounding box cache. -static void -wire_item_moved (SheetItem *object) +static void wire_item_finalize (GObject *object) { - WireItem *item; WireItemPriv *priv; - item = WIRE_ITEM (object); - priv = item->priv; + priv = WIRE_ITEM (object)->priv; - priv->cache_valid = FALSE; + g_free (priv); + priv = NULL; + + G_OBJECT_CLASS (wire_item_parent_class)->finalize (object); +} + +// TODO get rid of this +static void wire_item_moved (SheetItem *object) +{ + // NG_DEBUG ("wire MOVED callback called - LEGACY"); } -WireItem * -wire_item_new (Sheet *sheet, Wire *wire) +WireItem *wire_item_new (Sheet *sheet, Wire *wire) { GooCanvasItem *item; WireItem *wire_item; + ItemData *item_data; GooCanvasPoints *points; WireItemPriv *priv; - SheetPos start_pos, length; + Coords start_pos, length; g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); @@ -208,276 +202,235 @@ wire_item_new (Sheet *sheet, Wire *wire) wire_get_pos_and_length (wire, &start_pos, &length); item = g_object_new (TYPE_WIRE_ITEM, NULL); - - g_object_set (item, - "parent", sheet->object_group, - NULL); - + + g_object_set (item, "parent", sheet->object_group, NULL); + wire_item = WIRE_ITEM (item); - g_object_set (wire_item, - "data", wire, - NULL); + g_object_set (wire_item, "data", wire, NULL); priv = wire_item->priv; - - priv->resize1 = GOO_CANVAS_RECT (goo_canvas_rect_new ( - GOO_CANVAS_ITEM (wire_item), - -RESIZER_SIZE, - -RESIZER_SIZE, - 2 * RESIZER_SIZE, - 2 * RESIZER_SIZE, - "stroke-color", "blue", - "fill-color", "green", - "line-width", 1.0, - NULL)); - g_object_set (priv->resize1, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + + const int random_color_count = 9; + const char *random_color[] = {"blue", "red", "green" /*, "yellow"*/, "orange", "brown", + "purple", "pink", "lightblue", "lightgreen"}; + + priv->resize1 = GOO_CANVAS_RECT ( + goo_canvas_rect_new (GOO_CANVAS_ITEM (wire_item), -RESIZER_SIZE, -RESIZER_SIZE, + 2 * RESIZER_SIZE, 2 * RESIZER_SIZE, "stroke-color", + oregano_options_debug_wires () + ? random_color[g_random_int_range (0, random_color_count - 1)] + : "blue", + "fill-color", "green", "line-width", 1.0, NULL)); + g_object_set (priv->resize1, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); priv->resize2 = GOO_CANVAS_RECT (goo_canvas_rect_new ( - GOO_CANVAS_ITEM (wire_item), - length.x - RESIZER_SIZE, - length.y - RESIZER_SIZE, - 2 * RESIZER_SIZE, - 2 * RESIZER_SIZE, - "stroke-color", "blue", - "fill-color", "green", - "line-width", 1.0, - NULL)); - g_object_set (priv->resize2, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); - - points = goo_canvas_points_new (2); + GOO_CANVAS_ITEM (wire_item), length.x - RESIZER_SIZE, length.y - RESIZER_SIZE, + 2 * RESIZER_SIZE, 2 * RESIZER_SIZE, "stroke-color", + oregano_options_debug_wires () + ? random_color[g_random_int_range (0, random_color_count - 1)] + : "blue", + "fill-color", "green", "line-width", 1.0, NULL)); + g_object_set (priv->resize2, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + + points = goo_canvas_points_new (2); points->coords[0] = 0; points->coords[1] = 0; points->coords[2] = length.x; points->coords[3] = length.y; priv->line = GOO_CANVAS_POLYLINE (goo_canvas_polyline_new ( - GOO_CANVAS_ITEM (wire_item), - FALSE, 0, - "points", points, - "stroke-color", "blue", - "line-width", 1.0, - NULL)); - + GOO_CANVAS_ITEM (wire_item), FALSE, 0, "points", points, "stroke-color", + oregano_options_debug_wires () + ? random_color[g_random_int_range (0, random_color_count - 1)] + : "blue", + "line-width", 1.0, "start-arrow", oregano_options_debug_wires () ? TRUE : FALSE, + "end-arrow", oregano_options_debug_wires () ? TRUE : FALSE, NULL)); + goo_canvas_points_unref (points); - ITEM_DATA (wire)->rotated_handler_id = - g_signal_connect_data (wire, "rotated", - G_CALLBACK (wire_rotated_callback), G_OBJECT (wire_item), NULL, 0); - - g_signal_connect_data (wire, "flipped", - G_CALLBACK (wire_flipped_callback), G_OBJECT (wire_item), NULL, 0); - - ITEM_DATA (wire)->moved_handler_id = - g_signal_connect_object (wire, "moved", - G_CALLBACK (wire_moved_callback), G_OBJECT (wire_item), 0); - - g_signal_connect (wire, "changed", - G_CALLBACK (wire_changed_callback), wire_item); - - g_signal_connect (wire, "delete", - G_CALLBACK (wire_delete_callback), wire_item); - + item_data = ITEM_DATA (wire); + item_data->rotated_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "rotated", G_CALLBACK (wire_rotated_callback), G_OBJECT (wire_item), 0); + item_data->flipped_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "flipped", G_CALLBACK (wire_flipped_callback), G_OBJECT (wire_item), 0); + item_data->moved_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "moved", G_CALLBACK (wire_moved_callback), G_OBJECT (wire_item), 0); + item_data->changed_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "changed", G_CALLBACK (wire_changed_callback), G_OBJECT (wire_item), 0); + wire_update_bbox (wire); return wire_item; } -gboolean -wire_item_event (WireItem *wire_item, - GooCanvasItem *sheet_target_item, - GdkEvent *event, Sheet *sheet) +gboolean wire_item_event (WireItem *wire_item, GooCanvasItem *sheet_target_item, GdkEvent *event, + Sheet *sheet) { - SheetPos start_pos, length; + Coords start_pos, length; Wire *wire; GooCanvas *canvas; static double last_x, last_y; double dx, dy, zoom; // The selected group's bounding box in window resp. canvas coordinates. double snapped_x, snapped_y; - SheetPos pos; + Coords pos; - canvas = GOO_CANVAS (sheet); - g_object_get (G_OBJECT (wire_item), - "data", &wire, - NULL); + 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: { - double x, y; - g_signal_stop_emission_by_name (wire_item, - "button_press_event"); - sheet_get_pointer (sheet, &x, &y); - x = x - start_pos.x; - y = 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; - - sheet_get_pointer (sheet, &x, &y); - last_x = x; - last_y = 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; - - sheet_get_pointer (sheet, &x, &y); - last_x = x; - last_y = y; - item_data_unregister (ITEM_DATA (wire)); - return TRUE; - } - } - break; + case GDK_BUTTON_PRESS: + switch (event->button.button) { + case 1: { + double x, y; + g_signal_stop_emission_by_name (wire_item, "button_press_event"); + sheet_get_pointer_snapped (sheet, &x, &y); + x = x - start_pos.x; + y = 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; + + sheet_get_pointer_snapped (sheet, &x, &y); + last_x = x; + last_y = 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; + + sheet_get_pointer_snapped (sheet, &x, &y); + last_x = x; + last_y = 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; - 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 (wire_item->priv->resize_state == WIRE_RESIZER_NONE) - break; + if (sheet->state == SHEET_STATE_DRAG_START || sheet->state == SHEET_STATE_DRAG) { - if (sheet->state == SHEET_STATE_DRAG_START || - sheet->state == SHEET_STATE_DRAG) { - - g_signal_stop_emission_by_name (wire_item, - "motion-notify-event"); - - sheet->state = SHEET_STATE_DRAG; - - sheet_get_pointer (sheet, &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); + g_signal_stop_emission_by_name (wire_item, "motion-notify-event"); - 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) { + sheet->state = SHEET_STATE_DRAG; + + sheet_get_pointer_snapped (sheet, &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; } - if (wire_item->priv->resize_state == WIRE_RESIZER_NONE) { + } 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; } - - g_signal_stop_emission_by_name (wire_item, - "button-release-event"); - - goo_canvas_pointer_ungrab (canvas, GOO_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 (GOO_CANVAS_ITEM (wire_item), - GOO_CANVAS_ITEM (wire_item), event, sheet); + 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 (wire_item, "button-release-event"); + + goo_canvas_pointer_ungrab (canvas, GOO_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 (GOO_CANVAS_ITEM (wire_item), GOO_CANVAS_ITEM (wire_item), event, + sheet); } - return sheet_item_event (GOO_CANVAS_ITEM (wire_item), - GOO_CANVAS_ITEM (wire_item), event, sheet); + return sheet_item_event (GOO_CANVAS_ITEM (wire_item), GOO_CANVAS_ITEM (wire_item), event, + sheet); } -void -wire_item_signal_connect_placed (WireItem *wire_item, Sheet *sheet) +void wire_item_signal_connect_placed (WireItem *wire_item, Sheet *sheet) { ItemData *item; item = sheet_item_get_data (SHEET_ITEM (wire_item)); - g_signal_connect (wire_item, "button-press-event", - G_CALLBACK (wire_item_event), sheet); + g_signal_connect (wire_item, "button-press-event", G_CALLBACK (wire_item_event), sheet); - g_signal_connect (wire_item, "button-release-event", - G_CALLBACK (wire_item_event), sheet); - - g_signal_connect (wire_item, "motion-notify-event", - G_CALLBACK (wire_item_event), sheet); - - g_signal_connect (wire_item, "mouse_over", - G_CALLBACK (mouse_over_wire_callback), sheet); + g_signal_connect (wire_item, "button-release-event", G_CALLBACK (wire_item_event), sheet); - g_signal_connect (item, "highlight", - G_CALLBACK (highlight_wire_callback), wire_item); + g_signal_connect (wire_item, "motion-notify-event", G_CALLBACK (wire_item_event), sheet); + + g_signal_connect (wire_item, "mouse_over", G_CALLBACK (mouse_over_wire_callback), sheet); + + g_signal_connect (item, "highlight", G_CALLBACK (highlight_wire_callback), wire_item); } -static void -wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +static void wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) { WireItem *wire_item; GooCanvasPoints *points; - SheetPos start_pos, length; + Coords start_pos, length; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_WIRE_ITEM (sheet_item)); @@ -492,33 +445,24 @@ wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) points->coords[2] = length.x; points->coords[3] = length.y; - g_object_set (wire_item->priv->line, - "points", points, - NULL); + g_object_set (wire_item->priv->line, "points", points, NULL); goo_canvas_points_unref (points); - g_object_set (wire_item, - "x", start_pos.x, - "y", start_pos.y, - NULL); + g_object_set (wire_item, "x", start_pos.x, "y", start_pos.y, NULL); - g_object_set (wire_item-> priv->resize2, - "x", length.x-RESIZER_SIZE, - "y", length.y-RESIZER_SIZE, - NULL); + g_object_set (wire_item->priv->resize2, "x", length.x - RESIZER_SIZE, "y", + length.y - RESIZER_SIZE, NULL); - //Invalidate the bounding box cache. + // Invalidate the bounding box cache. wire_item->priv->cache_valid = FALSE; } -static void -wire_flipped_callback (ItemData *data, - gboolean horizontal, SheetItem *sheet_item) +static void wire_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item) { GooCanvasPoints *points; WireItem *item; WireItemPriv *priv; - SheetPos start_pos, length; + Coords start_pos, length; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_WIRE_ITEM (sheet_item)); @@ -534,34 +478,22 @@ wire_flipped_callback (ItemData *data, points->coords[2] = length.x; points->coords[3] = length.y; - g_object_set (item->priv->line, - "points", points, - NULL); + g_object_set (item->priv->line, "points", points, NULL); goo_canvas_points_unref (points); - g_object_set (item, - "x", start_pos.x, - "y", start_pos.y, - NULL); + g_object_set (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) +static int select_idle_callback (WireItem *item) { WireItemPriv *priv = item->priv; - g_object_set (priv->line, - "stroke-color", SELECTED_COLOR, - NULL); - g_object_set (item->priv->resize1, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); - g_object_set (item->priv->resize2, - "visibility", GOO_CANVAS_ITEM_VISIBLE, - NULL); + g_object_set (priv->line, "stroke-color", SELECTED_COLOR, NULL); + g_object_set (item->priv->resize1, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + g_object_set (item->priv->resize2, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); priv->highlight = TRUE; @@ -569,57 +501,43 @@ select_idle_callback (WireItem *item) return FALSE; } -static int -deselect_idle_callback (WireItem *item) +static int deselect_idle_callback (WireItem *item) { WireItemPriv *priv = item->priv; - g_object_set (priv->line, - "stroke_color", NORMAL_COLOR, - NULL); - g_object_set (item->priv->resize1, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); - g_object_set (item->priv->resize2, - "visibility", GOO_CANVAS_ITEM_INVISIBLE, - NULL); + g_object_set (priv->line, "stroke_color", NORMAL_COLOR, NULL); + g_object_set (item->priv->resize1, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + g_object_set (item->priv->resize2, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); priv->highlight = FALSE; - g_object_unref(G_OBJECT (item)); + g_object_unref (G_OBJECT (item)); return FALSE; } -static void -selection_changed ( WireItem *item, gboolean select, gpointer user) +static void selection_changed (WireItem *item, gboolean select, gpointer user) { g_object_ref (G_OBJECT (item)); if (select) { - g_idle_add ((gpointer) select_idle_callback, item); - } - else { - g_idle_add ((gpointer) deselect_idle_callback, item); + g_idle_add ((gpointer)select_idle_callback, item); + } else { + g_idle_add ((gpointer)deselect_idle_callback, item); } } // 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) +void wire_item_get_start_pos (WireItem *item, Coords *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); + 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) +void wire_item_get_length (WireItem *item, Coords *pos) { WireItemPriv *priv; GooCanvasPoints *points; @@ -630,9 +548,7 @@ wire_item_get_length (WireItem *item, SheetPos *pos) priv = item->priv; - g_object_get (G_OBJECT (priv->line), - "points", &points, - NULL); + 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 @@ -642,54 +558,50 @@ wire_item_get_length (WireItem *item, SheetPos *pos) goo_canvas_points_unref (points); } -static gboolean -is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2) { WireItem *item; - SheetPos bbox_start, bbox_end; + Coords bbox_start, bbox_end; item = WIRE_ITEM (object); get_boundingbox (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; + 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_boundingbox (WireItem *item, SheetPos *p1, SheetPos *p2) +inline static void get_boundingbox (WireItem *item, Coords *p1, Coords *p2) { + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + WireItemPriv *priv; priv = item->priv; if (!priv->cache_valid) { - SheetPos start_pos, end_pos; GooCanvasBounds bounds; //, canvas_bounds; goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds); - start_pos.x = bounds.x1; - start_pos.y = bounds.y1; - end_pos.x = bounds.x2; - end_pos.y = bounds.y2; + priv->bbox_start.x = bounds.x1; + priv->bbox_start.y = bounds.y1; + priv->bbox_end.x = bounds.x2; + priv->bbox_end.y = bounds.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)); + if (p1) + *p1 = priv->bbox_start; + if (p2) + *p2 = priv->bbox_end; } -static void -wire_item_paste (Sheet *sheet, ItemData *data) +static void wire_item_paste (Sheet *sheet, ItemData *data) { g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -701,10 +613,9 @@ wire_item_paste (Sheet *sheet, ItemData *data) static void wire_traverse (Wire *wire); -static void -node_traverse (Node *node) +static void node_traverse (Node *node) { - GSList *wires; + GSList *iter; g_return_if_fail (node != NULL); g_return_if_fail (IS_NODE (node)); @@ -714,17 +625,15 @@ node_traverse (Node *node) node_set_visited (node, TRUE); - for (wires = node->wires; wires; wires = wires->next) { - Wire *wire = wires->data; + for (iter = node->wires; iter; iter = iter->next) { + Wire *wire = iter->data; wire_traverse (wire); } - g_slist_free_full (wires, g_object_unref); } -static void -wire_traverse (Wire *wire) +static void wire_traverse (Wire *wire) { - GSList *nodes; + GSList *iter; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); @@ -736,155 +645,115 @@ wire_traverse (Wire *wire) g_signal_emit_by_name (wire, "highlight"); - for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { - Node *node = nodes->data; - + for (iter = wire_get_nodes (wire); iter; iter = iter->next) { + Node *node = iter->data; node_traverse (node); } - g_slist_free_full (nodes, g_object_unref); } -static void -node_foreach_reset (gpointer key, gpointer value, gpointer user_data) +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_callback (WireItem *item, Sheet *sheet) +static void mouse_over_wire_callback (WireItem *item, Sheet *sheet) { - GList *wires; + GList *iter; Wire *wire; NodeStore *store; if (sheet->state != SHEET_STATE_NONE) return; - store = schematic_get_store ( - schematic_view_get_schematic_from_sheet (sheet)); + store = schematic_get_store (schematic_view_get_schematic_from_sheet (sheet)); - node_store_node_foreach (store, (GHFunc *) node_foreach_reset, NULL); - for (wires = store->wires; wires; wires = wires->next) { - wire = wires->data; + node_store_node_foreach (store, (GHFunc *)node_foreach_reset, NULL); + for (iter = store->wires; iter; iter = iter->next) { + wire = iter->data; wire_set_visited (wire, FALSE); } wire = WIRE (sheet_item_get_data (SHEET_ITEM (item))); wire_traverse (wire); - g_list_free_full (wires, g_object_unref); } -static void -highlight_wire_callback (Wire *wire, WireItem *item) +static void highlight_wire_callback (Wire *wire, WireItem *item) { WireItemPriv *priv = item->priv; - g_object_set (priv->line, - "stroke-color", HIGHLIGHT_COLOR, - NULL); + g_object_set (priv->line, "stroke-color", HIGHLIGHT_COLOR, NULL); // Guard against removal during the highlighting. g_object_ref (G_OBJECT (item)); - g_timeout_add (1000, (gpointer) unhighlight_wire, item); + g_timeout_add (1000, (gpointer)unhighlight_wire, item); // FIXME XXX wtf??? } -static int -unhighlight_wire (WireItem *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; + color = sheet_item_get_selected (SHEET_ITEM (item)) ? SELECTED_COLOR : NORMAL_COLOR; - g_object_set (priv->line, - "stroke-color", color, - NULL); + g_object_set (priv->line, "stroke-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) +// FIXME get rid of +static void wire_moved_callback (ItemData *data, Coords *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. - goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (item), - pos->x, - pos->y, - 1.0, - 0.0); - wire_item->priv->cache_valid = FALSE; + // NG_DEBUG ("wire MOVED callback called - LEGACY"); } -static void -wire_item_place (SheetItem *item, Sheet *sheet) +static void wire_item_place (SheetItem *item, Sheet *sheet) { wire_item_signal_connect_placed (WIRE_ITEM (item), sheet); } -static void -wire_item_place_ghost (SheetItem *item, Sheet *sheet) +// FIXME get rid of +static void wire_item_place_ghost (SheetItem *item, Sheet *sheet) { -// wire_item_signal_connect_placed (WIRE_ITEM (item)); + // wire_item_signal_connect_placed (WIRE_ITEM (item)); } - -static void -wire_changed_callback (Wire *wire, WireItem *item) +static void wire_changed_callback (Wire *wire, WireItem *item) { - SheetPos start_pos, length; + Coords start_pos, length; GooCanvasPoints *points; - + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_ITEM_DATA (wire)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + wire_get_pos_and_length (wire, &start_pos, &length); + // Move the canvas item and invalidate the bbox cache. + goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (item), start_pos.x, start_pos.y, 1.0, + 0.0); + item->priv->cache_valid = FALSE; + points = goo_canvas_points_new (2); points->coords[0] = 0; points->coords[1] = 0; points->coords[2] = length.x; points->coords[3] = length.y; - g_object_set (item->priv->line, - "points", points, - NULL); + // this does handle cleanup of previous points internally + g_object_set (item->priv->line, "points", points, NULL); goo_canvas_points_unref (points); - g_object_set (item->priv->resize1, - "x", -RESIZER_SIZE, - "y", -RESIZER_SIZE, - "width", 2 * RESIZER_SIZE, - "height", 2 * RESIZER_SIZE, - NULL); + g_object_set (item->priv->resize1, "x", -RESIZER_SIZE, "y", -RESIZER_SIZE, "width", + 2 * RESIZER_SIZE, "height", 2 * RESIZER_SIZE, NULL); - g_object_set (item->priv->resize2, - "x", length.x-RESIZER_SIZE, - "y", length.y-RESIZER_SIZE, - "width", 2 * RESIZER_SIZE, - "height", 2 * RESIZER_SIZE, - NULL); + g_object_set (item->priv->resize2, "x", length.x - RESIZER_SIZE, "y", length.y - RESIZER_SIZE, + "width", 2 * RESIZER_SIZE, "height", 2 * RESIZER_SIZE, NULL); goo_canvas_item_request_update (GOO_CANVAS_ITEM (item->priv->line)); } - -static void -wire_delete_callback (Wire *wire, WireItem *item) -{ - g_object_unref (G_OBJECT (item)); -} diff --git a/src/sheet/wire-item.h b/src/sheet/wire-item.h index ed6d5cf..b7a47be 100644 --- a/src/sheet/wire-item.h +++ b/src/sheet/wire-item.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __WIRE_ITEM_H @@ -39,26 +39,29 @@ #include "sheet-item.h" #include "wire.h" -#define TYPE_WIRE_ITEM (wire_item_get_type ()) -#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 ())) +#define TYPE_WIRE_ITEM (wire_item_get_type ()) +#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; +typedef struct +{ + SheetItem parent_object; + WireItemPriv *priv; } WireItem; -typedef struct { +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); +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, Coords *pos); +void wire_item_get_length (WireItem *item, Coords *pos); #endif diff --git a/src/sim-settings-gui.c b/src/sim-settings-gui.c new file mode 100644 index 0000000..75eb6cc --- /dev/null +++ b/src/sim-settings-gui.c @@ -0,0 +1,1217 @@ +/* + * sim-settings-gui.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "engines/netlist-helper.h" +#include "schematic-view.h" +#include "sim-settings-gui.h" +#include <glib/gi18n.h> +#include <stdlib.h> + +static char *scale_types_list[] = {"DEC", "LIN", "OCT", NULL}; + +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}}; + +SimSettingsGui *sim_settings_gui_new() { + SimSettingsGui *sim_settings_gui = g_new0(SimSettingsGui, 1); + + sim_settings_gui->sim_settings = sim_settings_new(); + + return sim_settings_gui; +} + +void sim_settings_gui_finalize(SimSettingsGui *gui) { + sim_settings_finalize(gui->sim_settings); + g_free(gui); +} + +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 (SimSettingsGui *s_gui) +{ + int i; + gchar *name, *value; + SimOption *so; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeView *cl = s_gui->w_opt_list; + SimSettings *s = s_gui->sim_settings; + + // Empty the list + while (s->options) { + so = s->options->data; + if (so) { + g_free (so->name); + g_free (so->value); + s->options = g_list_remove (s->options, so); + g_free (so); + } + if (s->options) + s->options = s->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->options = g_list_append (s->options, so); + i++; + } +} + +static gboolean select_opt_callback (GtkWidget *widget, GdkEventButton *event, SimSettingsGui *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->w_opt_list); + model = gtk_tree_view_get_model (sim->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->w_opt_value, value); + else + gtk_entry_set_text (sim->w_opt_value, ""); + + return FALSE; +} + +static void option_setvalue (GtkWidget *w, SimSettingsGui *s) +{ + const gchar *value; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection; + + value = gtk_entry_get_text (s->w_opt_value); + if (!value) + return; + + // Get the current selected row + selection = gtk_tree_view_get_selection (s->w_opt_list); + model = gtk_tree_view_get_model (s->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); +} + +static void add_option (GtkWidget *w, SimSettingsGui *s) +{ + GtkEntry *entry; + GtkWidget *dialog = gtk_dialog_new_with_buttons ( + _ ("Add new option"), NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + "_Cancel", GTK_RESPONSE_REJECT, "_OK", GTK_RESPONSE_OK, NULL); + + entry = GTK_ENTRY (gtk_entry_new ()); + gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + 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->sim_settings, opt); + + gtk_list_store_append (GTK_LIST_STORE (gtk_tree_view_get_model (s->w_opt_list)), + &iter); + gtk_list_store_set (GTK_LIST_STORE (gtk_tree_view_get_model (s->w_opt_list)), &iter, + 0, opt->name, -1); + } + + gtk_widget_destroy (dialog); +} + +static void option_remove (GtkWidget *w, SimSettingsGui *s) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection; + + // Get the current selected row + selection = gtk_tree_view_get_selection (s->w_opt_list); + model = gtk_tree_view_get_model (s->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 entry_changed_cb (GtkWidget *widget, SimSettingsGui *s) +{ + // FIXME? +} + +static int delete_event_cb (GtkWidget *widget, GdkEvent *event, SimSettingsGui *s) +{ + s->pbox = NULL; + return FALSE; +} + +static void trans_enable_cb (GtkWidget *widget, SimSettingsGui *s) +{ + gboolean enable; + enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + + gtk_widget_set_sensitive (s->w_trans_frame, enable); +} + +static void trans_step_enable_cb (GtkWidget *widget, SimSettingsGui *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->w_trans_enable)); + + gtk_widget_set_sensitive (s->w_trans_step, step_enable & enable); +} + +static void ac_enable_cb (GtkWidget *widget, SimSettingsGui *s) +{ + gboolean enable; + enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_set_sensitive (s->w_ac_frame, enable); +} + +static void dc_enable_cb (GtkWidget *widget, SimSettingsGui *s) +{ + gboolean enable; + enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_set_sensitive (s->w_dcsweep_frame, enable); +} + +static void fourier_enable_cb (GtkWidget *widget, SimSettingsGui *s) +{ + gboolean enable; + enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_set_sensitive (s->w_fourier_frame, enable); +} + +static void fourier_add_vout_cb (GtkButton *w, SimSettingsGui *sim) +{ + GtkComboBoxText *node_box; + guint i; + gboolean result = FALSE; + gchar *text; + + node_box = GTK_COMBO_BOX_TEXT (sim->w_four_combobox); + + // Get the node identifier + for (i = 0; (i < 1000 && result == FALSE); ++i) { + text = g_strdup_printf ("V(%d)", i); + if (!g_strcmp0 (text, gtk_combo_box_text_get_active_text (node_box))) + result = TRUE; + g_free (text); + } + + text = NULL; + if (result == TRUE) + text = fourier_add_vout(sim->sim_settings, i); + + if (text) + gtk_entry_set_text (GTK_ENTRY (sim->w_four_vout), text); + else + gtk_entry_set_text (GTK_ENTRY (sim->w_four_vout), ""); +} + +static void fourier_remove_vout_cb (GtkButton *w, SimSettingsGui *sim) +{ + GtkComboBoxText *node_box; + gint result = FALSE; + gint i; + SimSettings *s = sim->sim_settings; + + node_box = GTK_COMBO_BOX_TEXT (sim->w_four_combobox); + + // Get the node identifier + for (i = 0; (i < 1000 && result == FALSE); i++) { + if (g_strcmp0 (g_strdup_printf ("V(%d)", i), + gtk_combo_box_text_get_active_text (node_box)) == 0) + result = TRUE; + } + + if (result) { + GSList *node_slist; + gchar *text = NULL; + + // Remove current data in the g_slist + { + GSList *tmp, *prev = NULL; + + tmp = s->fourier_vout; + while (tmp) { + if (atoi (tmp->data) == i - 1) { + if (prev) + prev->next = tmp->next; + else + s->fourier_vout = tmp->next; + + g_slist_free_1 (tmp); + break; + } + prev = tmp; + tmp = prev->next; + } + } + + // Update the fourier_vout widget + node_slist = g_slist_copy (s->fourier_vout); + if (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) + text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); + node_slist = node_slist->next; + } + while (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) { + if (text) + text = g_strdup_printf ("%s V(%d)", text, atoi (node_slist->data)); + else + text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); + } + node_slist = node_slist->next; + } + if (text) + gtk_entry_set_text (GTK_ENTRY (sim->w_four_vout), text); + else + gtk_entry_set_text (GTK_ENTRY (sim->w_four_vout), ""); + + g_slist_free_full (node_slist, g_free); + } +} + +static void noise_enable_cb (GtkWidget *widget, SimSettingsGui *s) +{ + gboolean enable; + enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + gtk_widget_set_sensitive (s->w_noise_frame, enable); +} + +static void response_callback (GtkButton *button, SchematicView *sv) +{ + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + g_return_if_fail (button != NULL); + g_return_if_fail (GTK_IS_BUTTON (button)); + gint page; + gchar *tmp = NULL; + gchar **node_ids = NULL; + + Schematic *sm = schematic_view_get_schematic (sv); + + SimSettingsGui *s_gui = schematic_get_sim_settings_gui(sm); + SimSettings *s = s_gui->sim_settings; + + g_object_get (s_gui->notebook, "page", &page, NULL); + + // Transient analysis + s->trans_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_trans_enable)); + + g_free (s->trans_start); + s->trans_start = gtk_editable_get_chars (GTK_EDITABLE (s_gui->w_trans_start), 0, -1); + + g_free (s->trans_stop); + s->trans_stop = gtk_editable_get_chars (GTK_EDITABLE (s_gui->w_trans_stop), 0, -1); + + g_free (s->trans_step); + s->trans_step = gtk_editable_get_chars (GTK_EDITABLE (s_gui->w_trans_step), 0, -1); + + s->trans_step_enable = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_trans_step_enable)); + + s->trans_init_cond = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_trans_init_cond)); + + s->trans_analyze_all = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_trans_analyze_all)); + + // DC + s->dc_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_dc_enable)); + + g_free (s->dc_vin); + s->dc_vin = NULL; + tmp = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_dc_vin)); + if (tmp) { + node_ids = g_strsplit (tmp, "V(", 0); + tmp = g_strdup (node_ids[1]); + g_strfreev (node_ids); + if (tmp) { + node_ids = g_strsplit (tmp, ")", 0); + g_free (tmp); + if (node_ids[0]) + s->dc_vin = g_strdup (node_ids[0]); + else + s->dc_vin = g_strdup(""); + g_strfreev (node_ids); + } + } + if (s->dc_vin == NULL) + s->dc_vin = g_strdup (""); + + g_free (s->dc_vout); + s->dc_vout = NULL; + tmp = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_dc_vout)); + if (tmp) { + node_ids = g_strsplit (tmp, "V(", 0); + tmp = g_strdup (node_ids[1]); + g_strfreev (node_ids); + if (tmp) { + node_ids = g_strsplit (tmp, ")", 0); + g_free (tmp); + if (node_ids[0]) + s->dc_vout = g_strdup (node_ids[0]); + else + s->dc_vout = g_strdup(""); + g_strfreev (node_ids); + } + } + if (s->dc_vout == NULL) + s->dc_vout = g_strdup(""); + + g_free (s->dc_start); + s->dc_start = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_dc_start))); + + g_free (s->dc_stop); + s->dc_stop = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_dc_stop))); + + g_free (s->dc_step); + s->dc_step = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_dc_step))); + + // AC + s->ac_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_ac_enable)); + + g_free (s->ac_vout); + s->ac_vout = NULL; + tmp = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_ac_vout)); + if (tmp) { + node_ids = g_strsplit (tmp, "V(", 0); + tmp = g_strdup (node_ids[1]); + g_strfreev (node_ids); + if (tmp) { + node_ids = g_strsplit (tmp, ")", 0); + g_free (tmp); + if (node_ids[0]) + s->ac_vout = g_strdup (node_ids[0]); + else + s->ac_vout = g_strdup(""); + g_strfreev (node_ids); + } + } + if (s->ac_vout == NULL) + s->ac_vout = g_strdup(""); + + g_free (s->ac_type); + s->ac_type = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_ac_type)); + + g_free (s->ac_npoints); + s->ac_npoints = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_ac_npoints))); + + g_free (s->ac_start); + s->ac_start = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_ac_start))); + + g_free (s->ac_stop); + s->ac_stop = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_ac_stop))); + + // Fourier analysis + s->fourier_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_four_enable)); + + g_free (s->fourier_frequency); + s->fourier_frequency = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_four_freq))); + + // Noise + s->noise_enable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (s_gui->w_noise_enable)); + + g_free (s->noise_vin); + s->noise_vin = NULL; + tmp = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_noise_vin)); + if (tmp) { + node_ids = g_strsplit (tmp, "V(", 0); + tmp = g_strdup (node_ids[1]); + g_strfreev (node_ids); + if (tmp) { + node_ids = g_strsplit (tmp, ")", 0); + g_free (tmp); + if (node_ids[0]) + s->noise_vin = g_strdup (node_ids[0]); + else + s->noise_vin = g_strdup(""); + g_strfreev (node_ids); + } + } + if (s->noise_vin == NULL) + s->noise_vin = g_strdup(""); + + g_free (s->noise_vout); + s->noise_vout = NULL; + tmp = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_noise_vout)); + if (tmp) { + node_ids = g_strsplit (tmp, "V(", 0); + tmp = g_strdup (node_ids[1]); + g_strfreev (node_ids); + if (tmp) { + node_ids = g_strsplit (tmp, ")", 0); + g_free (tmp); + if (node_ids[0]) + s->noise_vout = g_strdup (node_ids[0]); + else + s->noise_vout = g_strdup(""); + g_strfreev (node_ids); + } + } + if (s->noise_vout == NULL) + s->noise_vout = g_strdup(""); + + g_free (s->noise_type); + s->noise_type = g_strdup (gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (s_gui->w_noise_type))); + + g_free (s->noise_npoints); + s->noise_npoints = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_noise_npoints))); + + g_free (s->noise_start); + s->noise_start = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_noise_start))); + + g_free (s->noise_stop); + s->noise_stop = g_strdup (gtk_entry_get_text (GTK_ENTRY (s_gui->w_noise_stop))); + + // Options + get_options_from_list (s_gui); + gtk_widget_hide (GTK_WIDGET (s_gui->pbox)); + s_gui->pbox = NULL; + s_gui->notebook = NULL; + + // Schematic is dirty now ;-) + schematic_set_dirty (sm, TRUE); + + s->configured = TRUE; + + // The simulation settings configuration has + // been triggered by an attempt to lauch the + // simulation for the first time without + // configuring it first. + if (s->simulation_requested) { + s->simulation_requested = FALSE; + schematic_view_simulate_cmd (NULL, sv); + } +} + +/** + * Get the list of voltmeters (test clamps). + * + * In normal mode, this does not include the + * the type of measurement (normal, magnitude, + * phase, real, imaginary or dB) and it is used + * in DC and Fourier analysis. + * + * In AC mode, each element includes the type + * of measurement (normal, magnitude, phase, + * real, imaginary or dB) and it is used in + * AC analysis. +*/ +gint get_voltmeters_list(GList **voltmeters, Schematic *sm, GError *e, gboolean with_type) +{ + GSList *siter, *node_list = NULL; + + if (with_type) + node_list = netlist_helper_get_voltmeters_list (sm, &e, TRUE); + else + node_list = netlist_helper_get_voltmeters_list (sm, &e, FALSE); + if (e) { + log_append_error (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("Failed to create netlist"), e); + g_clear_error (&e); + return -1; + } + if (node_list == NULL) { + log_append (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("No node in the schematic!")); + return -2; + } + + *voltmeters = NULL; + for (siter = node_list; siter; siter = siter->next) { + gchar *tmp; + if (with_type) + tmp = g_strdup (siter->data); + else + tmp = g_strdup_printf ("V(%d)", atoi (siter->data)); + *voltmeters = g_list_prepend (*voltmeters, tmp); + } + + return 0; +} + +/** + * Get the list of sources (indipendent voltage) + */ +gint get_voltage_sources_list(GList **sources, Schematic *sm, GError *e, gboolean ac_only) +{ + GSList *siter, *node_list = NULL; + + node_list = netlist_helper_get_voltage_sources_list (sm, &e, ac_only); + if (e) { + log_append_error (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("Failed to create netlist"), e); + g_clear_error (&e); + return -1; + } + if (node_list == NULL) { + log_append (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("No node in the schematic!")); + return -2; + } + + *sources = NULL; + for (siter = node_list; siter; siter = siter->next) { + gchar *tmp; + tmp = g_strdup_printf ("V%d", atoi (siter->data)); + *sources = g_list_prepend (*sources, tmp); + } + + return 0; +} + +/** + * FIXME this code is an ugly piece of shit, fix it! + */ +void sim_settings_show (GtkWidget *widget, SchematicView *sv) +{ + int i; + gboolean found; + gint rc, active, index; + GtkWidget *toplevel, *w, *w1, *pbox, *combo_box; + GtkTreeView *opt_list; + GtkCellRenderer *cell_option, *cell_value; + GtkTreeViewColumn *column_option, *column_value; + GtkListStore *opt_model; + GtkTreeIter treeiter; + GtkBuilder *builder; + GError *e = NULL; + SimSettings *s; + GList *iter; + GList *voltmeters = NULL; + GList *voltmeters_with_type = NULL; + GList *sources = NULL; + GList *sources_ac = NULL; + GtkComboBox *node_box; + GtkListStore *node_list_store; + gchar *text, *text2; + gchar **node_ids; + GSList *slist = NULL; + + g_return_if_fail (sv != NULL); + + Schematic *sm = schematic_view_get_schematic (sv); + SimSettingsGui *s_gui = schematic_get_sim_settings_gui (sm); + s = s_gui->sim_settings; + + // Only allow one instance of the property box per schematic. + if (s_gui->pbox != NULL) { + if (gtk_widget_get_visible (s_gui->pbox) == FALSE) { + gtk_widget_set_visible (s_gui->pbox, TRUE); + } + GdkWindow *window = gtk_widget_get_window (s_gui->pbox); + g_assert (window != NULL); + gdk_window_raise (window); + return; + } + + if ((builder = gtk_builder_new ()) == NULL) { + log_append (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("Could not create simulation settings dialog")); + return; + } + gtk_builder_set_translation_domain (builder, NULL); + + gtk_builder_add_from_file (builder, OREGANO_UIDIR "/sim-settings.ui", &e); + if (e) { + log_append_error (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("Could not create simulation settings dialog due to " + "missing " OREGANO_UIDIR "/sim-settings.ui file"), + e); + g_clear_error (&e); + return; + } + + toplevel = GTK_WIDGET (gtk_builder_get_object (builder, "toplevel")); + if (toplevel == NULL) { + log_append (schematic_get_log_store (sm), _ ("SimulationSettings"), + _ ("Could not create simulation settings dialog due to missing " + "\"toplevel\" widget in " OREGANO_UIDIR "/sim-settings.ui file")); + return; + } + + pbox = toplevel; + s_gui->pbox = pbox; + s_gui->notebook = GTK_NOTEBOOK (gtk_builder_get_object (builder, "notebook")); + g_signal_connect (G_OBJECT (pbox), "delete_event", G_CALLBACK (delete_event_cb), s_gui); + + // Prepare options list + s_gui->w_opt_value = GTK_ENTRY (gtk_builder_get_object (builder, "opt_value")); + opt_list = GTK_TREE_VIEW (gtk_builder_get_object (builder, "option_list")); + s_gui->w_opt_list = opt_list; + + // Grab the frames + s_gui->w_trans_frame = GTK_WIDGET (gtk_builder_get_object (builder, "trans_frame")); + s_gui->w_ac_frame = GTK_WIDGET (gtk_builder_get_object (builder, "ac_frame")); + s_gui->w_dcsweep_frame = GTK_WIDGET (gtk_builder_get_object (builder, "dcsweep_frame")); + s_gui->w_fourier_frame = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_frame")); + s_gui->w_noise_frame = GTK_WIDGET (gtk_builder_get_object (builder, "noise_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->options == NULL) { + // Load defaults + for (i = 0; default_options[i].name; i++) { + gtk_list_store_append (opt_model, &treeiter); + gtk_list_store_set (opt_model, &treeiter, 0, default_options[i].name, -1); + } + } else { + // Load schematic options + + for (iter = s->options; iter; iter = iter->next) { + SimOption *so = iter->data; + if (so) { + gtk_list_store_append (opt_model, &treeiter); + gtk_list_store_set (opt_model, &treeiter, 0, so->name, -1); + } + } + } + + // Set the options already stored + for (iter = s->options; iter; iter = iter->next) { + SimOption *so = iter->data; + if (so) + set_options_in_list (so->name, so->value, opt_list); + } + + g_signal_connect (G_OBJECT (opt_list), "button_release_event", G_CALLBACK (select_opt_callback), + s); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "opt_accept")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (option_setvalue), s_gui); + w = GTK_WIDGET (gtk_builder_get_object (builder, "opt_remove")); + w = GTK_WIDGET (gtk_builder_get_object (builder, "add_option")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (option_remove), s_gui); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (add_option), s_gui); + + // Creation of Close Button + w = GTK_WIDGET (gtk_builder_get_object (builder, "button1")); + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (response_callback), sv); + + // Get the list of voltmeters (normal mode) + rc = get_voltmeters_list(&voltmeters, sm, e, FALSE); + if (rc) { + sim_settings_set_dc(s, FALSE); + sim_settings_set_fourier(s, FALSE); + } + + // Get the list of voltmeters (AC mode, i.e. with measurement type) + rc = get_voltmeters_list(&voltmeters_with_type, sm, e, TRUE); + if (rc) { + sim_settings_set_ac(s, FALSE); + } + + // Get the list of sources (all types) + rc = get_voltage_sources_list(&sources, sm, e, FALSE); + if (rc) { + sim_settings_set_dc(s, FALSE); + } + + // Get the list of AC sources + rc = get_voltage_sources_list(&sources_ac, sm, e, TRUE); + if (rc) { + sim_settings_set_noise(s, FALSE); + } + + // Transient // + // ********* // + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_start")); + s_gui->w_trans_start = w; + if (s->trans_start) + gtk_entry_set_text (GTK_ENTRY (w), s->trans_start); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_stop")); + s_gui->w_trans_stop = w; + if (s->trans_stop) + gtk_entry_set_text (GTK_ENTRY (w), s->trans_stop); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_step")); + s_gui->w_trans_step = w; + if (s->trans_step) + gtk_entry_set_text (GTK_ENTRY (w), s->trans_step); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_enable")); + s_gui->w_trans_enable = w; + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (trans_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->trans_enable); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_step_enable")); + s_gui->w_trans_step_enable = w; + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (trans_step_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->trans_step_enable); + + // get the gui element + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_init_cond")); + // save the gui element to struct + s_gui->w_trans_init_cond = w; + // Set checkbox enabled, if trans_init_cond equal true. + // trans_init_cond could be set to true because + // - user opened the settings dialog some seconds ago and has set the checkbox + // - user opened old file in which there was set the checkbox state to true + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->trans_init_cond); + + // get the gui element + w = GTK_WIDGET (gtk_builder_get_object (builder, "trans_analyze_all")); + // save the gui element to struct + s_gui->w_trans_analyze_all = w; + // Set checkbox enabled, if trans_analyze_all equal true. + // trans_init_cond could be set to true because + // - user opened the settings dialog some seconds ago and has set the checkbox + // - user opened old file in which there was set the checkbox state to true + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->trans_analyze_all); + + // AC // + // *** // + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_enable")); + s_gui->w_ac_enable = w; + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (ac_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->ac_enable); + + w1 = GTK_WIDGET (gtk_builder_get_object (builder, "grid14")); + + // FIXME: Should enable more than just one output as in the Fourier analysis + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_vout")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 0, 1, 1); + s_gui->w_ac_vout = combo_box; + if (voltmeters_with_type) { + index = 0; + active = 0; + for (iter = voltmeters_with_type; iter; iter = iter->next) { + if (g_strcmp0 (iter->data, "VM(0)")) + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), iter->data); + if (!g_strcmp0(s->ac_vout, iter->data)) + active = index; + index++; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s); + + // Initialisation of the various scale types + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_type")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 1, 1, 1); + s_gui->w_ac_type = combo_box; + + { + index = 0; + active = 0; + for (; scale_types_list[index] != NULL; index++) { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), scale_types_list[index]); + if (!g_strcmp0(s->ac_type, scale_types_list[index])) + active = index; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_assert (GTK_IS_COMBO_BOX (combo_box)); + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_npoints")); + s_gui->w_ac_npoints = w; + gtk_entry_set_text (GTK_ENTRY (w), s->ac_npoints); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_start")); + s_gui->w_ac_start = w; + gtk_entry_set_text (GTK_ENTRY (w), s->ac_start); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "ac_stop")); + s_gui->w_ac_stop = w; + gtk_entry_set_text (GTK_ENTRY (w), s->ac_stop); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + // DC // + // ***** // + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_enable")); + s_gui->w_dc_enable = w; + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (dc_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->dc_enable); + + w1 = GTK_WIDGET (gtk_builder_get_object (builder, "grid13")); + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_vin")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 0, 1, 1); + s_gui->w_dc_vin = combo_box; + if (sources) { + index = 0; + active = 0; + for (iter = sources; iter; iter = iter->next) { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), iter->data); + if (!g_strcmp0(s->dc_vin, iter->data)) + active = index; + index++; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s); + + // FIXME: Should enable more than just one output as in the Fourier analysis + // FIXME: Should also allow to print currents through voltage sources + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_vout")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 1, 1, 1); + s_gui->w_dc_vout = combo_box; + if (voltmeters) { + index = 0; + active = 0; + text = g_strdup_printf("V(%s)", s->dc_vout); + for (iter = voltmeters; iter; iter = iter->next) { + if (g_strcmp0 (iter->data, "V(0)")) + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), iter->data); + if (!g_strcmp0(text, iter->data)) + active = index; + index++; + } + g_free (text); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_start")); + s_gui->w_dc_start = w; + gtk_entry_set_text (GTK_ENTRY (w), s->dc_start); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_stop")); + s_gui->w_dc_stop = w; + gtk_entry_set_text (GTK_ENTRY (w), s->dc_stop); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "dc_step")); + s_gui->w_dc_step = w; + gtk_entry_set_text (GTK_ENTRY (w), s->dc_step); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + // Fourier // + // ******* // + g_print ("XXXXXXXXXXXXXXXX\n"); + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_enable")); + s_gui->w_four_enable = w; + g_print ("XXXXXXXXXXXXXXXX %p %s\n", w, s->fourier_enable ? "TRUE" : "FALSE"); + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (fourier_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->fourier_enable); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_freq")); + s_gui->w_four_freq = w; + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + gtk_entry_set_text (GTK_ENTRY (w), s->fourier_frequency); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_vout")); + s_gui->w_four_vout = w; + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + // Get rid of inexistent output vectors + text2 = NULL; + if (voltmeters) { + text = sim_settings_get_fourier_vout (s); + node_ids = g_strsplit (text, " ", 0); + g_free (text); + for (i = 0; node_ids[i] != NULL; i++) { + text = g_strdup_printf ("V(%s)", node_ids[i]); + found = FALSE; + for (iter = voltmeters; iter; iter = iter->next) { + if (!g_strcmp0 (text, iter->data)) + found = TRUE; + } + g_free (text); + if (found) { + if (text2) { + text = text2; + text2 = g_strdup_printf ("%s %s", text2, node_ids[i]); + g_free (text); + } else { + text2 = g_strdup_printf ("%s", node_ids[i]); + } + } + } + if (!text2) + text2 = g_strdup (""); + sim_settings_set_fourier_vout (s, text2); + g_free (text2); + g_strfreev (node_ids); + } + + text = NULL; + slist = g_slist_copy (s->fourier_vout); + if (slist) { + if (slist->data && atoi (slist->data) > 0) + text = g_strdup_printf ("V(%d)", atoi (slist->data)); + slist = slist->next; + } + while (slist) { + if (slist->data && atoi (slist->data) > 0) { + if (text) { + text2 = text; + text = g_strdup_printf ("%s V(%d)", text, atoi (slist->data)); + g_free (text2); + } else { + text = g_strdup_printf ("V(%d)", atoi (slist->data)); + } + } + slist = slist->next; + } + + if (text) + gtk_entry_set_text (GTK_ENTRY (w), text); + else + gtk_entry_set_text (GTK_ENTRY (w), ""); + + g_slist_free_full (slist, g_free); + + // Present in the combo box the nodes of the schematic + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_select_out")); + gtk_widget_destroy (w); // FIXME wtf??? + + w = GTK_WIDGET (gtk_builder_get_object (builder, "grid12")); + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w), combo_box, 2, 2, 1, 1); + + s_gui->w_four_combobox = combo_box; + node_box = GTK_COMBO_BOX (combo_box); + node_list_store = GTK_LIST_STORE (gtk_combo_box_get_model (node_box)); + gtk_list_store_clear (node_list_store); + + if (voltmeters) { + for (iter = voltmeters; iter; iter = iter->next) { + if (g_strcmp0 (iter->data, "V(0)")) + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (node_box), iter->data); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (node_box), 0); + } + g_signal_connect (G_OBJECT (node_box), "changed", G_CALLBACK (entry_changed_cb), s); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_add")); + s_gui->w_four_add = w; + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (fourier_add_vout_cb), s_gui); + w = GTK_WIDGET (gtk_builder_get_object (builder, "fourier_rem")); + s_gui->w_four_rem = w; + g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (fourier_remove_vout_cb), s_gui); + + // Noise // + // *** // + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_enable")); + s_gui->w_noise_enable = w; + g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (noise_enable_cb), s_gui); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->noise_enable); + + w1 = GTK_WIDGET (gtk_builder_get_object (builder, "grid1")); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_vin")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 0, 1, 1); + s_gui->w_noise_vin = combo_box; + if (sources_ac) { + index = 0; + active = 0; + for (iter = sources_ac; iter; iter = iter->next) { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), iter->data); + if (!g_strcmp0(s->noise_vin, iter->data)) + active = index; + index++; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s); + + // FIXME: Should enable more than just one output as in the Fourier analysis + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_vout")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 1, 1, 1); + s_gui->w_noise_vout = combo_box; + if (voltmeters) { + index = 0; + active = 0; + text = g_strdup_printf ("V(%s)", s->noise_vout); + for (iter = voltmeters; iter; iter = iter->next) { + if (g_strcmp0 (iter->data, "V(0)")) + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), iter->data); + if (!g_strcmp0(text, iter->data)) + active = index; + index++; + } + g_free (text); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s); + + // Initialisation of the various scale types + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_type")); + gtk_widget_destroy (w); // FIXME wtf?? + combo_box = gtk_combo_box_text_new (); + gtk_grid_attach (GTK_GRID (w1), combo_box, 1, 2, 1, 1); + s_gui->w_noise_type = combo_box; + + { + index = 0; + active = 0; + for (; scale_types_list[index] != NULL; index++) { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), scale_types_list[index]); + if (!g_strcmp0(s->noise_type, scale_types_list[index])) + active = index; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active); + } + g_assert (GTK_IS_COMBO_BOX (combo_box)); + g_signal_connect (G_OBJECT (combo_box), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_npoints")); + s_gui->w_noise_npoints = w; + gtk_entry_set_text (GTK_ENTRY (w), s->noise_npoints); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_start")); + s_gui->w_noise_start = w; + gtk_entry_set_text (GTK_ENTRY (w), s->noise_start); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "noise_stop")); + s_gui->w_noise_stop = w; + gtk_entry_set_text (GTK_ENTRY (w), s->noise_stop); + g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (entry_changed_cb), s_gui); + + gtk_widget_show_all (toplevel); + + trans_enable_cb (s_gui->w_trans_enable, s_gui); + trans_step_enable_cb (s_gui->w_trans_step_enable, s_gui); + ac_enable_cb (s_gui->w_ac_enable, s_gui); + dc_enable_cb (s_gui->w_dc_enable, s_gui); + fourier_enable_cb (s_gui->w_four_enable, s_gui); + noise_enable_cb (s_gui->w_noise_enable, s_gui); + + g_list_free(sources); + g_list_free(voltmeters); + g_list_free(voltmeters_with_type); +} diff --git a/src/sim-settings-gui.h b/src/sim-settings-gui.h new file mode 100644 index 0000000..f36dc80 --- /dev/null +++ b/src/sim-settings-gui.h @@ -0,0 +1,113 @@ +/* + * sim-settings-gui.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SIM_SETTINGS_GUI_H_ +#define SIM_SETTINGS_GUI_H_ + +typedef struct _SimSettingsGui SimSettingsGui; + +#include "model/schematic.h" +#include "sim-settings.h" +#include "schematic-view.h" + +struct _SimSettingsGui { + SimSettings *sim_settings; + + GtkWidget *pbox; + GtkNotebook *notebook; + + GtkWidget *w_main; + + // Transient analysis. + GtkWidget *w_trans_enable, + *w_trans_start, + *w_trans_stop, + *w_trans_step, + *w_trans_step_enable, + *w_trans_init_cond, + *w_trans_analyze_all, + *w_trans_frame; + + // AC + GtkWidget *w_ac_enable, + *w_ac_vout, + *w_ac_type, + *w_ac_npoints, + *w_ac_start, + *w_ac_stop, + *w_ac_frame; + + // DC + GtkWidget *w_dc_enable, + *w_dc_vin, + *w_dc_vout, + *w_dc_start, + *w_dc_stop, + *w_dc_step, + *w_dcsweep_frame; + + // Fourier analysis. Replace with something sane later. + GtkWidget *w_four_enable, + *w_four_freq, + *w_four_vout, + *w_four_combobox, + *w_four_add, + *w_four_rem, + *w_fourier_frame; + + // Noise + GtkWidget *w_noise_enable, + *w_noise_vin, + *w_noise_vout, + *w_noise_type, + *w_noise_npoints, + *w_noise_start, + *w_noise_stop, + *w_noise_frame; + + GtkEntry *w_opt_value; + GtkTreeView *w_opt_list; +}; + +SimSettingsGui *sim_settings_gui_new(); +void sim_settings_gui_finalize(SimSettingsGui *gui); +void sim_settings_show (GtkWidget *widget, SchematicView *sm); + +gint get_voltmeters_list (GList **voltmeters, Schematic *sm, GError *e, gboolean with_type); + +gint get_voltage_sources_list (GList **sources, Schematic *sm, GError *e, gboolean ac_only); + +#endif /* SIM_SETTINGS_GUI_H_ */ diff --git a/src/sim-settings.c b/src/sim-settings.c index 9f4dff3..1b02170 100644 --- a/src/sim-settings.c +++ b/src/sim-settings.c @@ -7,12 +7,16 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,1348 +30,580 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#include <unistd.h> #include <stdlib.h> -#include <ctype.h> -#include <gtk/gtk.h> -#include <glib/gi18n.h> +#include <string.h> +#include <glib.h> -#include "oregano.h" -#include "sim-settings.h" -#include "schematic.h" -#include "schematic-view.h" -#include "dialogs.h" #include "oregano-utils.h" -#include "netlist-helper.h" -#include "errors.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; +#include "sim-settings.h" - // DC - GtkWidget *w_dc_enable,*w_dc_vin,*w_dc_start,*w_dc_stop,*w_dc_step, - *w_dcsweep_frame; - gboolean dc_enable; - gchar *dc_vin; - gchar *dc_start,*dc_stop,*dc_step; - - // Fourier analysis. Replace with something sane later. - GtkWidget *w_four_enable,*w_four_freq,*w_four_vout,*w_four_combobox, - *w_four_add,*w_four_rem,*w_fourier_frame; - gboolean fourier_enable; - gchar *fourier_frequency; - gchar *fourier_nb_vout; - GSList *fourier_vout; - - // Options - GtkEntry *w_opt_value; - GtkTreeView *w_opt_list; - GList *options; -}; - -static char *AC_types_list[] = { - "DEC", - "LIN", - "OCT", - NULL -}; - -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} -}; - -#define NG_DEBUG(s) if (0) g_print ("%s\n", s) - -static void -fourier_add_vout_cb (GtkButton *w, SimSettings *sim) +SimSettings *sim_settings_new () { - GtkComboBoxText *node_box; - GSList *node_slist; - gint i; - gint result = FALSE; - - node_box = GTK_COMBO_BOX_TEXT (sim->priv->w_four_combobox); - - // Get the node identifier - for (i=0; (i <1000 && result == FALSE); ++i) { - if (g_strcmp0 (g_strdup_printf ("V(%d)", i), - gtk_combo_box_text_get_active_text (node_box)) == 0) - result = TRUE; - } - result=FALSE; - - // Is the node identifier already available? - node_slist = g_slist_copy (sim->priv->fourier_vout); - while (node_slist) { - if ((i-1) == atoi (node_slist->data)) { - result = TRUE; - } - node_slist=node_slist->next; - } - g_slist_free (node_slist); - if (!result) { - GSList *node_slist; - gchar *text = NULL; + SimSettings *sim_settings; - // Add Node (i-1) at the end of fourier_vout - text = g_strdup_printf ("%d", i-1); - sim->priv->fourier_vout = g_slist_append (sim->priv->fourier_vout, - g_strdup_printf ("%d", i-1)); + sim_settings = g_new0 (SimSettings, 1); - // Update the fourier_vout widget - node_slist = g_slist_copy (sim->priv->fourier_vout); - if (node_slist->data) - text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); - node_slist=node_slist->next; - while (node_slist) - { - if (node_slist->data) - text = g_strdup_printf ("%s V(%d)", text, - atoi (node_slist->data)); - node_slist = node_slist->next; - } - if (text) - gtk_entry_set_text (GTK_ENTRY (sim->priv->w_four_vout), text); - else gtk_entry_set_text (GTK_ENTRY (sim->priv->w_four_vout), ""); - g_slist_free (node_slist); - } -} + sim_settings->configured = FALSE; + sim_settings->simulation_requested = FALSE; -static void -fourier_remove_vout_cb (GtkButton *w, SimSettings *sim) -{ - GtkComboBoxText *node_box; - gint result = FALSE; - gint i; - - node_box = GTK_COMBO_BOX_TEXT (sim->priv->w_four_combobox); + // Set some default settings. + // transient + sim_settings->trans_enable = TRUE; + sim_settings->trans_start = g_strdup ("0 s"); + sim_settings->trans_stop = g_strdup ("5 ms"); + sim_settings->trans_step = g_strdup ("0.1 ms"); + sim_settings->trans_step_enable = FALSE; - // Get the node identifier - for (i=0; (i < 1000 && result == FALSE); i++) { - if (g_strcmp0 (g_strdup_printf ("V(%d)", i), - gtk_combo_box_text_get_active_text (node_box)) == 0) - result = TRUE; - } - - if (result) { - GSList *node_slist; - gchar *text = NULL; - - // Remove current data in the g_slist - { - GSList *tmp, *prev = NULL; - - tmp = sim->priv->fourier_vout; - while (tmp) - { - if (atoi (tmp->data) == i-1) - { - if (prev) prev->next = tmp->next; - else sim->priv->fourier_vout = tmp->next; - - g_slist_free_1 (tmp); - break; - } - prev = tmp; - tmp = prev->next; - } - } + // AC + sim_settings->ac_enable = FALSE; + sim_settings->ac_vout = g_strdup (""); + sim_settings->ac_type = g_strdup ("DEC"); + sim_settings->ac_npoints = g_strdup ("50"); + sim_settings->ac_start = g_strdup ("1 Hz"); + sim_settings->ac_stop = g_strdup ("1 MHz"); - // Update the fourier_vout widget - node_slist = g_slist_copy (sim->priv->fourier_vout); - if (node_slist->data) - text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); - if (node_slist) node_slist=node_slist->next; - while (node_slist) - { - if (node_slist->data) - text = g_strdup_printf ("%s V(%d)", text, - atoi (node_slist->data)); - node_slist = node_slist->next; - } - if (text) gtk_entry_set_text (GTK_ENTRY (sim->priv->w_four_vout), text); - else gtk_entry_set_text (GTK_ENTRY (sim->priv->w_four_vout), ""); + // DC + sim_settings->dc_enable = FALSE; + sim_settings->dc_vin = g_strdup (""); + sim_settings->dc_vout = g_strdup (""); + sim_settings->dc_start = g_strdup (""); + sim_settings->dc_stop = g_strdup (""); + sim_settings->dc_step = g_strdup (""); - g_slist_free (node_slist); - } -} + // Fourier + sim_settings->fourier_enable = FALSE; + sim_settings->fourier_frequency = g_strdup ("1 MHz"); + sim_settings->fourier_vout = 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++; - } -} + // Noise + sim_settings->noise_enable = FALSE; + sim_settings->noise_vin = g_strdup (""); + sim_settings->noise_vout = g_strdup (""); + sim_settings->noise_type = g_strdup ("DEC"); + sim_settings->noise_npoints = g_strdup ("50"); + sim_settings->noise_start = g_strdup ("1 Hz"); + sim_settings->noise_stop = g_strdup ("1 MHz"); -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; - } + sim_settings->options = NULL; - 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); - i++; - } + return sim_settings; } -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 sim_option_finalize(SimOption *option) { + g_free(option->name); + g_free(option->value); + g_free(option); } -static void -option_setvalue (GtkWidget *w, SimSettings *s) -{ - const gchar *value; - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreeSelection *selection; +void sim_settings_finalize(SimSettings *sim_settings) { - value = gtk_entry_get_text (s->priv->w_opt_value); - if (!value) return; + // Set some default settings. + // transient + g_free(sim_settings->trans_start); + g_free(sim_settings->trans_stop); + g_free(sim_settings->trans_step); - // 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); + // AC + g_free(sim_settings->ac_vout); + g_free(sim_settings->ac_type); + g_free(sim_settings->ac_npoints); + g_free(sim_settings->ac_start); + g_free(sim_settings->ac_stop); - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { - return; - } + // DC + g_free(sim_settings->dc_vin); + g_free(sim_settings->dc_vout); + g_free(sim_settings->dc_start); + g_free(sim_settings->dc_stop); + g_free(sim_settings->dc_step); - gtk_list_store_set(GTK_LIST_STORE (model), &iter, 1, value, -1); -} + // Fourier + sim_settings->fourier_enable = FALSE; + g_free(sim_settings->fourier_frequency); + if (sim_settings->fourier_vout != NULL) + g_slist_free_full(sim_settings->fourier_vout, g_free); + + // Noise + g_free(sim_settings->noise_vin); + g_free(sim_settings->noise_vout); + g_free(sim_settings->noise_type); + g_free(sim_settings->noise_npoints); + g_free(sim_settings->noise_start); + g_free(sim_settings->noise_stop); + + if (sim_settings->options != NULL) + g_list_free_full(sim_settings->options, (GDestroyNotify)sim_option_finalize); + + g_free(sim_settings); +} + +gchar *fourier_add_vout(SimSettings *sim_settings, guint node_index) { + gboolean result; + guint i; + gchar *ret_val = NULL; + gchar *text; + gchar **node_ids; + GSList *node_slist; -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_get_content_area ( - GTK_DIALOG (dialog))), 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); + // Is the node identifier for the output vector already + // stored in the fourier_vout list ? + node_slist = g_slist_copy (sim_settings->fourier_vout); + result = FALSE; + while (node_slist) { + if ((node_index - 1) == atoi (node_slist->data)) { + result = TRUE; + } + node_slist = node_slist->next; } - gtk_widget_destroy (dialog); -} + g_slist_free_full (node_slist, g_free); + // If the output vector is not already in the fourier_vout list + // then add it to the list and return the updated list. + // Otherwise, simply return the existing list of output vectors. + if (!result) { + // Add Node (node_index-1) at the end of fourier_vout + text = g_strdup_printf ("%d", node_index - 1); + sim_settings->fourier_vout = + g_slist_append (sim_settings->fourier_vout, text); -static void -option_remove (GtkWidget *w, SimSettings *s) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreeSelection *selection; + // Update the fourier_vout widget + node_slist = g_slist_copy (sim_settings->fourier_vout); + if (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) + ret_val = g_strdup_printf ("V(%d)", atoi (node_slist->data)); + node_slist = node_slist->next; + } + while (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) { + if (ret_val) { + text = ret_val; + ret_val = g_strdup_printf ("%s V(%d)", ret_val, atoi (node_slist->data)); + g_free (text); + } else { + ret_val = g_strdup_printf ("V(%d)", atoi (node_slist->data)); + } + } + node_slist = node_slist->next; + } - // 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); + g_slist_free_full (node_slist, g_free); + } else { + text = sim_settings_get_fourier_vout (sim_settings); + node_ids = g_strsplit (text, " ", 0); + + for (i = 0; node_ids[i] != NULL; i++) { + if (node_ids[i] && atoi (node_ids[i]) > 0) { + if (ret_val) { + text = ret_val; + ret_val = g_strdup_printf ("%s V(%d)", ret_val, atoi (node_ids[i])); + g_free (text); + } else { + ret_val = g_strdup_printf ("V(%d)", atoi (node_ids[i])); + } + } + } - if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { - return; + g_strfreev (node_ids); } - gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, "", -1); -} + if (!ret_val) + ret_val = g_strdup (""); -static void -trans_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_trans_frame); - else - gtk_widget_hide (s->priv->w_trans_frame); - gtk_container_resize_children (GTK_CONTAINER (s->notebook)); + return ret_val; } -static void -trans_step_enable_cb (GtkWidget *widget, SimSettings *s) +gboolean sim_settings_get_trans (const SimSettings *sim_settings) { - 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); + return sim_settings->trans_enable; } -static void -entry_changed_cb (GtkWidget *widget, SimSettings *s) +gboolean sim_settings_get_trans_init_cond (const SimSettings *sim_settings) { - + return sim_settings->trans_init_cond; } -static void -ac_enable_cb (GtkWidget *widget, SimSettings *s) +gboolean sim_settings_get_trans_analyze_all (const SimSettings *sim_settings) { - 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_container_resize_children (GTK_CONTAINER (s->notebook)); + return sim_settings->trans_analyze_all; } -static void -fourier_enable_cb (GtkWidget *widget, SimSettings *s) +gdouble sim_settings_get_trans_start (const SimSettings *sim_settings) { - 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_container_resize_children (GTK_CONTAINER (s->notebook)); + gchar *text = sim_settings->trans_start; + return oregano_strtod (text, "s"); } -static void -dc_enable_cb (GtkWidget *widget, SimSettings *s) +gdouble sim_settings_get_trans_stop (const SimSettings *sim_settings) { - 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_container_resize_children (GTK_CONTAINER (s->notebook)); + gchar *text = sim_settings->trans_stop; + return oregano_strtod (text, "s"); } -static int -delete_event_cb (GtkWidget *widget, GdkEvent *event, SimSettings *s) +gdouble sim_settings_get_trans_step (const SimSettings *sim_settings) { - s->pbox = NULL; - return FALSE; + gchar *text = sim_settings->trans_step; + return oregano_strtod (text, "s"); } -SimSettings * -sim_settings_new (Schematic *sm) +gdouble sim_settings_get_trans_step_enable (const SimSettings *sim_settings) { - 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_vin = g_strdup (""); - s->priv->dc_start = g_strdup (""); - s->priv->dc_stop = g_strdup (""); - s->priv->dc_step = g_strdup (""); - - // Fourier - s->priv->fourier_enable = FALSE; - s->priv->fourier_frequency = g_strdup (""); - s->priv->fourier_vout = NULL; - - s->priv->options=0; - - return s; + return sim_settings->trans_step_enable; } -gboolean -sim_settings_get_trans (SimSettings *sim_settings) +void sim_settings_set_trans (SimSettings *sim_settings, gboolean enable) { - return sim_settings->priv->trans_enable; + sim_settings->trans_enable = enable; } -gboolean -sim_settings_get_trans_init_cond (SimSettings *sim_settings) +void sim_settings_set_trans_start (SimSettings *sim_settings, gchar *str) { - return sim_settings->priv->trans_init_cond; + if (sim_settings->trans_start) + g_strdup (sim_settings->trans_start); + sim_settings->trans_start = g_strdup (str); } -gdouble -sim_settings_get_trans_start (SimSettings *sim_settings) +void sim_settings_set_trans_init_cond (SimSettings *sim_settings, gboolean enable) { - gchar *text = sim_settings->priv->trans_start; - return oregano_strtod (text, 's'); + sim_settings->trans_init_cond = enable; } -gdouble -sim_settings_get_trans_stop (SimSettings *sim_settings) +void sim_settings_set_trans_analyze_all (SimSettings *sim_settings, gboolean enable) { - gchar *text = sim_settings->priv->trans_stop; - return oregano_strtod (text, 's'); + sim_settings->trans_analyze_all = enable; } -gdouble -sim_settings_get_trans_step (SimSettings *sim_settings) +void sim_settings_set_trans_stop (SimSettings *sim_settings, gchar *str) { - gchar *text = sim_settings->priv->trans_step; - return oregano_strtod (text, 's'); + if (sim_settings->trans_stop) + g_strdup (sim_settings->trans_stop); + sim_settings->trans_stop = g_strdup (str); } -gdouble -sim_settings_get_trans_step_enable (SimSettings *sim_settings) +void sim_settings_set_trans_step (SimSettings *sim_settings, gchar *str) { - return sim_settings->priv->trans_step_enable; + if (sim_settings->trans_step) + g_strdup (sim_settings->trans_step); + sim_settings->trans_step = g_strdup (str); } -void -sim_settings_set_trans (SimSettings *sim_settings, gboolean enable) +void sim_settings_set_trans_step_enable (SimSettings *sim_settings, gboolean enable) { - sim_settings->priv->trans_enable = enable; + sim_settings->trans_step_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); -} +gboolean sim_settings_get_ac (const SimSettings *sim_settings) { return sim_settings->ac_enable; } -void -sim_settings_set_trans_init_cond (SimSettings *sim_settings, gboolean enable) -{ - sim_settings->priv->trans_init_cond = enable; -} +gchar *sim_settings_get_ac_vout (const SimSettings *sim_settings) { return sim_settings->ac_vout; } -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); -} +gchar *sim_settings_get_ac_type (const SimSettings *sim_settings) { return sim_settings->ac_type; } -void -sim_settings_set_trans_step (SimSettings *sim_settings, gchar *str) +gint sim_settings_get_ac_npoints (const SimSettings *sim_settings) { - if (sim_settings->priv->trans_step) - g_strdup (sim_settings->priv->trans_step); - sim_settings->priv->trans_step = g_strdup (str); + return atoi (sim_settings->ac_npoints); } -void -sim_settings_set_trans_step_enable (SimSettings *sim_settings, gboolean enable) +gdouble sim_settings_get_ac_start (const SimSettings *sim_settings) { - sim_settings->priv->trans_step_enable = enable; + return oregano_strtod (sim_settings->ac_start, "Hz"); } -gboolean -sim_settings_get_ac (SimSettings *sim_settings) +gdouble sim_settings_get_ac_stop (const SimSettings *sim_settings) { - return sim_settings->priv->ac_enable; + return oregano_strtod (sim_settings->ac_stop, "Hz"); } -gchar * -sim_settings_get_ac_type (SimSettings *sim_settings) +void sim_settings_set_ac (SimSettings *sim_settings, gboolean enable) { - return sim_settings->priv->ac_type; + sim_settings->ac_enable = enable; } -gint -sim_settings_get_ac_npoints (SimSettings *sim_settings) +void sim_settings_set_ac_vout (SimSettings *sim_settings, gchar *str) { - return atoi (sim_settings->priv->ac_npoints); + g_free (sim_settings->ac_vout); + sim_settings->ac_vout = g_strdup (str); } -gdouble -sim_settings_get_ac_start (SimSettings *sim_settings) +void sim_settings_set_ac_type (SimSettings *sim_settings, gchar *str) { - return oregano_strtod (sim_settings->priv->ac_start, 's'); + g_free (sim_settings->ac_type); + sim_settings->ac_type = g_strdup (str); } -gdouble -sim_settings_get_ac_stop (SimSettings *sim_settings) +void sim_settings_set_ac_npoints (SimSettings *sim_settings, gchar *str) { - return oregano_strtod (sim_settings->priv->ac_stop,'s'); + g_free (sim_settings->ac_npoints); + sim_settings->ac_npoints = g_strdup (str); } -void -sim_settings_set_ac (SimSettings *sim_settings,gboolean enable) +void sim_settings_set_ac_start (SimSettings *sim_settings, gchar *str) { - sim_settings->priv->ac_enable = enable; + g_free (sim_settings->ac_start); + sim_settings->ac_start = g_strdup (str); } -void -sim_settings_set_ac_type (SimSettings *sim_settings,gchar *str) +void sim_settings_set_ac_stop (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); + g_free (sim_settings->ac_stop); + sim_settings->ac_stop = 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); +gboolean sim_settings_get_dc (const SimSettings *sim_settings) { return sim_settings->dc_enable; } -} +gchar *sim_settings_get_dc_vsrc (const SimSettings *sim_settings) { return sim_settings->dc_vin; } + +gchar *sim_settings_get_dc_vout (const SimSettings *sim_settings) { return sim_settings->dc_vout; } -void -sim_settings_set_ac_start (SimSettings *sim_settings,gchar *str) +gdouble sim_settings_get_dc_start (const SimSettings *sim_settings) { - if (sim_settings->priv->ac_start) - g_free (sim_settings->priv->ac_start); - sim_settings->priv->ac_start = g_strdup (str); + return oregano_strtod (sim_settings->dc_start, "V"); } -void -sim_settings_set_ac_stop (SimSettings *sim_settings,gchar *str) +gdouble sim_settings_get_dc_stop (const SimSettings *sim_settings) { - if (sim_settings->priv->ac_stop) - g_free (sim_settings->priv->ac_stop); - sim_settings->priv->ac_stop = g_strdup (str); + return oregano_strtod (sim_settings->dc_stop, "V"); } -gboolean -sim_settings_get_dc (SimSettings *sim_settings) +gdouble sim_settings_get_dc_step (const SimSettings *sim_settings) { - return sim_settings->priv->dc_enable; + return oregano_strtod (sim_settings->dc_step, "V"); } -gchar* -sim_settings_get_dc_vsrc (SimSettings *sim_settings) +void sim_settings_set_dc (SimSettings *sim_settings, gboolean enable) { - return sim_settings->priv->dc_vin; + sim_settings->dc_enable = enable; } -gdouble -sim_settings_get_dc_start (SimSettings *sim_settings) +void sim_settings_set_dc_vsrc (SimSettings *sim_settings, gchar *str) { - return oregano_strtod (sim_settings->priv->dc_start, 's'); + g_free (sim_settings->dc_vin); + sim_settings->dc_vin = g_strdup (str); } -gdouble -sim_settings_get_dc_stop (SimSettings *sim_settings) +void sim_settings_set_dc_vout (SimSettings *sim_settings, gchar *str) { - return oregano_strtod (sim_settings->priv->dc_stop, 's'); + g_free (sim_settings->dc_vout); + sim_settings->dc_vout = g_strdup (str); } -gdouble -sim_settings_get_dc_step (SimSettings *sim_settings) +void sim_settings_set_dc_start (SimSettings *sim_settings, gchar *str) { - return oregano_strtod (sim_settings->priv->dc_step, 's'); + g_free (sim_settings->dc_start); + sim_settings->dc_start = g_strdup (str); } -void -sim_settings_set_dc (SimSettings *sim_settings, gboolean enable) +void sim_settings_set_dc_stop (SimSettings *sim_settings, gchar *str) { - sim_settings->priv->dc_enable = enable; + g_free (sim_settings->dc_stop); + sim_settings->dc_stop = g_strdup (str); } -void -sim_settings_set_dc_vsrc (SimSettings *sim_settings, gchar *str) +void sim_settings_set_dc_step (SimSettings *sim_settings, gchar *str) { - if (sim_settings->priv->dc_vin) - g_free (sim_settings->priv->dc_vin); - sim_settings->priv->dc_vin = g_strdup (str); + g_free (sim_settings->dc_step); + sim_settings->dc_step = g_strdup (str); } -void -sim_settings_set_dc_start (SimSettings *sim_settings, gchar *str) -{ - if (sim_settings->priv->dc_start) - g_free (sim_settings->priv->dc_start); - sim_settings->priv->dc_start = g_strdup (str); +void sim_settings_set_fourier (SimSettings *sim_settings, gboolean enable) +{ + sim_settings->fourier_enable = enable; } -void -sim_settings_set_dc_stop (SimSettings *sim_settings, gchar *str) +void sim_settings_set_fourier_frequency (SimSettings *sim_settings, gchar *str) { - if (sim_settings->priv->dc_stop) - g_free (sim_settings->priv->dc_stop); - sim_settings->priv->dc_stop = g_strdup (str); + g_free (sim_settings->fourier_frequency); + sim_settings->fourier_frequency = g_strdup (str); } -void -sim_settings_set_dc_step (SimSettings *sim_settings, gchar *str) +void sim_settings_set_fourier_vout (SimSettings *sim_settings, gchar *str) { - if (sim_settings->priv->dc_step) - g_free (sim_settings->priv->dc_step); - sim_settings->priv->dc_step = g_strdup (str); + gchar **node_ids = NULL; + gint i; + + if (!str) + return; + + g_slist_free_full (sim_settings->fourier_vout, g_free); + sim_settings->fourier_vout = NULL; + + node_ids = g_strsplit (str, " ", 0); + for (i = 0; node_ids[i] != NULL; i++) { + if (node_ids[i]) + sim_settings->fourier_vout = + g_slist_append (sim_settings->fourier_vout, g_strdup (node_ids[i])); + } + + g_strfreev (node_ids); } -void -sim_settings_set_fourier (SimSettings *sim_settings, gboolean enable) +gboolean sim_settings_get_fourier (const SimSettings *sim_settings) { - sim_settings->priv->fourier_enable = enable; + return sim_settings->fourier_enable; } -void -sim_settings_set_fourier_frequency (SimSettings *sim_settings,gchar *str) +gdouble sim_settings_get_fourier_frequency (const SimSettings *sim_settings) { - if (sim_settings->priv->fourier_frequency) - g_free (sim_settings->priv->fourier_frequency); - sim_settings->priv->fourier_frequency = g_strdup (str); + return oregano_strtod (sim_settings->fourier_frequency, "Hz"); } -void -sim_settings_set_fourier_vout (SimSettings *sim_settings, - gchar *str) +gchar *sim_settings_get_fourier_vout (const SimSettings *sim_settings) { - gchar **node_ids=NULL; - gint i; - if (sim_settings->priv->fourier_vout) - g_free (sim_settings->priv->fourier_vout); - node_ids = g_strsplit (g_strdup (str), " ", 0); - for (i=0; node_ids[i]!= NULL; i++) { - if (node_ids[i]) - sim_settings->priv->fourier_vout = - g_slist_append (sim_settings->priv->fourier_vout, - g_strdup (node_ids[i])); + GSList *node_slist; + gchar *text, *text2; + gchar *ret_val = NULL; + + text = NULL; + node_slist = g_slist_copy (sim_settings->fourier_vout); + if (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) + text = g_strdup_printf ("%d", atoi (node_slist->data)); + node_slist = node_slist->next; + } + while (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) { + if (text) { + text2 = text; + text = g_strdup_printf ("%s %d", text, atoi (node_slist->data)); + g_free (text2); + } else { + text = g_strdup_printf ("%d", atoi (node_slist->data)); + } + } + node_slist = node_slist->next; } -} + g_slist_free_full (node_slist, g_free); -gboolean -sim_settings_get_fourier (SimSettings *sim_settings) -{ - return sim_settings->priv->fourier_enable; -} + if (text) + ret_val = text; + else + ret_val = g_strdup (""); -gint -sim_settings_get_fourier_frequency (SimSettings *sim_settings) -{ - return atoi (sim_settings->priv->fourier_frequency); + return ret_val; } -gchar * -sim_settings_get_fourier_vout (SimSettings *sim) +gchar *sim_settings_get_fourier_nodes (const SimSettings *sim_settings) { GSList *node_slist; - gchar *text = NULL; - - node_slist = g_slist_copy (sim->priv->fourier_vout); - if (node_slist) text = g_strdup_printf ("%d", atoi (node_slist->data)); - if (node_slist) node_slist=node_slist->next; - while (node_slist) - { - if (node_slist->data) - text = g_strdup_printf ("%s %d", text, atoi (node_slist->data)); + gchar *ret_val = NULL; + gchar *text, *text2; + + text = NULL; + node_slist = g_slist_copy (sim_settings->fourier_vout); + if (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) + text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); node_slist = node_slist->next; } - g_slist_free (node_slist); - return text; -} - -gchar * -sim_settings_get_fourier_nodes (SimSettings *sim) -{ - GSList *node_slist; - gchar *text = NULL; - - node_slist = g_slist_copy (sim->priv->fourier_vout); - if (node_slist->data) - text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); - if (node_slist) node_slist=node_slist->next; - while (node_slist) - { - if (node_slist->data) - text = g_strdup_printf ("%s V(%d)", text, atoi (node_slist->data)); + while (node_slist) { + if (node_slist->data && atoi (node_slist->data) > 0) { + if (text) { + text2 = text; + text = g_strdup_printf ("%s V(%d)", text, atoi (node_slist->data)); + g_free (text2); + } else { + text = g_strdup_printf ("V(%d)", atoi (node_slist->data)); + } + } node_slist = node_slist->next; } - g_slist_free (node_slist); - return text; + + if (text) + ret_val = text; + else + ret_val = g_strdup (""); + + g_slist_free_full (node_slist, g_free); + + return ret_val; } -static void -response_callback (GtkButton *button, Schematic *sm) +gboolean sim_settings_get_noise (const SimSettings *sim_settings) { return sim_settings->noise_enable; } + +gchar *sim_settings_get_noise_vsrc (const SimSettings *sim_settings) { return sim_settings->noise_vin; } + +gchar *sim_settings_get_noise_vout (const SimSettings *sim_settings) { return sim_settings->noise_vout; } + +gchar *sim_settings_get_noise_type (const SimSettings *sim_settings) { return sim_settings->noise_type; } + +gint sim_settings_get_noise_npoints (const SimSettings *sim_settings) { - gint page; - SimSettings *s; - gchar *tmp = NULL, **node_ids= NULL; - - 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_vin) - g_free (s->priv->dc_vin); - - tmp = gtk_combo_box_text_get_active_text ( - GTK_COMBO_BOX_TEXT (s->priv->w_dc_vin)); - node_ids = g_strsplit (g_strdup (tmp), "V(", 0); - tmp = node_ids[1]; - node_ids = g_strsplit (g_strdup (tmp), ")", 0); - s->priv->dc_vin = node_ids[0]; - - if (s->priv->dc_start) - g_free (s->priv->dc_start); - s->priv->dc_start = g_strdup (gtk_entry_get_text ( - GTK_ENTRY (s->priv->w_dc_start))); - - if (s->priv->dc_stop) - g_free (s->priv->dc_stop); - s->priv->dc_stop = g_strdup (gtk_entry_get_text ( - GTK_ENTRY (s->priv->w_dc_stop))); - - if (s->priv->dc_step) - g_free (s->priv->dc_step); - s->priv->dc_step = g_strdup (gtk_entry_get_text ( - GTK_ENTRY (s->priv->w_dc_step))); - - // 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 = gtk_combo_box_text_get_active_text ( - GTK_COMBO_BOX_TEXT (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))); - - // Fourier analysis - s->priv->fourier_enable = gtk_toggle_button_get_active ( - GTK_TOGGLE_BUTTON (s->priv->w_four_enable)); - - if (s->priv->fourier_frequency) - g_free (s->priv->fourier_frequency); - s->priv->fourier_frequency = g_strdup (gtk_entry_get_text ( - GTK_ENTRY (s->priv->w_four_freq))); - - gtk_container_resize_children (GTK_CONTAINER (s->notebook)); - - // Options - get_options_from_list (s); - gtk_widget_hide (GTK_WIDGET(s->pbox)); - s->pbox = NULL; - s->notebook = NULL; - - // Schematic is dirty now ;-) - schematic_set_dirty (sm, TRUE); - g_free (tmp); - g_free (node_ids); + return atoi (sim_settings->noise_npoints); } -void -sim_settings_show (GtkWidget *widget, SchematicView *sv) +gdouble sim_settings_get_noise_start (const SimSettings *sim_settings) { - int i; - GtkWidget *toplevel, *w, *pbox, *combo_box; - GtkTreeView *opt_list; - GtkCellRenderer *cell_option, *cell_value; - GtkTreeViewColumn *column_option, *column_value; - GtkListStore *opt_model; - GtkTreeIter iter; - GtkBuilder *gui; - GError *perror = NULL; - gchar * msg; - SimSettings *s; - Schematic *sm; - GList *list; - GList *sources = NULL, *ltmp; - GtkComboBox *node_box; - GtkListStore *node_list_store; - gchar *text = NULL; - GSList * slist = NULL, *node_list = NULL; - - 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_get_window (GTK_WIDGET (s->pbox))); - return; - } - - if (!g_file_test (OREGANO_UIDIR "/sim-settings.ui", 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_UIDIR "/sim-settings.ui"); - oregano_error_with_title ( - _("Could not create simulation settings dialog"), msg); - g_free (msg); - return; - } + return oregano_strtod (sim_settings->noise_start, "Hz"); +} - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create simulation settings dialog")); - return; - } - else - gtk_builder_set_translation_domain (gui, NULL); - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/sim-settings.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title ( - _("Could not create simulation settings dialog"), msg); - g_error_free (perror); - return; - } +gdouble sim_settings_get_noise_stop (const SimSettings *sim_settings) +{ + return oregano_strtod (sim_settings->noise_stop, "Hz"); +} - toplevel = GTK_WIDGET (gtk_builder_get_object (gui, "toplevel")); - if (!toplevel) { - oregano_error (_("Could not create simulation settings dialog")); - return; - } +void sim_settings_set_noise (SimSettings *sim_settings, gboolean enable) +{ + sim_settings->noise_enable = enable; +} - pbox = toplevel; - s->pbox = pbox; - s->notebook = GTK_NOTEBOOK (gtk_builder_get_object (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 (gtk_builder_get_object (gui, "opt_value")); - opt_list = s->priv->w_opt_list = GTK_TREE_VIEW ( - gtk_builder_get_object (gui, "option_list")); - - // Grab the frames - s->priv->w_trans_frame = GTK_WIDGET (gtk_builder_get_object (gui, - "trans_frame")); - s->priv->w_ac_frame = GTK_WIDGET (gtk_builder_get_object (gui, - "ac_frame")); - s->priv->w_dcsweep_frame = GTK_WIDGET (gtk_builder_get_object (gui, - "dcsweep_frame")); - s->priv->w_fourier_frame = GTK_WIDGET (gtk_builder_get_object (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; - } - } +void sim_settings_set_noise_vsrc (SimSettings *sim_settings, gchar *str) +{ + g_free (sim_settings->noise_vin); + sim_settings->noise_vin = g_strdup (str); +} - // Set the options 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; - } +void sim_settings_set_noise_vout (SimSettings *sim_settings, gchar *str) +{ + g_free (sim_settings->noise_vout); + sim_settings->noise_vout = g_strdup (str); +} - g_signal_connect (G_OBJECT (opt_list), "button_release_event", - G_CALLBACK (select_opt_callback), s); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "opt_accept")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (option_setvalue), s); - w = GTK_WIDGET (gtk_builder_get_object (gui, "opt_remove")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (option_remove), s); - w = GTK_WIDGET (gtk_builder_get_object (gui, "add_option")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (add_option), s); - - // Creation of Close Button - w = GTK_WIDGET (gtk_builder_get_object (gui, "button1")); - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (response_callback), sm); - - // Transient // - // ********* // - w = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (gui, "trans_enable")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), s->priv->trans_enable); - s->priv->w_trans_enable = w; - g_signal_connect (G_OBJECT (s->priv->w_trans_enable), "clicked", - G_CALLBACK (trans_enable_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (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; - g_signal_connect (G_OBJECT (s->priv->w_trans_step_enable), "clicked", - G_CALLBACK (trans_step_enable_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (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); - - // Initialilisation of the various AC types - w = GTK_WIDGET (gtk_builder_get_object (gui, "ac_type")); - gtk_widget_destroy (w); - w = GTK_WIDGET (gtk_builder_get_object (gui, "table14")); - combo_box = gtk_combo_box_text_new (); - gtk_table_attach (GTK_TABLE (w),combo_box, 1, 2, 0, 1, - GTK_EXPAND | GTK_FILL, - GTK_SHRINK, - 0, 0); - s->priv->w_ac_type = combo_box; - - { - gint index = 0; - for (index = 0; AC_types_list[index]!= NULL ; index++) { - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), - AC_types_list[index]); - } - gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box),0); - } - g_signal_connect (G_OBJECT (GTK_COMBO_BOX (combo_box)), "changed", - G_CALLBACK (entry_changed_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (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 = GTK_WIDGET (gtk_builder_get_object (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 // - // ***** // - w = GTK_WIDGET (gtk_builder_get_object (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); - - // Get list of sources - node_list = netlist_helper_get_voltmeters_list ( - schematic_view_get_schematic (sv), &perror); - if (perror != NULL) { - msg = perror->message; - oregano_error_with_title (_("Could not create a netlist"), msg); - g_error_free (perror); - return; - } - if (!node_list) { - oregano_error (_("No node in the schematic!")); - return; - } - for (; node_list; node_list = node_list->next) { - gchar * tmp; - tmp = g_strdup_printf ("V(%d)", atoi (node_list->data)); - sources = g_list_prepend (sources, tmp); - } - w = GTK_WIDGET (gtk_builder_get_object (gui, "dc_vin1")); - gtk_widget_destroy (w); - w = GTK_WIDGET (gtk_builder_get_object (gui, "table13")); - combo_box = gtk_combo_box_text_new (); - - gtk_table_attach (GTK_TABLE (w),combo_box, 1, 2, 0, 1, - GTK_EXPAND | GTK_FILL, - GTK_SHRINK, - 0, 0); - s->priv->w_dc_vin = combo_box; - if (sources) { - for (; sources; sources = sources->next) { - gtk_combo_box_text_append_text ( - GTK_COMBO_BOX_TEXT (combo_box), sources->data); - } - gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box),0); - } - g_signal_connect (G_OBJECT (combo_box), "changed", - G_CALLBACK (entry_changed_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "dc_start1")); - s->priv->w_dc_start = w; - gtk_entry_set_text (GTK_ENTRY (w), s->priv->dc_start); - g_signal_connect (G_OBJECT (w), "changed", - G_CALLBACK (entry_changed_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "dc_stop1")); - s->priv->w_dc_stop = w; - gtk_entry_set_text (GTK_ENTRY (w), s->priv->dc_stop); - g_signal_connect (G_OBJECT (w), "changed", - G_CALLBACK (entry_changed_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "dc_step1")); - s->priv->w_dc_step = w; - gtk_entry_set_text (GTK_ENTRY (w), s->priv->dc_step); - 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 = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_enable")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), - s->priv->fourier_enable); - s->priv->w_four_enable = w; - g_signal_connect (G_OBJECT(w), "clicked", - G_CALLBACK (fourier_enable_cb), s); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_freq")); - s->priv->w_four_freq = w; - g_signal_connect (G_OBJECT(w), "changed", - G_CALLBACK (entry_changed_cb), s); - gtk_entry_set_text (GTK_ENTRY (w), s->priv->fourier_frequency); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_vout")); - s->priv->w_four_vout = w; - g_signal_connect (G_OBJECT (w), "changed", - G_CALLBACK (entry_changed_cb), s); +void sim_settings_set_noise_type (SimSettings *sim_settings, gchar *str) +{ + g_free (sim_settings->noise_type); + sim_settings->noise_type = g_strdup (str); +} - text = NULL; - slist = g_slist_copy (s->priv->fourier_vout); - if (slist) { - if (atoi (slist->data) != 0) - text = g_strdup_printf ("V(%d)", atoi (slist->data)); - - slist = slist->next; - while (slist) - { - if (atoi (slist->data) != 0) - text = g_strdup_printf ("%s V(%d)", text, atoi (slist->data)); - slist = slist->next; - } - - if (text) - gtk_entry_set_text (GTK_ENTRY (w), text); - else - gtk_entry_set_text (GTK_ENTRY (w), ""); - } - if (text) - gtk_entry_set_text (GTK_ENTRY (w), text); - else - gtk_entry_set_text (GTK_ENTRY (w), ""); - g_slist_free (slist); - - // Present in the combo box the nodes of the schematic - w = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_select_out")); - gtk_widget_destroy (w); - - w = GTK_WIDGET (gtk_builder_get_object (gui, "table12")); - combo_box = gtk_combo_box_text_new (); - - gtk_table_attach (GTK_TABLE (w),combo_box, 2, 3, 2, 3, - GTK_EXPAND | GTK_FILL, - GTK_SHRINK, - 0, 0); - - s->priv->w_four_combobox = combo_box; - node_box = GTK_COMBO_BOX (combo_box); - node_list_store = GTK_LIST_STORE (gtk_combo_box_get_model (node_box)); - gtk_list_store_clear (node_list_store); - - // Get the identification of the schematic nodes - node_list = netlist_helper_get_voltmeters_list ( - schematic_view_get_schematic (sv), &perror); - if (perror != NULL) { - msg = perror->message; - oregano_error_with_title (_("Could not create a netlist"), msg); - g_error_free (perror); - return; - } - if (!node_list) - oregano_error (_("No node in the schematic!")); +void sim_settings_set_noise_npoints (SimSettings *sim_settings, gchar *str) +{ + g_free (sim_settings->noise_npoints); + sim_settings->noise_npoints = g_strdup (str); +} - text = NULL; - while (node_list) { - if (node_list->data) - text = g_strdup_printf ("V(%d)", atoi (node_list->data)); - if (text) gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (node_box), text); - node_list = node_list->next; - } - gtk_combo_box_set_active (node_box, 0); - w = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_add")); - s->priv->w_four_add = w; - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (fourier_add_vout_cb), s); - w = GTK_WIDGET (gtk_builder_get_object (gui, "fourier_rem")); - s->priv->w_four_rem = w; - g_signal_connect (G_OBJECT (w), "clicked", - G_CALLBACK (fourier_remove_vout_cb), s); - - gtk_widget_show_all (toplevel); - - ac_enable_cb (s->priv->w_ac_enable,s); - fourier_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); - - // Cleanup - g_list_free_full (list, g_object_unref); +void sim_settings_set_noise_start (SimSettings *sim_settings, gchar *str) +{ + g_free (sim_settings->noise_start); + sim_settings->noise_start = g_strdup (str); } -GList * -sim_settings_get_options (SimSettings *s) +void sim_settings_set_noise_stop (SimSettings *sim_settings, gchar *str) { - return s->priv->options; + g_free (sim_settings->noise_stop); + sim_settings->noise_stop = g_strdup (str); } -void -sim_settings_add_option (SimSettings *s, SimOption *opt) +GList *sim_settings_get_options (const SimSettings *sim_settings) { - GList *list=s->priv->options; + g_return_val_if_fail (sim_settings != NULL, NULL); + return sim_settings->options; +} - // Remove the option if already in the list. - while (list) { - SimOption *so=list->data; - if (so && !strcmp (opt->name,so->name)) { +void sim_settings_add_option (SimSettings *sim_settings, SimOption *opt) +{ + GList *iter; + // Remove the option if already in the list. + for (iter = sim_settings->options; iter; iter = iter->next) { + SimOption *so = iter->data; + if (so && !strcmp (opt->name, so->name)) { g_free (so->name); g_free (so->value); - s->priv->options = g_list_remove (s->priv->options, so); - g_free (so); + sim_settings->options = g_list_remove (sim_settings->options, so); + sim_option_finalize(so); } - list = list->next; } - s->priv->options = g_list_append (s->priv->options, opt); - g_list_free_full (list, g_object_unref); + sim_settings->options = g_list_append (sim_settings->options, opt); } diff --git a/src/sim-settings.h b/src/sim-settings.h index 5cf0af5..7d9cb53 100644 --- a/src/sim-settings.h +++ b/src/sim-settings.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,141 +28,193 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SIM_SETTINGS_H #define __SIM_SETTINGS_H -#include <gtk/gtk.h> +typedef struct { + gboolean configured; + gboolean simulation_requested; + + // Transient analysis. + gboolean trans_enable; + gboolean trans_init_cond; + gboolean trans_analyze_all; + gchar *trans_start; + gchar *trans_stop; + gchar *trans_step; + gboolean trans_step_enable; + + // AC + gboolean ac_enable; + gchar *ac_vout; + gchar *ac_type; + gchar *ac_npoints; + gchar *ac_start; + gchar *ac_stop; + + // DC + gboolean dc_enable; + gchar *dc_vin; + gchar *dc_vout; + gchar *dc_start, *dc_stop, *dc_step; + + // Fourier analysis. Replace with something sane later. + gboolean fourier_enable; + gchar *fourier_frequency; + gchar *fourier_nb_vout; + GSList *fourier_vout; + + // Noise + gboolean noise_enable; + gchar *noise_vin; + gchar *noise_vout; + gchar *noise_type; + gchar *noise_npoints; + gchar *noise_start; + gchar *noise_stop; + + // Options + GList *options; +} SimSettings; + +typedef struct _SimOption +{ + gchar *name; + gchar *value; +} SimOption; -typedef struct _SimSettings SimSettings; +SimSettings *sim_settings_new (); -#include "schematic-view.h" -#include "schematic.h" +void sim_settings_finalize(SimSettings *s); -typedef struct _SimSettingsPriv SimSettingsPriv; +gdouble sim_settings_get_trans_start (const SimSettings *sim_settings); -struct _SimSettings { - Schematic *sm; - GtkWidget *pbox; - GtkNotebook *notebook; - SimSettingsPriv *priv; -}; +gdouble sim_settings_get_trans_stop (const SimSettings *sim_settings); -typedef struct _SimOption SimOption; +gdouble sim_settings_get_trans_step (const SimSettings *sim_settings); -struct _SimOption { - gchar *name; - gchar *value; -} _SimOption; +gboolean sim_settings_get_trans (const SimSettings *sim_settings); + +gdouble sim_settings_get_trans_step_enable (const SimSettings *sim_settings); + +gboolean sim_settings_get_trans_init_cond (const SimSettings *sim_settings); + +gboolean sim_settings_get_trans_analyze_all (const 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); + +void sim_settings_set_trans_analyze_all (SimSettings *sim_settings, gboolean enable); + +gboolean sim_settings_get_dc (const SimSettings *); + +gchar *sim_settings_get_dc_vsrc (const SimSettings *); + +gchar *sim_settings_get_dc_vout (const SimSettings *); + +gdouble sim_settings_get_dc_start (const SimSettings *); + +gdouble sim_settings_get_dc_start (const SimSettings *); + +gdouble sim_settings_get_dc_stop (const SimSettings *); -void sim_settings_show (GtkWidget *widget, - SchematicView *sv); +gdouble sim_settings_get_dc_step (const SimSettings *); -SimSettings *sim_settings_new (Schematic *sm); +void sim_settings_set_dc (SimSettings *, gboolean); -gdouble sim_settings_get_trans_start (SimSettings *sim_settings); +void sim_settings_set_dc_vsrc (SimSettings *, gchar *); -gdouble sim_settings_get_trans_stop (SimSettings *sim_settings); +void sim_settings_set_dc_vout (SimSettings *, gchar *); -gdouble sim_settings_get_trans_step (SimSettings *sim_settings); +void sim_settings_set_dc_start (SimSettings *, gchar *); -gboolean sim_settings_get_trans (SimSettings *sim_settings); +void sim_settings_set_dc_stop (SimSettings *, gchar *); -gdouble sim_settings_get_trans_step_enable (SimSettings *sim_settings); +void sim_settings_set_dc_step (SimSettings *, gchar *); -gboolean sim_settings_get_trans_init_cond (SimSettings *sim_settings); +gboolean sim_settings_get_ac (const SimSettings *); -void sim_settings_set_trans_start (SimSettings *sim_settings, - gchar *str); +gchar *sim_settings_get_ac_vout (const SimSettings *); -void sim_settings_set_trans_stop (SimSettings *sim_settings, - gchar *str); +gchar *sim_settings_get_ac_type (const SimSettings *); -void sim_settings_set_trans_step (SimSettings *sim_settings, - gchar *str); +gint sim_settings_get_ac_npoints (const SimSettings *); -void sim_settings_set_trans (SimSettings *sim_settings, - gboolean enable); +gdouble sim_settings_get_ac_start (const SimSettings *); -void sim_settings_set_trans_step_enable (SimSettings *sim_settings, - gboolean enable); +gdouble sim_settings_get_ac_stop (const SimSettings *); -void sim_settings_set_trans_init_cond (SimSettings *sim_settings, - gboolean enable); +void sim_settings_set_ac (SimSettings *, gboolean); -gboolean sim_settings_get_dc (SimSettings *); +void sim_settings_set_ac_vout (SimSettings *, gchar *); -gchar *sim_settings_get_dc_vsrc (SimSettings *); +void sim_settings_set_ac_type (SimSettings *, gchar *); -gdouble sim_settings_get_dc_start (SimSettings *); +void sim_settings_set_ac_npoints (SimSettings *, gchar *); -gdouble sim_settings_get_dc_stop (SimSettings *); +void sim_settings_set_ac_start (SimSettings *, gchar *); -gdouble sim_settings_get_dc_step (SimSettings *); +void sim_settings_set_ac_stop (SimSettings *, gchar *); -void sim_settings_set_dc (SimSettings *, - gboolean); +void sim_settings_set_fourier (SimSettings *, gboolean); -void sim_settings_set_dc_vsrc (SimSettings * , - gchar *); +void sim_settings_set_fourier_frequency (SimSettings *, gchar *); -void sim_settings_set_dc_start (SimSettings *, - gchar *); +void sim_settings_set_fourier_vout (SimSettings *, gchar *); -void sim_settings_set_dc_stop (SimSettings *, - gchar *); +gboolean sim_settings_get_fourier (const SimSettings *); -void sim_settings_set_dc_step (SimSettings *, - gchar *); +gdouble sim_settings_get_fourier_frequency (const SimSettings *); -gboolean sim_settings_get_ac (SimSettings *); +gchar *sim_settings_get_fourier_vout (const SimSettings *); -gchar *sim_settings_get_ac_type (SimSettings *); +gchar *sim_settings_get_fourier_nodes (const SimSettings *); -gint sim_settings_get_ac_npoints (SimSettings *); +gboolean sim_settings_get_noise (const SimSettings *); -gdouble sim_settings_get_ac_start (SimSettings *); +gchar *sim_settings_get_noise_vsrc (const SimSettings *); -gdouble sim_settings_get_ac_stop (SimSettings *); +gchar *sim_settings_get_noise_vout (const SimSettings *); -void sim_settings_set_ac (SimSettings *, - gboolean); +gchar *sim_settings_get_noise_type (const SimSettings *); -void sim_settings_set_ac_type (SimSettings *, - gchar *); +gint sim_settings_get_noise_npoints (const SimSettings *); -void sim_settings_set_ac_npoints (SimSettings *, - gchar *); +gdouble sim_settings_get_noise_start (const SimSettings *); -void sim_settings_set_ac_start (SimSettings *, - gchar *); +gdouble sim_settings_get_noise_stop (const SimSettings *); -void sim_settings_set_ac_stop (SimSettings *, - gchar *); +void sim_settings_set_noise (SimSettings *, gboolean); -void sim_settings_set_fourier (SimSettings *, - gboolean); +void sim_settings_set_noise_vsrc (SimSettings *, gchar *); -void sim_settings_set_fourier_frequency (SimSettings *, - gchar *); +void sim_settings_set_noise_vout (SimSettings *, gchar *); -void sim_settings_set_fourier_vout (SimSettings *, - gchar *); +void sim_settings_set_noise_type (SimSettings *, gchar *); -gboolean sim_settings_get_fourier (SimSettings *); +void sim_settings_set_noise_npoints (SimSettings *, gchar *); -gint sim_settings_get_fourier_frequency (SimSettings *); +void sim_settings_set_noise_start (SimSettings *, gchar *); -gchar* sim_settings_get_fourier_vout (SimSettings *); +void sim_settings_set_noise_stop (SimSettings *, gchar *); -gchar* sim_settings_get_fourier_nodes (SimSettings *); +GList *sim_settings_get_options (const SimSettings *sim_settings); -GList *sim_settings_get_options (SimSettings *sim_settings); +void sim_settings_add_option (SimSettings *, SimOption *); -void sim_settings_add_option (SimSettings *, - SimOption *); +gchar *fourier_add_vout(SimSettings *sim_settings, guint i); #endif diff --git a/src/simulation.c b/src/simulation.c index dc4872b..20175de 100644 --- a/src/simulation.c +++ b/src/simulation.c @@ -3,16 +3,18 @@ * * * Authors: - * Richard Hult <rhult@hem.passagen.se> - * Ricardo Markiewicz <rmarkie@fi.uba.ar> - * Andres de Barbara <adebarbara@fi.uba.ar> + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * - * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2006 Ricardo Markiewicz + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013-2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include <gtk/gtk.h> @@ -43,15 +45,27 @@ #include "oregano-config.h" #include "plot.h" #include "gnucap.h" +#include "log.h" -typedef struct { - Schematic *sm; - SchematicView *sv; - GtkDialog *dialog; - OreganoEngine *engine; - GtkProgressBar *progress; - GtkLabel *progress_label; - int progress_timeout_id; +//NULL terminated +const char *SimulationFunctionTypeString[] = { + "Subtraction", + "Division", + NULL +}; + +typedef struct +{ + Schematic *sm; + SchematicView *sv; + GtkDialog *dialog; + OreganoEngine *engine; + GtkProgressBar *progress_solver; + GtkLabel *progress_label_solver; + GtkProgressBar *progress_reader; + GtkLabel *progress_label_reader; + int progress_timeout_id; + Log *logstore; } Simulation; static int progress_bar_timeout_cb (Simulation *s); @@ -60,120 +74,131 @@ 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; -} +static int delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { return FALSE; } -gpointer -simulation_new (Schematic *sm) +gpointer simulation_new (Schematic *sm, Log *logstore) { Simulation *s; s = g_new0 (Simulation, 1); s->sm = sm; s->sv = NULL; + s->logstore = logstore; return s; } -void -simulation_show (GtkWidget *widget, SchematicView *sv) +void simulation_show_progress_bar (GtkWidget *widget, SchematicView *sv) { GtkWidget *w; GtkBuilder *gui; - GError *perror = NULL; + GError *e = NULL; Simulation *s; Schematic *sm; g_return_if_fail (sv != NULL); - if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create simulation dialog")); + if (oregano.engine < 0 || oregano.engine >= OREGANO_ENGINE_COUNT) return; - } - else gtk_builder_set_translation_domain (gui, 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_get_window (GTK_WIDGET (s->dialog))); + if ((gui = gtk_builder_new ()) == NULL) { + log_append (s->logstore, _ ("Simulation"), + _ ("Could not create simulation dialog - Builder creation failed.")); return; } + gtk_builder_set_translation_domain (gui, NULL); - if (!g_file_test (OREGANO_UIDIR "/simulation.ui", - G_FILE_TEST_EXISTS)) { - oregano_error (_("Could not create simulation dialog")); + // Only allow one instance of the dialog box per schematic. + if (s->dialog) { + gdk_window_raise (gtk_widget_get_window (GTK_WIDGET (s->dialog))); return; } - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/simulation.ui", - &perror) <= 0) { - gchar *msg; - msg = perror->message; - oregano_error_with_title (_("Could not create simulation dialog"), msg); - g_error_free (perror); + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/simulation.ui", &e) <= 0) { + log_append_error (s->logstore, _ ("Simulation"), _ ("Could not create simulation dialog"), + e); + g_clear_error (&e); return; } w = GTK_WIDGET (gtk_builder_get_object (gui, "toplevel")); if (!w) { - oregano_error (_("Could not create simulation dialog")); + log_append (s->logstore, _ ("Simulation"), + _ ("Could not create simulation dialog - .ui file lacks widget " + "called \"toplevel\".")); return; } s->dialog = GTK_DIALOG (w); - g_signal_connect (G_OBJECT (w), "delete_event", - G_CALLBACK (delete_event_cb), s); + g_signal_connect (G_OBJECT (w), "delete_event", G_CALLBACK (delete_event_cb), s); + + /** + * progress bars and progress labels + */ + w = GTK_WIDGET (gtk_builder_get_object (gui, "progressbar_solver")); + s->progress_solver = GTK_PROGRESS_BAR (w); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_solver), 0.0); - w = GTK_WIDGET (gtk_builder_get_object (gui, "progressbar")); - s->progress = GTK_PROGRESS_BAR (w); - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress), 0.0); + w = GTK_WIDGET (gtk_builder_get_object (gui, "progress_label_solver")); + s->progress_label_solver = GTK_LABEL (w); - w = GTK_WIDGET (gtk_builder_get_object (gui, "progress_label")); - s->progress_label = GTK_LABEL (w); + w = GTK_WIDGET (gtk_builder_get_object (gui, "progressbar_reader")); + s->progress_reader = GTK_PROGRESS_BAR (w); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_reader), 0.0); - g_signal_connect (G_OBJECT (s->dialog), "response", - G_CALLBACK (cancel_cb), s); + w = GTK_WIDGET (gtk_builder_get_object (gui, "progress_label_reader")); + s->progress_label_reader = 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) +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); + double p = 0; + oregano_engine_get_progress_solver (s->engine, &p); - if (p >= 1) p = 0; + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_solver), p); - gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress), p); + gchar *current_operation = oregano_engine_get_current_operation_solver(s->engine); + gchar *str = g_strdup_printf (_ ("Progress: <b>%s</b>"), current_operation); + g_free(current_operation); - str = g_strdup_printf (_("Progress: <b>%s</b>"), - oregano_engine_get_current_operation (s->engine)); - gtk_label_set_markup (s->progress_label, str); + gtk_label_set_markup (s->progress_label_solver, str); + g_free (str); + + p = 0; + oregano_engine_get_progress_reader (s->engine, &p); + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_reader), p); + + str = g_strdup_printf (_ ("Progress: <b>%s</b>"), + oregano_engine_get_current_operation_reader (s->engine)); + gtk_label_set_markup (s->progress_label_reader, str); g_free (str); return TRUE; } -static void -engine_done_cb (OreganoEngine *engine, Simulation *s) +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_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_solver), 1.0); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (s->progress_reader), 1.0); } gtk_widget_destroy (GTK_WIDGET (s->dialog)); @@ -181,57 +206,46 @@ engine_done_cb (OreganoEngine *engine, Simulation *s) plot_show (s->engine); - if (oregano_engine_has_warnings (s->engine)) - schematic_view_log_show (s->sv, FALSE); + if (oregano_engine_has_warnings (s->engine)) { + log_append (s->logstore, _ ("Simulation"), + _ ("Finished with warnings:")); // FIXME add actual warnings + } else { + log_append (s->logstore, _ ("Simulation"), _ ("Finished.")); + } + + // show log window if this is enabled in preferences + schematic_view_log_show (s->sv, FALSE); sheet_clear_op_values (schematic_view_get_sheet (s->sv)); - // I don't need the engine anymore. The plot window owns its reference to + // I don't need the engine anymore. The plot window owns its reference to // the engine g_object_unref (s->engine); s->engine = NULL; - } -static void -engine_aborted_cb (OreganoEngine *engine, Simulation *s) +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_get_log_window_exists (s->sv)) { - dialog = gtk_message_dialog_new_with_markup ( - GTK_WINDOW (schematic_view_get_toplevel (s->sv)), - 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\nWould 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); + // g_clear_object (&(s->dialog)); + if (s->dialog != NULL) { + gtk_widget_destroy (GTK_WIDGET (s->dialog)); + s->dialog = NULL; } + + log_append (s->logstore, _ ("Simulation"), _ ("Aborted. See lines below for details.")); + if (s->sv != NULL) + schematic_view_log_show (s->sv, TRUE); + + g_object_unref (s->engine); + s->engine = NULL; } -static void -cancel_cb (GtkWidget *widget, gint arg1, Simulation *s) +static void cancel_cb (GtkWidget *widget, gint arg1, Simulation *s) { g_return_if_fail (s != NULL); @@ -246,31 +260,30 @@ cancel_cb (GtkWidget *widget, gint arg1, Simulation *s) gtk_widget_destroy (GTK_WIDGET (s->dialog)); s->dialog = NULL; s->sv = NULL; + + log_append (s->logstore, _ ("Simulation"), _ ("Canceled.")); } -static gboolean -simulate_cmd (Simulation *s) +static gboolean simulate_cmd (Simulation *s) { OreganoEngine *engine; if (s->engine != NULL) { - g_object_unref (G_OBJECT (s->engine)); - s->engine = NULL; + g_clear_object (&(s->engine)); } engine = oregano_engine_factory_create_engine (oregano.engine, s->sm); + if (!engine) + return FALSE; + s->engine = engine; - s->progress_timeout_id = g_timeout_add (100, - (GSourceFunc)progress_bar_timeout_cb, s); + s->progress_timeout_id = g_timeout_add (250, (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); + 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); oregano_engine_start (engine); return TRUE; } - diff --git a/src/simulation.h b/src/simulation.h index f8ce7cf..4dca06c 100644 --- a/src/simulation.h +++ b/src/simulation.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Guido Trentalancia <guido@trentalancia.com> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2017 Guido Trentalancia * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __SIMULATION_H @@ -41,94 +43,112 @@ typedef struct _SimulationData SimulationData; typedef enum { - OP_POINT , - TRANSIENT , - DC_TRANSFER , - AC , - TRANSFER , - DISTORTION , - NOISE , - POLE_ZERO , - SENSITIVITY , - FOURIER , - ANALYSIS_UNKNOWN + ANALYSIS_TYPE_NONE, + ANALYSIS_TYPE_OP_POINT, + ANALYSIS_TYPE_TRANSIENT, + ANALYSIS_TYPE_DC_TRANSFER, + ANALYSIS_TYPE_AC, + ANALYSIS_TYPE_TRANSFER, + ANALYSIS_TYPE_DISTORTION, + ANALYSIS_TYPE_NOISE, + ANALYSIS_TYPE_POLE_ZERO, + ANALYSIS_TYPE_SENSITIVITY, + ANALYSIS_TYPE_FOURIER, + ANALYSIS_TYPE_UNKNOWN } AnalysisType; #define INFINITE 1e50f +//keep in mind the relation to global variable +//const char const *SimulationFunctionTypeString[] +//in simulation.c (strings representing the functions in GUI) typedef enum { - FUNCTION_MINUS = 0, - FUNCTION_TRANSFER + FUNCTION_SUBTRACT = 0, + FUNCTION_DIVIDE } SimulationFunctionType; -typedef struct _SimulationFunction { +typedef struct _SimulationFunction +{ SimulationFunctionType type; guint first; guint second; } SimulationFunction; -struct _SimulationData { +struct _SimulationData +{ AnalysisType type; - gint state; - gint n_variables; - gchar **var_names; - gchar **var_units; - GArray **data; - gdouble *min_data; - gdouble *max_data; - gint got_var; - gint got_points; + gint n_variables; + gchar **var_names; + gchar **var_units; + GArray **data; + gdouble *min_data; + gdouble *max_data; + gint got_var; + gint got_points; // Functions typeof SimulationFunction - GList *functions; + GList *functions; }; - -typedef struct { +typedef struct +{ SimulationData sim_data; - int state; + int state; } SimOp; -typedef struct { +typedef struct +{ SimulationData sim_data; - double freq; - gint nb_var; + double freq; + gint nb_var; } SimFourier; -typedef struct { +typedef struct +{ SimulationData sim_data; - int state; - double sim_length; - double step_size; + int state; + double sim_length; + double step_size; } SimTransient; -typedef struct { +typedef struct +{ SimulationData sim_data; - int state; - double sim_length; - double start,stop; + int state; + double sim_length; + double start, stop; } SimAC; -typedef struct { +typedef struct +{ SimulationData sim_data; - int state; - double sim_length; - double start,stop,step; + int state; + double sim_length; + double start, stop, step; } SimDC; -typedef union { - SimOp op; +typedef struct +{ + SimulationData sim_data; + int state; + double sim_length; + double start, stop; +} SimNoise; + +typedef union +{ + SimOp op; SimTransient transient; - SimFourier fourier; - SimAC ac; - SimDC dc; + SimFourier fourier; + SimAC ac; + SimDC dc; + SimNoise noise; } Analysis; -void simulation_show (GtkWidget *widget, SchematicView *sv); -gpointer simulation_new (Schematic *sm); -gchar *sim_engine_analysis_name (SimulationData *); +void simulation_show_progress_bar (GtkWidget *widget, SchematicView *sv); +gpointer simulation_new (Schematic *sm, Log *logstore); -#define SIM_DATA(obj) ((SimulationData *)(obj)) -#define ANALYSIS(obj) ((Analysis *)(obj)) +#define SIM_DATA(obj) ((SimulationData *)(obj)) +#define ANALYSIS(obj) ((Analysis *)(obj)) #endif /* __SIMULATION_H */ diff --git a/src/splash.c b/src/splash.c index 8e211d1..367ea4e 100644 --- a/src/splash.c +++ b/src/splash.c @@ -2,15 +2,17 @@ * splash.c * * - * Author: + * Authors: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * Bernhard Schuster <bernhard@ahoi.io> * - * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2006 Ricardo Markiewicz + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2013 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -24,19 +26,18 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ - #include <unistd.h> #include <glib/gi18n.h> #include "splash.h" #include "dialogs.h" +#include "errors.h" -static void -oregano_splash_destroy (GtkWidget *w, GdkEvent *event, Splash *sp) +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) @@ -44,36 +45,20 @@ oregano_splash_destroy (GtkWidget *w, GdkEvent *event, Splash *sp) } } -Splash * -oregano_splash_new () +Splash *oregano_splash_new (GError **error) { GtkBuilder *gui; - GError *perror = NULL; Splash *sp; GtkEventBox *event; - gchar *msg; - + if ((gui = gtk_builder_new ()) == NULL) { - oregano_error (_("Could not create splash message.")); - return NULL; - } - else gtk_builder_set_translation_domain (gui, NULL); - - if (!g_file_test (OREGANO_UIDIR "/splash.ui", G_FILE_TEST_EXISTS) || - !g_file_test (OREGANO_UIDIR "/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_UIDIR "/splash.ui", OREGANO_UIDIR "/splash.xpm"); - oregano_error_with_title (_("Could not create splash message."), msg); - g_free (msg); + g_set_error_literal (error, OREGANO_ERROR, OREGANO_UI_ERROR_NO_BUILDER, + _ ("Failed to spawn builder")); return NULL; } - - if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/splash.ui", - &perror) <= 0) { - msg = perror->message; - oregano_error_with_title (_("Could not create splash message."), msg); - g_error_free (perror); + gtk_builder_set_translation_domain (gui, NULL); + + if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/splash.ui", error) <= 0) { return NULL; } @@ -81,6 +66,7 @@ oregano_splash_new () sp->can_destroy = FALSE; sp->win = GTK_WINDOW (gtk_builder_get_object (gui, "splash")); + gtk_window_set_keep_above (sp->win, TRUE); sp->lbl = GTK_LABEL (gtk_builder_get_object (gui, "label")); sp->progress = GTK_WIDGET (gtk_builder_get_object (gui, "pbar")); @@ -95,8 +81,7 @@ oregano_splash_new () return sp; } -gboolean -oregano_splash_free (Splash *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); @@ -105,8 +90,7 @@ oregano_splash_free (Splash *sp) return FALSE; } -void -oregano_splash_step (Splash *sp, char *s) +void oregano_splash_step (Splash *sp, char *s) { gtk_label_set_text (sp->lbl, s); gtk_progress_bar_pulse (GTK_PROGRESS_BAR (sp->progress)); @@ -114,12 +98,9 @@ oregano_splash_step (Splash *sp, char *s) gtk_main_iteration (); } - -void -oregano_splash_done (Splash *sp, char *s) +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 index 2075779..90ae445 100644 --- a/src/splash.h +++ b/src/splash.h @@ -5,8 +5,8 @@ * Author: * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> - * - * Web page: https://github.com/marc-lorber/oregano + * + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -24,8 +24,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef _OREGANO_SPLASH_H_ @@ -35,15 +35,16 @@ typedef struct _Splash Splash; -struct _Splash { +struct _Splash +{ GtkWindow *win; GtkWidget *progress; GtkWidget *event; - GtkLabel *lbl; + GtkLabel *lbl; gboolean can_destroy; }; -Splash *oregano_splash_new (); +Splash *oregano_splash_new (GError **error); gboolean oregano_splash_free (Splash *); void oregano_splash_step (Splash *, char *s); void oregano_splash_done (Splash *, char *s); diff --git a/src/stock.c b/src/stock.c index 0564aa1..b86c478 100644 --- a/src/stock.c +++ b/src/stock.c @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /* * Stock icon code, stolen from: @@ -57,15 +57,14 @@ #include "stock/zoom_pan.xpm" #include "stock/zoom_region.xpm" -static void -add_stock_entry (const gchar *stock_id, char **xpm_data) +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 (); + factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (factory); } @@ -76,8 +75,7 @@ add_stock_entry (const gchar *stock_id, char **xpm_data) g_object_unref (G_OBJECT (pixbuf)); } -void -stock_init (void) +void stock_init (void) { add_stock_entry (STOCK_PIXMAP_SIM_SETTINGS, sim_settings_xpm); add_stock_entry (STOCK_PIXMAP_ROTATE, rotate_xpm); diff --git a/src/stock.h b/src/stock.h index ff5e396..8fb4b80 100644 --- a/src/stock.h +++ b/src/stock.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __STOCK_H diff --git a/src/stock/Makefile.am b/src/stock/Makefile.am deleted file mode 100644 index e4a0270..0000000 --- a/src/stock/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -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/tools/cancel-info.c b/src/tools/cancel-info.c new file mode 100644 index 0000000..5abeec6 --- /dev/null +++ b/src/tools/cancel-info.c @@ -0,0 +1,79 @@ +/* + * cancel-info.c + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include "cancel-info.h" + +struct _CancelInfo { + guint ref_count; + GMutex mutex; + gboolean is_cancel; +}; + +static void cancel_info_finalize(CancelInfo *info); + +CancelInfo *cancel_info_new() { + CancelInfo *info = g_new0(CancelInfo, 1); + info->ref_count = 1; + g_mutex_init(&info->mutex); + + return info; +} + +void cancel_info_subscribe(CancelInfo *info) { + g_mutex_lock(&info->mutex); + info->ref_count++; + g_mutex_unlock(&info->mutex); +} + +void cancel_info_unsubscribe(CancelInfo *info) { + g_mutex_lock(&info->mutex); + info->ref_count--; + guint ref_count = info->ref_count; + g_mutex_unlock(&info->mutex); + + if (ref_count == 0) + cancel_info_finalize(info); +} + +gboolean cancel_info_is_cancel(CancelInfo *info) { + g_mutex_lock(&info->mutex); + gboolean is_cancel = info->is_cancel; + g_mutex_unlock(&info->mutex); + + return is_cancel; +} + +void cancel_info_set_cancel(CancelInfo *info) { + g_mutex_lock(&info->mutex); + info->is_cancel = TRUE; + g_mutex_unlock(&info->mutex); +} + +static void cancel_info_finalize(CancelInfo *info) { + g_mutex_clear(&info->mutex); + g_free(info); +} diff --git a/src/model/sheet-pos.h b/src/tools/cancel-info.h index bfda9e0..6b99a22 100644 --- a/src/model/sheet-pos.h +++ b/src/tools/cancel-info.h @@ -1,18 +1,12 @@ /* - * sheet-pos.h + * cancel-info.h * * * Authors: - * Richard Hult <rhult@hem.passagen.se> - * Ricardo Markiewicz <rmarkie@fi.uba.ar> - * Andres de Barbara <adebarbara@fi.uba.ar> - * Marc Lorber <lorber.marc@wanadoo.fr> + * Michi <st101564@stud.uni-stuttgart.de> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * - * Copyright (C) 1999-2001 Richard Hult - * Copyright (C) 2003,2004 Ricardo Markiewicz - * Copyright (C) 2009-2012 Marc Lorber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,17 +20,19 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ -#ifndef __SHEET_POS_H -#define __SHEET_POS_H -#include <gtk/gtk.h> -typedef struct _SheetPos SheetPos; -struct _SheetPos { - gdouble x; - gdouble y; -}; +#ifndef TOOLS_CANCEL_INFO_H_ +#define TOOLS_CANCEL_INFO_H_ -#endif +typedef struct _CancelInfo CancelInfo; + +CancelInfo *cancel_info_new(); +void cancel_info_subscribe(CancelInfo *info); +void cancel_info_unsubscribe(CancelInfo *info); +gboolean cancel_info_is_cancel(CancelInfo *info); +void cancel_info_set_cancel(CancelInfo *info); + +#endif /* TOOLS_CANCEL_INFO_H_ */ diff --git a/src/tools/thread-pipe.c b/src/tools/thread-pipe.c new file mode 100644 index 0000000..8f714ef --- /dev/null +++ b/src/tools/thread-pipe.c @@ -0,0 +1,600 @@ +/** + * thread-pipe.c + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * + * BASICS + * ------ + * ThreadPipe can be used to efficiently communicate large + * or small data between 2 threads, whereas the data pops up + * bit by bit (or at once). + * It consists of a chain of data blocks. New + * data is appended at the one end of the chain, old data + * can be read at the other end of the pipe. + * + * If there is data (chain has more than 1 chain link), the + * reader gets to know this by locking a mutex and asking + * the state of a shared variable. + * + * If all data is read and the reader needs more data, he + * will wait until new data is available by locking a mutex + * and waiting for a conditional signal of the writer. + * + * + * Locking, unlocking and signaling can slow down the + * communication process very hard, if the data blocks are + * small. To solve this problem, an additional buffer is + * introduced. + * + * BUFFERED VERSION + * ---------------- + * The problem of unbuffered ThreadPipe is that locking + * and unlocking is a very heavy work that should not + * be done too frequently. Now if the data blocks pushed + * to ThreadPipe are too small, the lock operation is + * done very often. However the positive side of small blocks + * is the real time ability. + * + * If you need only weak real time or no real time but an + * efficient use of time and money, you should use the + * buffered version of thread pipe with large (but not too + * large) buffer numbers (you can use a buffer by setting + * the buffer numbers in the constructor unequal 1). + * + * There are some approaches how to realize a buffer in the + * case of thread pipe: + * - melting incoming blocks together with realloc, + * counting the number of bytes or melting operations, + * release the block for popping when number is large enough, + * - appending incoming blocks together, + * counting the number of bytes or/and number of blocks not released, + * release the block queue if number is large enough, + * - counting time instead of space, + * - other shit + * + * Because blocks are structure elements that can be used to + * make the code more efficient AND melting operations + * can be heavy work, I think that simply appending some + * blocks and not locking/releasing every single block is + * a good way to go. Counting time instead of space may be + * a good approach for real time applications that want to + * be also efficient. But because real time is not demanded + * in a non real time simulation like ngspice or gnucap, we don't + * need to bother. But you can set the buffer numbers to 1 + * and then you can use thread pipe in real time applications, + * that have far too much nop-time and really small amounts of + * data workload. + * + * Furthermore I have decided to count the number of blocks + * and the total size of not released blocks. When one of + * the counting numbers are larger than their corresponding + * buffer constants, the whole pipe queue is released for + * popping (what needs a small locking access). + * + * You have some possibilities to define the buffer constants: + * - define statements in thread-pipe.h + * - telling the constructor function what are your wishes + * + * THE FUTURE + * ---------- + * An enhanced version of this could be an adaptive buffer size + * decision maker that detects the frequency of collisions + * and uses this information to adapt the optimal buffer size + * as a function of time, by extrapolating the behavior of + * the program like branch prediction technology that is used in + * processors. A good version of this could be real time capable + * in low workload times and would be optimal for time, space + * and money saving in times of high workload. + * + */ + +#include <glib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include "thread-pipe.h" + +typedef struct _ThreadPipeData ThreadPipeData; + +/** + * chain link/chain element + */ +struct _ThreadPipeData { + // introduced for efficient string support + gpointer malloc_address; + // data block + gpointer data; + // size of data block + gsize size; + // link to next chain element + ThreadPipeData *next; +}; + +/** + * structuring structure + */ +typedef struct { + // closing/freeing information + gboolean write_eof; + gboolean read_eof; + + // buffer state information + gsize size_total; + gsize block_counter; +} ThreadPipeBufferData; + +struct _ThreadPipe { + /** + * variables of reading thread + */ + ThreadPipeData *read_data; + ThreadPipeBufferData read_buffer_data; + + /** + * variables of writing thread + */ + ThreadPipeData *write_data; + ThreadPipeBufferData write_buffer_data; + + /** + * shared variables + */ + ThreadPipeBufferData ready_buffer_data; + + /** + * synchronizing variables + */ + GMutex mutex; + GCond cond; + + /** + * read-only variables + */ + guint max_block_counter; + gsize max_size_total; +}; + +static ThreadPipeData *thread_pipe_data_new(gpointer data, gsize size); +static ThreadPipeData *thread_pipe_data_destroy(ThreadPipe *pipe); +static void thread_pipe_destroy(ThreadPipe *pipe); + +/** + * Creates a new ThreadPipe structure. + * + * I recommend using ThreadPipes like normal FIFO pipes, whereas one thread + * uses only read functions and another thread uses only write functions. + * The reading thread should make sure that read_eof will be set, + * the writing thread should make sure that write_eof will be set, + * because: + * + * ThreadPipes have 3 possibilities to close: + * 1. call thread_pipe_set_write_eof and after that read the pipe to the end + * 2. call thread_pipe_set_write_eof and after that call thread_pipe_set_read_eof + * 3. call thread_pipe_set_read_eof and after that call thread_pipe_set_write_eof + * + * Now if the creating thread of the ThreadPipe wants to close the ThreadPipe, + * and he did not touch any reading or writing functions, he does not know + * whether the pipe has been closed already automatically and will cause a + * segmentation fault eventually if he tries. + * + * @max_buffer_block_counter: 1 is the lowest number (you can't push no data). + * If 0, the default value from thread-pipe.h will be used. + * @max_buffer_block_counter: 1 is the lowest value (ThreadPipe does not allow + * to push blocks of size 0). If 0, the default value from thread-pipe.h + * will be used. + * + * returns a new ThreadPipe structure + */ +ThreadPipe *thread_pipe_new( + guint max_buffer_block_counter, + gsize max_buffer_size_total) { + ThreadPipe *thread_pipe = g_new0(ThreadPipe, 1); + + g_mutex_init(&thread_pipe->mutex); + g_cond_init(&thread_pipe->cond); + + ThreadPipeData *pipe_data = thread_pipe_data_new(NULL, 0); + thread_pipe->read_data = pipe_data; + thread_pipe->write_data = pipe_data; + + thread_pipe->max_block_counter = max_buffer_block_counter != 0 ? max_buffer_block_counter : THREAD_PIPE_MAX_BUFFER_BLOCK_COUNTER_DEFAULT; + thread_pipe->max_size_total = max_buffer_size_total != 0 ? max_buffer_size_total : THREAD_PIPE_MAX_BUFFER_SIZE_TOTAL_DEFAULT; + + return thread_pipe; +} + +/** + * Pushes a block of size size to the end of the pipe. The data is copied + * to heap. + * + * Don't push, if you set write_eof already. + * + * return value will be NULL, if + * - read_eof has been set by thread_pipe_set_read_eof or + * - write_eof has been set by thread_pipe_set_write_eof (with fail message) or + * - pipe is NULL (with fail message). + * + * If read_eof has been set by thread_pipe_set_read_eof before, you can close + * the pipe by setting write_eof and you will save much time and money, + * so be sure to check return value every call. But you can also call this + * function if it makes no sense. The function recognizes then that + * pushing makes no sense and returns fast. The function is not closing the + * pipe automatically in this case because it is safer for example if the + * programmer does not check the return value. + * + * returns TRUE or FALSE + * - FALSE, if the pipe has been closed by read_eof or other events that + * indicate that you can close the pipe by setting write_eof + * - TRUE, if the pipe is living further on and it makes sense that it + * will live further on + */ +gboolean thread_pipe_push(ThreadPipe *pipe, gpointer data, gsize size) { + // Give me an object. + g_return_val_if_fail(pipe != NULL, FALSE); + + // Don't push, if you set write_eof already. + g_return_val_if_fail(pipe->write_buffer_data.write_eof != TRUE, FALSE); + + // Don't push no data to pipe. + g_return_val_if_fail(data != NULL, !pipe->write_buffer_data.read_eof); + g_return_val_if_fail(size != 0, !pipe->write_buffer_data.read_eof); + + // pipe not active any more because no reader has interest. + if (pipe->write_buffer_data.read_eof) + return FALSE; + + + pipe->write_data->next = thread_pipe_data_new(data, size); + pipe->write_data = pipe->write_data->next; + + pipe->write_buffer_data.block_counter++; + pipe->write_buffer_data.size_total += size; + + if (pipe->write_buffer_data.block_counter < pipe->max_block_counter + && pipe->write_buffer_data.size_total < pipe->max_size_total) + return TRUE; + + + g_mutex_lock(&pipe->mutex); + + pipe->ready_buffer_data.block_counter += pipe->write_buffer_data.block_counter; + pipe->write_buffer_data.block_counter = 0; + + pipe->ready_buffer_data.size_total += pipe->write_buffer_data.size_total; + pipe->write_buffer_data.size_total = 0; + + pipe->write_buffer_data.read_eof = pipe->ready_buffer_data.read_eof; + + g_cond_signal(&pipe->cond); + g_mutex_unlock(&pipe->mutex); + + return !pipe->write_buffer_data.read_eof; +} + +/** + * Reads a block of memory that has been pushed earlier by thread_pipe_push. + * If there is no block that has been pushed earlier, this function will + * wait until a block will be pushed or write_eof will be set. + * + * pipe will be destroyed automatically if write_eof has been set && pipe is + * empty, so be sure to check return value always! + * + * The data, stored to data_out, will be freed in the next thread_pipe_pop call, + * so be sure to copy the data, if you need it longer than one thread_pipe_pop + * cycle. + * + * Don't pop, if you set read_eof already. + * + * returns pipe or NULL + * - NULL, if the pipe has been destroyed + * - pipe, if the pipe is living further on + */ +ThreadPipe *thread_pipe_pop(ThreadPipe *pipe, gpointer *data_out, gsize *size) { + g_return_val_if_fail(pipe != NULL, pipe); + g_return_val_if_fail(data_out != NULL, pipe); + g_return_val_if_fail(size != NULL, pipe); + //Don't pop, if you set read_eof already. + g_return_val_if_fail(pipe->read_buffer_data.read_eof != TRUE, NULL); + + *data_out = NULL; + *size = 0; + + if (pipe->read_buffer_data.block_counter <= 0) { + + g_mutex_lock(&pipe->mutex); + + while (pipe->ready_buffer_data.block_counter <= 0 && !pipe->ready_buffer_data.write_eof) + g_cond_wait(&pipe->cond, &pipe->mutex); + + pipe->read_buffer_data.block_counter = pipe->ready_buffer_data.block_counter; + pipe->ready_buffer_data.block_counter = 0; + + pipe->read_buffer_data.size_total = pipe->ready_buffer_data.size_total; + pipe->ready_buffer_data.size_total = 0; + + pipe->read_buffer_data.write_eof = pipe->ready_buffer_data.write_eof; + + g_mutex_unlock(&pipe->mutex); + + } + + if (!thread_pipe_data_destroy(pipe)) { + thread_pipe_destroy(pipe); + return NULL; + } + *data_out = pipe->read_data->data; + *size = pipe->read_data->size; + + return pipe; +} + +/** + * Reads to the end of a line like fgets and pops it, or reads to the end of pipe. + * + * size_out will be the length + 1 of the string like strlen. + * + * Pushed data blocks should be 0 terminated, but don't have to. + * + * possible independent cases: + * - newline at position of block, where position in Po := {nowhere, beginning/middle, end} + * - newline at is_first block, where is_first in If := {first, not first} + * - newline at is_last block, where is_last in Il := {last, not last} + * In total there are 12 pairwise unequal cases by forming the Cartesian product of Po, If and Il + * and adding the case where there is no newline at all. + * + * returns pipe or NULL like thread_pipe_pop + */ +ThreadPipe *thread_pipe_pop_line(ThreadPipe *pipe_in, gchar **string_out, gsize *size_out) { + g_return_val_if_fail(pipe_in != NULL, pipe_in); + g_return_val_if_fail(string_out != NULL, pipe_in); + g_return_val_if_fail(size_out != NULL, pipe_in); + //Don't pop, if you set read_eof already. + g_return_val_if_fail(pipe_in->read_buffer_data.read_eof != TRUE, NULL); + + + *string_out = NULL; + *size_out = 0; + + size_t line_size; + gchar *line; + FILE *line_file = open_memstream(&line, &line_size); + + ThreadPipeData *current = NULL; + gchar *ptr = NULL; + + while (TRUE) { + + ptr = NULL; + + if (pipe_in->read_buffer_data.block_counter <= 0) { + + g_mutex_lock(&pipe_in->mutex); + + while (pipe_in->ready_buffer_data.block_counter <= 0 && !pipe_in->ready_buffer_data.write_eof) + g_cond_wait(&pipe_in->cond, &pipe_in->mutex); + + pipe_in->read_buffer_data.block_counter = pipe_in->ready_buffer_data.block_counter; + pipe_in->ready_buffer_data.block_counter = 0; + + pipe_in->read_buffer_data.size_total = pipe_in->ready_buffer_data.size_total; + pipe_in->ready_buffer_data.size_total = 0; + + pipe_in->read_buffer_data.write_eof = pipe_in->ready_buffer_data.write_eof; + + g_mutex_unlock(&pipe_in->mutex); + + } + + current = pipe_in->read_data->next; + + + if (current == NULL) + break; + + ptr = current->data; + while (*ptr != '\n' && + *ptr != 0 && + ptr - (gchar *)current->data < current->size //somebody forgot to close the string with 0? + ) { + fputc(*ptr, line_file); + ptr++; + } + + if (ptr - (gchar *)current->data < current->size && *ptr == '\n') { + fputc(*ptr, line_file); + ptr++; + break; + } + + thread_pipe_data_destroy(pipe_in); + } + + fputc(0, line_file); + fclose(line_file); + + if (current == NULL) { + + if (line_size == 1) { + g_free(line); + thread_pipe_destroy(pipe_in); + return NULL; + } + + } else { + + gchar **current_data = (gchar **)¤t->data; + gsize *current_size = ¤t->size; + + *current_size -= (ptr - *current_data); + pipe_in->read_buffer_data.size_total -= (ptr - *current_data); + *current_data = ptr; + + if (*current_size == 0 || (*current_size == 1 && *ptr == 0)) + thread_pipe_data_destroy(pipe_in); + + } + + /** + * current == NULL && line_size != 1 + * || + * current != NULL + */ + + gchar **old_data = (gchar **)&pipe_in->read_data->malloc_address; + gsize *old_size = &pipe_in->read_data->size; + + g_free(*old_data); + *old_data = line; + *old_size = line_size; + + *string_out = *old_data; + *size_out = *old_size; + + return pipe_in; +} + +/** + * If you are finished with writing, you have to set write_eof so that the + * memory of pipe can be freed. You can set write_eof independent of + * - read_eof + * - emptiness of pipe + * - other shit + * + * Don't push, after you called thread_pipe_set_write_eof. + * + * The memory of data and pipe will be freed, if read_eof && write_eof == TRUE. + */ +void thread_pipe_set_write_eof(ThreadPipe *pipe) { + g_return_if_fail(pipe != NULL); + g_return_if_fail(pipe->write_buffer_data.write_eof != TRUE); + + g_mutex_lock(&pipe->mutex); + gboolean destroy = pipe->ready_buffer_data.read_eof; + + pipe->ready_buffer_data.write_eof = TRUE; +// pipe->read_buffer_data.write_eof = TRUE; + pipe->write_buffer_data.write_eof = TRUE; + + pipe->ready_buffer_data.block_counter += pipe->write_buffer_data.block_counter; + pipe->write_buffer_data.block_counter = 0; + + pipe->ready_buffer_data.size_total += pipe->write_buffer_data.size_total; + pipe->write_buffer_data.size_total = 0; + + g_cond_signal(&pipe->cond); + g_mutex_unlock(&pipe->mutex); + + if (destroy) + thread_pipe_destroy(pipe); +} + +/** + * If you are finished with reading, you have to set read_eof so that the + * memory of pipe can be freed. You can set read_eof independent of + * - write_eof + * - emptiness of pipe + * - other shit + * + * Don't pop, after you called thread_pipe_set_read_eof. + * + * The memory of data waiting in pipe to be popped, will all be freed. + * + * The memory of pipe will be freed also, if write_eof && write_eof == TRUE. + */ +void thread_pipe_set_read_eof(ThreadPipe *pipe) { + g_return_if_fail(pipe != NULL); + g_return_if_fail(pipe->read_buffer_data.read_eof != TRUE); + + g_mutex_lock(&pipe->mutex); + gboolean destroy = pipe->ready_buffer_data.write_eof; + pipe->ready_buffer_data.read_eof = TRUE; + pipe->read_buffer_data.read_eof = TRUE; +// pipe->write_buffer_data.read_eof = TRUE; + + pipe->read_buffer_data.write_eof = pipe->ready_buffer_data.write_eof; + + pipe->read_buffer_data.block_counter += pipe->ready_buffer_data.block_counter; + pipe->ready_buffer_data.block_counter = 0; + + pipe->read_buffer_data.size_total += pipe->ready_buffer_data.size_total; + pipe->ready_buffer_data.size_total = 0; + + while (pipe->read_buffer_data.block_counter) + thread_pipe_data_destroy(pipe); + g_mutex_unlock(&pipe->mutex); + + if (destroy) + thread_pipe_destroy(pipe); +} + +/** + * copy data to a new ThreadPipeData structure + */ +static ThreadPipeData *thread_pipe_data_new(gpointer data, gsize size) { + ThreadPipeData *pipe_data = g_new0(ThreadPipeData, 1); + if (data != NULL && size != 0) { + pipe_data->malloc_address = g_malloc(size); + memcpy(pipe_data->malloc_address, data, size); + pipe_data->data = pipe_data->malloc_address; + pipe_data->size = size; + } + + return pipe_data; +} + +/** + * free all memory of a ThreadPipeData structure + * + * returns + * - the next block of the linked list, if there is one + * - else NULL + */ +static ThreadPipeData *thread_pipe_data_destroy(ThreadPipe *pipe) { + ThreadPipeData *pipe_data = pipe->read_data; + ThreadPipeData *next = pipe_data->next; + g_free(pipe_data->malloc_address); + g_free(pipe_data); + if (next != NULL) { + pipe->read_buffer_data.block_counter--; + pipe->read_buffer_data.size_total -= next->size; + } + pipe->read_data = next; + return next; +} + +/** + * free all memory of a ThreadPipe structure + */ +static void thread_pipe_destroy(ThreadPipe *pipe) { + g_return_if_fail(pipe != NULL); + + while (pipe->read_data) + thread_pipe_data_destroy(pipe); + g_mutex_clear(&pipe->mutex); + g_cond_clear(&pipe->cond); + g_free(pipe); +} diff --git a/src/tools/thread-pipe.h b/src/tools/thread-pipe.h new file mode 100644 index 0000000..1a93c7f --- /dev/null +++ b/src/tools/thread-pipe.h @@ -0,0 +1,57 @@ +/* + * thread-pipe.h + * + * + * Authors: + * Michi <st101564@stud.uni-stuttgart.de> + * + * Web page: https://ahoi.io/project/oregano + * + * + * 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., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef THREAD_PIPE_H_ +#define THREAD_PIPE_H_ + +#define THREAD_PIPE_MAX_BUFFER_BLOCK_COUNTER_DEFAULT 20 +#define THREAD_PIPE_MAX_BUFFER_SIZE_TOTAL_DEFAULT 2048 + +typedef struct _ThreadPipe ThreadPipe; + +/** + * Constructor + */ +ThreadPipe *thread_pipe_new( + guint max_buffer_block_counter, + gsize max_buffer_size_total); + +/** + * functions for writing thread + */ +gboolean thread_pipe_push(ThreadPipe *pipe, gpointer data, gsize size); +// Destructor +void thread_pipe_set_write_eof(ThreadPipe *pipe); + +/** + * functions for reading thread + */ +ThreadPipe *thread_pipe_pop(ThreadPipe *pipe, gpointer *data_out, gsize *size); +ThreadPipe *thread_pipe_pop_line(ThreadPipe *pipe, gchar **data_out, gsize *size); +// Destructor +void thread_pipe_set_read_eof(ThreadPipe *pipe); + +#endif /* THREAD_PIPE_H_ */ diff --git a/src/wscript b/src/wscript new file mode 100644 index 0000000..0243dae --- /dev/null +++ b/src/wscript @@ -0,0 +1,49 @@ +#! /usr/bin/env python3 +# encoding: utf-8 + +import os +from waflib import Logs as logs +from waflib import Utils as utils + +def options(opt): + pass + +def configure(conf): + pass + +def build(bld): + if bld.cmd == 'install' or bld.cmd == 'uninstall': + if not os.geteuid()==0: + logs.warn ('You most likely need root privileges to install or uninstall properly.') + + + nodes = bld.path.ant_glob(\ + ['*.c', + 'tools/*.c', + 'gplot/*.c', + 'engines/*.c', + 'sheet/*.c', + 'model/*.c'], + excl='main.c') + + bld.objects ( + ['c','glib2'], + source = nodes, + includes = ['.', 'tools/', 'engines/', 'gplot/', 'model/', 'sheet/'], + uselib = 'M XML GOBJECT GLIB GTK3 XML GOOCANVAS GTKSOURCEVIEW3', + target = 'shared_objects' + ) + + exe = bld.program( + features = ['c', 'glib2'], + target = bld.env.appname, + source = ['main.c'], + includes = ['.', 'tools/', 'engines/', 'gplot/', 'model/', 'sheet/'], + use = 'shared_objects', + uselib = 'M XML GOBJECT GLIB GTK3 XML GOOCANVAS GTKSOURCEVIEW3', + settings_schema_files = ['../data/settings/'+bld.env.gschema_name ] if not bld.options.no_install_gschema else [], + install_path = "${BINDIR}" + ) + + for item in exe.includes: + logs.debug(item) diff --git a/src/xml-compat.h b/src/xml-compat.h index 94baf24..a16e8b1 100644 --- a/src/xml-compat.h +++ b/src/xml-compat.h @@ -8,7 +8,7 @@ * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz @@ -26,8 +26,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __XML_COMPAT_H @@ -40,4 +40,3 @@ #define childs children #endif /* __XML_COMPAT_H */ - diff --git a/src/xml-helper.c b/src/xml-helper.c index e9da224..24f23c7 100644 --- a/src/xml-helper.c +++ b/src/xml-helper.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> * - * Web page: https://github.com/marc-lorber/oregano + * Web page: https://ahoi.io/project/oregano * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #include "xml-compat.h" @@ -36,37 +38,43 @@ // 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) +gboolean oreganoXmlSAXParseFile (xmlSAXHandlerPtr sax, gpointer user_data, const gchar *filename) { - int ret = 0; + g_return_val_if_fail (filename != NULL, FALSE); + + gboolean parser_failed, ret = TRUE; xmlParserCtxtPtr ctxt; ctxt = xmlCreateFileParserCtxt (filename); - if (ctxt == NULL) return -1; + if (ctxt == NULL) + return FALSE; + ctxt->sax = sax; ctxt->userData = user_data; #if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20000 xmlKeepBlanksDefault (0); #endif - xmlParseDocument (ctxt); + parser_failed = FALSE; + if (xmlParseDocument (ctxt) < 0) { + // FIXME post a message to the log buffer with as much details as possible + g_message ("Failed to parse \"%s\"", filename); + ret = FALSE; + parser_failed = TRUE; + } else { + ret = ctxt->wellFormed ? TRUE : FALSE; + if (sax != NULL) + ctxt->sax = NULL; + } - if (ctxt->wellFormed) - ret = 0; - else - ret = -1; - if (sax != NULL) - ctxt->sax = NULL; - xmlFreeParserCtxt (ctxt); + if (!parser_failed) + 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) +void xmlSetCoordinate (xmlNodePtr node, const char *name, double x, double y) { xmlNodePtr child; gchar *str; @@ -91,12 +99,9 @@ xmlSetCoordinate (xmlNodePtr node, const char *name, double x, double y) 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) +void xmlSetCoordinates (xmlNodePtr node, const char *name, double x1, double y1, double x2, + double y2) { xmlNodePtr child; gchar *str; @@ -121,10 +126,9 @@ xmlSetCoordinates (xmlNodePtr node, const char *name, g_free (str); } -// Set a string value for a node either carried as an attibute or as the +// 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) +void xmlSetValue (xmlNodePtr node, const char *name, const char *val) { xmlChar *ret; xmlNodePtr child; @@ -147,15 +151,14 @@ xmlSetValue (xmlNodePtr node, const char *name, const char *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) +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); + ret = xmlGetProp (node, BAD_CAST name); if (ret != NULL) { xmlSetProp (node, BAD_CAST name, BAD_CAST str); return; @@ -171,17 +174,15 @@ xmlSetIntValue (xmlNodePtr node, const char *name, int val) 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) +void xmlSetDoubleValue (xmlNodePtr node, const char *name, double val) { xmlChar *ret; xmlNodePtr child; char str[101]; - snprintf (str, 100, "%g", (float) val); + snprintf (str, 100, "%g", (float)val); ret = xmlGetProp (node, BAD_CAST name); if (ret != NULL) { xmlSetProp (node, BAD_CAST name, BAD_CAST str); diff --git a/src/xml-helper.h b/src/xml-helper.h index 41942d3..6111846 100644 --- a/src/xml-helper.h +++ b/src/xml-helper.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz <rmarkie@fi.uba.ar> * Andres de Barbara <adebarbara@fi.uba.ar> * Marc Lorber <lorber.marc@wanadoo.fr> + * Bernhard Schuster <bernhard@ahoi.io> + * + * Web page: https://ahoi.io/project/oregano * - * Web page: https://github.com/marc-lorber/oregano - * * Copyright (C) 1999-2001 Richard Hult * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber + * Copyright (C) 2014 Bernhard Schuster * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,8 +28,8 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ #ifndef __XML_HELPER_H #define __XML_HELPER_H @@ -36,8 +38,7 @@ #include "xml-compat.h" -int oreganoXmlSAXParseFile (xmlSAXHandlerPtr sax, - gpointer user_data, const gchar *filename); +gboolean oreganoXmlSAXParseFile (xmlSAXHandlerPtr sax, gpointer user_data, const gchar *filename); void xmlSetValue (xmlNodePtr node, const char *name, const char *val); @@ -47,7 +48,7 @@ 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); +void xmlSetCoordinates (xmlNodePtr node, const char *name, double x1, double y1, double x2, + double y2); #endif |