diff --git a/oregano-graphical-testing.ogv b/oregano-graphical-testing.ogv new file mode 100644 index 00000000..142b800a Binary files /dev/null and b/oregano-graphical-testing.ogv differ diff --git a/src/clipboard.c b/src/clipboard.c index 66c04b77..e335a549 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -33,6 +33,7 @@ #include #include "oregano.h" +#include "sheet.h" #include "sheet-item.h" #include "clipboard.h" diff --git a/src/debug.h b/src/debug.h index e9e4003d..2f71ace2 100644 --- a/src/debug.h +++ b/src/debug.h @@ -4,10 +4,12 @@ * * Authors: * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * * Copyright (C) 2012-2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -43,3 +45,27 @@ ##__VA_ARGS__); \ } \ } + +#define color_black "\033[00;30m" +#define color_red "\033[00;31m" +#define color_green "\033[00;32m" +#define color_orange "\033[00;33m" +#define color_blue "\033[00;34m" +#define color_magenta "\033[00;35m" +#define color_cyan "\033[00;36m" +#define color_lightgray "\033[00;37m" +#define color_darkgray "\033[01;30m" +#define color_lightred "\033[01;31m" +#define color_lightgreen "\033[01;32m" +#define color_yellow "\033[01;33m" +#define color_lightblue "\033[01;34m" +#define color_lightmagenta "\033[01;35m" +#define color_lightcyan "\033[01;36m" +#define color_white "\033[01;37m" + +#define color_restore "\033[00m" + +#define COLOR_SHEETITEM color_blue +#define COLOR_COORDS color_green +#define COLOR_CENTER color_red +#define COLOR_NORMAL color_restore diff --git a/src/engines/gnucap.c b/src/engines/gnucap.c index f7121534..d16929bc 100644 --- a/src/engines/gnucap.c +++ b/src/engines/gnucap.c @@ -231,7 +231,7 @@ static gboolean gnucap_generate_netlist (OreganoEngine *engine, const gchar *fil for (iter = output.models; iter; iter = iter->next) { gchar *model; model = (gchar *)iter->data; - g_fprintf (file, ".include %s/%s.model\n", OREGANO_MODELDIR, model); + g_fprintf (file, ".include %s%s.model\n", OREGANO_MODELDIR, model); } // Prints template parts diff --git a/src/load-library.h b/src/load-library.h index 03b836d0..9a3b6a5f 100644 --- a/src/load-library.h +++ b/src/load-library.h @@ -53,7 +53,6 @@ typedef enum { SYMBOL_OBJECT_LINE, SYMBOL_OBJECT_ARC, SYMBOL_OBJECT_TEXT } Symbo struct _SymbolObject { SymbolObjectType type; - union { struct @@ -79,5 +78,4 @@ struct _SymbolObject 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 8c19bd1e..81bc31c9 100644 --- a/src/load-schematic.c +++ b/src/load-schematic.c @@ -9,6 +9,7 @@ * Marc Lorber * Bernhard Schuster * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -17,6 +18,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -50,6 +52,7 @@ #include "errors.h" #include "engines/netlist-helper.h" +#include "part-private.h" #include "debug.h" typedef enum { @@ -219,7 +222,7 @@ static void create_textbox (ParseState *state) textbox = textbox_new (NULL); textbox_set_text (textbox, state->textbox_text); - item_data_set_pos (ITEM_DATA (textbox), &state->pos); + item_data_set_pos (ITEM_DATA (textbox), &state->pos, EMIT_SIGNAL_CHANGED); schematic_add_item (state->schematic, ITEM_DATA (textbox)); } @@ -236,7 +239,7 @@ static void create_wire (ParseState *state) wire = wire_new (); wire_set_length (wire, &length); - item_data_set_pos (ITEM_DATA (wire), &state->wire_start); + item_data_set_pos (ITEM_DATA (wire), &state->wire_start, EMIT_SIGNAL_CHANGED); schematic_add_item (state->schematic, ITEM_DATA (wire)); } @@ -251,13 +254,7 @@ static void create_part (ParseState *state) 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), ID_FLIP_HORIZ, NULL); - if (state->flip & ID_FLIP_VERT) - item_data_flip (ITEM_DATA (part), ID_FLIP_VERT, NULL); - + item_data_set_pos (ITEM_DATA (part), &state->pos, EMIT_SIGNAL_MOVED | EMIT_SIGNAL_CHANGED); schematic_add_item (state->schematic, ITEM_DATA (part)); } diff --git a/src/main.c b/src/main.c index b31aebe2..bb54e89d 100644 --- a/src/main.c +++ b/src/main.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -40,6 +42,8 @@ #include "options.h" #include "schematic.h" +#include "stack.h" + int main (int argc, char *argv[]) { // keep in mind the app is a subclass of GtkApplication @@ -75,6 +79,14 @@ int main (int argc, char *argv[]) // required? gtk_init (&argc, &argv); + undo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + if (!undo_stack) + return 1; + + redo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + if (!redo_stack) + return 1; + // required, as we possibly need signal // information within oregano.c _before_ the // first Schematic instance exists diff --git a/src/model/item-data.c b/src/model/item-data.c index c9391d46..1b91f80f 100644 --- a/src/model/item-data.c +++ b/src/model/item-data.c @@ -9,6 +9,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -16,6 +17,7 @@ * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -39,7 +41,9 @@ #include "item-data.h" #include "node-store.h" +#include "sheet.h" #include "debug.h" +#include "stack.h" static void item_data_class_init (ItemDataClass *klass); static void item_data_init (ItemData *item_data); @@ -52,10 +56,12 @@ static void item_data_copy (ItemData *dest, ItemData *src); enum { ARG_0, ARG_STORE, ARG_POS }; enum { + CREATED, MOVED, ROTATED, FLIPPED, CHANGED, // used to notify the view to reset and recalc the transformation + DELETED, HIGHLIGHT, LAST_SIGNAL }; @@ -132,18 +138,28 @@ static void item_data_class_init (ItemDataClass *klass) object_class->dispose = item_data_dispose; object_class->finalize = item_data_finalize; + item_data_signals[CREATED] = + g_signal_new ("created", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ItemDataClass, created), NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + 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[DELETED] = + g_signal_new ("deleted", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (ItemDataClass, deleted), 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); + NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); 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); + NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); item_data_signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, @@ -163,7 +179,9 @@ static void item_data_class_init (ItemDataClass *klass) klass->changed = NULL; // Signals. + klass->created = NULL; klass->moved = NULL; + klass->deleted = NULL; } //////////////////////////////////////////////////////////////////////////////// @@ -221,29 +239,34 @@ void item_data_get_pos (ItemData *item_data, Coords *pos) pos->y = priv->translate.y0; } -void item_data_set_pos (ItemData *item_data, Coords *pos) +void item_data_set_pos (ItemData *item_data, Coords *pos, guint signals) { ItemDataPriv *priv; - gboolean handler_connected; - g_return_if_fail (pos); - g_return_if_fail (item_data); + g_return_if_fail (item_data != NULL); g_return_if_fail (IS_ITEM_DATA (item_data)); + g_return_if_fail (pos != NULL); priv = item_data->priv; - cairo_matrix_init_translate (&(priv->translate), pos->x, pos->y); - 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", 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"); - } + if (signals & EMIT_SIGNAL_CREATED) + if (g_signal_handler_is_connected (G_OBJECT (item_data), item_data->created_handler_id)) + g_signal_emit_by_name (G_OBJECT (item_data), "created", pos); + if (signals & EMIT_SIGNAL_MOVED) + if (g_signal_handler_is_connected (G_OBJECT (item_data), item_data->moved_handler_id)) + g_signal_emit_by_name (G_OBJECT (item_data), "moved", pos); + if (signals & EMIT_SIGNAL_ROTATED) + if (g_signal_handler_is_connected (G_OBJECT (item_data), item_data->rotated_handler_id)) + g_signal_emit_by_name (G_OBJECT (item_data), "rotated", NULL); + if (signals & EMIT_SIGNAL_DELETED) + if (g_signal_handler_is_connected (G_OBJECT (item_data), item_data->deleted_handler_id)) + g_signal_emit_by_name (G_OBJECT (item_data), "deleted", pos); + if (signals & EMIT_SIGNAL_CHANGED) + if (g_signal_handler_is_connected (G_OBJECT (item_data), item_data->changed_handler_id)) + g_signal_emit_by_name (G_OBJECT (item_data), "changed"); + if (signals & EMIT_SIGNAL_NONE) + return; } void item_data_move (ItemData *item_data, const Coords *delta) @@ -381,7 +404,7 @@ void item_data_list_get_absolute_bbox (GList *item_data_list, Coords *p1, Coords } } -void item_data_rotate (ItemData *data, int angle, Coords *center) +void item_data_create (ItemData *data) { ItemDataClass *id_class; @@ -389,22 +412,36 @@ void item_data_rotate (ItemData *data, int angle, Coords *center) g_return_if_fail (IS_ITEM_DATA (data)); id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data)); - if (id_class->rotate) { - id_class->rotate (data, angle, center); - } + if (id_class->create) + id_class->create (data); } -void item_data_flip (ItemData *data, IDFlip direction, Coords *center) +void item_data_rotate (ItemData *data, int angle, Coords *center, Coords *b1, Coords *b2, const char *caller_fn) { ItemDataClass *id_class; g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (center != NULL); + g_return_if_fail (b1 != NULL); + g_return_if_fail (b2 != NULL); + g_return_if_fail (caller_fn != NULL); id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data)); - if (id_class->flip) { + if (id_class->rotate) + id_class->rotate (data, angle, center, b1, b2, caller_fn); +} + +void item_data_flip (ItemData *data, SheetItem *item, Coords *center, IDFlip direction) +{ + ItemDataClass *id_class; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + + id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (data)); + if (id_class->flip) id_class->flip (data, direction, center); - } } void item_data_unregister (ItemData *data) diff --git a/src/model/item-data.h b/src/model/item-data.h index 4ebf1773..4f04492e 100644 --- a/src/model/item-data.h +++ b/src/model/item-data.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -31,10 +33,10 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ -#ifndef __ITEM_DATA_H -#define __ITEM_DATA_H // Base class for schematic model. +#ifndef __ITEM_DATA_H +#define __ITEM_DATA_H #include @@ -42,13 +44,6 @@ #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)) - typedef struct _ItemData ItemData; typedef struct _ItemDataClass ItemDataClass; typedef struct _ItemDataPriv ItemDataPriv; @@ -63,10 +58,12 @@ typedef enum { struct _ItemData { GObject parent; + gulong created_handler_id; gulong moved_handler_id; gulong rotated_handler_id; gulong flipped_handler_id; gulong changed_handler_id; + gulong deleted_handler_id; ItemDataPriv *priv; }; @@ -75,12 +72,15 @@ struct _ItemDataClass GObjectClass parent_class; // Signals. + void (*created)(ItemData *data, Coords *abs_pos); void (*moved)(ItemData *data, Coords *delta); + void (*deleted)(ItemData *data, Coords *abs_pos); // Methods. ItemData *(*clone)(ItemData *src); void (*copy)(ItemData *dest, ItemData *src); - void (*rotate)(ItemData *data, int angle, Coords *center); + void (*create)(ItemData *data); + void (*rotate)(ItemData *data, gint angle, Coords *center, Coords *bbox1, Coords *bbox2, const char *caller_fn); void (*flip)(ItemData *data, IDFlip direction, Coords *center); void (*unreg)(ItemData *data); int (*reg)(ItemData *data); @@ -93,6 +93,25 @@ struct _ItemDataClass void (*print)(ItemData *data, cairo_t *cr, SchematicPrintContext *ctx); }; +#include "sheet.h" + +#include "stack.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 EMIT_SIGNAL_NONE (1 << 0) +#define EMIT_SIGNAL_CREATED (1 << 1) +#define EMIT_SIGNAL_MOVED (1 << 2) +#define EMIT_SIGNAL_ROTATED (1 << 3) +#define EMIT_SIGNAL_FLIPPED (1 << 4) +#define EMIT_SIGNAL_CHANGED (1 << 5) +#define EMIT_SIGNAL_DELETED (1 << 6) + GType item_data_get_type (void); // Create a new ItemData object ItemData *item_data_new (void); @@ -105,7 +124,7 @@ ItemData *item_data_clone (ItemData *src); void item_data_get_pos (ItemData *item_data, Coords *pos); // Set Item position -void item_data_set_pos (ItemData *item_data, Coords *pos); +void item_data_set_pos (ItemData *item_data, Coords *pos, guint signals); // Move an ItemData // \param delta offset to move the item @@ -129,10 +148,10 @@ void item_data_get_absolute_bbox (ItemData *data, Coords *p1, Coords *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, Coords *center); +void item_data_rotate (ItemData *data, int angle, Coords *center, Coords *b1, Coords *b2, const char *caller_fn); // Flip an item -void item_data_flip (ItemData *data, IDFlip direction, Coords *center); +void item_data_flip (ItemData *data, SheetItem *item, Coords *coords, IDFlip direction); // Get the Store associated for this item // Store is a class that hold all items in a schematic diff --git a/src/model/node-store-private.h b/src/model/node-store-private.h index a41a0670..e5a498b6 100644 --- a/src/model/node-store-private.h +++ b/src/model/node-store-private.h @@ -336,7 +336,7 @@ static Wire *vulcanize_wire (NodeStore *store, Wire *a, Wire *b) #else Wire *w = a; #endif - item_data_set_pos (ITEM_DATA (w), &start); + item_data_set_pos (ITEM_DATA (w), &start, EMIT_SIGNAL_CHANGED); wire_set_length (w, &len); for (list = wire_get_nodes (b); list;) { diff --git a/src/model/node-store.h b/src/model/node-store.h index 9d6aba56..4f57ad85 100644 --- a/src/model/node-store.h +++ b/src/model/node-store.h @@ -37,6 +37,11 @@ #include #include +typedef struct _NodeStore NodeStore; +typedef struct _NodeStoreClass NodeStoreClass; +typedef struct _NodeRect NodeRect; + +#include "item-data.h" #include "coords.h" #define TYPE_NODE_STORE node_store_get_type () @@ -46,10 +51,6 @@ #define NODE_STORE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_NODE_STORE, NodeStoreClass)) -typedef struct _NodeStore NodeStore; -typedef struct _NodeStoreClass NodeStoreClass; -typedef struct _NodeRect NodeRect; - #include "schematic-print-context.h" #include "node.h" #include "wire.h" diff --git a/src/model/node.h b/src/model/node.h index c170f31f..328221f9 100644 --- a/src/model/node.h +++ b/src/model/node.h @@ -32,10 +32,13 @@ #ifndef __NODE_H #define __NODE_H -#include +typedef struct _Node Node; +typedef struct _NodeClass NodeClass; -#include "coords.h" +#include #include "part.h" +#include "coords.h" +#include "wire.h" #define TYPE_NODE (node_get_type ()) #define NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NODE, Node)) @@ -43,11 +46,6 @@ #define IS_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NODE)) #define IS_NODE_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((klass), TYPE_NODE, NodeClass)) -typedef struct _Node Node; -typedef struct _NodeClass NodeClass; - -#include "wire.h" - struct _Node { GObject parent; diff --git a/src/model/part-private.h b/src/model/part-private.h index 771db337..bb6e968f 100644 --- a/src/model/part-private.h +++ b/src/model/part-private.h @@ -40,8 +40,9 @@ struct _PartPriv { guint16 num_pins : 16; - // guint16 rotation : 16; + guint16 rotation : 16; IDFlip flip : 8; + gboolean create : 8; gchar *name; GSList *properties; diff --git a/src/model/part.c b/src/model/part.c index 0ef7336f..5ac8f34f 100644 --- a/src/model/part.c +++ b/src/model/part.c @@ -10,6 +10,7 @@ * Marc Lorber * Bernhard Schuster * Guido Trentalancia + * Daniel Dwek * * Web page: https://beerbach.me/oregano * @@ -18,6 +19,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * * This program is free software; you can redistribute it and/or @@ -42,7 +44,10 @@ #include #include +#include + #include "part.h" +#include "part-item.h" #include "part-property.h" #include "part-label.h" #include "node-store.h" @@ -54,6 +59,8 @@ #include "debug.h" +#include "stack.h" + static void part_class_init (PartClass *klass); static void part_init (Part *part); @@ -72,7 +79,8 @@ static void part_copy (ItemData *dest, ItemData *src); static ItemData *part_clone (ItemData *src); -static void part_rotate (ItemData *data, int angle, Coords *center); +static void part_rotate (ItemData *data, int angle, Coords *center, + Coords *bbox1, Coords *bbox2, const char *caller_fn); static void part_flip (ItemData *data, IDFlip direction, Coords *center); @@ -517,9 +525,12 @@ GSList *part_get_labels (Part *part) * @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) +static void part_rotate (ItemData *data, int angle, Coords *center_pos, + Coords *b1, Coords *b2, const char *caller_fn) { - g_return_if_fail (data); + callback_params_t params = { 0 }; + + g_return_if_fail (data != NULL); g_return_if_fail (IS_PART (data)); cairo_matrix_t morph, morph_rot, local_rot; @@ -527,7 +538,6 @@ static void part_rotate (ItemData *data, int angle, Coords *center_pos) gdouble x, y; Part *part; PartPriv *priv; - gboolean handler_connected; part = PART (data); @@ -558,10 +568,11 @@ static void part_rotate (ItemData *data, int angle, Coords *center_pos) Coords delta_to_center, delta_to_center_transformed; Coords delta_to_apply; Coords item_pos; + Coords rotation_center; - item_data_get_pos (ITEM_DATA (part), &item_pos); + morph_rot = *(item_data_get_rotate (data)); - Coords rotation_center; + item_data_get_pos (ITEM_DATA (part), &item_pos); if (center_pos == NULL) { rotation_center = item_pos; @@ -571,11 +582,14 @@ static void part_rotate (ItemData *data, int angle, Coords *center_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)); + &(delta_to_center_transformed.y)); delta_to_apply = coords_sub (&delta_to_center, &delta_to_center_transformed); -#define DEBUG_THIS 0 + + cairo_matrix_multiply (&morph, &morph_rot, item_data_get_translate (data)); + +// #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 @@ -601,13 +615,22 @@ static void part_rotate (ItemData *data, int angle, Coords *center_pos) item_data_move (data, &delta_to_apply); - handler_connected = g_signal_handler_is_connected (G_OBJECT (data), data->changed_handler_id); - if (handler_connected) { - g_signal_emit_by_name (G_OBJECT (data), "changed"); - } else { - NG_DEBUG ("handler not yet registerd."); + params.s_item = SHEET_ITEM (stack_get_sheetitem_by_itemdata (data)); + params.center.x = rotation_center.x; + params.center.y = rotation_center.y; + params.angle = angle; + params.bbox1 = b1; + params.bbox2 = b2; + if (!strcmp (caller_fn, "undo_cmd") || !strcmp (caller_fn, "redo_cmd")) + return; + + if (!strcmp (caller_fn, "rotate_items")) { + params.group = stack_get_group_for_rotation (params.s_item, ¶ms.center, angle, b1, b2); + if (g_signal_handler_is_connected (G_OBJECT (data), data->rotated_handler_id)) + g_signal_emit_by_name (G_OBJECT (data), "rotated", ¶ms); + if (g_signal_handler_is_connected (G_OBJECT (data), data->changed_handler_id)) + g_signal_emit_by_name (G_OBJECT (data), "changed"); } - NG_DEBUG ("\n\n"); } /** @@ -618,35 +641,32 @@ static void part_rotate (ItemData *data, int angle, Coords *center_pos) static void part_flip (ItemData *data, IDFlip direction, Coords *center) { #if 0 + gint i; Part *part; PartPriv *priv; - int i; cairo_matrix_t affine; - double x, y; - 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; + gdouble x, y, scale_v, scale_h; + GSList *objs = NULL; + LibrarySymbol *symbol = NULL; - g_return_if_fail (data); - g_return_if_fail (IS_PART (data)); + 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_SHEET_ITEM (item)); + g_return_if_fail (center != NULL); + + if (stack_is_item_registered (undo_stack, center)) + return; part = PART (data); priv = part->priv; - 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 @@ -658,31 +678,32 @@ static void part_flip (ItemData *data, IDFlip direction, Coords *center) // 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) { + 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) { + 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); + symbol = library_get_symbol (part->priv->symbol_name); + if (!symbol) + return; - item_data_get_pos (data, &pos_old); - pos_new = pos_old; - cairo_matrix_transform_point (&affine, &pos_new.x, &pos_new.y); + cairo_matrix_init_identity (&affine); + cairo_matrix_translate (&affine, center->x, center->y); + cairo_matrix_init_scale (&affine, scale_h, scale_v); - 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; + for (objs = symbol->symbol_objects; objs; objs = objs->next) { + part_item_canvas_draw (&item->canvas_group, objs->data, OBJECT_ERASE); + show_labels (item, FALSE); + } // 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); @@ -697,29 +718,10 @@ static void part_flip (ItemData *data, IDFlip direction, Coords *center) } // 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", priv->flip); - - // TODO - proper boundingbox center calculation - - item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); - - // flip the bounding box. - cairo_matrix_transform_point (&affine, &b1.x, &b1.y); - cairo_matrix_transform_point (&affine, &b2.x, &b2.y); - - item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); - item_data_set_pos (ITEM_DATA (part), &pos); - - // FIXME - proper recenter to boundingbox center - } - if (g_signal_handler_is_connected (G_OBJECT (part), - ITEM_DATA (part)->changed_handler_id)) { - g_signal_emit_by_name (G_OBJECT (part), - "changed"); - } + if (g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA (part)->flipped_handler_id)) + g_signal_emit_by_name (G_OBJECT (part), "flipped", priv); + 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 } @@ -1112,3 +1114,4 @@ static void part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) } cairo_restore (cr); } + diff --git a/src/model/part.h b/src/model/part.h index 7a9f7c73..47fa4f08 100644 --- a/src/model/part.h +++ b/src/model/part.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * * This program is free software; you can redistribute it and/or @@ -36,8 +38,13 @@ #ifndef __PART_H #define __PART_H -#include +typedef struct _Part Part; +typedef struct _PartClass PartClass; +typedef struct _Pin Pin; +typedef struct _PartPriv PartPriv; +#include +#include "item-data.h" #include "coords.h" #include "clipboard.h" #include "load-common.h" @@ -48,13 +55,6 @@ #define IS_PART(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_PART)) #define IS_PART_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((klass), TYPE_PART, PartClass)) -typedef struct _Part Part; -typedef struct _PartClass PartClass; -typedef struct _Pin Pin; -typedef struct _PartPriv PartPriv; - -#include "item-data.h" - struct _Pin { Coords offset; @@ -78,6 +78,15 @@ struct _PartClass void (*changed)(); }; +typedef struct callback_params_st { + SheetItem *s_item; + Coords center; + gint angle; + Coords *bbox1; + Coords *bbox2; + gint group; +} callback_params_t; + GType part_get_type (void); Part *part_new (); Part *part_new_from_library_part (LibraryPart *library_part); diff --git a/src/model/textbox.c b/src/model/textbox.c index ba41b677..805fc3e6 100644 --- a/src/model/textbox.c +++ b/src/model/textbox.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,14 +48,13 @@ 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, Coords *center); +static void textbox_rotate (ItemData *data, gint angle, Coords *center, Coords *bbox1, Coords *bbox2, const char *caller_fn); +static void textbox_flip (ItemData *data, IDFlip direction, 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, IDFlip direction, Coords *center); - enum { TEXT_CHANGED, FONT_CHANGED, LAST_SIGNAL }; struct _TextboxPriv @@ -168,7 +169,7 @@ static void textbox_copy (ItemData *dest, ItemData *src) dest_textbox->priv->font = src_textbox->priv->font; } -static void textbox_rotate (ItemData *data, int angle, Coords *center) +static void textbox_rotate (ItemData *data, gint angle, Coords *center, Coords *bbox1, Coords *bbox2, const char *caller_fn) { cairo_matrix_t affine; double x, y; @@ -178,6 +179,9 @@ static void textbox_rotate (ItemData *data, int angle, Coords *center) g_return_if_fail (data != NULL); g_return_if_fail (IS_TEXTBOX (data)); + g_return_if_fail (center != NULL); + g_return_if_fail (bbox1 != NULL); + g_return_if_fail (bbox2 != NULL); if (angle == 0) return; @@ -219,8 +223,7 @@ static void textbox_flip (ItemData *data, IDFlip direction, Coords *center) Coords b1, b2; Coords textbox_center = {0.0, 0.0}, delta; - g_return_if_fail (data != NULL); - g_return_if_fail (IS_TEXTBOX (data)); +// g_return_if_fail (objs != NULL); textbox = TEXTBOX (data); diff --git a/src/model/textbox.h b/src/model/textbox.h index c7e8260d..79ab185f 100644 --- a/src/model/textbox.h +++ b/src/model/textbox.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,6 +37,10 @@ #include +typedef struct _Textbox Textbox; +typedef struct _TextboxClass TextboxClass; +typedef struct _TextboxPriv TextboxPriv; + #include "clipboard.h" #include "item-data.h" @@ -44,13 +50,9 @@ #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; + ItemData *parent; TextboxPriv *priv; gulong text_changed_handler_id; gulong font_changed_handler_id; @@ -58,7 +60,7 @@ struct _Textbox struct _TextboxClass { - ItemDataClass parent_class; + ItemDataClass *parent_class; Textbox *(*dup)(Textbox *textbox); }; diff --git a/src/model/wire.c b/src/model/wire.c index 8ab064bd..a3adf0c3 100644 --- a/src/model/wire.c +++ b/src/model/wire.c @@ -9,6 +9,7 @@ * Marc Lorber * Bernhard Schuster * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -17,6 +18,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -49,8 +51,9 @@ 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, Coords *center); -static void wire_flip (ItemData *data, IDFlip direction, Coords *center); +static void wire_create (ItemData *data); +static void wire_rotate (ItemData *data, gint angle, Coords *center_pos, + Coords *bbox1, Coords *bbox2, const char *caller_fn); static void wire_unregister (ItemData *data); static int wire_register (ItemData *data); static gboolean wire_has_properties (ItemData *data); @@ -58,6 +61,7 @@ static void wire_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) static void wire_changed (ItemData *data); #include "debug.h" +#include "stack.h" enum { ARG_0, ARG_DELETE, ARG_LAST_SIGNAL }; @@ -99,10 +103,11 @@ static void wire_class_init (WireClass *klass) G_STRUCT_OFFSET (WireClass, delete), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + item_data_class->create = wire_create; item_data_class->clone = wire_clone; item_data_class->copy = wire_copy; item_data_class->rotate = wire_rotate; - item_data_class->flip = wire_flip; +// item_data_class->flip = wire_flip; item_data_class->unreg = wire_unregister; item_data_class->reg = wire_register; item_data_class->has_properties = wire_has_properties; @@ -133,7 +138,13 @@ void wire_dbg_print (Wire *w) w->priv->length.x, w->priv->length.y); } -Wire *wire_new () { return WIRE (g_object_new (TYPE_WIRE, NULL)); } +Wire *wire_new (void) +{ + Wire *wire = NULL; + + wire = WIRE (g_object_new (TYPE_WIRE, NULL)); + return wire; +} gint wire_add_node (Wire *wire, Node *node) { @@ -246,7 +257,7 @@ void wire_set_pos (Wire *wire, Coords *pos) g_return_if_fail (IS_WIRE (wire)); g_return_if_fail (pos != NULL); - item_data_set_pos (ITEM_DATA (wire), pos); + item_data_set_pos (ITEM_DATA (wire), pos, EMIT_SIGNAL_CHANGED); } void wire_set_length (Wire *wire, Coords *length) @@ -332,16 +343,48 @@ static void wire_copy (ItemData *dest, ItemData *src) dest_wire->priv->length = src_wire->priv->length; } -static void wire_rotate (ItemData *data, int angle, Coords *center_pos) +static void wire_create (ItemData *data) +{ + cairo_matrix_t affine; + Coords start_pos; + Wire *wire; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_WIRE (data)); + + wire = WIRE (data); + wire_get_start_pos (wire, &start_pos); + + start_pos.x = - start_pos.x - 100.0; + start_pos.y = - start_pos.y - 100.0; + + cairo_matrix_init_identity (&affine); + cairo_matrix_translate (&affine, start_pos.x, start_pos.y); + + // cairo_matrix_transform_distance (&affine, &priv->length.x, &priv->length.y); + cairo_matrix_transform_point (&affine, &start_pos.x, &start_pos.y); + + wire_set_pos (wire, &start_pos); + wire_update_bbox (wire); + + g_signal_emit_by_name (G_OBJECT (wire), "changed"); +} + +static void wire_rotate (ItemData *data, gint angle, Coords *center_pos, + Coords *b1, Coords *b2, const char *caller_fn) { cairo_matrix_t affine; Coords start_pos; Wire *wire; WirePriv *priv; + callback_params_t params = { 0 }; g_return_if_fail (data != NULL); g_return_if_fail (IS_WIRE (data)); g_return_if_fail (center_pos != NULL); + g_return_if_fail (b1 != NULL); + g_return_if_fail (b2 != NULL); + g_return_if_fail (caller_fn != NULL); if (angle == 0) return; @@ -372,14 +415,28 @@ static void wire_rotate (ItemData *data, int angle, Coords *center_pos) // Update bounding box. wire_update_bbox (wire); + params.s_item = SHEET_ITEM (stack_get_sheetitem_by_itemdata (data)); + params.center.x = center_pos->x; + params.center.y = center_pos->y; + params.angle = angle; + params.bbox1 = b1; + params.bbox2 = b2; + if (!strcmp (caller_fn, "undo_cmd") || !strcmp (caller_fn, "redo_cmd")) + return; + + if (!strcmp (caller_fn, "rotate_items")) { + params.group = stack_get_group_for_rotation (params.s_item, ¶ms.center, angle, b1, b2); + if (g_signal_handler_is_connected (G_OBJECT (data), data->rotated_handler_id)) + g_signal_emit_by_name (G_OBJECT (data), "rotated", ¶ms); + if (g_signal_handler_is_connected (G_OBJECT (data), data->changed_handler_id)) + g_signal_emit_by_name (G_OBJECT (data), "changed"); + } + // 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"); +// g_signal_emit_by_name (G_OBJECT (wire), "rotated", angle); // legacy +// g_signal_emit_by_name (G_OBJECT (wire), "changed"); } -// 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) { Coords b1, b2, pos, length; diff --git a/src/model/wire.h b/src/model/wire.h index 352cd416..e84f2457 100644 --- a/src/model/wire.h +++ b/src/model/wire.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,25 +37,10 @@ #ifndef __WIRE_H #define __WIRE_H -#include - -#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 IS_WIRE_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((klass), TYPE_WIRE)) - typedef struct _Wire Wire; typedef struct _WireClass WireClass; typedef struct _WirePriv WirePriv; -#include "node-store.h" -#include "node.h" -#include "grid.h" - typedef enum { WIRE_DIR_NONE = 0, WIRE_DIR_HORIZ = 1, @@ -61,6 +48,21 @@ typedef enum { WIRE_DIR_DIAG = 3 } WireDir; +#include +#include "item-data.h" +#include "node-store.h" +#include "node.h" +#include "grid.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 IS_WIRE_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((klass), TYPE_WIRE)) + struct _Wire { ItemData parent; @@ -77,7 +79,7 @@ struct _WireClass }; GType wire_get_type (void); -Wire *wire_new (); +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); diff --git a/src/options.c b/src/options.c index 9d7cd454..ad3b6257 100644 --- a/src/options.c +++ b/src/options.c @@ -45,15 +45,15 @@ gboolean oregano_options_parse (int *argc, char **argv[], GError **e) } -inline gboolean oregano_options_version () { return opts.version; } +gboolean oregano_options_version () { return opts.version; } -inline gboolean oregano_options_debug_wires () { return opts.debug.wires || opts.debug.all; } +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; } +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; } +gboolean oregano_options_debug_dots () { return opts.debug.dots || opts.debug.all; } -inline gboolean oregano_options_debug_directions () +gboolean oregano_options_debug_directions () { return opts.debug.directions || opts.debug.all; } diff --git a/src/part-browser.c b/src/part-browser.c index a77dd26f..63ab32e9 100644 --- a/src/part-browser.c +++ b/src/part-browser.c @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -184,7 +186,7 @@ static void place_cmd (GtkWidget *widget, Browser *br) } pos.x = pos.y = 0; - item_data_set_pos (ITEM_DATA (part), &pos); + item_data_set_pos (ITEM_DATA (part), &pos, EMIT_SIGNAL_MOVED); sheet_connect_part_item_to_floating_group (sheet, (gpointer)br->schematic_view); sheet_select_all (sheet, FALSE); @@ -375,7 +377,7 @@ void part_browser_dnd (GtkSelectionData *selection_data, int x, int y) return; } - item_data_set_pos (ITEM_DATA (part), &pos); + item_data_set_pos (ITEM_DATA (part), &pos, EMIT_SIGNAL_CREATED); sheet_connect_part_item_to_floating_group (sheet, (gpointer)data->schematic_view); sheet_select_all (sheet, FALSE); diff --git a/src/schematic-view-menu.h b/src/schematic-view-menu.h index 7855ddab..12fb17ef 100644 --- a/src/schematic-view-menu.h +++ b/src/schematic-view-menu.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -66,8 +68,9 @@ static GtkActionEntry entries[] = { {"Export", NULL, N_ ("_Export..."), NULL, N_ ("Export schematic"), G_CALLBACK (export_cmd)}, {"Close", GTK_STOCK_CLOSE, N_ ("_Close"), "W", N_ ("Close the current schematic"), G_CALLBACK (close_cmd)}, - {"Quit", GTK_STOCK_QUIT, N_ ("_Quit"), "Q", N_ ("Close all schematics"), - G_CALLBACK (quit_cmd)}, + {"Quit", GTK_STOCK_QUIT, N_ ("_Quit"), "Q", N_ ("Close all schematics"), G_CALLBACK (quit_cmd)}, + {"Undo", GTK_STOCK_UNDO, N_ ("_Undo"), "Z", N_ ("Undo last action"), G_CALLBACK (undo_cmd)}, + {"Redo", GTK_STOCK_REDO, N_ ("_Redo"), "Z", N_ ("Redo actions undone in reverse order"), G_CALLBACK (redo_cmd)}, {"Cut", GTK_STOCK_CUT, N_ ("C_ut"), "X", NULL, G_CALLBACK (cut_cmd)}, {"Copy", GTK_STOCK_COPY, N_ ("_Copy"), "C", NULL, G_CALLBACK (copy_cmd)}, {"Paste", GTK_STOCK_PASTE, N_ ("_Paste"), "V", NULL, G_CALLBACK (paste_cmd)}, @@ -160,6 +163,8 @@ static const char *ui_description = "" " " " " " " + " " + " " " " " " " " @@ -207,6 +212,8 @@ static const char *ui_description = "" " " " " " " + " " + " " " " " " " " diff --git a/src/schematic-view.c b/src/schematic-view.c index f1cf234c..8464ba97 100644 --- a/src/schematic-view.c +++ b/src/schematic-view.c @@ -9,6 +9,7 @@ * Marc Lorber * Bernhard Schuster * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -17,6 +18,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -62,8 +64,15 @@ #include "textbox-item.h" #include "log-view.h" #include "log.h" +#include "sheet-private.h" #include "debug.h" +#include "part-item.h" +#include "part-private.h" +#include "sheet-private.h" +#include "wire-item.h" +#include "stack.h" + #define ZOOM_MIN 0.35 #define ZOOM_MAX 3 @@ -124,6 +133,42 @@ struct _SchematicViewPriv LogInfo *log_info; }; +struct _SchematicPriv +{ + char *oregano_version; + char *title; + char *filename; + char *author; + char *comments; + char *netlist_filename; + +// SchematicColors colors; +// SchematicPrintOptions *printoptions; + + // Data for various dialogs. + gpointer settings; + SimSettingsGui *sim_settings; + gpointer simulation; + + GList *current_items; + + NodeStore *store; + GHashTable *symbols; + GHashTable *refdes_values; + + guint width; + guint height; + + double zoom; + + gboolean dirty; + + Log *logstore; + + GtkTextBuffer *log; + GtkTextTag *tag_error; +}; + G_DEFINE_TYPE (SchematicView, schematic_view, G_TYPE_OBJECT) // Class functions and members. @@ -134,6 +179,7 @@ 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); +GtkUIManager *schematic_view_get_ui_manager (SchematicView *sv); // Signal callbacks. static void title_changed_callback (Schematic *schematic, char *new_title, SchematicView *sv); @@ -583,6 +629,140 @@ static void close_cmd (GtkWidget *widget, SchematicView *sv) } } +static void undo_cmd (GtkWidget *widget, SchematicView *sv) +{ + stack_data_t **sdata = NULL; + ItemData *item_data = NULL; + Coords abs_coords = { .0 }; + gint n_items = 0, i, j; + GooCanvasGroup *canvas_group_wire = NULL; + + if (stack_get_size (undo_stack)) { + sdata = stack_pop (undo_stack, &n_items); + if (!sdata) + return; + + for (i = 0; i < n_items; i++) { + switch (sdata[i]->type) { + case PART_CREATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + abs_coords.x = - sdata[i]->u.moved.coords.x - 100.0; + abs_coords.y = - sdata[i]->u.moved.coords.y - 100.0; + item_data_set_pos (item_data, &abs_coords, EMIT_SIGNAL_CHANGED); + break; + case PART_MOVED: + item_data = sheet_item_get_data (sdata[i]->s_item); + abs_coords.x = sdata[i]->u.moved.coords.x - sdata[i]->u.moved.delta.x; + abs_coords.y = sdata[i]->u.moved.coords.y - sdata[i]->u.moved.delta.y; + item_data_set_pos (item_data, &abs_coords, EMIT_SIGNAL_MOVED | EMIT_SIGNAL_CHANGED); + break; + case PART_ROTATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_rotate (item_data, - sdata[i]->u.rotated.angle, &sdata[i]->u.rotated.center, + &sdata[i]->u.rotated.bbox1, &sdata[i]->u.rotated.bbox2, "undo_cmd"); + break; + case PART_DELETED: + for (j = 0; j < sdata[i]->u.deleted.canvas_group->items->len; j++) { + goo_canvas_item_translate (GOO_CANVAS_ITEM (sdata[i]->u.deleted.canvas_group->items->pdata[j]), + - sdata[i]->u.deleted.coords.x + 100.0, + - sdata[i]->u.deleted.coords.y + 100.0); + } + break; + case WIRE_CREATED: + g_object_set (G_OBJECT (sdata[i]->s_item), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + break; + case WIRE_MOVED: + canvas_group_wire = GOO_CANVAS_GROUP (WIRE_ITEM (SHEET_ITEM (sdata[i]->s_item))); + abs_coords.x = - sdata[i]->u.moved.delta.x; + abs_coords.y = - sdata[i]->u.moved.delta.y; + for (j = 0; j < canvas_group_wire->items->len; j++) + goo_canvas_item_translate (GOO_CANVAS_ITEM (canvas_group_wire->items->pdata[j]), + abs_coords.x, abs_coords.y); + break; + case WIRE_ROTATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_rotate (item_data, - sdata[i]->u.rotated.angle, &sdata[i]->u.rotated.center, + &sdata[i]->u.rotated.bbox1, &sdata[i]->u.rotated.bbox2, "undo_cmd"); + break; + case WIRE_DELETED: + for (j = 0; j < sdata[i]->u.deleted.canvas_group->items->len; j++) { + goo_canvas_item_translate (GOO_CANVAS_ITEM (sdata[i]->u.deleted.canvas_group->items->pdata[j]), + - sdata[i]->u.deleted.coords.x + 100.0, + - sdata[i]->u.deleted.coords.y + 100.0); + } + break; + }; + + stack_push (redo_stack, sdata[i], sv); + g_free (sdata[i]); + sdata[i] = NULL; + } + } +} + +static void redo_cmd (GtkWidget *widget, SchematicView *sv) +{ + stack_data_t **sdata = NULL; + ItemData *item_data = NULL; + gint i, j, n_items = 0; + GooCanvasGroup *canvas_group_wire = NULL; + + if (stack_get_size (redo_stack)) { + sdata = stack_pop (redo_stack, &n_items); + if (!sdata) + return; + + for (i = 0; i < n_items; i++) { + switch (sdata[i]->type) { + case PART_CREATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_set_pos (item_data, &sdata[i]->u.moved.coords, EMIT_SIGNAL_CHANGED); + break; + case PART_MOVED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_set_pos (item_data, &sdata[i]->u.moved.coords, EMIT_SIGNAL_CHANGED); + break; + case PART_ROTATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_rotate (item_data, - sdata[i]->u.rotated.angle, &sdata[i]->u.rotated.center, + &sdata[i]->u.rotated.bbox1, &sdata[i]->u.rotated.bbox2, "redo_cmd"); + break; + case PART_DELETED: + for (j = 0; j < sdata[i]->u.deleted.canvas_group->items->len; j++) + goo_canvas_item_translate (GOO_CANVAS_ITEM (sdata[i]->u.deleted.canvas_group->items->pdata[j]), + sdata[i]->u.deleted.coords.x - 100.0, + sdata[i]->u.deleted.coords.y - 100.0); + break; + case WIRE_CREATED: + g_object_set (G_OBJECT (sdata[i]->s_item), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + break; + case WIRE_MOVED: + canvas_group_wire = GOO_CANVAS_GROUP (WIRE_ITEM (SHEET_ITEM (sdata[i]->s_item))); + for (j = 0; j < canvas_group_wire->items->len; j++) + goo_canvas_item_translate (GOO_CANVAS_ITEM (canvas_group_wire->items->pdata[j]), + sdata[i]->u.moved.delta.x, sdata[i]->u.moved.delta.y); + g_object_set (G_OBJECT (sdata[i]->s_item), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + break; + case WIRE_ROTATED: + item_data = sheet_item_get_data (sdata[i]->s_item); + item_data_rotate (item_data, - sdata[i]->u.rotated.angle, &sdata[i]->u.rotated.center, + &sdata[i]->u.rotated.bbox1, &sdata[i]->u.rotated.bbox2, "redo_cmd"); + break; + case WIRE_DELETED: + for (j = 0; j < sdata[i]->u.deleted.canvas_group->items->len; j++) + goo_canvas_item_translate (GOO_CANVAS_ITEM (sdata[i]->u.deleted.canvas_group->items->pdata[j]), + sdata[i]->u.deleted.coords.x - 100.0, + sdata[i]->u.deleted.coords.y - 100.0); + break; + }; + + stack_push (undo_stack, sdata[i], sv); + g_free (sdata[i]); + sdata[i] = NULL; + } + } +} + static void select_all_cmd (GtkWidget *widget, SchematicView *sv) { sheet_select_all (sv->priv->sheet, TRUE); @@ -609,20 +789,44 @@ static void rotate_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) - sheet_flip_ghosts (sv->priv->sheet, TRUE); + GList *iter = NULL; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + for (iter = sheet_get_floating_objects (sv->priv->sheet); iter; iter = iter->next) + sv->priv->sheet->priv->selected_objects = g_list_prepend (sv->priv->sheet->priv->selected_objects, iter); + + for (iter = sv->priv->sheet->priv->selected_objects; iter; iter = iter->next) { + if (sv->priv->sheet->state == SHEET_STATE_NONE) + flip_items (sheet_get_selection (sv->priv->sheet), sv->priv->sheet, ID_FLIP_HORIZ); + else if (sv->priv->sheet->state == SHEET_STATE_FLOAT || sv->priv->sheet->state == SHEET_STATE_FLOAT_START) + flip_items (sheet_get_floating_objects (sv->priv->sheet), sv->priv->sheet, ID_FLIP_HORIZ); + } } 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) - sheet_flip_ghosts (sv->priv->sheet, FALSE); +#if 0 + Part *part; + LibrarySymbol *symbol; + GSList *objects; + SymbolObject *object; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + part = PART (sv->priv->sheet->priv->data); + symbol = library_get_symbol (part->priv->symbol_name); + + for (objects = symbol->symbol_objects; objects; objects = objects->next) { + object = (SymbolObject *)(objects->data); + if (sv->priv->sheet->state == SHEET_STATE_NONE) + sheet_flip_selection (sv, FALSE); + else if (sv->priv->sheet->state == SHEET_STATE_FLOAT || sv->priv->sheet->state == SHEET_STATE_FLOAT_START) + sheet_flip_ghosts (sv, FALSE); + } +#endif } static void object_properties_cmd (GtkWidget *widget, SchematicView *sv) @@ -780,7 +984,7 @@ static void v_clamp_cmd (SchematicView *sv) return; } pos.x = pos.y = 0; - item_data_set_pos (ITEM_DATA (part), &pos); + item_data_set_pos (ITEM_DATA (part), &pos, EMIT_SIGNAL_CHANGED); item_data_set_property (ITEM_DATA (part), "type", "v"); sheet_select_all (sv->priv->sheet, FALSE); @@ -1296,10 +1500,10 @@ SchematicView *schematic_view_new (Schematic *schematic) 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); + 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/Undo"), FALSE); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, "/MainMenu/MenuEdit/Redo"), FALSE); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, "/MainMenu/MenuEdit/Paste"), FALSE); g_signal_connect_object (G_OBJECT (sv), "reset_tool", G_CALLBACK (reset_tool_cb), G_OBJECT (sv), 0); @@ -1351,9 +1555,8 @@ static void schematic_view_do_load (SchematicView *sv, Schematic *sm, const gboo list = schematic_get_items (sm); - for (; list; list = list->next) { + for (; list; list = list->next) g_signal_emit_by_name (G_OBJECT (sm), "item_data_added", list->data); - } sheet_connect_node_dots_to_signals (sv->priv->sheet); @@ -1371,6 +1574,14 @@ static void schematic_view_reload (SchematicView *sv, Schematic *sm) schematic_view_do_load (sv, sm, TRUE); } +GtkUIManager *schematic_view_get_ui_manager (SchematicView *sv) +{ + if (!sv) + return NULL; + + return sv->priv->ui_manager; +} + static void item_selection_changed_callback (SheetItem *item, gboolean selected, SchematicView *sv) { guint length; @@ -1397,20 +1608,29 @@ static void item_data_added_callback (Schematic *schematic, ItemData *data, Sche { Sheet *sheet; SheetItem *item; + Coords pos = { .0 }; + cairo_matrix_t *mtx = NULL; + gdouble ret_points[4] = { .0 }; sheet = sv->priv->sheet; - - item = sheet_item_factory_create_sheet_item (sheet, data); - + item = sheet_item_factory_create_sheet_item (sheet, data, &ret_points[0]); if (item != NULL) { sheet_item_place (item, sv->priv->sheet); - 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_signal_connect (G_OBJECT (item), "selection_changed", G_CALLBACK (item_selection_changed_callback), sv); + + mtx = item_data_get_translate (data); + if (mtx) { + pos.x = mtx->x0; + pos.y = mtx->y0; + g_signal_emit_by_name (G_OBJECT (data), "created", &pos); + } else { + g_warning ("Could not get current position of %p item_data\n", data); + } sheet_add_item (sheet, item); + sv->priv->empty = FALSE; if (sv->priv->tool == SCHEMATIC_TOOL_PART) schematic_view_reset_tool (sv); diff --git a/src/schematic-view.h b/src/schematic-view.h index dd63a08e..69385898 100644 --- a/src/schematic-view.h +++ b/src/schematic-view.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -40,8 +42,8 @@ typedef struct _SchematicView SchematicView; #include -#include "schematic.h" #include "sheet.h" +#include "schematic.h" /* * When stretching a schematic to resize @@ -83,6 +85,7 @@ void schematic_view_reset_tool (SchematicView *sv); 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); +GtkUIManager *schematic_view_get_ui_manager (SchematicView *sv); // Logging. void schematic_view_log_show (SchematicView *sv, gboolean explicit); diff --git a/src/sheet/create-wire.c b/src/sheet/create-wire.c index 3b6a65e1..4f2efc24 100644 --- a/src/sheet/create-wire.c +++ b/src/sheet/create-wire.c @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Description: Handles the user interaction when creating wires. * The name is not really right. This part handles creation of wires and @@ -19,6 +20,7 @@ * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2012-2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -48,6 +50,7 @@ #include "sheet-private.h" #include "debug.h" +#include "stack.h" CreateWireInfo *create_wire_info_new (Sheet *sheet) { @@ -95,7 +98,7 @@ void create_wire_info_destroy (CreateWireInfo *create_wire_info) g_free (create_wire_info); } -inline static gboolean create_wire_start (Sheet *sheet, GdkEvent *event) +static gboolean create_wire_start (Sheet *sheet, GdkEvent *event) { double x, y; GooCanvasPoints *points; @@ -148,7 +151,7 @@ inline static gboolean create_wire_start (Sheet *sheet, GdkEvent *event) return TRUE; } -inline static gboolean create_wire_update (Sheet *sheet, GdkEvent *event) +static gboolean create_wire_update (Sheet *sheet, GdkEvent *event) { CreateWireInfo *create_wire_info; double new_x, new_y, x1, y1; @@ -230,7 +233,7 @@ inline static gboolean create_wire_update (Sheet *sheet, GdkEvent *event) return TRUE; } -inline static gboolean create_wire_discard (Sheet *sheet, GdkEvent *event) +static gboolean create_wire_discard (Sheet *sheet, GdkEvent *event) { CreateWireInfo *create_wire_info; @@ -249,7 +252,7 @@ inline static gboolean create_wire_discard (Sheet *sheet, GdkEvent *event) return TRUE; } -inline static Wire *create_wire_spawn (Sheet *sheet, Coords start_pos, Coords end_pos) +static Wire *create_wire_spawn (Sheet *sheet, Coords *start_pos, Coords *end_pos) { Wire *wire = NULL; Coords length; @@ -258,13 +261,13 @@ inline static Wire *create_wire_spawn (Sheet *sheet, Coords start_pos, Coords en g_assert (sheet); g_assert (IS_SHEET (sheet)); - wire = wire_new (sheet->grid); + wire = wire_new (/* sheet->grid */); - length.x = end_pos.x - start_pos.x; - length.y = end_pos.y - start_pos.y; + length.x = end_pos->x - start_pos->x; + length.y = end_pos->y - start_pos->y; wire_set_length (wire, &length); - item_data_set_pos (ITEM_DATA (wire), &start_pos); + item_data_set_pos (ITEM_DATA (wire), start_pos, EMIT_SIGNAL_CHANGED); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), ITEM_DATA (wire)); NG_DEBUG ("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- spawning wire %p", wire); @@ -273,7 +276,7 @@ inline static Wire *create_wire_spawn (Sheet *sheet, Coords start_pos, Coords en } #define FINISH_ON_WIRE_CLICK 0 -inline static gboolean create_wire_fixate (Sheet *sheet, GdkEvent *event) +static gboolean create_wire_fixate (Sheet *sheet, GdkEvent *event) { NodeStore *store; Schematic *schematic; @@ -346,7 +349,7 @@ inline static gboolean create_wire_fixate (Sheet *sheet, GdkEvent *event) } if (!b_start_eq_mid) { - wire = create_wire_spawn (sheet, start_pos, mid_pos); + wire = create_wire_spawn (sheet, &start_pos, &mid_pos); g_assert (wire); g_assert (IS_WIRE (wire)); } else { @@ -354,7 +357,7 @@ inline static gboolean create_wire_fixate (Sheet *sheet, GdkEvent *event) } if (!b_mid_eq_end) { - wire = create_wire_spawn (sheet, mid_pos, end_pos); + wire = create_wire_spawn (sheet, &mid_pos, &end_pos); g_assert (wire); g_assert (IS_WIRE (wire)); } else { @@ -462,6 +465,7 @@ gboolean create_wire_orientationtoggle (Sheet *sheet) gboolean create_wire_event (Sheet *sheet, GdkEvent *event, gpointer data) { CreateWireInfo *create_wire_info; + g_return_val_if_fail (sheet, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); diff --git a/src/sheet/create-wire.h b/src/sheet/create-wire.h index c7df634e..8e62aa76 100644 --- a/src/sheet/create-wire.h +++ b/src/sheet/create-wire.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -34,8 +36,9 @@ #define __CREATE_WIRE_H #include - +#include "item-data.h" #include "sheet.h" +#include "sheet-item.h" #include "wire.h" #include "wire-item.h" #include "schematic-view.h" diff --git a/src/sheet/grid.c b/src/sheet/grid.c index f2db618e..59308d6c 100644 --- a/src/sheet/grid.c +++ b/src/sheet/grid.c @@ -175,7 +175,7 @@ Grid *grid_new (GooCanvasItem *root, gdouble width, gdouble height) return grid; } -inline gboolean snap_to_grid (Grid *grid, gdouble *x, gdouble *y) +gboolean snap_to_grid (Grid *grid, gdouble *x, gdouble *y) { GridPriv *priv; gdouble spacing; diff --git a/src/sheet/node-item.c b/src/sheet/node-item.c index d7a6d950..0c9ceb3f 100644 --- a/src/sheet/node-item.c +++ b/src/sheet/node-item.c @@ -5,12 +5,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -29,6 +31,9 @@ */ #include +#include "sheet.h" +#include "sheet-private.h" +#include "coords.h" #include "options.h" #include "node-item.h" @@ -70,19 +75,31 @@ 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); + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (item), 0.0, 0.0, 2.0, 2.0, "fill-color", "black", 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_dots (Sheet *sheet, Coords *pos, gboolean show) { - g_return_if_fail (item != NULL); - g_return_if_fail (IS_NODE_ITEM (item)); + GList *iter = NULL; - g_object_set (item->priv->dot_item, "visibility", - show ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL); + g_return_if_fail (sheet != NULL); + g_return_if_fail (IS_SHEET (sheet)); + g_return_if_fail (pos != NULL); + + for (iter = g_hash_table_get_values (sheet->priv->node_dots); iter; iter = iter->next) { + if (show) { + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (iter->data), 0.0, 0.0, 2.0, 2.0, "fill-color", "black", NULL); + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (iter->data), 0.0, 0.0, 3.0, 3.0, "fill-color", "black", + "stroke-color-rgba", "0x3399FFFF", "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + } else { + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (iter->data), 0.0, 0.0, 2.0, 2.0, "fill-color", "white", NULL); + goo_canvas_ellipse_new (GOO_CANVAS_ITEM (iter->data), 0.0, 0.0, 3.0, 3.0, "fill-color", "white", + "stroke-color-rgba", "white", "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); + goo_canvas_item_remove (GOO_CANVAS_ITEM (iter->data)); + g_hash_table_remove (sheet->priv->node_dots, pos); + } + } } diff --git a/src/sheet/node-item.h b/src/sheet/node-item.h index c1f59865..9043b491 100644 --- a/src/sheet/node-item.h +++ b/src/sheet/node-item.h @@ -8,12 +8,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -59,6 +61,6 @@ struct _NodeItemClass GType node_item_get_type (void); GtkWidget *node_item_new (void); -void node_item_show_dot (NodeItem *item, gboolean show); +void node_item_show_dots (Sheet *sheet, Coords *pos, gboolean show); #endif /* __NODE_ITEM_H__ */ diff --git a/src/sheet/part-item.c b/src/sheet/part-item.c index 172fd2cb..80220ad5 100644 --- a/src/sheet/part-item.c +++ b/src/sheet/part-item.c @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -16,6 +17,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster * Copyright (C) 2018 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -39,6 +41,7 @@ #include #include "oregano.h" +#include "sheet.h" #include "sheet-item.h" #include "part-item.h" #include "part-private.h" @@ -48,15 +51,18 @@ #include "part-label.h" #include "stock.h" #include "dialogs.h" -#include "sheet.h" #include "oregano-utils.h" #include "options.h" +#include "sheet-private.h" -#define NORMAL_COLOR "red" -#define LABEL_COLOR "dark cyan" -#define SELECTED_COLOR "green" +#define NORMAL_COLOR "red" +#define LABEL_COLOR "dark cyan" +#define SELECTED_COLOR "green" +#define CANVAS_COLOR "white" +#include "node-item.h" #include "debug.h" +#include "stack.h" static void part_item_class_init (PartItemClass *klass); static void part_item_init (PartItem *gspart); @@ -68,14 +74,14 @@ 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 get_cached_bounds (PartItem *item, Coords *p1, Coords *p2); 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_created_callback (ItemData *data, Coords *pos, SheetItem *item); static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item); +static void part_rotated_callback (ItemData *data, gpointer params /* guint angle, Coords *center, SheetItem *item */); +static void part_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item); static void part_changed_callback (ItemData *data, SheetItem *sheet_item); - +static void part_deleted_callback (ItemData *data, Coords *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); @@ -245,7 +251,7 @@ static void part_item_moved (SheetItem *sheet_item) // g_warning ("part MOVED callback called - LEGACY"); } -PartItem *part_item_canvas_new (Sheet *sheet, Part *part) +static PartItem *part_item_canvas_new (Sheet *sheet, Part *part) { PartItem *part_item; PartItemPriv *priv; @@ -270,7 +276,7 @@ PartItem *part_item_canvas_new (Sheet *sheet, Part *part) 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, + goo_item, b1.x, b1.y, b2.x - b1.x, b2.y - b1.y, "stroke-color", "green", "line-width", .5, "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); @@ -281,14 +287,18 @@ PartItem *part_item_canvas_new (Sheet *sheet, Part *part) g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); item_data = ITEM_DATA (part); + item_data->created_handler_id = + g_signal_connect_object (G_OBJECT (part), "created", G_CALLBACK (part_created_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); + 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 (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); + g_signal_connect_object (G_OBJECT (part), "flipped", G_CALLBACK (part_flipped_callback), G_OBJECT (part_item), 0); item_data->changed_handler_id = - g_signal_connect_object (part, "changed", G_CALLBACK (part_changed_callback), part_item, 0); + g_signal_connect_object (G_OBJECT (part), "changed", G_CALLBACK (part_changed_callback), G_OBJECT (part_item), 0); + item_data->deleted_handler_id = + g_signal_connect_object (G_OBJECT (part), "deleted", G_CALLBACK (part_deleted_callback), G_OBJECT (part_item), 0); return part_item; } @@ -653,7 +663,7 @@ static void edit_properties (SheetItem *object) txtmodel = GTK_TEXT_VIEW (gtk_builder_get_object (gui, "txtmodel")); txtbuffer = gtk_text_buffer_new (NULL); - filename = g_strdup_printf ("%s/%s.model", OREGANO_MODELDIR, model_name); + filename = g_strdup_printf ("%s%s.model", OREGANO_MODELDIR, model_name); if (g_file_get_contents (filename, &str, NULL, &read_error)) { gtk_text_buffer_set_text (txtbuffer, str, -1); g_free (str); @@ -677,7 +687,11 @@ static void edit_properties (SheetItem *object) gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); } -inline static GooCanvasAnchorType angle_to_anchor (int angle) +/** + * Defined but not used function + */ +#if 0 +static GooCanvasAnchorType angle_to_anchor (int angle) { GooCanvasAnchorType anchor; // Get the right anchor for the labels. This is needed since the @@ -700,6 +714,7 @@ inline static GooCanvasAnchorType angle_to_anchor (int angle) return anchor; } +#endif /** * whenever the model changes, this one gets called to update the view @@ -777,29 +792,6 @@ static void part_changed_callback (ItemData *data, SheetItem *sheet_item) #endif } -/** - * 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"); -} - -/** - * 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; @@ -829,6 +821,7 @@ static int select_idle_callback (PartItem *item) 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, "line-width", 0.5, NULL); } g_object_unref (G_OBJECT (item)); return FALSE; @@ -868,7 +861,7 @@ static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2) return FALSE; } -static void show_labels (SheetItem *sheet_item, gboolean show) +void show_labels (SheetItem *sheet_item, gboolean show) { PartItem *item; PartItemPriv *priv; @@ -887,7 +880,7 @@ static void show_labels (SheetItem *sheet_item, gboolean show) // 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, Coords *p1, Coords *p2) +static void get_cached_bounds (PartItem *item, Coords *p1, Coords *p2) { PartItemPriv *priv; priv = item->priv; @@ -922,6 +915,44 @@ static void part_item_paste (Sheet *sheet, ItemData *data) sheet_add_ghost_item (sheet, data); } +GooCanvasItem *part_item_canvas_draw (GooCanvasGroup *group, SymbolObject *object) +{ + GooCanvasItem *item = NULL; + GooCanvasPoints *points = NULL; + + g_return_val_if_fail (group != NULL, NULL); + g_return_val_if_fail (object != NULL, NULL); + + 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, + 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"); + break; + } + + return item; +} + PartItem *part_item_new (Sheet *sheet, Part *part) { Library *library; @@ -940,6 +971,7 @@ PartItem *part_item_new (Sheet *sheet, Part *part) create_canvas_labels (item, part); create_canvas_label_nodes (item, part); goo_canvas_item_ensure_updated (GOO_CANVAS_ITEM (item)); + return item; } @@ -954,7 +986,6 @@ void part_item_create_canvas_items_for_preview (GooCanvasGroup *group, LibraryPa static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part) { GooCanvasItem *item; - GooCanvasPoints *points; GSList *objects; LibrarySymbol *symbol; SymbolObject *object; @@ -974,33 +1005,9 @@ static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_par 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); - break; - default: - g_warning ("Unknown symbol object.\n"); - continue; - } + + item = part_item_canvas_draw (group, object); + goo_canvas_item_get_bounds (item, &bounds); if (group_bounds.x1 > bounds.x1) group_bounds.x1 = bounds.x1; @@ -1110,8 +1117,138 @@ static void create_canvas_label_nodes (PartItem *item, Part *part) item->priv->label_nodes = item_list; } +static void part_created_callback (ItemData *data, Coords *pos, SheetItem *item) +{ + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; + + g_return_if_fail (data != NULL); + g_return_if_fail (pos != NULL); + g_return_if_fail (item != NULL); + + sheet = sheet_item_get_sheet (item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + + sdata.type = PART_CREATED; + sdata.s_item = item; + sdata.u.moved.coords.x = pos->x; + sdata.u.moved.coords.y = pos->y; + sdata.u.moved.delta.x = .0; + sdata.u.moved.delta.y = .0; + if (stack_is_item_registered (&sdata)) + return; + sdata.group = stack_get_group (sdata.s_item, NEW_GROUP); + stack_push (undo_stack, &sdata, sv); +} + // This is called when the part data was moved. Update the view accordingly. -static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item) {} +static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item) +{ + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; + GList *list = NULL; + Coords *delta = NULL; + + g_return_if_fail (data != NULL); + g_return_if_fail (pos != NULL); + g_return_if_fail (item != NULL); + + sheet = sheet_item_get_sheet (item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + for (list = sheet_get_selection (sheet); list; list = list->next) + if (item == SHEET_ITEM (list->data)) + return; + + if (fabs (pos->x + 100.0) < 1e-2 && fabs (pos->y + 100.0) < 1e-2) + return; + + sdata.type = PART_MOVED; + sdata.s_item = item; + sdata.u.moved.coords.x = pos->x; + sdata.u.moved.coords.y = pos->y; + if (stack_is_item_registered (&sdata)) + return; + + delta = stack_get_multiple_group (item, pos, &sdata.group); + if (delta) { + sdata.u.moved.delta.x = delta->x; + sdata.u.moved.delta.y = delta->y; + } + + stack_push (undo_stack, &sdata, sv); +} + +/** + * a part got rotated + * + * @angle the angle the item is rotated towards the default (0) rotation + * + */ +static void part_rotated_callback (ItemData *data, gpointer params) +{ + stack_data_t sdata = { 0 }; + Sheet *sheet = NULL; + SchematicView *sv = NULL; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (params != NULL); + + sdata.type = PART_ROTATED; + sdata.s_item = ((callback_params_t *)params)->s_item; + sdata.u.rotated.center = ((callback_params_t *)params)->center; + sdata.u.rotated.angle = ((callback_params_t *)params)->angle; + sdata.u.rotated.bbox1 = *(((callback_params_t *)params)->bbox1); + sdata.u.rotated.bbox2 = *(((callback_params_t *)params)->bbox2); + sdata.group = ((callback_params_t *)params)->group; + if (stack_is_item_registered (&sdata)) + return; + + sheet = sheet_item_get_sheet (sdata.s_item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + stack_push (undo_stack, &sdata, sv); +} + +/** + * 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"); +} + +static void part_deleted_callback (ItemData *data, Coords *pos, SheetItem *item) +{ + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; + + g_return_if_fail (data != NULL); + g_return_if_fail (pos != NULL); + g_return_if_fail (item != NULL); + + sheet = sheet_item_get_sheet (item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + + sdata.type = PART_DELETED; + sdata.s_item = item; + sdata.u.deleted.coords.x = -2.0 * pos->x - 100; + sdata.u.deleted.coords.y = -2.0 * pos->y - 100; + sdata.u.deleted.canvas_group = GOO_CANVAS_GROUP (PART_ITEM (item)); + if (stack_is_item_registered (&sdata)) + return; + + sdata.group = stack_get_group (sdata.s_item, NEW_GROUP); + g_object_set (item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); + node_item_show_dots (sheet, &sdata.u.deleted.coords, FALSE); + stack_push (undo_stack, &sdata, sv); +} static void part_item_place (SheetItem *item, Sheet *sheet) { diff --git a/src/sheet/part-item.h b/src/sheet/part-item.h index ddb471e5..8bebcb6d 100644 --- a/src/sheet/part-item.h +++ b/src/sheet/part-item.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2014 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -63,8 +65,10 @@ struct _PartItemClass GType part_item_get_type (void); PartItem *part_item_new (Sheet *sheet, Part *part); +GooCanvasItem *part_item_canvas_draw (GooCanvasGroup *group, SymbolObject *object); 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 show_labels (SheetItem *sheet_item, gboolean show); #endif diff --git a/src/sheet/rubberband.c b/src/sheet/rubberband.c index 6332dfa6..4301cb24 100644 --- a/src/sheet/rubberband.c +++ b/src/sheet/rubberband.c @@ -46,7 +46,7 @@ #include "debug.h" -inline static cairo_pattern_t *create_stipple (const char *color_name, guchar stipple_data[]) +static cairo_pattern_t *create_stipple (const char *color_name, guchar stipple_data[]) { cairo_surface_t *surface; cairo_pattern_t *pattern; diff --git a/src/sheet/sheet-item-factory.c b/src/sheet/sheet-item-factory.c index 4d48e8f7..2b663787 100644 --- a/src/sheet/sheet-item-factory.c +++ b/src/sheet/sheet-item-factory.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -30,9 +32,11 @@ * Boston, MA 02110-1301, USA. */ +#include "sheet.h" +#include "sheet-item.h" #include "sheet-item-factory.h" -#include "wire-item.h" #include "part-item.h" +#include "wire-item.h" #include "textbox-item.h" #include "debug.h" @@ -40,23 +44,21 @@ // 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, gdouble *ret_points) { - SheetItem *item; + SheetItem *item = NULL; g_return_val_if_fail (data != NULL, NULL); g_return_val_if_fail (IS_ITEM_DATA (data), NULL); g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); - item = NULL; - // Pick the right model. if (IS_PART (data)) { item = SHEET_ITEM (part_item_new (sheet, PART (data))); NG_DEBUG ("part %p", item); } else if (IS_WIRE (data)) { - item = SHEET_ITEM (wire_item_new (sheet, WIRE (data))); + item = SHEET_ITEM (wire_item_new (sheet, WIRE (data), ret_points)); NG_DEBUG ("wire %p", item); } else if (IS_TEXTBOX (data)) { item = SHEET_ITEM (textbox_item_new (sheet, TEXTBOX (data))); diff --git a/src/sheet/sheet-item-factory.h b/src/sheet/sheet-item-factory.h index 9ba39905..72b1fe69 100644 --- a/src/sheet/sheet-item-factory.h +++ b/src/sheet/sheet-item-factory.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,6 +37,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, gdouble *ret_points); #endif /* __SHEET_ITEM_FACTORY_H */ diff --git a/src/sheet/sheet-item.c b/src/sheet/sheet-item.c index c61a056a..01a82012 100644 --- a/src/sheet/sheet-item.c +++ b/src/sheet/sheet-item.c @@ -44,11 +44,13 @@ #include "oregano.h" #include "sheet-private.h" -#include "sheet-item.h" +#include "sheet-item-factory.h" #include "stock.h" #include "clipboard.h" #include "options.h" +#include "stack.h" + static void sheet_item_class_init (SheetItemClass *klass); static void sheet_item_init (SheetItem *item); static void sheet_item_set_property (GObject *object, guint prop_id, const GValue *value, @@ -251,8 +253,7 @@ static void sheet_item_set_property (GObject *object, guint prop_id, const GValu goo_canvas_item_simple_changed (simple, TRUE); } -static void sheet_item_get_property (GObject *object, guint prop_id, GValue *value, - GParamSpec *spec) +void sheet_item_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) { SheetItem *sheet_item; @@ -313,7 +314,7 @@ gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_targe SheetPriv *priv; GList *list; - static Coords last, current, snapped; + static Coords last, snapped; // snapped : Mouse cursor position in window coordinates, snapped to the grid // spacing. // delta : Move the selected item(s) by this movement. @@ -418,7 +419,7 @@ gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_targe 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_set_pos (item_data, &after, EMIT_SIGNAL_MOVED | EMIT_SIGNAL_CHANGED); item_data_register (item_data); } break; @@ -428,6 +429,8 @@ gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_targe switch (event->key.keyval) { case GDK_KEY_r: { #ifndef FIXME_STILL_MINI_OFFSET + stack_data_t sdata = { 0 }; + ItemData *idata = NULL; Coords bbdelta; GooCanvasBounds bounds; @@ -437,6 +440,12 @@ gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_targe bbdelta.x = (bounds.x2 - bounds.x1) / 2.; bbdelta.y = (bounds.y2 - bounds.y1) / 2.; #endif + sdata.type = PART_ROTATED; + idata = item_data_new (); + sdata.s_item = sheet_item_factory_create_sheet_item (sheet, idata, NULL); + sdata.group = stack_get_group (sdata.s_item, NEW_GROUP); + sdata.u.rotated.center = bbdelta; + sdata.u.rotated.angle = 90; sheet_rotate_selection (sheet, 90); #ifndef FIXME_STILL_MINI_OFFSET // Center the objects around the mouse pointer. @@ -637,7 +646,7 @@ int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) NG_DEBUG ("Item Data Pos will be %lf %lf", snapped.x, snapped.y) - item_data_set_pos (floating_data, &snapped); + item_data_set_pos (floating_data, &snapped, EMIT_SIGNAL_NONE); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), floating_data); @@ -817,10 +826,13 @@ void sheet_item_edit_properties (SheetItem *item) void sheet_item_rotate (SheetItem *sheet_item, int angle, Coords *center) { + Coords b1 = { .0 }, b2 = { .0 }; + g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_SHEET_ITEM (sheet_item)); - item_data_rotate (sheet_item->priv->data, angle, center); + item_data_get_absolute_bbox (sheet_item->priv->data, &b1, &b2); + item_data_rotate (sheet_item->priv->data, angle, center, &b1, &b2, "sheet_item_rotate"); } void sheet_item_paste (Sheet *sheet, ClipboardData *data) diff --git a/src/sheet/sheet-item.h b/src/sheet/sheet-item.h index df91798a..de67be69 100644 --- a/src/sheet/sheet-item.h +++ b/src/sheet/sheet-item.h @@ -45,11 +45,12 @@ (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)) +#include "clipboard.h" +#include "item-data.h" + typedef struct _SheetItemClass SheetItemClass; typedef struct _SheetItemPriv SheetItemPriv; - -#include "sheet.h" -#include "clipboard.h" +typedef struct _SheetItem SheetItem; struct _SheetItem { @@ -75,7 +76,7 @@ struct _SheetItemClass // Signal handlers. void (*moved)(SheetItem *item); - void (*selection_changed)(SheetItem *item); + void (*selection_changed)(gpointer *item, gboolean select, gpointer user_data); void (*mouse_over)(SheetItem *item); }; @@ -99,5 +100,4 @@ 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.c b/src/sheet/sheet.c index d47a8b72..fe524578 100644 --- a/src/sheet/sheet.c +++ b/src/sheet/sheet.c @@ -9,6 +9,7 @@ * Marc Lorber * Bernhard Schuster * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -17,6 +18,7 @@ * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013-2019 Bernhard Schuster * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -34,6 +36,7 @@ * Boston, MA 02110-1301, USA. */ +#include #include #include #include @@ -53,6 +56,8 @@ #include "rubberband.h" #include "create-wire.h" +#include "stack.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, @@ -62,7 +67,7 @@ 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); +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); @@ -74,6 +79,15 @@ static guint dot_hash (gconstpointer key); #include "debug.h" +typedef struct _SheetItemPriv +{ + guint selected : 1; + guint preserve_selection : 1; + ItemData *data; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; +} SheetItemPriv; + enum { SELECTION_CHANGED, BUTTON_PRESS, CONTEXT_CLICK, CANCEL, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; @@ -790,9 +804,8 @@ int sheet_event_callback (GtkWidget *widget, GdkEvent *event, Sheet *sheet) } if (event->button.button == 1) { - if (!(event->button.state & GDK_SHIFT_MASK)) { + if (!(event->button.state & GDK_SHIFT_MASK)) sheet_select_all (sheet, FALSE); - } rubberband_start (sheet, event); return TRUE; @@ -949,7 +962,7 @@ 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) + if (sheet->priv->selected_objects) rotate_items (sheet, sheet->priv->selected_objects, angle); } @@ -958,10 +971,11 @@ void sheet_rotate_selection (Sheet *sheet, gint angle) */ void sheet_move_selection (Sheet *sheet, gdouble x, gdouble y) { + const Coords delta = { x, 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) move_items (sheet, sheet->priv->selected_objects, &delta); } @@ -980,13 +994,14 @@ void sheet_rotate_ghosts (Sheet *sheet) static void rotate_items (Sheet *sheet, GList *items, gint angle) { - GList *list, *item_data_list; - Coords center, b1, b2; + GList *list = NULL, *item_data_list = NULL; + Coords center = { .0 }, b1 = { .0 }, b2 = { .0 }; - item_data_list = NULL; - for (list = items; list; list = list->next) { + g_return_if_fail (sheet != NULL); + g_return_if_fail (items != NULL); + + for (list = items; list; list = list->next) item_data_list = g_list_prepend (item_data_list, sheet_item_get_data (list->data)); - } item_data_list_get_absolute_bbox (item_data_list, &b1, &b2); @@ -996,16 +1011,14 @@ static void rotate_items (Sheet *sheet, GList *items, gint angle) 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_unregister (list->data); - item_data_rotate (item_data, angle, ¢er); + item_data_rotate (item_data, angle, ¢er, &b1, &b2, "rotate_items"); if (sheet->state == SHEET_STATE_NONE) - item_data_register (item_data); + item_data_register (list->data); } g_list_free (item_data_list); @@ -1036,7 +1049,13 @@ static void move_items (Sheet *sheet, GList *items, const Coords *trans) */ void sheet_delete_selection (Sheet *sheet) { - GList *copy, *iter; + GList *copy = NULL, *iter = NULL; + gint i = 0, index; + SchematicView *sv = NULL; + Coords pos = { 0 }; + stack_data_t sdata = { 0 }; + PartItem *part_item = NULL; + WireItem *wire_item = NULL; g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); @@ -1044,12 +1063,39 @@ void sheet_delete_selection (Sheet *sheet) if (sheet->state != SHEET_STATE_NONE) return; + sv = schematic_view_get_schematicview_from_sheet (sheet); copy = g_list_copy (sheet->priv->selected_objects); 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); + if (IS_PART (sheet_item_get_data (SHEET_ITEM (iter->data)))) + sdata.type = PART_DELETED; + else + sdata.type = WIRE_DELETED; + + sdata.s_item = SHEET_ITEM (iter->data); + item_data_get_pos (sheet_item_get_data (SHEET_ITEM (iter->data)), &pos); + sdata.u.deleted.coords.x = - pos.x - 100.0; + sdata.u.deleted.coords.y = - pos.y - 100.0; + if (stack_is_item_registered (&sdata)) + return; + + sdata.group = stack_get_group (SHEET_ITEM (iter->data), i ? SAME_GROUP : NEW_GROUP); + if (IS_PART_ITEM (SHEET_ITEM (iter->data))) { + part_item = PART_ITEM (SHEET_ITEM (iter->data)); + sdata.u.deleted.canvas_group = GOO_CANVAS_GROUP (part_item); + } else if (IS_WIRE_ITEM (SHEET_ITEM (iter->data))) { + wire_item = WIRE_ITEM (SHEET_ITEM (iter->data)); + sdata.u.deleted.canvas_group = GOO_CANVAS_GROUP (wire_item); + } + + for (index = 0; index < sdata.u.deleted.canvas_group->items->len; index++) + goo_canvas_item_translate (sdata.u.deleted.canvas_group->items->pdata[index], + sdata.u.deleted.coords.x - 100.0, + sdata.u.deleted.coords.y - 100.0); + + node_item_show_dots (sheet, &pos, FALSE); + stack_push (undo_stack, &sdata, sv); + i++; } g_list_free (copy); @@ -1139,39 +1185,40 @@ void sheet_flip_ghosts (Sheet *sheet, IDFlip direction) flip_items (sheet, sheet->priv->floating_objects, direction); } -static void flip_items (Sheet *sheet, GList *items, IDFlip direction) +void flip_items (Sheet *sheet, GList *objs, IDFlip direction) { - GList *iter, *item_data_list; + GList *iter, *list, *item_data_list = NULL, *items = NULL; Coords center, b1, b2; Coords after; - item_data_list = NULL; - for (iter = items; iter; iter = iter->next) { + g_return_if_fail (objs != NULL); + + for (iter = objs; iter; iter = iter->next) { item_data_list = g_list_prepend (item_data_list, sheet_item_get_data (iter->data)); + items = g_list_prepend (items, SHEET_ITEM (iter->data)); } item_data_list_get_absolute_bbox (item_data_list, &b1, &b2); // 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; + center = coords_average (&b1, &b2); // 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) { + for (iter = item_data_list, list = items; iter; iter = iter->next, items = items->next) { ItemData *item_data = iter->data; if (sheet->state == SHEET_STATE_NONE) item_data_unregister (item_data); - item_data_flip (item_data, direction, ¢er); + item_data_flip (item_data, list->data, ¢er, direction); // Make sure we snap to grid. 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_set_pos (item_data, &after, EMIT_SIGNAL_FLIPPED); if (sheet->state == SHEET_STATE_NONE) item_data_register (item_data); @@ -1258,7 +1305,7 @@ void sheet_add_ghost_item (Sheet *sheet, ItemData *data) g_return_if_fail (sheet != NULL); g_return_if_fail (IS_SHEET (sheet)); - item = sheet_item_factory_create_sheet_item (sheet, data); + item = sheet_item_factory_create_sheet_item (sheet, data, NULL); g_object_set (G_OBJECT (item), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); @@ -1296,7 +1343,7 @@ static void node_dot_added_callback (Schematic *schematic, Coords *pos, Sheet *s pos->x, "y", pos->y, NULL); } - node_item_show_dot (node_item, TRUE); + node_item_show_dots (sheet, pos, TRUE); key = g_new0 (Coords, 1); key->x = pos->x; key->y = pos->y; @@ -1317,6 +1364,7 @@ static void node_dot_removed_callback (Schematic *schematic, Coords *pos, Sheet (gpointer)&node_item); if (found) { + g_object_set (node_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); goo_canvas_item_remove (GOO_CANVAS_ITEM (node_item)); g_hash_table_remove (sheet->priv->node_dots, pos); } else { @@ -1406,7 +1454,7 @@ void sheet_remove_item_in_sheet (SheetItem *item, Sheet *sheet) g_object_unref (data); } -inline static guint32 extract_time (GdkEvent *event) +static guint32 extract_time (GdkEvent *event) { if (event) { switch (event->type) { diff --git a/src/sheet/sheet.h b/src/sheet/sheet.h index 331c0441..e78d3a88 100644 --- a/src/sheet/sheet.h +++ b/src/sheet/sheet.h @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Guido Trentalancia + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2004 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2017 Guido Trentalancia + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -38,10 +40,6 @@ #include #include -#include "grid.h" - -#include "item-data.h" - // typedef to avoid circular dependency typedef struct _SchematicView SchematicView; @@ -58,6 +56,9 @@ typedef struct _SheetItem SheetItem; #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)) +#include "grid.h" +#include "stack.h" + typedef enum { SHEET_STATE_NONE, SHEET_STATE_DRAG_START, diff --git a/src/sheet/textbox-item.c b/src/sheet/textbox-item.c index ed618ef6..83d744ae 100644 --- a/src/sheet/textbox-item.c +++ b/src/sheet/textbox-item.c @@ -8,6 +8,7 @@ * Andres de Barbara * Marc Lorber * Bernhard Schuster + * Daniel Dwek * * Web page: https://ahoi.io/project/oregano * @@ -15,6 +16,7 @@ * Copyright (C) 2003,2006 Ricardo Markiewicz * Copyright (C) 2009-2012 Marc Lorber * Copyright (C) 2013 Bernhard Schuster + * Copyright (C) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,6 +37,9 @@ #include #include +#include "sheet.h" +#include "sheet-item.h" +#include "stack.h" #include "cursors.h" #include "coords.h" #include "textbox-item.h" @@ -45,6 +50,8 @@ #define SELECTED_COLOR "green" #define TEXTBOX_FONT "Arial 10" +#define CANVAS_COLOR "white" + static void textbox_item_class_init (TextboxItemClass *klass); static void textbox_item_init (TextboxItem *item); static void textbox_item_finalize (GObject *object); @@ -58,7 +65,7 @@ static void selection_changed (TextboxItem *item, gboolean select, gpointer user 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 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); @@ -194,6 +201,7 @@ TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox) 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); @@ -288,7 +296,7 @@ static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *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, Coords *p1, Coords *p2) +static void get_cached_bounds (TextboxItem *item, Coords *p1, Coords *p2) { PangoFontDescription *font; Coords pos; @@ -409,7 +417,7 @@ static gboolean create_textbox_event (Sheet *sheet, GdkEvent *event) textbox_set_text (textbox, _ ("Label")); - item_data_set_pos (ITEM_DATA (textbox), &pos); + item_data_set_pos (ITEM_DATA (textbox), &pos, EMIT_SIGNAL_CHANGED); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), ITEM_DATA (textbox)); diff --git a/src/sheet/wire-item.c b/src/sheet/wire-item.c index 208710c2..7a6508c8 100644 --- a/src/sheet/wire-item.c +++ b/src/sheet/wire-item.c @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -31,8 +33,11 @@ */ #include +#include #include +#include "sheet.h" +#include "sheet-item.h" #include "cursors.h" #include "coords.h" #include "wire-item.h" @@ -42,10 +47,14 @@ #include "schematic.h" #include "schematic-view.h" #include "options.h" +#include "stack.h" + +#include "sheet-private.h" #define NORMAL_COLOR "blue" #define SELECTED_COLOR "green" #define HIGHLIGHT_COLOR "yellow" +#define CANVAS_COLOR "white" #define RESIZER_SIZE 4.0f @@ -54,16 +63,16 @@ 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_created_callback (ItemData *data, Coords *pos, SheetItem *item); +static void wire_rotated_callback (ItemData *data, gpointer params); 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 void selection_changed (gpointer *item, gboolean select, gpointer user); 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 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); @@ -162,10 +171,6 @@ static void wire_item_init (WireItem *item) static void wire_item_dispose (GObject *object) { - WireItemPriv *priv; - - priv = WIRE_ITEM (object)->priv; - G_OBJECT_CLASS (wire_item_parent_class)->dispose (object); } @@ -187,7 +192,7 @@ 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, gdouble *ret_points) { GooCanvasItem *item; WireItem *wire_item; @@ -198,6 +203,7 @@ WireItem *wire_item_new (Sheet *sheet, Wire *wire) g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); + g_return_val_if_fail (ret_points != NULL, NULL); wire_get_pos_and_length (wire, &start_pos, &length); @@ -238,6 +244,11 @@ WireItem *wire_item_new (Sheet *sheet, Wire *wire) points->coords[2] = length.x; points->coords[3] = length.y; + ret_points[0] = start_pos.x; + ret_points[1] = start_pos.y; + ret_points[2] = start_pos.x + length.x; + ret_points[3] = start_pos.x + length.y; + priv->line = GOO_CANVAS_POLYLINE (goo_canvas_polyline_new ( GOO_CANVAS_ITEM (wire_item), FALSE, 0, "points", points, "stroke-color", oregano_options_debug_wires () @@ -249,12 +260,12 @@ WireItem *wire_item_new (Sheet *sheet, Wire *wire) goo_canvas_points_unref (points); 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->created_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "created", G_CALLBACK (wire_created_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->rotated_handler_id = g_signal_connect_object ( + G_OBJECT (wire), "rotated", G_CALLBACK (wire_rotated_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); @@ -375,7 +386,7 @@ gboolean wire_item_event (WireItem *wire_item, GooCanvasItem *sheet_target_item, } } snap_to_grid (sheet->grid, &length.x, &length.y); - item_data_set_pos (sheet_item_get_data (SHEET_ITEM (wire_item)), &pos); + item_data_set_pos (sheet_item_get_data (SHEET_ITEM (wire_item)), &pos, EMIT_SIGNAL_CHANGED); wire_set_length (wire, &length); return TRUE; @@ -426,37 +437,60 @@ void wire_item_signal_connect_placed (WireItem *wire_item, Sheet *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_created_callback (ItemData *data, Coords *pos, SheetItem *item) { - WireItem *wire_item; - GooCanvasPoints *points; - Coords start_pos, length; - - g_return_if_fail (sheet_item != NULL); - g_return_if_fail (IS_WIRE_ITEM (sheet_item)); + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; - wire_item = WIRE_ITEM (sheet_item); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_WIRE (data)); + g_return_if_fail (pos != NULL); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); - wire_get_pos_and_length (WIRE (data), &start_pos, &length); + sheet = sheet_item_get_sheet (item); + sv = schematic_view_get_schematicview_from_sheet (sheet); - points = goo_canvas_points_new (2); - points->coords[0] = 0; - points->coords[1] = 0; - points->coords[2] = length.x; - points->coords[3] = length.y; + sdata.type = WIRE_CREATED; + sdata.s_item = item; + sdata.u.moved.coords.x = pos->x; + sdata.u.moved.coords.y = pos->y; + sdata.u.moved.delta.x = .0; + sdata.u.moved.delta.y = .0; + if (stack_is_item_registered (&sdata)) + return; - g_object_set (wire_item->priv->line, "points", points, NULL); - goo_canvas_points_unref (points); + sdata.group = stack_get_group (sdata.s_item, NEW_GROUP); + stack_push (undo_stack, &sdata, sv); +} - g_object_set (wire_item, "x", start_pos.x, "y", start_pos.y, NULL); +static void wire_rotated_callback (ItemData *data, gpointer params) +{ + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; - g_object_set (wire_item->priv->resize2, "x", length.x - RESIZER_SIZE, "y", - length.y - RESIZER_SIZE, NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_WIRE (data)); + g_return_if_fail (params != NULL); + + sdata.type = WIRE_ROTATED; + sdata.s_item = ((callback_params_t *)params)->s_item; + sdata.u.rotated.center = ((callback_params_t *)params)->center; + sdata.u.rotated.angle = ((callback_params_t *)params)->angle; + sdata.u.rotated.bbox1 = *(((callback_params_t *)params)->bbox1); + sdata.u.rotated.bbox2 = *(((callback_params_t *)params)->bbox2); + sdata.group = ((callback_params_t *)params)->group; + if (stack_is_item_registered (&sdata)) + return; - // Invalidate the bounding box cache. - wire_item->priv->cache_valid = FALSE; + sheet = sheet_item_get_sheet (sdata.s_item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + stack_push (undo_stack, &sdata, sv); } +#if 0 static void wire_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item) { GooCanvasPoints *points; @@ -486,6 +520,7 @@ static void wire_flipped_callback (ItemData *data, IDFlip direction, SheetItem * // Invalidate the bounding box cache. priv->cache_valid = FALSE; } +#endif static int select_idle_callback (WireItem *item) { @@ -515,7 +550,7 @@ static int deselect_idle_callback (WireItem *item) return FALSE; } -static void selection_changed (WireItem *item, gboolean select, gpointer user) +static void selection_changed (gpointer *item, gboolean select, gpointer user) { g_object_ref (G_OBJECT (item)); if (select) { @@ -575,7 +610,7 @@ static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *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_boundingbox (WireItem *item, Coords *p1, Coords *p2) +static void get_boundingbox (WireItem *item, Coords *p1, Coords *p2) { g_return_if_fail (item != NULL); g_return_if_fail (IS_WIRE_ITEM (item)); @@ -708,7 +743,34 @@ static int unhighlight_wire (WireItem *item) // FIXME get rid of static void wire_moved_callback (ItemData *data, Coords *pos, SheetItem *item) { - // NG_DEBUG ("wire MOVED callback called - LEGACY"); + SchematicView *sv = NULL; + Sheet *sheet = NULL; + stack_data_t sdata = { 0 }; + Coords *delta = NULL; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_WIRE (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + g_return_if_fail (pos != NULL); + + sheet = sheet_item_get_sheet (item); + sv = schematic_view_get_schematicview_from_sheet (sheet); + + sdata.type = WIRE_MOVED; + sdata.s_item = item; + sdata.u.moved.coords.x = pos->x; + sdata.u.moved.coords.y = pos->y; + if (stack_is_item_registered (&sdata)) + return; + + delta = stack_get_multiple_group (item, pos, &sdata.group); + if (delta) { + sdata.u.moved.delta.x = delta->x; + sdata.u.moved.delta.y = delta->y; + } + + stack_push (undo_stack, &sdata, sv); } static void wire_item_place (SheetItem *item, Sheet *sheet) diff --git a/src/sheet/wire-item.h b/src/sheet/wire-item.h index b7a47be3..e1ceb454 100644 --- a/src/sheet/wire-item.h +++ b/src/sheet/wire-item.h @@ -7,12 +7,14 @@ * Ricardo Markiewicz * Andres de Barbara * Marc Lorber + * Daniel Dwek * * 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) 2022-2023 Daniel Dwek * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -35,9 +37,9 @@ #include +#include "wire.h" #include "sheet.h" #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)) @@ -59,7 +61,7 @@ typedef struct } WireItemClass; GType wire_item_get_type (void); -WireItem *wire_item_new (Sheet *sheet, Wire *wire); +WireItem *wire_item_new (Sheet *sheet, Wire *wire, gdouble *ret_points); 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); diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 00000000..4dc695e6 --- /dev/null +++ b/src/stack.c @@ -0,0 +1,334 @@ +/* + * stack.c + * + * + * Authors: + * Daniel Dwek + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 2022-2023 Daniel Dwek + * + * 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 +#include +#include +#include +#include +#include +#include "debug.h" +#include "stack.h" + +historial_stack_t *undo_stack = NULL; +historial_stack_t *redo_stack = NULL; + +guint stack_get_size (historial_stack_t *stack) +{ + return stack->size; +} + +stack_item_t *stack_get_top (historial_stack_t *stack) +{ + if (stack->size) + return stack->top; + else + return NULL; +} + +SheetItem *stack_get_sheetitem_by_itemdata (ItemData *data) +{ + stack_item_t *iter = NULL; + + if (!data) + return NULL; + + for (iter = stack_get_top (undo_stack); iter; iter = iter->prev) { + if (sheet_item_get_data (iter->data->s_item) == data) + return iter->data->s_item; + } + + return NULL; +} + +Coords *stack_get_multiple_group (SheetItem *item, Coords *pos, gint *ret_group) +{ + stack_item_t *iter = NULL; + Coords *delta = NULL; + static Coords prev_delta = { .0 }; + + g_return_val_if_fail (item != NULL, NULL); + g_return_val_if_fail (ret_group != NULL, NULL); + + delta = g_new0 (Coords, 1); + g_assert (delta != NULL); + + for (iter = stack_get_top (undo_stack); iter; iter = iter->prev) { + if (iter->data->s_item != item) + continue; + + delta->x = pos->x - iter->data->u.moved.coords.x; + delta->y = pos->y - iter->data->u.moved.coords.y; + + if (fabs (delta->x - prev_delta.x) < 1e-2 && fabs (delta->y - prev_delta.y) < 1e-2) { + *ret_group = stack_get_group (iter->data->s_item, SAME_GROUP); + } else { + *ret_group = stack_get_group (iter->data->s_item, NEW_GROUP); + prev_delta.x = delta->x; + prev_delta.y = delta->y; + } + + return delta; + } + + return NULL; +} + +gint stack_get_group_for_rotation (SheetItem *item, Coords *center, gint angle, Coords *bbox1, Coords *bbox2) +{ + gint ret; + stack_item_t *iter = NULL; + static Coords gcenter = { .0 }; + + if (!item || !center) + return -1; + + for (iter = stack_get_top (undo_stack); iter; iter = iter->prev) { + if (iter->data->type == PART_MOVED || iter->data->type == WIRE_MOVED) { + gcenter.x = center->x; + gcenter.y = center->y; + ret = stack_get_group (item, NEW_GROUP); + break; + } else if (iter->data->type == PART_ROTATED || iter->data->type == WIRE_ROTATED) { + if (fabs (gcenter.x - center->x) < 1e-2 && fabs (gcenter.y - center->y) < 1e-2) { + ret = stack_get_group (item, SAME_GROUP); + break; + } else { + gcenter.x = center->x; + gcenter.y = center->y; + ret = stack_get_group (item, NEW_GROUP); + break; + } + } + } + + return ret; +} + +gint stack_get_group (SheetItem *item, group_assignment_t hint) +{ + stack_item_t *iter = NULL; + static gint group = 0; + + g_return_val_if_fail (item != NULL, 0); + + for (iter = stack_get_top (undo_stack); iter; iter = iter->prev) { + if (hint == SAME_GROUP) + return group; + else if (hint == NEW_GROUP) + return ++group; + } + + return ++group; +} + +gboolean stack_is_item_registered (stack_data_t *ref) +{ + stack_item_t *iter = NULL; + + g_return_val_if_fail (ref != NULL, FALSE); + + for (iter = stack_get_top (undo_stack); iter; iter = iter->prev) { + switch (ref->type) { + case PART_CREATED: + case WIRE_CREATED: + case PART_MOVED: + case WIRE_MOVED: + if (fabs (iter->data->u.moved.coords.x - ref->u.moved.coords.x) < 1e-2 && + fabs (iter->data->u.moved.coords.y - ref->u.moved.coords.y) < 1e-2) + return TRUE; + break; + case PART_ROTATED: + case WIRE_ROTATED: + if (iter->data->s_item == ref->s_item && iter->data->u.rotated.angle == ref->u.rotated.angle && + fabs (iter->data->u.rotated.center.x - ref->u.rotated.center.x) < 1e-2 && + fabs (iter->data->u.rotated.center.y - ref->u.rotated.center.y) < 1e-2 && + fabs (iter->data->u.rotated.bbox1.x - ref->u.rotated.bbox1.x) < 1e-2 && + fabs (iter->data->u.rotated.bbox1.y - ref->u.rotated.bbox1.y) < 1e-2 && + fabs (iter->data->u.rotated.bbox2.x - ref->u.rotated.bbox2.x) < 1e-2 && + fabs (iter->data->u.rotated.bbox2.y - ref->u.rotated.bbox2.y) < 1e-2) + return TRUE; + break; + case PART_DELETED: + case WIRE_DELETED: + if (fabs (iter->data->u.deleted.coords.x - ref->u.deleted.coords.x) < 1e-2 && + fabs (iter->data->u.deleted.coords.y - ref->u.deleted.coords.y) < 1e-2) + return TRUE; + break; + }; + } + + return FALSE; +} + +static void stack_set_sensitive (historial_stack_t *stack, SchematicView *sv, gboolean activate) +{ + GtkUIManager *ui_mgr = NULL; + const gchar *commands[] = { "/MainMenu/MenuEdit/Undo", "/MainMenu/MenuEdit/Redo" }; + + if (!stack || !sv) + return; + + ui_mgr = schematic_view_get_ui_manager (sv); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_mgr, (stack == undo_stack) ? commands[0] : commands[1]), activate); +} + +static void stack_debug (historial_stack_t *stack) +{ + gint i; + stack_item_t *iter = NULL; + gchar *str[] = { "PART_CREATED", "PART_MOVED", "PART_ROTATED", "PART_DELETED", + "WIRE_CREATED", "WIRE_MOVED", "WIRE_ROTATED", "WIRE_DELETED" }; + + if (stack == undo_stack) + fprintf (stderr, "%sUNDO:%s\n", color_orange, color_restore); + else + fprintf (stderr, "%sREDO:%s\n", color_magenta, color_restore); + + i = stack_get_size (stack) - 1; + for (iter = stack_get_top (stack); iter; iter = iter->prev, i--) { + fprintf (stderr, "type = %s / group = %d / item = %s%p%s / ", str[iter->data->type], + iter->data->group, COLOR_SHEETITEM, iter->data->s_item, COLOR_NORMAL); + switch (iter->data->type) { + case PART_CREATED: + fprintf (stderr, "part created at (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.moved.coords.x, iter->data->u.moved.coords.y, COLOR_NORMAL); + break; + case PART_MOVED: + fprintf (stderr, "part moved to (%s%f, %f%s) with delta = (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.moved.coords.x, iter->data->u.moved.coords.y, COLOR_NORMAL, + COLOR_COORDS, iter->data->u.moved.delta.x, iter->data->u.moved.delta.y, COLOR_NORMAL); + break; + case PART_ROTATED: + fprintf (stderr, "part rotated %d degrees around (%s%f, %f%s) and bounds (%f, %f) -> (%f, %f)\n", + iter->data->u.rotated.angle, COLOR_COORDS, + iter->data->u.rotated.center.x, iter->data->u.rotated.center.y, COLOR_NORMAL, + iter->data->u.rotated.bbox1.x, iter->data->u.rotated.bbox1.y, + iter->data->u.rotated.bbox2.x, iter->data->u.rotated.bbox2.y); + break; + case PART_DELETED: + fprintf (stderr, "part deleted and relocated to (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.deleted.coords.x, iter->data->u.deleted.coords.y, COLOR_NORMAL); + break; + case WIRE_CREATED: + fprintf (stderr, "wire created at (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.moved.coords.x, iter->data->u.moved.coords.y, COLOR_NORMAL); + break; + case WIRE_MOVED: + fprintf (stderr, "wire moved to (%s%f, %f%s) with delta = (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.moved.coords.x, iter->data->u.moved.coords.y, COLOR_NORMAL, + COLOR_COORDS, iter->data->u.moved.delta.x, iter->data->u.moved.delta.y, COLOR_NORMAL); + break; + case WIRE_ROTATED: + fprintf (stderr, "wire rotated %d degrees around (%s%f, %f%s)\n", + iter->data->u.rotated.angle, COLOR_COORDS, + iter->data->u.rotated.center.x, iter->data->u.rotated.center.y, COLOR_NORMAL); + break; + case WIRE_DELETED: + fprintf (stderr, "wire deleted and relocated to (%s%f, %f%s)\n", COLOR_COORDS, + iter->data->u.deleted.coords.x, iter->data->u.deleted.coords.y, COLOR_NORMAL); + break; + }; + } +} + +gboolean stack_push (historial_stack_t *stack, stack_data_t *data, SchematicView *sv) +{ + stack_item_t *top = NULL; + + if (stack && data && sv) { + if (stack_is_item_registered (data)) + return FALSE; + + top = g_new0 (stack_item_t, 1); + if (!top) + return FALSE; + + if (!stack->size) { + stack->top = top; + stack->top->prev = NULL; + } else { + top->prev =stack->top; + stack->top = top; + } + + stack->top->data = g_new0 (stack_data_t, 1); + if (!stack->top->data) + return FALSE; + stack->top->data = memcpy (stack->top->data, data, sizeof (stack_data_t)); + stack->size++; + + if (stack_get_size (undo_stack)) + stack_set_sensitive (undo_stack, sv, TRUE); + else + stack_set_sensitive (undo_stack, sv, FALSE); + + if (stack_get_size (redo_stack)) + stack_set_sensitive (redo_stack, sv, TRUE); + else + stack_set_sensitive (redo_stack, sv, FALSE); + + stack_debug (stack); + + return TRUE; + } else { + return FALSE; + } +} + +stack_data_t **stack_pop (historial_stack_t *stack, gint *ret_nitems) +{ + stack_data_t **ret = NULL; + gint i = 0, prev_group = -1; + stack_item_t *iter = NULL; + + for (iter = stack_get_top (stack); iter; iter = iter->prev) { + if (!i) + prev_group = iter->data->group; + if (iter->data->group == prev_group) + i++; + } + + if (ret_nitems) + *ret_nitems = i; + + ret = g_new0 (stack_data_t *, i); + if (!ret) + return NULL; + + for (; i > 0; i--) { + ret[i - 1] = g_new0 (stack_data_t, 1); + ret[i - 1] = memcpy (ret[i - 1], stack->top->data, sizeof (stack_data_t)); + g_free (stack->top->data); + stack->top->data = NULL; + stack->top = stack->top->prev; + stack->size--; + } + + return ret; +} + diff --git a/src/stack.h b/src/stack.h new file mode 100644 index 00000000..2ba8a3cd --- /dev/null +++ b/src/stack.h @@ -0,0 +1,87 @@ +/* + * stack.h + * + * + * Authors: + * Daniel Dwek + * + * Web page: https://ahoi.io/project/oregano + * + * Copyright (C) 2022-2023 Daniel Dwek + * + * 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 __STACK_H +#define __STACK_H 1 + +#include "goocanvas.h" +#include "item-data.h" +#include "wire-item.h" + +typedef enum { PART_CREATED, PART_MOVED, PART_ROTATED, PART_DELETED, + WIRE_CREATED, WIRE_MOVED, WIRE_ROTATED, WIRE_DELETED, } item_type_t; + +typedef enum { SAME_GROUP, NEW_GROUP } group_assignment_t; + +typedef struct stack_data_st { + item_type_t type; + gint group; + SheetItem *s_item; + union { + struct { + Coords coords; + Coords delta; + } moved; + struct { + Coords center; + gint angle; + Coords bbox1; + Coords bbox2; + } rotated; + struct { + Coords coords; + GooCanvasGroup *canvas_group; + } deleted; + } u; +} stack_data_t; + +typedef struct stack_item_st { + stack_data_t *data; + struct stack_item_st *prev; +} stack_item_t; + +typedef struct stack_st { + stack_item_t *top; + unsigned long long size; +} historial_stack_t; + +extern historial_stack_t *undo_stack; +extern historial_stack_t *redo_stack; + +#include "sheet-item.h" +#include "load-library.h" + +guint stack_get_size (historial_stack_t *stack); +stack_item_t *stack_get_top (historial_stack_t *stack); +SheetItem *stack_get_sheetitem_by_itemdata (ItemData *data); +Coords *stack_get_multiple_group (SheetItem *item, Coords *pos, gint *ret_group); +gint stack_get_group_for_rotation (SheetItem *item, Coords *center, gint angle, Coords *bbox1, Coords *bbox2); +gint stack_get_group (SheetItem *item, group_assignment_t hint); +gboolean stack_is_item_registered (stack_data_t *data); +gboolean stack_push (historial_stack_t *stack, stack_data_t *data, SchematicView *sv); +stack_data_t **stack_pop (historial_stack_t *stack, gint *ret_nitems); +#endif diff --git a/test/test.c b/test/test.c index 9741532e..c504862c 100644 --- a/test/test.c +++ b/test/test.c @@ -12,6 +12,7 @@ #include "test_update_connection_designators.c" #include "test_thread_pipe.c" #include "test_engine_ngspice.c" +#include "test_stack.c" #if DEBUG_FORCE_FAIL void @@ -30,6 +31,7 @@ main (int argc, char *argv[]) g_test_init (&argc, &argv, NULL); + g_test_add_func ("/core/stack", test_stack); g_test_add_func ("/core/coords", test_coords); g_test_add_func ("/core/model/wire/intersection", test_wire_intersection); g_test_add_func ("/core/model/wire/tcrossing", test_wire_tcrossing); diff --git a/test/test_nodestore.c b/test/test_nodestore.c index 50bbd399..2ee99f23 100644 --- a/test/test_nodestore.c +++ b/test/test_nodestore.c @@ -2,6 +2,7 @@ #define TEST_NODESTORE #include +#include "stack.h" void test_nodestore () @@ -11,12 +12,16 @@ test_nodestore () Part *part; Wire *wire; Node *node; + Coords bbox1, bbox2, center; Coords p_pos = {111.,22.}; Coords n_pos = {111.,33.}; Coords w_pos = {111.,7.}; Coords w_len = {0.,88.}; + undo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + redo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + store = node_store_new (); part = part_new (); wire = wire_new (); @@ -30,24 +35,26 @@ test_nodestore () part_set_pins (part, list); g_slist_free (list); - item_data_set_pos (ITEM_DATA (part), &p_pos); + item_data_set_pos (ITEM_DATA (part), &p_pos, EMIT_SIGNAL_CHANGED); - item_data_set_pos (ITEM_DATA (wire), &w_pos); + item_data_set_pos (ITEM_DATA (wire), &w_pos, EMIT_SIGNAL_CHANGED); wire_set_length (wire, &w_len); node_store_add_part (store, part); node_store_add_wire (store, wire); + item_data_get_absolute_bbox (ITEM_DATA (part), &bbox1, &bbox2); + center = coords_average (&bbox1, &bbox2); { for (i=0; i<11; i++) - item_data_rotate (ITEM_DATA (part), 90, NULL); - item_data_set_pos (ITEM_DATA (part), &w_len); + item_data_rotate (ITEM_DATA (part), 90, ¢er, &bbox1, &bbox2, "rotate_items"); + item_data_set_pos (ITEM_DATA (part), &w_len, EMIT_SIGNAL_CHANGED); for (i=0; i<4; i++) - item_data_rotate (ITEM_DATA (part), 90, NULL); - item_data_set_pos (ITEM_DATA (part), &n_pos); + item_data_rotate (ITEM_DATA (part), 90, ¢er, &bbox1, &bbox2, "rotate_items"); + item_data_set_pos (ITEM_DATA (part), &n_pos, EMIT_SIGNAL_CHANGED); for (i=0; i<7; i++) - item_data_rotate (ITEM_DATA (part), -90, NULL); - item_data_set_pos (ITEM_DATA (part), &p_pos); + item_data_rotate (ITEM_DATA (part), -90, ¢er, &bbox1, &bbox2, "rotate_items"); + item_data_set_pos (ITEM_DATA (part), &p_pos, EMIT_SIGNAL_CHANGED); } g_assert (node_store_is_wire_at_pos (store, n_pos)); g_assert (node_store_is_pin_at_pos (store, n_pos)); diff --git a/test/test_stack.c b/test/test_stack.c new file mode 100644 index 00000000..a575a89d --- /dev/null +++ b/test/test_stack.c @@ -0,0 +1,122 @@ +#ifndef TEST_STACK +#define TEST_STACK + +#include +#include +#include "test_stack.h" + +historial_stack_t *test_undo_stack = NULL; +historial_stack_t *test_redo_stack = NULL; + +unsigned long long test_stack_get_size (historial_stack_t *stack) +{ + return stack->size; +} + +stack_item_t *test_stack_get_top (historial_stack_t *stack) +{ + if (stack->size) + return stack->top; + else + return NULL; +} + +gboolean test_stack_push (historial_stack_t *stack, stack_data_t *data) +{ + stack_item_t *top = NULL; + + if (stack && data) { + top = (stack_item_t *) calloc (1, sizeof (stack_item_t)); + if (!top) + return FALSE; + + if (!stack->size) { + stack->top = top; + stack->top->prev = NULL; + } else { + top->prev =stack->top; + stack->top = top; + } + + stack->top->data = (stack_data_t *) calloc (1, sizeof (stack_data_t)); + if (!stack->top->data) + return FALSE; + + stack->top->data = memcpy (stack->top->data, data, sizeof (stack_data_t)); + stack->size++; + return TRUE; + } else { + return FALSE; + } +} + +stack_data_t *test_stack_pop (historial_stack_t *stack) +{ + stack_data_t *ret = NULL; + + if (stack->top) { + ret = (stack_data_t *) calloc (1, sizeof (stack_data_t)); + if (!ret) + return NULL; + + ret = memcpy (ret, stack->top->data, sizeof (stack_data_t)); + free (stack->top->data); + stack->top->data = NULL; + stack->top =stack->top->prev; + stack->size--; + return ret; + } else { + return NULL; + } +} + +void test_stack (void) +{ + int i; + gboolean ret = FALSE; + stack_data_t data, *rdata = NULL; + + test_undo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + if (!test_undo_stack) + return; + + test_redo_stack = (historial_stack_t *) calloc (1, sizeof (historial_stack_t)); + if (!test_redo_stack) + return; + + for (i = 0; i < 10; i++) { + ret = test_stack_push (test_undo_stack, &data); + if (ret) + fprintf (stderr, "Pushing item %d onto the UNDO-STACK\n", i); + } + + fprintf (stderr, "\n"); + + i = test_stack_get_size (test_undo_stack) - 1; + while (1) { + rdata = test_stack_pop (test_undo_stack); + if (!rdata) + break; + + fprintf (stderr, "Popping item %d from the UNDO-STACK\n", i); + ret = test_stack_push (test_redo_stack, rdata); + if (ret) + fprintf (stderr, "Pushing item %d onto the REDO-STACK\n", i); + + i--; + }; + + fprintf (stderr, "\n"); + + i = test_stack_get_size (test_redo_stack) - 1; + while (1) { + rdata = test_stack_pop (test_redo_stack); + if (!rdata) + break; + + fprintf (stderr, "Popping item %d from the REDO-STACK\n", i); + i--; + } +} + +#endif diff --git a/test/test_stack.h b/test/test_stack.h new file mode 100644 index 00000000..041238e3 --- /dev/null +++ b/test/test_stack.h @@ -0,0 +1,13 @@ +#ifndef __TEST_STACK_H +#define __TEST_STACK_H + +#include "sheet-item.h" + +extern historial_stack_t *test_undo_stack; +extern historial_stack_t *test_redo_stack; + +unsigned long long test_stack_get_size (historial_stack_t *stack); +stack_item_t *test_stack_get_top (historial_stack_t *stack); +gboolean test_stack_push (historial_stack_t *stack, stack_data_t *data); +stack_data_t *test_stack_pop (historial_stack_t *stack); +#endif diff --git a/test/test_wire.c b/test/test_wire.c index ddbeee7a..4b0f4d46 100644 --- a/test/test_wire.c +++ b/test/test_wire.c @@ -43,12 +43,12 @@ test_wire_intersection () Coords where = {-77777.77,-77.7777}; const Coords expected = {50.0,50.0}; - Wire *a = wire_new (NULL); - Wire *b = wire_new (NULL); + Wire *a = wire_new (); + Wire *b = wire_new (); - item_data_set_pos (ITEM_DATA (a), &p1); + item_data_set_pos (ITEM_DATA (a), &p1, EMIT_SIGNAL_CHANGED); wire_set_length (a, &l1); - item_data_set_pos (ITEM_DATA (b), &p2); + item_data_set_pos (ITEM_DATA (b), &p2, EMIT_SIGNAL_CHANGED); wire_set_length (b, &l2); g_assert (do_wires_intersect (a,b,&where)); @@ -72,13 +72,13 @@ test_wire_tcrossing () Coords where = {-77.77,-77.77}; const Coords expected = p1; - Wire *a = wire_new (NULL); - Wire *b = wire_new (NULL); + Wire *a = wire_new (); + Wire *b = wire_new (); { - item_data_set_pos (ITEM_DATA (a), &p1); + item_data_set_pos (ITEM_DATA (a), &p1, EMIT_SIGNAL_CHANGED); wire_set_length (a, &l1); - item_data_set_pos (ITEM_DATA (b), &p2); + item_data_set_pos (ITEM_DATA (b), &p2, EMIT_SIGNAL_CHANGED); wire_set_length (b, &l2); g_assert (is_t_crossing (a, b, &where)); @@ -86,9 +86,9 @@ test_wire_tcrossing () } { - item_data_set_pos (ITEM_DATA (a), &p1); + item_data_set_pos (ITEM_DATA (a), &p1, EMIT_SIGNAL_CHANGED); wire_set_length (a, &l2); - item_data_set_pos (ITEM_DATA (b), &p2); + item_data_set_pos (ITEM_DATA (b), &p2, EMIT_SIGNAL_CHANGED); wire_set_length (b, &l1); g_assert (!is_t_crossing (a, b, &where)); diff --git a/wscript b/wscript index 03de4b3e..c301575a 100644 --- a/wscript +++ b/wscript @@ -85,7 +85,12 @@ def configure(conf): # -ggdb vs -g -- http://stackoverflow.com/questions/668962 if conf.options.build_debug: - conf.env.CFLAGS = ['-ggdb', '-Wall'] + # We do not want to see so many warnings about deprecated declarations, + # especially those ones which do not have recommended functions to replace them. + # That's the reason why we include "-Wno-deprecated-declarations" switch below, however, + # as software evolutionates, linkage with dependant libraries could be impossible to + # achieve without any prior notice from such object files. + conf.env.CFLAGS = ['-ggdb', '-Wall', '-Wno-deprecated-declarations'] conf.define('DEBUG',1) elif conf.options.build_release: conf.env.CFLAGS = ['-O2', '-Wall']