diff options
Diffstat (limited to 'src/sheet')
-rw-r--r-- | src/sheet/Makefile.am | 28 | ||||
-rw-r--r-- | src/sheet/grid.c | 394 | ||||
-rw-r--r-- | src/sheet/grid.h | 61 | ||||
-rw-r--r-- | src/sheet/node-item.c | 112 | ||||
-rw-r--r-- | src/sheet/node-item.h | 64 | ||||
-rw-r--r-- | src/sheet/part-item.c | 1632 | ||||
-rw-r--r-- | src/sheet/part-item.h | 70 | ||||
-rw-r--r-- | src/sheet/sheet-item-factory.c | 70 | ||||
-rw-r--r-- | src/sheet/sheet-item-factory.h | 38 | ||||
-rw-r--r-- | src/sheet/sheet-item.c | 1222 | ||||
-rw-r--r-- | src/sheet/sheet-item.h | 129 | ||||
-rw-r--r-- | src/sheet/sheet-private.h | 54 | ||||
-rw-r--r-- | src/sheet/sheet.c | 469 | ||||
-rw-r--r-- | src/sheet/sheet.h | 92 | ||||
-rw-r--r-- | src/sheet/textbox-item.c | 699 | ||||
-rw-r--r-- | src/sheet/textbox-item.h | 67 | ||||
-rw-r--r-- | src/sheet/wire-item.c | 916 | ||||
-rw-r--r-- | src/sheet/wire-item.h | 60 |
18 files changed, 6177 insertions, 0 deletions
diff --git a/src/sheet/Makefile.am b/src/sheet/Makefile.am new file mode 100644 index 0000000..37291fc --- /dev/null +++ b/src/sheet/Makefile.am @@ -0,0 +1,28 @@ +oreganodir = $(datadir)/oregano +INCLUDES = \ + $(OREGANO_CFLAGS) -I$(top_srcdir)/src -I$(top_srcdir)/src/model \ + -DOREGANO_GLADEDIR=\""$(oreganodir)/glade"\" \ + -DOREGANO_LIBRARYDIR=\""$(oreganodir)/libraries"\" \ + -DOREGANO_MODELDIR=\""$(oreganodir)/models"\" + +noinst_LIBRARIES = libsheet.a + +libsheet_a_SOURCES = \ + grid.c \ + grid.h \ + node-item.c \ + node-item.h \ + part-item.c \ + part-item.h \ + sheet.c \ + sheet.h \ + sheet-item.c \ + sheet-item.h \ + sheet-item-factory.c \ + sheet-item-factory.h \ + sheet-private.h \ + textbox-item.c \ + textbox-item.h \ + wire-item.c \ + wire-item.h +libsheet_a_LIBADD = libsheet.a diff --git a/src/sheet/grid.c b/src/sheet/grid.c new file mode 100644 index 0000000..9a06ee7 --- /dev/null +++ b/src/sheet/grid.c @@ -0,0 +1,394 @@ +/* + * grid.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <math.h> +#include "grid.h" +#include "sheet-private.h" + +#define ROUND(x) (floor((x)+0.5)) + +enum { + ARG_0, + ARG_COLOR, + ARG_SPACING, + ARG_SNAP +}; + +typedef struct { + guint snap : 1; + guint visible : 1; + GdkColor color; + gdouble spacing; + gdouble cached_zoom; + GdkGC *gc; +} GridPriv; + +static void grid_class_init (GridClass *class); +static void grid_init (Grid *grid); +static void grid_destroy (GtkObject *object); +static void grid_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *spec); +static void grid_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec); +static void grid_update (GnomeCanvasItem *item, gdouble *affine, + ArtSVP *clip_path, gint flags); +static void grid_realize (GnomeCanvasItem *item); +static void grid_unrealize (GnomeCanvasItem *item); +static void grid_draw (GnomeCanvasItem *grid, GdkDrawable *drawable, + gint x, gint y, gint width, gint height); +static double grid_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, + gint cy, GnomeCanvasItem **actual_item); + +static GnomeCanvasItemClass *grid_parent_class; + +GType +grid_get_type (void) +{ + static GType grid_type = 0; + + if (!grid_type) { + static const GTypeInfo grid_info = { + sizeof (GridClass), + NULL, + NULL, + (GClassInitFunc) grid_class_init, + NULL, + NULL, + sizeof (Grid), + 0, + (GInstanceInitFunc) grid_init, + NULL + }; + + grid_type = g_type_register_static(GNOME_TYPE_CANVAS_ITEM, + "Grid", &grid_info, 0); + } + + return grid_type; +} + +static void +grid_class_init (GridClass *class) +{ + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + GnomeCanvasItemClass *item_class; + + object_class = G_OBJECT_CLASS (class); + gtk_object_class = GTK_OBJECT_CLASS(class); + item_class = GNOME_CANVAS_ITEM_CLASS(class); + + gtk_object_class->destroy = grid_destroy; + + grid_parent_class = g_type_class_peek_parent (class); + + object_class->set_property = grid_set_property; + object_class->get_property = grid_get_property; + + g_object_class_install_property( + object_class, + ARG_COLOR, + g_param_spec_string("color", "Grid::color", "the color", + "black", G_PARAM_WRITABLE) + ); + g_object_class_install_property( + object_class, + ARG_SPACING, + g_param_spec_double("spacing", "Grid::spacing", + "the grid spacing", 0.0f, 100.0f, 10.0f, + G_PARAM_READWRITE) + ); + g_object_class_install_property( + object_class, + ARG_SNAP, + g_param_spec_boolean("snap", "Grid::snap", "snap to grid?", + TRUE,G_PARAM_READWRITE) + ); + + item_class->update = grid_update; + item_class->realize = grid_realize; + item_class->unrealize = grid_unrealize; + item_class->draw = grid_draw; + item_class->point = grid_point; +} + +static void +grid_init (Grid *grid) +{ + GridPriv *priv; + + priv = g_new0 (GridPriv, 1); + + grid->priv = priv; + + priv->spacing = 10.0; + priv->snap = TRUE; + priv->visible = TRUE; +} + +static void +grid_destroy (GtkObject *object) +{ + Grid *grid; + GridPriv *priv; + + grid = GRID (object); + priv = grid->priv; + + if (GTK_OBJECT_CLASS(grid_parent_class)->destroy) { + GTK_OBJECT_CLASS(grid_parent_class)->destroy(object); + } +} + +inline void +snap_to_grid (Grid *grid, gdouble *x, gdouble *y) +{ + GridPriv *priv; + gdouble spacing; + + priv = grid->priv; + spacing = priv->spacing; + + if (priv->snap) { + if (x) *x = ROUND((*x) / spacing) * spacing; + if (y) *y = ROUND((*y) / spacing) * spacing; + } +} + +static void +grid_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) +{ + GnomeCanvasItem *item; + Grid *grid; + GridPriv *priv; + GdkColor color; + + item = GNOME_CANVAS_ITEM (object); + grid = GRID (object); + priv = grid->priv; + + switch (prop_id){ + case ARG_COLOR: + if (gnome_canvas_get_color (item->canvas, + g_value_get_string(value), &color)) { + priv->color = color; + } else { + color.pixel = 0; + priv->color = color; + } + if (priv->gc) + gdk_gc_set_foreground (priv->gc, &color); + break; + + case ARG_SPACING: + priv->spacing = g_value_get_double(value); + break; + + case ARG_SNAP: + priv->snap = g_value_get_boolean (value); + break; + + default: + break; + } +} + +static void +grid_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec) +{ + Grid *grid; + GridPriv *priv; + + grid = GRID (object); + priv = grid->priv; + + switch (prop_id){ + case ARG_SPACING: + g_value_set_double(value, priv->spacing); + break; + case ARG_SNAP: + g_value_set_boolean(value, priv->snap); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(grid, prop_id, spec); + break; + } +} + +void +grid_show (Grid *grid, gboolean show) +{ + GridPriv *priv; + + priv = grid->priv; + + priv->visible = show; + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (grid)); +} + +gint +grid_is_snap (Grid *grid) +{ + GridPriv *priv; + + priv = grid->priv; + + return priv->snap; +} + +gint +grid_is_show (Grid *grid) +{ + GridPriv *priv; + + priv = grid->priv; + + return priv->visible; +} + +void +grid_snap (Grid *grid, gboolean snap) +{ + GridPriv *priv; + + priv = grid->priv; + + priv->snap = snap; +} + +static void +grid_update (GnomeCanvasItem *item, gdouble *affine, ArtSVP *clip_path, + gint flags) +{ + Grid *grid; + GridPriv *priv; + + grid = GRID (item); + + priv = grid->priv; + + priv->cached_zoom = SHEET(item->canvas)->priv->zoom; + + if (grid_parent_class->update) + (* grid_parent_class->update) (item, affine, clip_path, flags); + + gdk_gc_set_foreground (priv->gc, &priv->color); + gnome_canvas_update_bbox (item, 0, 0, INT_MAX, INT_MAX); +} + +static void +grid_realize (GnomeCanvasItem *item) +{ + Grid *grid; + GridPriv *priv; + + grid = GRID (item); + + priv = grid->priv; + + if (grid_parent_class->realize) + (* grid_parent_class->realize) (item); + + priv->gc = gdk_gc_new (item->canvas->layout.bin_window); +} + +static void +grid_unrealize (GnomeCanvasItem *item) +{ + Grid *grid; + GridPriv *priv; + + grid = GRID (item); + priv = grid->priv; + + g_object_unref (priv->gc); + + if (grid_parent_class->unrealize) + (* grid_parent_class->unrealize) (item); +} + +inline static gint +start_coord (long c, long g) +{ + long m; + + if (c > 0){ + m = c % g; + if (m == 0) + return m; + else + return g - m; + } else + return (-c) % g; +} + +static void +grid_draw (GnomeCanvasItem *canvas, GdkDrawable *drawable, gint x, gint y, + gint width, gint height) +{ + GridPriv *priv; + Grid *grid; + GdkGC *gc; + gdouble gx, gy, sgx, sgy; + gdouble spacing; + + grid = GRID(canvas); + priv = grid->priv; + + if (!priv->visible) + return; + + spacing = priv->spacing * priv->cached_zoom * 10000; + gc = priv->gc; + + sgx = start_coord (x * 10000, spacing) - spacing; + sgy = start_coord (y * 10000, spacing) - spacing; + + for (gx = sgx; gx <= width * 10000; gx += spacing) + for (gy = sgy; gy <= height * 10000; gy += spacing) { + gdk_draw_point (drawable, gc, rint (gx/10000 + 0.45), + rint (gy/10000 + 0.45)); + } +} + +static double +grid_point (GnomeCanvasItem *item, gdouble x, gdouble y, gint cx, gint cy, + GnomeCanvasItem **actual_item) +{ + /* + * The grid is everywhere. (That's a bug). + */ + *actual_item = item; + return 0.0; +} + diff --git a/src/sheet/grid.h b/src/sheet/grid.h new file mode 100644 index 0000000..3835e9d --- /dev/null +++ b/src/sheet/grid.h @@ -0,0 +1,61 @@ +/* + * grid.h + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GRID_H +#define __GRID_H + +#include <libgnomecanvas/gnome-canvas.h> + +#define GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, grid_get_type (), Grid)) +#define GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, grid_get_type (), GridClass)) +#define IS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, grid_get_type ())) + +typedef struct _Grid Grid; +typedef struct _GridClass GridClass; + +struct _Grid { + GnomeCanvasItem item; + + gpointer priv; +}; + +struct _GridClass { + GnomeCanvasItemClass parent_class; +}; + +GType grid_get_type (void); +void snap_to_grid(Grid *grid, double *x, double *y); + +void grid_show (Grid *grid, gboolean snap); +void grid_snap (Grid *grid, gboolean snap); +gint grid_is_show (Grid *grid); +gint grid_is_snap (Grid *grid); + +#endif + diff --git a/src/sheet/node-item.c b/src/sheet/node-item.c new file mode 100644 index 0000000..a4319bf --- /dev/null +++ b/src/sheet/node-item.c @@ -0,0 +1,112 @@ +/* + * node-item.c + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <gnome.h> +#include "node-item.h" + +static void node_item_init (NodeItem *item); +static void node_item_class_init (NodeItemClass *klass); + +struct _NodeItemPriv { + GnomeCanvasItem *dot_item; +}; + +static GnomeCanvasGroupClass *parent_class = NULL; + + +GType +node_item_get_type (void) +{ + static GType item_type = 0; + + if (!item_type) + { + static const GTypeInfo item_info = + { + sizeof (NodeItemClass), + NULL, + NULL, + (GClassInitFunc) node_item_class_init, + NULL, + NULL, + sizeof (NodeItem), + 0, + (GInstanceInitFunc) node_item_init, + NULL + }; + + item_type = g_type_register_static(GNOME_TYPE_CANVAS_GROUP, "NodeItem", + &item_info, 0); + } + + return item_type; +} + +static void +node_item_class_init (NodeItemClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); +} + + +static void +node_item_init (NodeItem *item) +{ + item->priv = g_new0 (NodeItemPriv, 1); +} + +void +node_item_show_dot (NodeItem *item, gboolean show) +{ + g_return_if_fail (item != NULL); + g_return_if_fail (IS_NODE_ITEM (item)); + + if (show) { + if (item->priv->dot_item == NULL) { + item->priv->dot_item = gnome_canvas_item_new ( + GNOME_CANVAS_GROUP (item), + gnome_canvas_ellipse_get_type (), + "x1", -2.0, + "y1", -2.0, + "x2", 2.0, + "y2", 2.0, + "fill_color", "black", + NULL); + } + + gnome_canvas_item_show (item->priv->dot_item); + } else { + if (item->priv->dot_item != NULL) + gnome_canvas_item_hide (item->priv->dot_item); + } +}
\ No newline at end of file diff --git a/src/sheet/node-item.h b/src/sheet/node-item.h new file mode 100644 index 0000000..141c72c --- /dev/null +++ b/src/sheet/node-item.h @@ -0,0 +1,64 @@ +/* + * + * node-item.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __NODE_ITEM_H__ +#define __NODE_ITEM_H__ + +#include <gtk/gtk.h> +#include <gnome.h> + +#define TYPE_NODE_ITEM (node_item_get_type ()) +#define NODE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_NODE_ITEM, NodeItem)) +#define NODE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_NODE_ITEM, NodeItemClass)) +#define IS_NODE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_NODE_ITEM)) +#define IS_NODE_ITEM_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_NODE_ITEM, NodeItemClass)) + + +typedef struct _NodeItem NodeItem; +typedef struct _NodeItemClass NodeItemClass; +typedef struct _NodeItemPriv NodeItemPriv; + +struct _NodeItem +{ + GnomeCanvasGroup parent; + NodeItemPriv *priv; +}; + +struct _NodeItemClass +{ + GnomeCanvasGroupClass parent_class; +}; + + +GType node_item_get_type (void); +GtkWidget *node_item_new (void); +void node_item_show_dot (NodeItem *item, gboolean show); + +#endif /* __NODE_ITEM_H__ */ diff --git a/src/sheet/part-item.c b/src/sheet/part-item.c new file mode 100644 index 0000000..cdad022 --- /dev/null +++ b/src/sheet/part-item.c @@ -0,0 +1,1632 @@ +/* + * part-item.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glade/glade.h> +#include <math.h> +#include <string.h> +#include "main.h" +#include "schematic-view.h" +#include "sheet-private.h" +#include "sheet-item.h" +#include "part-item.h" +#include "part-private.h" +#include "part-property.h" +#include "load-library.h" +#include "load-common.h" +#include "part-label.h" +#include "stock.h" +#include "dialogs.h" + + +#define NORMAL_COLOR "red" +#define LABEL_COLOR "dark cyan" +#define SELECTED_COLOR "green" +#define O_DEBUG 0 + +static void part_item_class_init(PartItemClass *klass); +static void part_item_init(PartItem *gspart); +static void part_item_destroy(GtkObject *object); +static void part_item_moved(SheetItem *sheet_item); + +static void edit_properties (SheetItem *object); + +static void selection_changed (PartItem *item, gboolean select, + gpointer user_data); +static int select_idle_callback (PartItem *item); +static int deselect_idle_callback (PartItem *item); + +static void update_canvas_labels (PartItem *part_item); + +static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2); +inline static void get_cached_bounds (PartItem *item, SheetPos *p1, + SheetPos *p2); +static void show_labels (SheetItem *sheet_item, gboolean show); +static void part_item_paste (SchematicView *sv, ItemData *data); + +static void part_rotated_callback (ItemData *data, int angle, SheetItem *item); +static void part_flipped_callback (ItemData *data, gboolean horizontal, + SheetItem *sheet_item); + +static void part_moved_callback (ItemData *data, SheetPos *pos, + SheetItem *item); + +static void part_item_place (SheetItem *item, SchematicView *sv); +static void part_item_place_ghost (SheetItem *item, SchematicView *sv); + +static void create_canvas_items (GnomeCanvasGroup *group, + LibraryPart *library_part); +static void create_canvas_labels (PartItem *item, Part *part); +static void create_canvas_label_nodes (PartItem *item, Part *part); + +static void part_item_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *spec); +static void part_item_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *spec); + +enum { + ARG_0, + ARG_NAME, + ARG_SYMNAME, + ARG_LIBNAME, + ARG_REFDES, + ARG_TEMPLATE, + ARG_MODEL +}; + +struct _PartItemPriv { + guint cache_valid : 1; + guint highlight : 1; + + GnomeCanvasGroup *label_group; + GSList *label_items; + + GnomeCanvasGroup *node_group; + GSList *label_nodes; + + /* + * Cached bounding box. This is used to make + * the rubberband selection a bit faster. + */ + SheetPos bbox_start; + SheetPos bbox_end; +}; + +typedef struct { + GtkDialog *dialog; + PartItem *part_item; + + /* List of GtkEntry's */ + GList *widgets; +} PartPropDialog; + +static PartPropDialog *prop_dialog = NULL; +static SheetItemClass *parent_class = NULL; + +static const char *part_item_context_menu = +"<ui>" +" <popup name='ItemMenu'>" +" <menuitem action='ObjectProperties'/>" +" </popup>" +"</ui>"; +static GtkActionEntry action_entries[] = { + {"ObjectProperties", GTK_STOCK_PROPERTIES, N_("_Object Properties..."), NULL, N_("Modify the object's properties"), NULL}//G_CALLBACK (object_properties_cmd)} +}; + +enum { + ANCHOR_NORTH, + ANCHOR_SOUTH, + ANCHOR_WEST, + ANCHOR_EAST +}; + +GtkAnchorType part_item_get_anchor_from_part (Part *part) +{ + int anchor_h, anchor_v; + int angle; + IDFlip flip; + + flip = part_get_flip (part); + angle = part_get_rotation (part); + + switch (angle) { + case 0: + anchor_h = ANCHOR_SOUTH; + anchor_v = ANCHOR_WEST; + break; + case 90: + anchor_h = ANCHOR_NORTH; + anchor_v = ANCHOR_WEST; + /* Invert Rotation */ + if (flip & ID_FLIP_HORIZ) + flip = ID_FLIP_VERT; + else if (flip & ID_FLIP_VERT) + flip = ID_FLIP_HORIZ; + break; + } + + if (flip & ID_FLIP_HORIZ) { + anchor_v = ANCHOR_EAST; + } + if (flip & ID_FLIP_VERT) { + anchor_h = ANCHOR_NORTH; + } + + if ((anchor_v == ANCHOR_EAST) && (anchor_h == ANCHOR_NORTH)) + return GTK_ANCHOR_NORTH_EAST; + if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_NORTH)) + return GTK_ANCHOR_NORTH_WEST; + if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_SOUTH)) + return GTK_ANCHOR_SOUTH_WEST; + + return GTK_ANCHOR_SOUTH_EAST; +} + +GType +part_item_get_type () +{ + static GType part_item_type = 0; + + if (!part_item_type) { + static const GTypeInfo part_item_info = { + sizeof(PartItemClass), + NULL, + NULL, + (GClassInitFunc) part_item_class_init, + NULL, + NULL, + sizeof (PartItem), + 0, + (GInstanceInitFunc) part_item_init, + NULL + }; + part_item_type = g_type_register_static(TYPE_SHEET_ITEM, + "PartItem", &part_item_info, 0); + } + return part_item_type; +} + +static void +part_item_class_init (PartItemClass *part_item_class) +{ + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + SheetItemClass *sheet_item_class; + + object_class = G_OBJECT_CLASS(part_item_class); + gtk_object_class = GTK_OBJECT_CLASS(part_item_class); + sheet_item_class = SHEET_ITEM_CLASS(part_item_class); + parent_class = g_type_class_peek_parent(part_item_class); + + object_class->set_property = part_item_set_property; + object_class->get_property = part_item_get_property; + + g_object_class_install_property ( + object_class, + ARG_NAME, + g_param_spec_pointer ("name", + "PartItem::name", + "name", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property ( + object_class, + ARG_SYMNAME, + g_param_spec_pointer ("symbol_name", + "PartItem::symbol_name", + "symbol name", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property ( + object_class, + ARG_REFDES, + g_param_spec_pointer ("refdes", + "PartItem::refdes", + "refdes", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property ( + object_class, + ARG_LIBNAME, + g_param_spec_pointer ("library_name", + "PartItem::library_name", + "library_name", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property ( + object_class, + ARG_TEMPLATE, + g_param_spec_pointer ("template", + "PartItem::template", + "template", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + g_object_class_install_property ( + object_class, + ARG_MODEL, + g_param_spec_pointer ("model", + "PartItem::model", + "model", + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + gtk_object_class->destroy = part_item_destroy; + + sheet_item_class->moved = part_item_moved; + sheet_item_class->is_in_area = is_in_area; + sheet_item_class->show_labels = show_labels; + sheet_item_class->paste = part_item_paste; + sheet_item_class->edit_properties = edit_properties; + sheet_item_class->selection_changed = (gpointer) selection_changed; + + sheet_item_class->place = part_item_place; + sheet_item_class->place_ghost = part_item_place_ghost; +} + +static void +part_item_init (PartItem *item) +{ + PartItemPriv *priv; + + priv = g_new0 (PartItemPriv, 1); + + priv->highlight = FALSE; + priv->cache_valid = FALSE; + + item->priv = priv; + + sheet_item_add_menu (SHEET_ITEM (item), part_item_context_menu, + action_entries, G_N_ELEMENTS (action_entries)); +} + +static void +part_item_set_property (GObject *object, guint propety_id, const GValue *value, + GParamSpec *pspec) +{ + PartItem *part_item; + PartItemPriv *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_PART_ITEM(object)); + + part_item = PART_ITEM(object); + priv = part_item->priv; +} + +static void +part_item_get_property (GObject *object, guint propety_id, GValue *value, + GParamSpec *pspec) +{ + PartItem *part_item; + PartItemPriv *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_PART_ITEM (object)); + + part_item = PART_ITEM (object); + priv = part_item->priv; +} + +static void +part_item_destroy(GtkObject *object) +{ + PartItem *item; + PartItemPriv *priv; + /* + * Unused variable + Part *part; + */ + ArtPoint *old; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_PART_ITEM (object)); + + /* + * Free the stored coordinate that lets us rotate circles. + */ + old = g_object_get_data(G_OBJECT(object), "hack"); + if (old) + g_free (old); + + item = PART_ITEM (object); + priv = item->priv; + + if (priv) { + if (priv->label_group) { + /* GnomeCanvasGroups utiliza GtkObject todavia */ + gtk_object_destroy(GTK_OBJECT(priv->label_group)); + priv->label_group = NULL; + } + + g_free (priv); + item->priv = NULL; + } + + if (GTK_OBJECT_CLASS (parent_class)->destroy) { + GTK_OBJECT_CLASS (parent_class)->destroy(object); + } +} + +static void +part_item_set_label_items (PartItem *item, GSList *item_list) +{ + PartItemPriv *priv; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_PART_ITEM (item)); + + priv = item->priv; + + if (priv->label_items) + g_slist_free (priv->label_items); + + priv->label_items = item_list; +} + +/* + * part_item_moved + * + * "moved" signal handler. Invalidates the bounding box cache. + */ +static void +part_item_moved (SheetItem *sheet_item) +{ + PartItem *part_item; + + part_item = PART_ITEM (sheet_item); + part_item->priv->cache_valid = FALSE; +} + +PartItem * +part_item_new (Sheet *sheet, Part *part) +{ + PartItem *item; + PartItemPriv *priv; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(IS_SHEET (sheet), NULL); + g_return_val_if_fail(part != NULL, NULL); + g_return_val_if_fail(IS_PART (part), NULL); + + item = PART_ITEM(gnome_canvas_item_new( + sheet->object_group, + TYPE_PART_ITEM, + "data", part, + NULL)); + + priv = item->priv; + + priv->label_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new( + GNOME_CANVAS_GROUP(item), + GNOME_TYPE_CANVAS_GROUP, + "x", 0.0, + "y", 0.0, + NULL)); + + priv->node_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new( + GNOME_CANVAS_GROUP(item), + GNOME_TYPE_CANVAS_GROUP, + "x", 0.0, + "y", 0.0, + NULL)); + + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->node_group)); + + g_signal_connect_object(G_OBJECT(part), + "rotated", + G_CALLBACK (part_rotated_callback), + G_OBJECT(item), + 0); + g_signal_connect_object(G_OBJECT(part), + "flipped", + G_CALLBACK(part_flipped_callback), + G_OBJECT(item), + 0); + g_signal_connect_object(G_OBJECT(part), + "moved", + G_CALLBACK(part_moved_callback), + G_OBJECT(item), + 0); + + return item; +} + +static void +update_canvas_labels (PartItem *item) +{ + PartItemPriv *priv; + Part *part; + GSList *labels, *label_items; + GnomeCanvasItem *canvas_item; + + g_return_if_fail(item != NULL); + g_return_if_fail(IS_PART_ITEM (item)); + + priv = item->priv; + part = PART(sheet_item_get_data(SHEET_ITEM(item))); + + label_items = priv->label_items; + + /* Put the label of each item */ + for (labels = part_get_labels (part); labels; + labels = labels->next, label_items = label_items->next) { + char *text; + PartLabel *label = (PartLabel*) labels->data; + g_assert (label_items != NULL); + canvas_item = label_items->data; + + text = part_property_expand_macros (part, label->text); + gnome_canvas_item_set (canvas_item, "text", text, NULL); + g_free (text); + } +} + +void +part_item_update_node_label (PartItem *item) +{ + PartItemPriv *priv; + Part *part; + GSList *labels; + GnomeCanvasItem *canvas_item; + Pin *pins; + gint num_pins, i; + + + g_return_if_fail(item != NULL); + g_return_if_fail(IS_PART_ITEM (item)); + priv = item->priv; + part = PART(sheet_item_get_data(SHEET_ITEM(item))); + + g_return_if_fail( IS_PART(part) ); + + /* Put the label of each node */ + num_pins = part_get_num_pins(part); + pins = part_get_pins(part); + labels = priv->label_nodes; + for (i=0; i<num_pins; i++, labels=labels->next) { + int x, y; + char *txt; + x = pins[i].offset.x; + y = pins[i].offset.y; + + txt = g_strdup_printf("%d", pins[i].node_nr); + canvas_item = labels->data; + gnome_canvas_item_set (canvas_item, "text", txt, NULL); + + g_free(txt); + } +} + +static void +prop_dialog_destroy (GtkWidget *widget, PartPropDialog *prop_dialog) +{ + g_free (prop_dialog); +} + +static void +prop_dialog_response(GtkWidget *dialog, gint response, + PartPropDialog *prop_dialog) +{ + GSList *props; + GList *widget; + Property *prop; + PartItem *item; + PartItemPriv *priv; + Part *part; + gchar *prop_name; + const gchar *prop_value; + GtkWidget *w; + + item = prop_dialog->part_item; + + priv = item->priv; + part = PART (sheet_item_get_data (SHEET_ITEM (item))); + + for (widget = prop_dialog->widgets; widget; + widget = widget->next) { + w = widget->data; + + prop_name = g_object_get_data (G_OBJECT (w), "user"); + prop_value = gtk_entry_get_text (GTK_ENTRY (w)); + + for (props = part_get_properties (part); props; props = props->next) { + prop = props->data; + if (g_strcasecmp (prop->name, prop_name) == 0) { + if (prop->value) g_free (prop->value); + prop->value = g_strdup (prop_value); + } + } + g_free (prop_name); + } + + update_canvas_labels (item); +} + +static void +edit_properties_point (PartItem *item) +{ + GSList *properties; + PartItemPriv *priv; + Part *part; + char *msg; + GladeXML *gui; + GtkRadioButton *radio_v, *radio_c; + GtkRadioButton *ac_r, *ac_m, *ac_i, *ac_p; + GtkCheckButton *chk_db; + gint response; + + priv = item->priv; + part = PART (sheet_item_get_data (SHEET_ITEM (item))); + + if (!g_file_test( + OREGANO_GLADEDIR "/clamp-properties-dialog.glade", + G_FILE_TEST_EXISTS)) { + msg = g_strdup_printf ( + _("The file %s could not be found. You might need to reinstall Oregano to fix this."), + OREGANO_GLADEDIR "/clamp-properties-dialog.glade"); + oregano_error_with_title (_("Could not create part properties dialog."), msg); + g_free (msg); + return; + } + + gui = glade_xml_new (OREGANO_GLADEDIR "/clamp-properties-dialog.glade", + NULL, NULL); + if (!gui) { + oregano_error (_("Could not create part properties dialog.")); + return; + } + + prop_dialog = g_new0 (PartPropDialog, 1); + + prop_dialog->part_item = item; + + prop_dialog->dialog = GTK_DIALOG (glade_xml_get_widget (gui, "clamp-properties-dialog")); + gtk_dialog_set_has_separator (prop_dialog->dialog, FALSE); + + radio_v = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_v")); + radio_c = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_c")); + + /* FIXME: Deactivated up to finalisation of the analysis by the backend */ + gtk_widget_set_sensitive (GTK_WIDGET (radio_c), FALSE); + + ac_r = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_r")); + ac_m = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_m")); + ac_p = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_p")); + ac_i = GTK_RADIO_BUTTON (glade_xml_get_widget (gui, "radio_i")); + + chk_db = GTK_CHECK_BUTTON (glade_xml_get_widget (gui, "check_db")); + + /* Setup GUI from properties */ + for (properties = part_get_properties (part); properties; + properties = properties->next) { + Property *prop; + prop = properties->data; + if (prop->name) { + if (!g_strcasecmp (prop->name, "internal")) + continue; + + if (!g_strcasecmp (prop->name, "type")) { + if (!g_strcasecmp (prop->value, "v")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_v), TRUE); + } else { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_c), TRUE); + } + } else if (!g_strcasecmp (prop->name, "ac_type")) { + if (!g_strcasecmp (prop->value, "m")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_m), TRUE); + } else if (!g_strcasecmp (prop->value, "i")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_i), TRUE); + } else if (!g_strcasecmp (prop->value, "p")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_p), TRUE); + } else if (!g_strcasecmp (prop->value, "r")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_r), TRUE); + } + } else if (!g_strcasecmp (prop->name, "ac_db")) { + if (!g_strcasecmp (prop->value, "true")) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chk_db), TRUE); + } + } + } + + response = gtk_dialog_run(prop_dialog->dialog); + + /* Save properties from GUI */ + for (properties = part_get_properties (part); properties; + properties = properties->next) { + Property *prop; + prop = properties->data; + + if (prop->name) { + if (!g_strcasecmp (prop->name, "internal")) + continue; + + if (!g_strcasecmp (prop->name, "type")) { + g_free (prop->value); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_v))) { + prop->value = g_strdup ("v"); + } else { + prop->value = g_strdup ("i"); + } + } else if (!g_strcasecmp (prop->name, "ac_type")) { + g_free (prop->value); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_m))) { + prop->value = g_strdup ("m"); + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_i))) { + prop->value = g_strdup ("i"); + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_p))) { + prop->value = g_strdup ("p"); + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_r))) { + prop->value = g_strdup ("r"); + } + } else if (!g_strcasecmp (prop->name, "ac_db")) { + g_free (prop->value); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chk_db))) + prop->value = g_strdup ("true"); + else + prop->value = g_strdup ("false"); + } + } + } + + gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); +} + +static void +edit_properties (SheetItem *object) +{ + GSList *properties; + PartItem *item; + PartItemPriv *priv; + Part *part; + char *internal, *msg; + GladeXML *gui; + GtkTable *prop_table; + GtkNotebook *notebook; + gint response, y = 0; + gboolean has_model; + gchar *model_name = NULL; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_PART_ITEM (object)); + + item = PART_ITEM (object); + priv = item->priv; + part = PART (sheet_item_get_data (SHEET_ITEM (item))); + + internal = part_get_property (part, "internal"); + if (internal) { + if (g_strcasecmp (internal, "ground") == 0) { + g_free (internal); + return; + } + /* Hack!! */ + if (g_strcasecmp (internal, "point") == 0) { + edit_properties_point (item); + return; + } + } + + g_free (internal); + + if (!g_file_test( + OREGANO_GLADEDIR "/part-properties-dialog.glade", + G_FILE_TEST_EXISTS)) { + msg = g_strdup_printf ( + _("The file %s could not be found. You might need to reinstall Oregano to fix this."), + OREGANO_GLADEDIR "/part-properties-dialog.glade"); + oregano_error_with_title (_("Could not create part properties dialog."), msg); + g_free (msg); + return; + } + + gui = glade_xml_new (OREGANO_GLADEDIR "/part-properties-dialog.glade", + NULL, NULL); + if (!gui) { + oregano_error (_("Could not create part properties dialog.")); + return; + } + + prop_dialog = g_new0 (PartPropDialog, 1); + + prop_dialog->part_item = item; + + prop_dialog->dialog = GTK_DIALOG ( glade_xml_get_widget (gui, "part-properties-dialog")); + + prop_table = GTK_TABLE (glade_xml_get_widget (gui, "prop_table")); + notebook = GTK_NOTEBOOK (glade_xml_get_widget (gui, "notebook")); + + g_signal_connect (prop_dialog->dialog, "destroy", + (GCallback) prop_dialog_destroy, prop_dialog); + + prop_dialog->widgets = NULL; + has_model = FALSE; + for (properties = part_get_properties (part); properties; + properties = properties->next) { + Property *prop; + + prop = properties->data; + if (prop->name) { + GtkWidget *entry; + GtkWidget *label; + gchar *temp=NULL; + + if (!g_strcasecmp (prop->name, "internal")) + continue; + + if (!g_strcasecmp (prop->name, "model")) { + has_model = TRUE; + model_name = g_strdup (prop->value); + } + + + /* Find the Refdes and replace by their real value */ + temp = prop->name; + if (!g_ascii_strcasecmp (temp, "Refdes")) temp = _("Designation"); + if (!g_ascii_strcasecmp (temp, "Template")) temp = _("Template"); + if (!g_ascii_strcasecmp (temp, "Res")) temp = _("Resistor"); + if (!g_ascii_strcasecmp (temp, "Cap")) temp = _("Capacitor"); + if (!g_ascii_strcasecmp (temp, "Ind")) temp = _("Inductor"); + label = gtk_label_new (temp); + entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (entry), prop->value); + g_object_set_data (G_OBJECT (entry), "user", g_strdup (prop->name)); + + gtk_table_attach ( + prop_table, label, + 0, 1, y, y+1, + GTK_FILL|GTK_SHRINK, + GTK_FILL|GTK_SHRINK, + 8, 8); + + gtk_table_attach ( + prop_table, entry, + 1, 2, y, y+1, + GTK_EXPAND|GTK_FILL, + GTK_FILL|GTK_SHRINK, + 8, 8); + + y++; + gtk_widget_show (label); + gtk_widget_show (entry); + + prop_dialog->widgets = g_list_prepend (prop_dialog->widgets, entry); + } + } + + if (!has_model) { + gtk_notebook_remove_page (notebook, 1); + } else { + GtkTextBuffer *txtbuffer; + GtkTextView *txtmodel; + gchar *filename, *str; + GError *read_error = NULL; + + txtmodel = GTK_TEXT_VIEW (glade_xml_get_widget (gui, "txtmodel")); + txtbuffer = gtk_text_buffer_new (NULL); + + filename = g_strdup_printf ("%s/%s.model", OREGANO_MODELDIR, model_name); + if (g_file_get_contents (filename, &str, NULL, &read_error)) { + gtk_text_buffer_set_text (txtbuffer, str, -1); + g_free (str); + } else { + gtk_text_buffer_set_text (txtbuffer, read_error->message, -1); + g_error_free (read_error); + } + + g_free (filename); + g_free (model_name); + + gtk_text_view_set_buffer (txtmodel, txtbuffer); + } + + gtk_dialog_set_default_response (prop_dialog->dialog, 1); + + response = gtk_dialog_run(prop_dialog->dialog); + + prop_dialog_response (GTK_WIDGET (prop_dialog->dialog), response, prop_dialog); + + gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog)); +} + +/* Matrix Multiplication + Tried art_affine_multiply, but that didn't work */ +static void matrix_mult(double* a,double* b,double* c){ + c[0]=a[0]*b[0]+a[1]*b[2]; + c[2]=a[2]*b[0]+a[3]*b[2]; + c[1]=a[0]*b[1]+a[1]*b[3]; + c[3]=a[2]*b[1]+a[3]*b[3]; +} + +static void +part_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +{ + double affine[6]; + double affine_scale[6]; + double affine_rotate[6]; + + GList *list; + GSList *label_items; + GtkAnchorType anchor; + GnomeCanvasGroup *group; + GnomeCanvasItem *canvas_item; + PartItem *item; + PartItemPriv *priv; + Part *part; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_PART_ITEM (sheet_item)); + + item = PART_ITEM (sheet_item); + group = GNOME_CANVAS_GROUP (item); + part = PART(data); + + priv = item->priv; + + if (angle!=0) { + // check if the part is flipped + if ((part->priv->flip & 1) && (part->priv->flip & 2)) { + // Nothing to do in this case + art_affine_rotate (affine, angle); + } else { + if ((part->priv->flip & 1) || (part->priv->flip & 2)) { + // mirror over point (0,0) + art_affine_scale(affine_scale,-1,-1); + art_affine_rotate(affine_rotate,angle); + /* Matrix multiplication */ + /* Don't use art_affine_multiply() here !*/ + matrix_mult(affine_rotate,affine_scale,affine); + affine[4]=affine_rotate[4]; + affine[5]=affine_rotate[5]; + } else + art_affine_rotate (affine, angle); + } + } else { + // angle==0: Nothing has changed, therefore do nothing + art_affine_scale(affine,1,1); + } + + for (list = group->item_list; list; list = list->next) { + canvas_item = GNOME_CANVAS_ITEM (list->data); + + gnome_canvas_item_affine_relative (canvas_item, affine); + } + + /* + * Get the right anchor for the labels. This is needed since the + * canvas don't know how to rotate text and since we rotate the + * label_group instead of the labels directly. + */ + + switch (part_get_rotation (part)) { + case 0: + anchor = GTK_ANCHOR_SOUTH_WEST; + break; + case 90: + anchor = GTK_ANCHOR_NORTH_WEST; + break; + case 180: + anchor = GTK_ANCHOR_NORTH_EAST; + break; + case 270: + anchor = GTK_ANCHOR_SOUTH_EAST; + break; + default: + anchor = GTK_ANCHOR_SOUTH_WEST; + break; + } + + + for (label_items = priv->label_items; label_items; + label_items = label_items->next) { + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (label_items->data), + "anchor", anchor, + NULL); + } + + for (label_items = priv->label_nodes; label_items; + label_items = label_items->next) { + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (label_items->data), + "anchor", anchor, + NULL); + } + + /* + * Invalidate the bounding box cache. + */ + priv->cache_valid = FALSE; +} + +static void +part_flipped_callback (ItemData *data, gboolean horizontal, + SheetItem *sheet_item) +{ + GList *list; + GSList *label; + GtkAnchorType anchor; + GnomeCanvasGroup *group; + GnomeCanvasItem *canvas_item; + PartItem *item; + PartItemPriv *priv; + Part *part; + IDFlip flip; + double affine[6]; + double affine_rotate[6]; + double affine_scale[6]; + int angle; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_PART_ITEM (sheet_item)); + + item = PART_ITEM (sheet_item); + group = GNOME_CANVAS_GROUP (item); + part = PART (data); + flip = part_get_flip (part); + + priv = item->priv; + + if (horizontal) + art_affine_scale (affine, -1, 1); + else + art_affine_scale (affine, 1, -1); + + angle=part_get_rotation(part); + if (angle!=0) { + switch (angle){ + case 90: art_affine_rotate(affine_rotate,180); + memcpy(affine_scale,affine,6*sizeof(double)); + break; + case 180: //don't do a thing + art_affine_rotate(affine_rotate,0); + memcpy(affine_scale,affine,6*sizeof(double)); + break; + case 270: art_affine_rotate(affine_rotate,0); + // only to reset the memory of affine_scale, just a hack + memcpy(affine_scale,affine,6*sizeof(double)); + // switch the flipping from horizontal to vertical and vice versa + affine_scale[0]=affine[3]; + affine_scale[3]=affine[0]; + } + matrix_mult(affine_scale,affine_rotate,affine); + } + + + for (list = group->item_list; list; list = list->next) { + canvas_item = GNOME_CANVAS_ITEM (list->data); + + gnome_canvas_item_affine_relative (canvas_item, affine); + } + + anchor = part_item_get_anchor_from_part (part); + + for (label = item->priv->label_items; label; label = label->next) { + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (label->data), + "anchor", anchor, + NULL); + } + + /* + * Invalidate the bounding box cache. + */ + priv->cache_valid = FALSE; +} + +/* + Wherefore this function? + Formerly, the above defined callback functions were called after + reading data from file or from clipboard, in both cases there was neither + a change in flip nor rotate, but the above functions only work for + data which has changed. The "Not Changed, Just Redraw" was missing here. + */ +static void +part_arrange_canvas_item (ItemData *data, SheetItem *sheet_item) +{ + double affine[6]; + double affine_scale[6]; + double affine_rotate[6]; + GList *list; + GSList *label_items; + GtkAnchorType anchor; + GnomeCanvasGroup *group; + GnomeCanvasItem *canvas_item; + PartItem *item; + PartItemPriv *priv; + Part *part; + int angle; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_PART_ITEM (sheet_item)); + + item = PART_ITEM (sheet_item); + group = GNOME_CANVAS_GROUP (item); + part = PART(data); + + priv = item->priv; + + angle=part_get_rotation(part); + + if (angle!=0) { + if ((part->priv->flip & ID_FLIP_HORIZ) + && (part->priv->flip & ID_FLIP_VERT)) { + art_affine_rotate(affine,angle+180); + } else { if ((part->priv->flip & ID_FLIP_HORIZ) + || (part->priv->flip & ID_FLIP_VERT)) { + if (part->priv->flip & ID_FLIP_HORIZ) + art_affine_scale(affine_scale,-1,1); + else + art_affine_scale(affine_scale,1,-1); + + art_affine_rotate(affine_rotate,angle); + + matrix_mult(affine_rotate,affine_scale,affine); + affine[4]=affine_rotate[4]; + affine[5]=affine_rotate[5]; + } else + art_affine_rotate (affine, angle); + } + } else { + art_affine_scale(affine,1,1); //default + if ((part->priv->flip & ID_FLIP_HORIZ) + && (part->priv->flip & ID_FLIP_VERT)) + art_affine_scale(affine,-1,-1); + if ((part->priv->flip & ID_FLIP_HORIZ) + && !(part->priv->flip & ID_FLIP_VERT)) + art_affine_scale(affine,-1,1); + if (!(part->priv->flip & ID_FLIP_HORIZ) + && (part->priv->flip & ID_FLIP_VERT)) + art_affine_scale(affine,1,-1); + } + + for (list = group->item_list; list; list = list->next) { + canvas_item = GNOME_CANVAS_ITEM (list->data); + gnome_canvas_item_affine_relative (canvas_item, affine); + } + + /* + * Get the right anchor for the labels. This is needed since the + * canvas don't know how to rotate text and since we rotate the + * label_group instead of the labels directly. + */ + switch (part_get_rotation (part)) { + case 0: + anchor = GTK_ANCHOR_SOUTH_WEST; + break; + case 90: + anchor = GTK_ANCHOR_NORTH_WEST; + break; + case 180: + anchor = GTK_ANCHOR_NORTH_EAST; + break; + case 270: + anchor = GTK_ANCHOR_SOUTH_EAST; + break; + default: + anchor = GTK_ANCHOR_SOUTH_WEST; + break; + } + + + for (label_items = priv->label_items; label_items; + label_items = label_items->next) { + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (label_items->data), + "anchor", anchor, + NULL); + } + + for (label_items = priv->label_nodes; label_items; + label_items = label_items->next) { + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (label_items->data), + "anchor", anchor, + NULL); + } + + /* + * Invalidate the bounding box cache. + */ + priv->cache_valid = FALSE; +} + +void +part_item_signal_connect_floating_group (Sheet *sheet, SchematicView *sv) +{ + g_return_if_fail (sheet != NULL); + g_return_if_fail (IS_SHEET (sheet)); + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sheet->state = SHEET_STATE_FLOAT_START; + + /* FIXME: clean all this mess with floating groups etc... */ + if (sheet->priv->float_handler_id != 0) + return; + + sheet->priv->float_handler_id = g_signal_connect( + G_OBJECT (sheet), + "event", + G_CALLBACK(sheet_item_floating_event), + sv); +} + +void +part_item_signal_connect_floating (PartItem *item) +{ + Sheet *sheet; + + sheet = sheet_item_get_sheet (SHEET_ITEM (item)); + sheet->state = SHEET_STATE_FLOAT_START; + + g_signal_connect ( + G_OBJECT (item), + "double_clicked", + G_CALLBACK(edit_properties), + item); +} + +static void +selection_changed (PartItem *item, gboolean select, gpointer user_data) +{ + g_object_ref (G_OBJECT (item)); + if (select) + gtk_idle_add ((gpointer) select_idle_callback, item); + else + gtk_idle_add ((gpointer) deselect_idle_callback, item); +} + +static int +select_idle_callback (PartItem *item) +{ + PartItemPriv *priv; + GnomeCanvasItem *canvas_item; + GList *list; + + priv = item->priv; + + if (priv->highlight) { + g_object_unref(G_OBJECT (item)); + return FALSE; + } + + for (list = GNOME_CANVAS_GROUP (item)->item_list; list; + list = list->next){ + canvas_item = GNOME_CANVAS_ITEM (list->data); + if (GNOME_IS_CANVAS_LINE (canvas_item)) + gnome_canvas_item_set (canvas_item, "fill_color", SELECTED_COLOR, NULL); + else if (GNOME_IS_CANVAS_ELLIPSE (canvas_item)) + gnome_canvas_item_set (canvas_item, "outline_color", SELECTED_COLOR, NULL); + else if (GNOME_IS_CANVAS_TEXT (canvas_item)) + gnome_canvas_item_set (canvas_item, "fill_color", SELECTED_COLOR, NULL); + } + + priv->highlight = TRUE; + + g_object_unref(G_OBJECT(item)); + return FALSE; +} + +static int +deselect_idle_callback (PartItem *item) +{ + GList *list; + GnomeCanvasItem *canvas_item; + PartItemPriv *priv; + + priv = item->priv; + + if (!priv->highlight) { + g_object_unref (G_OBJECT (item)); + return FALSE; + } + + for (list = GNOME_CANVAS_GROUP (item)->item_list; list; + list = list->next){ + canvas_item = GNOME_CANVAS_ITEM (list->data); + if (GNOME_IS_CANVAS_LINE (canvas_item)) + gnome_canvas_item_set (canvas_item, "fill_color", NORMAL_COLOR, NULL); + else if (GNOME_IS_CANVAS_ELLIPSE (canvas_item)) + gnome_canvas_item_set (canvas_item, "outline_color", NORMAL_COLOR, NULL); + else if (GNOME_IS_CANVAS_TEXT (canvas_item)) + gnome_canvas_item_set (canvas_item, "fill_color", LABEL_COLOR, NULL); + } + + priv->highlight = FALSE; + + g_object_unref(G_OBJECT(item)); + return FALSE; +} + +static gboolean +is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +{ + PartItem *item; + SheetPos bbox_start, bbox_end; + + item = PART_ITEM (object); + + get_cached_bounds (item, &bbox_start, &bbox_end); + + if ((p1->x < bbox_start.x) && + (p2->x > bbox_end.x) && + (p1->y < bbox_start.y) && + (p2->y > bbox_end.y)) + return TRUE; + + return FALSE; +} + +static void +show_labels (SheetItem *sheet_item, gboolean show) +{ + PartItem *item; + PartItemPriv *priv; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_PART_ITEM (sheet_item)); + + item = PART_ITEM (sheet_item); + priv = item->priv; + + if (show) + gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->label_group)); + else + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->label_group)); +} + +/** + * Retrieves the bounding box. We use a caching scheme for this + * since it's too expensive to calculate it every time we need it. + */ +inline static void +get_cached_bounds (PartItem *item, SheetPos *p1, SheetPos *p2) +{ + PartItemPriv *priv; + priv = item->priv; + + if (!priv->cache_valid) { + SheetPos start_pos, end_pos; + gdouble x1, y1, x2, y2; + + /* + * Hide the labels, then get bounding box, then show them + * again. + */ + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->label_group)); + gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (item), + &x1, &y1, &x2, &y2); + gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->label_group)); + + start_pos.x = x1; + start_pos.y = y1; + end_pos.x = x2; + end_pos.y = y2; + + priv->bbox_start = start_pos; + priv->bbox_end = end_pos; + priv->cache_valid = TRUE; + } + + memcpy (p1, &priv->bbox_start, sizeof (SheetPos)); + memcpy (p2, &priv->bbox_end, sizeof (SheetPos)); +} + +static void +part_item_paste (SchematicView *sv, ItemData *data) +{ + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_PART (data)); + + schematic_view_add_ghost_item (sv, data); +} + +/** + * FIXME: make this the default constructor for PartItem (rename to + * part_item_new). + */ +PartItem * +part_item_new_from_part (Sheet *sheet, Part *part) +{ + Library *library; + LibraryPart *library_part; + PartPriv *priv; + PartItem *item; + + priv = part->priv; + + library = priv->library; + library_part = library_get_part (library, priv->name); + + /* + * Create the PartItem canvas item. + */ + item = part_item_new (sheet, part); + + create_canvas_items (GNOME_CANVAS_GROUP (item), library_part); + create_canvas_labels (item, part); + create_canvas_label_nodes(item, part); + + part_arrange_canvas_item(ITEM_DATA (part), SHEET_ITEM (item)); + return item; +} + +void +part_item_create_canvas_items_for_preview (GnomeCanvasGroup *group, + LibraryPart *library_part) +{ + g_return_if_fail (group != NULL); + g_return_if_fail (GNOME_IS_CANVAS_GROUP (group)); + g_return_if_fail (library_part != NULL); + + create_canvas_items (group, library_part); +} + +static void +create_canvas_items (GnomeCanvasGroup *group, LibraryPart *library_part) +{ + GnomeCanvasItem *item; + GnomeCanvasPoints *points; + GSList *objects; + LibrarySymbol *symbol; + SymbolObject *object; + + g_return_if_fail (group != NULL); + g_return_if_fail (GNOME_IS_CANVAS_GROUP (group)); + g_return_if_fail (library_part != NULL); + + symbol = library_get_symbol (library_part->symbol_name); + if (symbol == NULL){ + g_warning ("Couldn't find the requested symbol %s for part %s in library.\n", + library_part->symbol_name, + library_part->name); + return; + } + + for (objects = symbol->symbol_objects; objects; + objects = objects->next) { + object = (SymbolObject *)(objects->data); + switch (object->type){ + case SYMBOL_OBJECT_LINE: + points = object->u.uline.line; + item = gnome_canvas_item_new ( + group, + gnome_canvas_line_get_type (), + "points", points, + "fill_color", NORMAL_COLOR, + "width_pixels", 0, + "cap_style", GDK_CAP_BUTT, + NULL); + if (object->u.uline.spline) { + gnome_canvas_item_set ( + item, + "smooth", TRUE, + "spline_steps", 5, + NULL); + } + break; + case SYMBOL_OBJECT_ARC: + item = gnome_canvas_item_new ( + group, + gnome_canvas_ellipse_get_type (), + "x1", object->u.arc.x1, + "y1", object->u.arc.y1, + "x2", object->u.arc.x2, + "y2", object->u.arc.y2, + "outline_color", NORMAL_COLOR, + "width_pixels", 0, + NULL); + break; + case SYMBOL_OBJECT_TEXT: + item = gnome_canvas_item_new ( + group, + gnome_canvas_text_get_type (), + "text",object->u.text.str, + "x", (double) object->u.text.x, + "y", (double) object->u.text.y, + "fill_color", LABEL_COLOR, + "font", "Sans 8", + NULL); + break; + default: + g_warning ("Unknown symbol object.\n"); + continue; + } + } +} + +static void +create_canvas_labels (PartItem *item, Part *part) +{ + GnomeCanvasItem *canvas_item; + GSList *list, *labels, *item_list; + GnomeCanvasGroup *group; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_PART_ITEM (item)); + g_return_if_fail (part != NULL); + g_return_if_fail (IS_PART (part)); + + labels = part_get_labels (part); + group = item->priv->label_group; + item_list = NULL; + + for (list = labels; list; list = list->next) { + PartLabel *label = list->data; + char *text; + + text = part_property_expand_macros (part, label->text); + + canvas_item = gnome_canvas_item_new ( + group, + gnome_canvas_text_get_type (), + "x", (double) label->pos.x, + "y", (double) label->pos.y, + "text", text, + "anchor", GTK_ANCHOR_SOUTH_WEST, + "fill_color", LABEL_COLOR, + "font", "Sans 8", + NULL); + + item_list = g_slist_prepend (item_list, canvas_item); + + g_free (text); + } + item_list = g_slist_reverse (item_list); + part_item_set_label_items (item, item_list); +} + + +static void +create_canvas_label_nodes (PartItem *item, Part *part) +{ + GnomeCanvasItem *canvas_item; + GSList *item_list; + GnomeCanvasGroup *group; + Pin *pins; + int num_pins, i; + SheetPos p1, p2; + GtkAnchorType anchor; + int w, h; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_PART_ITEM (item)); + g_return_if_fail (part != NULL); + g_return_if_fail (IS_PART (part)); + + num_pins = part_get_num_pins(part); + pins = part_get_pins(part); + group = item->priv->node_group; + item_list = NULL; + + get_cached_bounds (item, &p1, &p2); + + w = p2.x - p1.x; + h = p2.y - p1.y; + + switch (part_get_rotation (part)) { + case 0: + anchor = GTK_ANCHOR_SOUTH_WEST; + break; + case 90: + anchor = GTK_ANCHOR_NORTH_WEST; + break; + case 180: + anchor = GTK_ANCHOR_NORTH_EAST; + break; + case 270: + anchor = GTK_ANCHOR_SOUTH_EAST; + break; + default: + anchor = GTK_ANCHOR_SOUTH_WEST; + } + + for (i=0; i<num_pins; i++) { + int x, y; + char *txt; + x = pins[i].offset.x; + y = pins[i].offset.y; + + txt = g_strdup_printf("%d", pins[i].node_nr); + canvas_item = gnome_canvas_item_new ( + group, + gnome_canvas_text_get_type (), + "x", (double) x, + "y", (double) y, + "text", txt, + "anchor", anchor, + "fill_color", "black", + "font", "Sans 8", + NULL); + + item_list = g_slist_prepend (item_list, canvas_item); + g_free(txt); + } + item_list = g_slist_reverse (item_list); + item->priv->label_nodes = item_list; +} + + +/** + * This is called when the part data was moved. Update the view accordingly. + */ +static void +part_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item) +{ + PartItem *part_item; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_PART_ITEM (item)); + + if (pos == NULL) + return; + + part_item = PART_ITEM (item); + + /* + * Move the canvas item and invalidate the bbox cache. + */ + gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y); + part_item->priv->cache_valid = FALSE; +} + +static void +part_item_place (SheetItem *item, SchematicView *sv) +{ + g_signal_connect ( + G_OBJECT(item), + "event", + G_CALLBACK(sheet_item_event), + sv); + + g_signal_connect ( + G_OBJECT(item), + "double_clicked", + G_CALLBACK(edit_properties), + item); +} + +static void +part_item_place_ghost (SheetItem *item, SchematicView *sv) +{ +// part_item_signal_connect_placed (PART_ITEM (item)); +} + + +void +part_item_show_node_labels (PartItem *part, gboolean b) +{ + PartItemPriv *priv; + + priv = part->priv; + + if (b) + gnome_canvas_item_show (GNOME_CANVAS_ITEM (priv->node_group)); + else + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->node_group)); +} + diff --git a/src/sheet/part-item.h b/src/sheet/part-item.h new file mode 100644 index 0000000..0ead795 --- /dev/null +++ b/src/sheet/part-item.h @@ -0,0 +1,70 @@ +/* + * part-item.h + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __PART_ITEM_H +#define __PART_ITEM_H + +typedef struct _PartItem PartItem; +typedef struct _PartItemClass PartItemClass; +typedef struct _PartItemPriv PartItemPriv; + +#include "sheet-item.h" +#include "load-library.h" +#include "load-common.h" +#include "part.h" + +#define TYPE_PART_ITEM (part_item_get_type ()) +#define PART_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_PART_ITEM, PartItem)) +#define PART_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_PART_ITEM, PartItemClass)) +#define IS_PART_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_PART_ITEM)) +#define PART_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_PART_ITEM, PartItemClass)) + +#include "schematic-view.h" + +struct _PartItem { + SheetItem parent_object; + PartItemPriv *priv; +}; + +struct _PartItemClass { + SheetItemClass parent_class; +}; + +GType part_item_get_type (void); +PartItem *part_item_new (Sheet *sheet, Part *part); +PartItem *part_item_new_from_part (Sheet *sheet, Part *part); +void part_item_signal_connect_floating (PartItem *item); +void part_item_signal_connect_floating_group (Sheet *sheet, + SchematicView *schematic_view); +void part_item_create_canvas_items_for_preview (GnomeCanvasGroup *group, + LibraryPart *library_part); +void part_item_update_node_label (PartItem *part); +void part_item_show_node_labels (PartItem *part, gboolean b); + +#endif diff --git a/src/sheet/sheet-item-factory.c b/src/sheet/sheet-item-factory.c new file mode 100644 index 0000000..97f31b2 --- /dev/null +++ b/src/sheet/sheet-item-factory.c @@ -0,0 +1,70 @@ +/* + * sheet-item-factory.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "schematic-view.h" +#include "sheet-item-factory.h" +#include "wire-item.h" +#include "part-item.h" +#include "textbox-item.h" + +/* + * Create a SheetItem from an ItemData object. This is a bit ugly. + * It could be beautified by having a method that creates the item. + * E.g. sheet_item->new_from_data (data); + */ + +SheetItem * +sheet_item_factory_create_sheet_item (SchematicView *sv, ItemData *data) +{ + Sheet *sheet; + SheetItem *item; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (IS_ITEM_DATA (data), NULL); + + sheet = schematic_view_get_sheet (sv); + item = NULL; + + /* + * Pick the right model. + */ + if (IS_PART(data)) { + item = SHEET_ITEM(part_item_new_from_part(sheet, PART(data))); + } else if (IS_WIRE(data)) { + item = SHEET_ITEM(wire_item_new(sheet, WIRE(data))); + } else if (IS_TEXTBOX(data)) { + item = SHEET_ITEM(textbox_item_new(sheet, TEXTBOX(data))); + } else + g_warning ("Unknown Item type."); + + return item; +} + + diff --git a/src/sheet/sheet-item-factory.h b/src/sheet/sheet-item-factory.h new file mode 100644 index 0000000..4a6825b --- /dev/null +++ b/src/sheet/sheet-item-factory.h @@ -0,0 +1,38 @@ +/* + * sheet-item-factory.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __SHEET_ITEM_FACTORY_H +#define __SHEET_ITEM_FACTORY_H + +#include "item-data.h" +#include "sheet-item.h" + +SheetItem *sheet_item_factory_create_sheet_item (SchematicView *sv, ItemData *data); + +#endif /* __SHEET_ITEM_FACTORY_H */ diff --git a/src/sheet/sheet-item.c b/src/sheet/sheet-item.c new file mode 100644 index 0000000..35120e3 --- /dev/null +++ b/src/sheet/sheet-item.c @@ -0,0 +1,1222 @@ +/* + * sheet-item.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <math.h> +#include "main.h" +#include "sheet-private.h" +#include "sheet-item.h" +#include "stock.h" +#include "schematic.h" +#include "schematic-view.h" +#include "config.h" + +static void sheet_item_class_init (SheetItemClass * klass); + +static void sheet_item_init (SheetItem *item); + +static void sheet_item_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *spec); + +static void sheet_item_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *spec); + +static void sheet_item_destroy (GtkObject *object); + +static void sheet_item_run_menu (SheetItem *item, SchematicView *sv, + GdkEventButton *event); + +static GnomeCanvasGroupClass *sheet_item_parent_class = NULL; +extern GObject *clipboard_data_get_item_data (); +extern GObjectClass *clipboard_data_get_item_class (); + +struct _SheetItemPriv { + guint selected : 1; + guint preserve_selection : 1; + ItemData *data; + GtkActionGroup *action_group; + GtkUIManager *ui_manager; +}; + +enum { + ARG_0, + ARG_DATA, + ARG_SHEET, + ARG_ACTION_GROUP +}; + +enum { + MOVED, + PLACED, + SELECTION_CHANGED, + MOUSE_OVER, + DOUBLE_CLICKED, + + LAST_SIGNAL +}; + +static guint so_signals[LAST_SIGNAL] = { 0 }; +/* + * This is the upper part of the object popup menu. It contains actions + * that are the same for all objects, e.g. parts and wires. + */ +static const char *sheet_item_context_menu = +"<ui>" +" <popup name='ItemMenu'>" +" <menuitem action='Copy'/>" +" <menuitem action='Cut'/>" +" <menuitem action='Delete'/>" +" <separator/>" +" <menuitem action='Rotate'/>" +" <menuitem action='FlipH'/>" +" <menuitem action='FlipV'/>" +" <separator/>" +" </popup>" +"</ui>"; + +static GtkActionEntry action_entries[] = { + {"Copy", GTK_STOCK_COPY, N_("_Copy"), "<control>C", NULL, NULL}, + {"Cut", GTK_STOCK_CUT, N_("C_ut"), "<control>X", NULL, NULL}, + {"Delete", GTK_STOCK_DELETE, N_("_Delete"), "<control>D", N_("Delete the selection"), NULL}, + {"Rotate", STOCK_PIXMAP_ROTATE, N_("_Rotate"), "<control>R", N_("Rotate the selection clockwise"), NULL}, + {"FlipH", NULL, N_("Flip _horizontally"), "<control>F", N_("Flip the selection horizontally"), NULL}, + {"FlipV", NULL, N_("Flip _vertically"), "<control><shift>F", N_("Flip the selection vertically"), NULL} +}; +GType +sheet_item_get_type () +{ + static GType sheet_item_type = 0; + + if (!sheet_item_type) { + static const GTypeInfo sheet_item_info = { + sizeof (SheetItemClass), + NULL, + NULL, + (GClassInitFunc) sheet_item_class_init, + NULL, + NULL, + sizeof (SheetItem), + 0, + (GInstanceInitFunc)sheet_item_init, + NULL + }; + sheet_item_type = g_type_register_static(GNOME_TYPE_CANVAS_GROUP, + "SheetItem", + &sheet_item_info, 0); + } + return sheet_item_type; +} + +static void +sheet_item_class_init (SheetItemClass *sheet_item_class) +{ + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + + object_class = G_OBJECT_CLASS(sheet_item_class); + gtk_object_class = GTK_OBJECT_CLASS(sheet_item_class); + sheet_item_parent_class = g_type_class_peek_parent(sheet_item_class); + + object_class->set_property = sheet_item_set_property; + object_class->get_property = sheet_item_get_property; + + g_object_class_install_property( + object_class, + ARG_DATA, + g_param_spec_pointer("data", "SheetItem::data", "the data", + G_PARAM_READWRITE) + ); + g_object_class_install_property( + object_class, + ARG_SHEET, + g_param_spec_pointer("sheet", "SheetItem::sheet", "the sheet", + G_PARAM_READABLE) + ); + g_object_class_install_property( + object_class, + ARG_ACTION_GROUP, + g_param_spec_pointer("action_group", "SheetItem::action_group", "action group", + G_PARAM_READWRITE) + ); + + gtk_object_class->destroy = sheet_item_destroy; + + sheet_item_class->is_in_area = NULL; + sheet_item_class->show_labels = NULL; + sheet_item_class->paste = NULL; + + sheet_item_class->moved = NULL; + sheet_item_class->selection_changed = NULL; + sheet_item_class->mouse_over = NULL; + + so_signals[PLACED] = + g_signal_new ("placed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + so_signals[MOVED] = g_signal_new ("moved", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetItemClass, moved), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + so_signals[SELECTION_CHANGED] = g_signal_new ("selection_changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetItemClass, selection_changed), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + so_signals[MOUSE_OVER] = g_signal_new ("mouse_over", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetItemClass, mouse_over), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + so_signals[DOUBLE_CLICKED] = g_signal_new ("double_clicked", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +sheet_item_init (SheetItem *item) +{ + GError *error = NULL; + + item->priv = g_new0 (SheetItemPriv, 1); + item->priv->selected = FALSE; + item->priv->preserve_selection = FALSE; + item->priv->data = NULL; + item->priv->ui_manager = NULL; + item->priv->action_group = NULL; + + item->priv->ui_manager = gtk_ui_manager_new (); + item->priv->action_group = gtk_action_group_new ("action_group"); + gtk_action_group_set_translation_domain (item->priv->action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions (item->priv->action_group, + action_entries, + G_N_ELEMENTS (action_entries), + NULL); + gtk_ui_manager_insert_action_group (item->priv->ui_manager, item->priv->action_group, 0); + + if (!gtk_ui_manager_add_ui_from_string (item->priv->ui_manager, sheet_item_context_menu, -1, &error)) { + g_message ("building menus failed: %s", error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } +} + +static void +sheet_item_set_property (GObject *object, guint prop_id, const GValue *value, + GParamSpec *spec) +{ + SheetItem *sheet_item; + SheetPos pos; + + sheet_item = SHEET_ITEM (object); + + switch (prop_id) { + case ARG_DATA: + if (sheet_item->priv->data) { + g_warning ("Cannot set ItemData after creation."); + break; + } + sheet_item->priv->data = g_value_get_pointer (value); + item_data_get_pos (sheet_item->priv->data, &pos); + gnome_canvas_item_set (GNOME_CANVAS_ITEM (object), + "x", pos.x, + "y", pos.y, + NULL); + break; + case ARG_ACTION_GROUP: + sheet_item->priv->action_group = g_value_get_pointer (value); + gtk_ui_manager_insert_action_group (sheet_item->priv->ui_manager, sheet_item->priv->action_group, 0); + default: + break; + } +} + +static void +sheet_item_get_property (GObject *object, guint prop_id, GValue *value, + GParamSpec *spec) +{ + SheetItem *sheet_item; + + sheet_item = SHEET_ITEM (object); + + switch (prop_id) { + case ARG_DATA: + g_value_set_pointer(value, sheet_item->priv->data); + break; + case ARG_SHEET: + g_value_set_pointer (value, sheet_item_get_sheet (sheet_item)); + break; + case ARG_ACTION_GROUP: + g_value_set_pointer (value, sheet_item->priv->action_group); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(sheet_item, prop_id, spec); + break; + } +} + +static void +sheet_item_destroy (GtkObject *object) +{ + SheetItem *sheet_item; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_SHEET_ITEM (object)); + + sheet_item = SHEET_ITEM (object); + + if (sheet_item->priv) { + g_object_unref(G_OBJECT(sheet_item->priv->data)); + g_free(sheet_item->priv); + sheet_item->priv = NULL; + } + + if (GTK_OBJECT_CLASS(sheet_item_parent_class)->destroy) { + GTK_OBJECT_CLASS(sheet_item_parent_class)->destroy(object); + } +} + +static void +sheet_item_run_menu (SheetItem *item, SchematicView *sv, GdkEventButton *event) +{ + GtkWidget *menu; + + menu = gtk_ui_manager_get_widget (item->priv->ui_manager, "/ItemMenu"); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, sv, event->button, event->time); +} + +static int +scroll_timeout_callback (Sheet *sheet) +{ + int width, height; + int x, y, dx = 0, dy = 0; + + /* Get the current mouse position so that we can decide if the pointer is + inside the viewport. */ + gdk_window_get_pointer (GTK_WIDGET (sheet)->window, &x, &y, NULL); + + width = GTK_WIDGET (sheet)->allocation.width; + height = GTK_WIDGET (sheet)->allocation.height; + + if (x < 0) + dx = -1; + else if (x > width) + dx = 1; + + if (y < 0) + dy = -1; + else if (y > height) + dy = 1; + + if (!(x > 0 && x < width && y > 0 && y < height)) + sheet_scroll (sheet, dx*5, dy*5); + + return TRUE; +} + + +/* + * sheet_item_event + * + * Event handler for a SheetItem + */ +int +sheet_item_event (SheetItem *sheet_item, const GdkEvent *event, SchematicView *sv) +{ + SheetItemClass *class; + GnomeCanvas *canvas; + Sheet *sheet; + SheetPriv *priv; + GList *list; + SheetPos delta; + /* Remember the last position of the mouse cursor. */ + static double last_x, last_y; + /* Mouse cursor position in window coordinates, snapped to the grid spacing. */ + double snapped_x, snapped_y; + /* Move the selected item(s) by this movement. */ + double dx, dy; + /* The selected group's bounding box in window resp. canvas coordinates. */ + double x1, y1, x2, y2; + static double bb_x1, bb_y1, bb_x2, bb_y2; + int cx1, cy1, cx2, cy2; + /* The sheet's width and the its viewport's width. */ + guint sheet_width, sheet_height; + + g_return_val_if_fail (sheet_item != NULL, FALSE); + g_return_val_if_fail (IS_SHEET_ITEM (sheet_item), FALSE); + g_return_val_if_fail (sv != NULL, FALSE); + g_return_val_if_fail (IS_SCHEMATIC_VIEW (sv), FALSE); + + sheet = schematic_view_get_sheet (sv); + priv = sheet->priv; + canvas = GNOME_CANVAS (sheet); + + switch (event->type){ + case GDK_ENTER_NOTIFY: + /* + * Debugging... + */ + if (event->crossing.state & GDK_CONTROL_MASK) + g_signal_emit_by_name (G_OBJECT (sheet_item), "mouse_over"); + return TRUE; + + case GDK_BUTTON_PRESS: + /* Grab focus to sheet for correct use of events */ + gtk_widget_grab_focus (GTK_WIDGET (sheet)); + switch (event->button.button){ + case 1: + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + sheet->state = SHEET_STATE_DRAG_START; + + last_x = event->button.x; + last_y = event->button.y; + snap_to_grid (sheet->grid, &last_x, &last_y); + break; + case 3: + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + + if (sheet->state != SHEET_STATE_NONE) + return TRUE; + + /* + * Bring up a context menu for right button clicks. + */ + if (!sheet_item->priv->selected && + !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) + schematic_view_select_all (sv, FALSE); + + //if (!sheet_item->priv->selected) + sheet_item_select (sheet_item, TRUE); + + class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS(sheet_item)); + sheet_item_run_menu ( sheet_item, sv, (GdkEventButton *) event); + break; + default: + return FALSE; + } + break; + + case GDK_2BUTTON_PRESS: + /* + * Do not interfere with object dragging. + */ + if (sheet->state == SHEET_STATE_DRAG) + return FALSE; + + switch (event->button.button){ + case 1: + if (sheet->state == SHEET_STATE_DRAG_START) + sheet->state = SHEET_STATE_NONE; + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + g_signal_emit_by_name (G_OBJECT (sheet_item), "double_clicked"); + break; + + default: + return FALSE; + } + break; + + case GDK_3BUTTON_PRESS: + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + return TRUE; + + case GDK_BUTTON_RELEASE: + switch (event->button.button){ + case 1: + if (sheet->state != SHEET_STATE_DRAG && + sheet->state != SHEET_STATE_DRAG_START) + return TRUE; + + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + + if (sheet->state == SHEET_STATE_DRAG_START) { + sheet->state = SHEET_STATE_NONE; + + if (!(event->button.state & GDK_SHIFT_MASK)) + schematic_view_select_all (sv, FALSE); + + sheet_item_select (sheet_item, TRUE); + + return TRUE; + } + + g_object_get (G_OBJECT (priv->selected_group), + "x", &delta.x, + "y", &delta.y, + NULL); + + gtk_timeout_remove (priv->scroll_timeout_id); // Tricky... + + sheet->state = SHEET_STATE_NONE; + gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (sheet_item), event->button.time); + + + /* + * HACK :( + * FIXME: fix this later. The problem is that we don't want to + * update the current view, since it acts as the controller and + * already is updated. It's not really a problem, but an ugly hack. + */ + gnome_canvas_item_move ( + GNOME_CANVAS_ITEM (priv->selected_group), + -delta.x, -delta.y + ); + + /* + * Make sure the objects are reparented back to the object + * group _before_ we register them. Otherwise they will get + * incorrect coordinates. + */ + for (list = priv->selected_objects; list; list = list->next) { + sheet_item_reparent (SHEET_ITEM (list->data), sheet->object_group); + } + + /* + * FIXME: this is not the best way to solve this. Ideally, the + * moving would take care of re-registering items. + */ + for (list = priv->selected_objects; list; list = list->next) { + ItemData *item_data; + + item_data = SHEET_ITEM (list->data)->priv->data; + item_data_move (item_data, &delta); + item_data_register (item_data); + } + + break; + case GDK_KEY_PRESS: + switch (event->key.keyval) { + case GDK_r: + schematic_view_rotate_selection (sv); + { + int x, y; + + gdk_window_get_pointer (GDK_WINDOW (GTK_WIDGET (sheet)->window), &x, &y, NULL); + gnome_canvas_window_to_world (GNOME_CANVAS (sheet), x, y, &snapped_x, &snapped_y); + + /* + * Center the objects around the mouse pointer. + */ + gnome_canvas_item_get_bounds ( + GNOME_CANVAS_ITEM (priv->floating_group), + &x1, &y1, &x2, &y2 + ); + + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + dx = snapped_x - (x1 + (x2 - x1) / 2); + dy = snapped_y - (y1 + (y2 - y1) / 2); + snap_to_grid (sheet->grid, &dx, &dy); + + gnome_canvas_item_move ( + GNOME_CANVAS_ITEM (priv->floating_group), + dx, dy + ); + + last_x = snapped_x; + last_y = snapped_y; + } + break; + default: + return FALSE; + } + return TRUE; + + default: + return FALSE; + } + break; + + case GDK_MOTION_NOTIFY: + if (sheet->state != SHEET_STATE_DRAG && + sheet->state != SHEET_STATE_DRAG_START) + return FALSE; + + if (sheet->state == SHEET_STATE_DRAG_START) { + sheet->state = SHEET_STATE_DRAG; + + /* + * Update the selection if needed. + */ + if (!sheet_item->priv->selected){ + if (!(event->motion.state & GDK_SHIFT_MASK)) + schematic_view_select_all (sv, FALSE); + sheet_item_select (sheet_item, TRUE); + } + + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (priv->selected_group), + "x", 0.0, "y", 0.0, NULL + ); + + /* + * Reparent the selected objects so that we can + * move them efficiently. + */ + for (list = priv->selected_objects; list; list = list->next){ + ItemData *item_data; + + item_data = SHEET_ITEM (list->data)->priv->data; + item_data_unregister (item_data); + sheet_item_reparent (SHEET_ITEM (list->data), priv->selected_group); + } + + gnome_canvas_item_grab (GNOME_CANVAS_ITEM (sheet_item), + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, event->button.time); + + gnome_canvas_item_get_bounds ( + GNOME_CANVAS_ITEM (priv->selected_group), + &bb_x1, &bb_y1, &bb_x2, &bb_y2 + ); + + /* + * Start the autoscroll timeout. + */ + priv->scroll_timeout_id = + g_timeout_add (50, (void *) scroll_timeout_callback, sheet); + } + + snapped_x = event->motion.x; + snapped_y = event->motion.y; + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + dx = snapped_x - last_x; + dy = snapped_y - last_y; + + x1 = bb_x1 + dx; + x2 = bb_x2 + dx; + y1 = bb_y1 + dy; + y2 = bb_y2 + dy; + + gnome_canvas_w2c (canvas, x1, y1, &cx1, &cy1); + gnome_canvas_w2c (canvas, x2, y2, &cx2, &cy2); + sheet_get_size_pixels (sheet, &sheet_width, &sheet_height); + + /* Check that we don't move outside the sheet... horizontally: */ + if (cx1 <= 0){ /* leftmost edge */ + dx = dx - x1; + snap_to_grid (sheet->grid, &dx, NULL); + snapped_x = last_x + dx; + } else if (cx2 >= sheet_width){ /* rightmost edge */ + dx = dx - (x2 - sheet_width / priv->zoom); + snap_to_grid (sheet->grid, &dx, NULL); + snapped_x = last_x + dx; + } + + /* And vertically: */ + if (cy1 <= 0){ /* upper edge */ + dy = dy - y1; + snap_to_grid (sheet->grid, NULL, &dy); + snapped_y = last_y + dy; + } else if (cy2 >= sheet_height){ /* lower edge */ + dy = dy - (y2 - sheet_height / priv->zoom); + snap_to_grid (sheet->grid, NULL, &dy); + snapped_y = last_y + dy; + } + + last_x = snapped_x; + last_y = snapped_y; + + if (dx != 0 || dy != 0) + gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->selected_group), dx, dy); + + /* Update the bounding box. */ + bb_x1 += dx; + bb_x2 += dx; + bb_y1 += dy; + bb_y2 += dy; + + break; + + default: + return FALSE; + } + return TRUE; +} + +/* + * Cancel the placement of floating items and remove them. + */ +void +sheet_item_cancel_floating (SchematicView *sv) +{ + GnomeCanvasGroup *group; + Sheet *sheet; + GList *list; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sheet = schematic_view_get_sheet (sv); + group = sheet->priv->floating_group; + if (group == NULL) + return; + + if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START) + return; + + g_signal_handler_disconnect(G_OBJECT (sheet), sheet->priv->float_handler_id); + + gtk_object_destroy (GTK_OBJECT (group)); + + /* + * If the state is _START, the items are not yet put in the + * object_group. This means we have to destroy them one by one. + */ + if (sheet->state == SHEET_STATE_FLOAT_START) { + for (list = sheet->priv->floating_objects; list; list = list->next) { + gtk_object_destroy (GTK_OBJECT (list->data)); + } + } + + sheet->priv->floating_group = GNOME_CANVAS_GROUP ( + gnome_canvas_item_new ( + sheet->object_group, + gnome_canvas_group_get_type (), + "x", 0.0, + "y", 0.0, + NULL) + ); + + sheet->priv->float_handler_id = 0; + sheet->state = SHEET_STATE_NONE; + schematic_view_clear_ghosts (sv); +} + +/* + * Event handler for a "floating" group of objects. + */ +int +sheet_item_floating_event (Sheet *sheet, const GdkEvent *event, SchematicView *schematic_view) +{ + GnomeCanvas *canvas; + SheetPriv *priv; + GList *list; + static SheetPos delta, tmp; + static int control_key_down = 0; + + /* Remember the last position of the mouse cursor. */ + static double last_x, last_y; + + /* Mouse cursor position in window coordinates, snapped to the grid + spacing. */ + double snapped_x, snapped_y; + + /* Move the selected item(s) by this movement. */ + double dx, dy; + + /* The sheet is scrolled by (canvas coordinates): */ + int offset_x, offset_y; + + /* The selected group's bounding box in window resp. canvas coordinates. */ + double x1, y1, x2, y2; + int cx1, cy1, cx2, cy2; + + /* The sheet's width and the its viewport's width. */ + guint sheet_width, sheet_height; + + g_return_val_if_fail (sheet != NULL, FALSE); + g_return_val_if_fail (IS_SHEET (sheet), FALSE); + g_return_val_if_fail (schematic_view != NULL, FALSE); + g_return_val_if_fail (IS_SCHEMATIC_VIEW (schematic_view), FALSE); + /* assert? */ + g_return_val_if_fail (sheet->priv->floating_objects != NULL, FALSE); + + priv = sheet->priv; + canvas = GNOME_CANVAS (sheet); + + switch (event->type) { + case GDK_BUTTON_RELEASE: + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + break; + + case GDK_BUTTON_PRESS: + if (sheet->state != SHEET_STATE_FLOAT) + return TRUE; + + switch (event->button.button) { + case 2: + case 4: + case 5: + return FALSE; + + case 1: + control_key_down = event->button.state & GDK_CONTROL_MASK; + + /* Continue adding if CTRL is pressed */ + if (!control_key_down) { + sheet->state = SHEET_STATE_NONE; + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + g_signal_handler_disconnect ( G_OBJECT (sheet), sheet->priv->float_handler_id); + sheet->priv->float_handler_id = 0; + } + + g_object_get (G_OBJECT (sheet->priv->floating_group), + "x", &tmp.x, + "y", &tmp.y, + NULL); + + delta.x = tmp.x - delta.x; + delta.y = tmp.y - delta.y; + + for (list = priv->floating_objects; list; list = list->next) { + SheetItem *floating_item; + ItemData *floating_data; + /* + * Destroy the ghost item and place a real item. + */ + floating_item = list->data; + if (!control_key_down) + floating_data = sheet_item_get_data (floating_item); + else + floating_data = item_data_clone (sheet_item_get_data (floating_item)); + + g_object_ref (G_OBJECT (floating_data)); + + item_data_move (floating_data, &delta); + schematic_add_item (schematic_view_get_schematic (schematic_view), + floating_data); + + if (!control_key_down) + gtk_object_destroy (GTK_OBJECT(floating_item)); + } + + if (!control_key_down) { + g_list_free (sheet->priv->floating_objects); + sheet->priv->floating_objects = NULL; + } else { + gtk_object_set (GTK_OBJECT (sheet->priv->floating_group), + "x", tmp.x, + "y", tmp.y, + NULL); + } + delta.x = 0; + delta.y = 0; + break; + + case 3: + /* + * Cancel the "float-placement" for button-3 clicks. + */ + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + sheet_item_cancel_floating (schematic_view); + break; + } + break; + + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + return TRUE; + + case GDK_MOTION_NOTIFY: + if (sheet->state != SHEET_STATE_FLOAT && + sheet->state != SHEET_STATE_FLOAT_START) + return FALSE; + + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + + if (sheet->state == SHEET_STATE_FLOAT_START) { + sheet->state = SHEET_STATE_FLOAT; + + /* + * Reparent the selected objects so that we can + * move them efficiently. + */ + for (list = priv->floating_objects; list; list = list->next) { + sheet_item_reparent (SHEET_ITEM (list->data), priv->floating_group); + } + + gtk_object_get (GTK_OBJECT (sheet->priv->floating_group), + "x", &delta.x, + "y", &delta.y, + NULL); + + /* + * Center the objects around the mouse pointer. + */ + gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group), + &x1, &y1, &x2, &y2); + + gnome_canvas_window_to_world (canvas, event->motion.x, event->motion.y, + &snapped_x, &snapped_y); + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + dx = snapped_x - (x1 + (x2 - x1) / 2); + dy = snapped_y - (y1 + (y2 - y1) / 2); + snap_to_grid (sheet->grid, &dx, &dy); + + gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group), + dx, dy); + + x1 += dx; + y1 += dy; + x2 += dx; + y2 += dy; + + last_x = snapped_x; + last_y = snapped_y; + + return TRUE; + } + + /* + * We have to convert from window to world coordinates here, + * since we get the coordinates relative the sheet and not the + * item like we do in sheet_item_event (). + */ + gnome_canvas_window_to_world (canvas, event->motion.x, event->motion.y, + &snapped_x, &snapped_y); + + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + /* Calculate which amount to move the selected objects by. */ + dx = snapped_x - last_x; + dy = snapped_y - last_y; + + /* We need the bounding box to check that we don't move anything + off-sheet. */ + gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group), + &x1, &y1, &x2, &y2); + + x1 += dx; + x2 += dx; + y1 += dy; + y2 += dy; + + gnome_canvas_get_scroll_offsets (canvas, &offset_x, &offset_y); + gnome_canvas_w2c (canvas, x1, y1, &cx1, &cy1); + gnome_canvas_w2c (canvas, x2, y2, &cx2, &cy2); + sheet_get_size_pixels (sheet, &sheet_width, &sheet_height); + + /* Check that we don't move outside the sheet horizontally */ + if (cx1 <= 0){ /* leftmost edge */ + dx = dx - x1; + snap_to_grid (sheet->grid, &dx, NULL); + snapped_x = last_x + dx; + } else if (cx2 >= sheet_width){ /* rightmost edge */ + dx = dx - (x2 - sheet_width/sheet->priv->zoom); + snap_to_grid (sheet->grid, &dx, NULL); + snapped_x = last_x + dx; + } + + /* And vertically */ + if (cy1 <= 0){ /* upper edge */ + dy = dy - y1; + snap_to_grid (sheet->grid, NULL, &dy); + snapped_y = last_y + dy; + } else if (cy2 >= sheet_height){ /* lower edge */ + dy = dy - (y2 - sheet_height/sheet->priv->zoom); + snap_to_grid (sheet->grid, NULL, &dy); + snapped_y = last_y + dy; + } + + last_x = snapped_x; + last_y = snapped_y; + + if (dx != 0 || dy != 0){ + gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group), + dx, dy); + } + break; + + case GDK_KEY_PRESS: + switch (event->key.keyval) { + case GDK_r: + schematic_view_rotate_ghosts (schematic_view); + { + int x, y; + + gdk_window_get_pointer (GDK_WINDOW (GTK_WIDGET (sheet)->window), + &x, &y, NULL); + gnome_canvas_window_to_world (GNOME_CANVAS (sheet), x, y, + &snapped_x, &snapped_y); + + /* + * Center the objects around the mouse pointer. + */ + gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (priv->floating_group), + &x1, &y1, &x2, &y2); + + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + dx = snapped_x - (x1 + (x2 - x1) / 2); + dy = snapped_y - (y1 + (y2 - y1) / 2); + snap_to_grid (sheet->grid, &dx, &dy); + + gnome_canvas_item_move (GNOME_CANVAS_ITEM (priv->floating_group), + dx, dy); + + last_x = snapped_x; + last_y = snapped_y; + } + break; + default: + return FALSE; + } + return TRUE; + + default: + return FALSE; + } + return TRUE; +} + +gboolean +sheet_item_select (SheetItem *item, gboolean select) +{ + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE); + + if ((item->priv->selected && select) || + (!item->priv->selected && !select)) { + return FALSE; + } + + g_signal_emit_by_name (G_OBJECT (item), "selection_changed", select); + item->priv->selected = select; + + return TRUE; +} + +void +sheet_item_select_in_area (SheetItem *item, SheetPos *p1, SheetPos *p2) +{ + SheetItemClass *si_class; + gboolean in_area; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + g_return_if_fail (p1 != NULL); + g_return_if_fail (p2 != NULL); + + si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item)); + in_area = si_class->is_in_area (item, p1, p2); + + if (in_area && !item->priv->selected) + sheet_item_select (item, TRUE); + else if (!in_area && item->priv->selected && + !item->priv->preserve_selection) + sheet_item_select (item, FALSE); +} + +/* + * sheet_item_reparent () + * + * Reparent a sheet object without moving it on the sheet. + */ +void +sheet_item_reparent (SheetItem *object, GnomeCanvasGroup *group) +{ + double x1, y1, x2, y2; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_SHEET_ITEM (object)); + g_return_if_fail (group != NULL); + g_return_if_fail (GNOME_IS_CANVAS_GROUP (group)); + + g_object_get (G_OBJECT (object), + "x", &x1, + "y", &y1, + NULL); + + gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (object), &x1, &y1); + + gnome_canvas_item_reparent (GNOME_CANVAS_ITEM (object), group); + + g_object_get (G_OBJECT (object), "x", &x2, "y", &y2, NULL); + + gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (object), &x2, &y2); + + gnome_canvas_item_move (GNOME_CANVAS_ITEM (object), x1 - x2, y1 - y2); + + /* This is needed because of a bug (?) in gnome-libs. */ + gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (object)); +} + +void +sheet_item_edit_properties (SheetItem *item) +{ + SheetItemClass *si_class; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + + si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item)); + + if (si_class->edit_properties) + si_class->edit_properties (item); +} + +void +sheet_item_rotate (SheetItem *sheet_item, int angle, SheetPos *center) +{ + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_SHEET_ITEM (sheet_item)); + + item_data_rotate (sheet_item->priv->data, angle, center); +} + +void +sheet_item_paste (SchematicView *schematic_view, ClipboardData *data) +{ + SheetItemClass *item_class; + ItemDataClass *id_class; + ItemData *item_data, *clone; + + g_return_if_fail (schematic_view != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (schematic_view)); + g_return_if_fail (data != NULL); + + item_data = ITEM_DATA (clipboard_data_get_item_data (data)); + + id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (item_data)); + if (id_class->clone == NULL) + return; + + /* + * Duplicate the data for the item and paste the item on the sheet. + */ + item_class = SHEET_ITEM_CLASS (clipboard_data_get_item_class (data)); + if (item_class->paste) { + clone = id_class->clone (item_data); + item_class->paste (schematic_view, clone); + } +} + +ItemData * +sheet_item_get_data (SheetItem *item) +{ + g_return_val_if_fail (item != NULL, NULL); + g_return_val_if_fail (IS_SHEET_ITEM (item), NULL); + + return item->priv->data; +} + +Sheet * +sheet_item_get_sheet (SheetItem *item) +{ + g_return_val_if_fail (item != NULL, NULL); + g_return_val_if_fail (IS_SHEET_ITEM (item), NULL); + + return SHEET (GNOME_CANVAS_ITEM (item)->canvas); +} + +gboolean +sheet_item_get_selected (SheetItem *item) +{ + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE); + + return item->priv->selected; +} + +gboolean +sheet_item_get_preserve_selection (SheetItem *item) +{ + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (IS_SHEET_ITEM (item), FALSE); + + return item->priv->preserve_selection; +} + +void +sheet_item_set_preserve_selection (SheetItem *item, gboolean set) +{ + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + + item->priv->preserve_selection = set; +} + +void +sheet_item_place (SheetItem *item, SchematicView *sv) +{ + SheetItemClass *si_class; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + + si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item)); + + if (si_class->place) + si_class->place (item, sv); +} + +void +sheet_item_place_ghost (SheetItem *item, SchematicView *sv) +{ + SheetItemClass *si_class; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_SHEET_ITEM (item)); + + si_class = SHEET_ITEM_CLASS (GTK_OBJECT_GET_CLASS (item)); + + if (si_class->place_ghost) + si_class->place_ghost (item, sv); +} + +void +sheet_item_add_menu (SheetItem *item, const char *menu, + const GtkActionEntry *action_entries, int nb_entries) +{ + GError *error = NULL; + gtk_action_group_add_actions (item->priv->action_group, + action_entries, + nb_entries, + NULL); + + if (!gtk_ui_manager_add_ui_from_string (item->priv->ui_manager, menu, -1, &error)) { + g_message ("building menus failed: %s", error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } +} diff --git a/src/sheet/sheet-item.h b/src/sheet/sheet-item.h new file mode 100644 index 0000000..d7d544f --- /dev/null +++ b/src/sheet/sheet-item.h @@ -0,0 +1,129 @@ +/* + * sheet-item.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __SHEET_ITEM_H +#define __SHEET_ITEM_H + +#include <gtk/gtk.h> +#include <glib.h> +#include <libgnomecanvas/libgnomecanvas.h> +#include "sheet-pos.h" +#include "item-data.h" + + +#define TYPE_SHEET_ITEM (sheet_item_get_type()) +#define SHEET_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, sheet_item_get_type (), SheetItem)) +#define SHEET_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, sheet_item_get_type (), SheetItemClass)) +#define IS_SHEET_ITEM(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, TYPE_SHEET_ITEM, SheetItemClass)) + +typedef struct _SheetItem SheetItem; +typedef struct _SheetItemClass SheetItemClass; +typedef struct _SheetItemPriv SheetItemPriv; + +#include "schematic-view.h" +#include "sheet.h" +#include "clipboard.h" + +struct _SheetItem { + GnomeCanvasGroup canvas_group; + SheetItemPriv *priv; +}; + +struct _SheetItemClass { + GnomeCanvasGroupClass parent_class; + + /* + * Methods. + */ + gboolean (*is_in_area) (SheetItem *item, SheetPos *p1, SheetPos *p2); + void (*show_labels) (SheetItem *sheet_item, gboolean show); + void (*edit_properties) (SheetItem *item); + void (*paste) (SchematicView *schematic_view, + ItemData *data); + void (*place) (SheetItem *item, SchematicView *sv); + void (*place_ghost) (SheetItem *item, SchematicView *sv); + + /* + * Signal handlers. + */ + void (*moved) (SheetItem *item); + void (*selection_changed)(SheetItem *item); + void (*mouse_over) (SheetItem *item); +}; + +GType sheet_item_get_type (void); + +void sheet_item_select_all (SchematicView *sv, + gboolean select); + +gboolean sheet_item_select (SheetItem *item, + gboolean select); + +Sheet *sheet_item_get_sheet (SheetItem *item); + +int sheet_item_event (SheetItem *sheet_item, + const GdkEvent *event, + SchematicView *sv); + +int sheet_item_floating_event (Sheet *sheet, + const GdkEvent *event, + SchematicView *schematic_view); + +void sheet_item_reparent (SheetItem *item, + GnomeCanvasGroup *group); + +void sheet_item_cancel_floating (SchematicView *sv); + +void sheet_item_edit_properties (SheetItem *item); + +ItemData *sheet_item_get_data (SheetItem *item); + +void sheet_item_paste (SchematicView *schematic_view, + ClipboardData *data); + +void sheet_item_rotate (SheetItem *sheet_item, + int angle, SheetPos *center); + +gboolean sheet_item_get_selected (SheetItem *item); + +gboolean sheet_item_get_preserve_selection (SheetItem *item); + +void sheet_item_set_preserve_selection (SheetItem *item, + gboolean set); + +void sheet_item_select_in_area (SheetItem *item, + SheetPos *p1, + SheetPos *p2); + +void sheet_item_place (SheetItem *item, SchematicView *sv); +void sheet_item_place_ghost (SheetItem *item, SchematicView *sv); +void sheet_item_add_menu (SheetItem *item, const char *menu, + const GtkActionEntry *action_entries, int nb_entries); + +#endif diff --git a/src/sheet/sheet-private.h b/src/sheet/sheet-private.h new file mode 100644 index 0000000..14025ee --- /dev/null +++ b/src/sheet/sheet-private.h @@ -0,0 +1,54 @@ +/* + * sheet-private.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __SHEET_PRIVATE_H +#define __SHEET_PRIVATE_H + +#include "sheet.h" + +struct _SheetPriv { + + int wire_handler_id; /* Keeps the current signal handler for wire + creation. */ + int float_handler_id; /* Keeps the signal handler for floating objects. */ + int scroll_timeout_id; + + double zoom; + gulong width; + gulong height; + + void *current_object; /* SheetItem */ + + GnomeCanvasGroup *selected_group; + GnomeCanvasGroup *floating_group; + GList *selected_objects; + GList *floating_objects; +}; + +#endif diff --git a/src/sheet/sheet.c b/src/sheet/sheet.c new file mode 100644 index 0000000..aa73760 --- /dev/null +++ b/src/sheet/sheet.c @@ -0,0 +1,469 @@ +/* + * sheet.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gdk/gdkprivate.h> +#include "sheet-private.h" +#include "sheet-item.h" +#include "node-store.h" +#include "wire-item.h" +#include "part-item.h" +#include "grid.h" + +static void sheet_class_init (SheetClass *klass); +static void sheet_init (Sheet *sheet); +static void sheet_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *spec); +static void sheet_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *spec); +static void sheet_destroy (GtkObject *object); +static void sheet_set_zoom (const Sheet *sheet, double zoom); +static void sheet_realized (GtkWidget *widget, gpointer data); + +enum { + SELECTION_CHANGED, + BUTTON_PRESS, + CONTEXT_CLICK, + CANCEL, + RESET_TOOL, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +GnomeCanvasClass *sheet_parent_class = NULL; + +enum { + ARG_0, + ARG_ZOOM +}; + +GType +sheet_get_type () +{ + static GType sheet_type = 0; + + if (!sheet_type) { + static const GTypeInfo sheet_info = { + sizeof(SheetClass), + NULL, + NULL, + (GClassInitFunc)sheet_class_init, + NULL, + NULL, + sizeof(Sheet), + 0, + (GInstanceInitFunc)sheet_init, + NULL + }; + + sheet_type = g_type_register_static(GNOME_TYPE_CANVAS, + "Sheet", &sheet_info, 0); + } + return sheet_type; +} + +static void +sheet_class_init (SheetClass *sheet_class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS (sheet_class); + widget_class = GTK_WIDGET_CLASS (sheet_class); + sheet_parent_class = g_type_class_peek(GNOME_TYPE_CANVAS); + + object_class->set_property = sheet_set_property; + object_class->get_property = sheet_get_property; + + g_object_class_install_property( + object_class, + ARG_ZOOM, + g_param_spec_double("zoom", "Sheet::zoom", "the zoom factor", + 0.01f, 10.0f, 1.0f, G_PARAM_READWRITE) + ); + + //object_class->destroy = sheet_destroy; + + /* Signals. */ + signals[SELECTION_CHANGED] = g_signal_new ("selection_changed", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SheetClass, selection_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[BUTTON_PRESS] = g_signal_new ("button_press", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetClass, button_press), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + /* TODO deberia ser boolean? */ + G_TYPE_NONE, 1, GDK_TYPE_EVENT); + signals[CONTEXT_CLICK] = g_signal_new ("context_click", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetClass, context_click), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER); + signals[CONTEXT_CLICK] = g_signal_new ("cancel", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetClass, cancel), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[RESET_TOOL] = g_signal_new ("reset_tool", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(SheetClass, reset_tool), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +sheet_init (Sheet *sheet) +{ + sheet->priv = g_new0 (SheetPriv, 1); + sheet->priv->zoom = 1.0; + sheet->priv->selected_objects = NULL; + sheet->priv->selected_group = NULL; + sheet->priv->floating_group = NULL; + sheet->priv->wire_handler_id = 0; + sheet->priv->float_handler_id = 0; + + sheet->state = SHEET_STATE_NONE; + +/* GNOME_CANVAS (sheet)->aa = TRUE;*/ +} + +void +sheet_get_zoom (const Sheet *sheet, gdouble *zoom) +{ + *zoom = sheet->priv->zoom; +} + +static void +sheet_set_zoom (const Sheet *sheet, double zoom) +{ + gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet), zoom); + sheet->priv->zoom = zoom; +} + +void +sheet_change_zoom (const Sheet *sheet, gdouble rate) +{ + sheet->priv->zoom *= rate; + + gnome_canvas_set_pixels_per_unit (GNOME_CANVAS (sheet), sheet->priv->zoom); +} + +static void +sheet_realized (GtkWidget *widget, gpointer data) +{ + GdkWindow *window; + GdkColormap *colormap; + GtkStyle *style; + + /* + * We set the background pixmap to NULL so that X won't clear + * exposed areas and thus be faster. + */ + gdk_window_set_back_pixmap (GTK_LAYOUT (widget)->bin_window, NULL, FALSE); + + window = widget->window; + + /* + * Set the background to white. + */ + style = gtk_style_copy (widget->style); + colormap = gtk_widget_get_colormap (widget); + gdk_color_white (colormap, &style->bg[GTK_STATE_NORMAL]); + gtk_widget_set_style (widget, style); + gtk_style_unref (style); +} + +GtkWidget * +sheet_new (int width, int height) +{ + GnomeCanvas *sheet_canvas; + GnomeCanvasGroup *sheet_group; + GnomeCanvasPoints *points; + Sheet *sheet; + GtkWidget *sheet_widget; + + /* Creo el Canvas con Anti-aliasing */ + sheet = SHEET(g_object_new(TYPE_SHEET, NULL)); + + g_signal_connect(G_OBJECT(sheet), + "realize", G_CALLBACK(sheet_realized), 0); + + sheet_canvas = GNOME_CANVAS(sheet); + sheet_group = GNOME_CANVAS_GROUP(sheet_canvas->root); + sheet_widget = GTK_WIDGET(sheet); + + gnome_canvas_set_scroll_region (sheet_canvas, + 0, 0, width + 20, height + 20); + + /* Since there is no API for this (yet), tweak it directly. */ + sheet_canvas->close_enough = 6.0; + + sheet->priv->width = width; + sheet->priv->height = height; + + /* Create the dot grid. */ + sheet->grid = GRID (gnome_canvas_item_new ( + sheet_group, + grid_get_type (), + "color", "dark gray", + "spacing", 10.0, + "snap", TRUE, + NULL)); + + grid_show (sheet->grid, TRUE); + + /* Everything outside the sheet should be gray. */ + /* top */ + gnome_canvas_item_new ( + sheet_group, + gnome_canvas_rect_get_type (), + "fill_color", "gray", + "outline_color", NULL, + "width_pixels", (int)1, + "x1", 0.0, + "y1", 0.0, + "x2", (double)width + 20, + "y2", 20.0, + NULL); + + gnome_canvas_item_new ( + sheet_group, + gnome_canvas_rect_get_type (), + "fill_color", "gray", + "outline_color", NULL, + "width_pixels", (int)1, + "x1", 0.0, + "y1", (double)height, + "x2", (double)width + 20, + "y2", (double)height + 20, + NULL); + + /* right */ + gnome_canvas_item_new ( + sheet_group, + gnome_canvas_rect_get_type (), + "fill_color", "gray", + "outline_color", NULL, + "width_pixels", (int)1, + "x1", 0.0, + "y1", 0.0, + "x2", 20.0, + "y2", (double)height + 20, + NULL); + + gnome_canvas_item_new ( + sheet_group, + gnome_canvas_rect_get_type (), + "fill_color", "gray", + "outline_color", NULL, + "width_pixels", (int)1, + "x1", (double)width, + "y1", 0.0, + "x2", (double)width + 20, + "y2", (double)height + 20, + NULL); + + /* Draw a thin black border around the sheet. */ + points = gnome_canvas_points_new (5); + points->coords[0] = 20.0; + points->coords[1] = 20.0; + points->coords[2] = width; + points->coords[3] = 20.0; + points->coords[4] = width; + points->coords[5] = height; + points->coords[6] = 20.0; + points->coords[7] = height; + points->coords[8] = 20.0; + points->coords[9] = 20.0; + gnome_canvas_item_new ( + sheet_group, + gnome_canvas_line_get_type (), + "fill_color", "black", + "width_pixels", (int)1, + "points", points, + NULL); + + gnome_canvas_points_free (points); + + /* Finally, create the object group that holds all objects. */ + sheet->object_group = GNOME_CANVAS_GROUP(gnome_canvas_item_new ( + gnome_canvas_root(GNOME_CANVAS(sheet_canvas)), + GNOME_TYPE_CANVAS_GROUP, + "x", 0.0, + "y", 0.0, + NULL)); + + sheet->priv->selected_group = GNOME_CANVAS_GROUP (gnome_canvas_item_new ( + sheet->object_group, + GNOME_TYPE_CANVAS_GROUP, + "x", 0.0, + "y", 0.0, + NULL)); + + sheet->priv->floating_group = GNOME_CANVAS_GROUP (gnome_canvas_item_new ( + sheet->object_group, + gnome_canvas_group_get_type (), + "x", 0.0, + "y", 0.0, + NULL)); + + return sheet_widget; +} + +static void +sheet_set_property (GObject *object, + guint prop_id, const GValue *value, GParamSpec *spec) +{ + const Sheet *sheet = SHEET (object); + + switch (prop_id) { + case ARG_ZOOM: + sheet_set_zoom (sheet, g_value_get_double(value)); + break; + } +} + +static void +sheet_get_property (GObject *object, + guint prop_id, GValue *value, GParamSpec *spec) +{ + const Sheet *sheet = SHEET (object); + + switch (prop_id) { + case ARG_ZOOM: + g_value_set_double(value, sheet->priv->zoom); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(sheet, prop_id, spec); + break; + } +} + +static void +sheet_destroy (GtkObject *object) +{ + Sheet *sheet; + SheetPriv *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_SHEET (object)); + + sheet = SHEET (object); + priv = sheet->priv; + + if (priv) { + g_list_free (priv->selected_objects); + g_list_free (priv->floating_objects); + + /* + * We need to destroy this first so that things get destroyed in the + * right order. Label groups are destroyed in their parent parts' + * destroy handler, so when the sheet gets destroyed, label groups + * will screw things up if this is not done. + */ + gtk_object_destroy (GTK_OBJECT (sheet->object_group)); + + g_free (priv); + sheet->priv = NULL; + } + + if (GTK_OBJECT_CLASS (sheet_parent_class)->destroy) + (* GTK_OBJECT_CLASS (sheet_parent_class)->destroy) (object); +} + +void +sheet_scroll (const Sheet *sheet, int delta_x, int delta_y) +{ + GtkAdjustment *hadj, *vadj; + GtkAllocation *allocation; + gfloat vnew, hnew; + gfloat hmax, vmax; + const SheetPriv *priv = sheet->priv; + + hadj = GTK_LAYOUT (sheet)->hadjustment; + vadj = GTK_LAYOUT (sheet)->vadjustment; + + allocation = >K_WIDGET (sheet)->allocation; + + if (priv->width > allocation->width) + hmax = (gfloat) (priv->width - allocation->width); + else + hmax = 0.0; + + if (priv->height > allocation->height) + vmax = (gfloat) (priv->height - allocation->height); + else + vmax = 0.0; + + hnew = CLAMP (hadj->value + (gfloat) delta_x, 0.0, hmax); + vnew = CLAMP (vadj->value + (gfloat) delta_y, 0.0, vmax); + + if (hnew != hadj->value) { + hadj->value = hnew; + g_signal_emit_by_name (G_OBJECT (hadj), "value_changed"); + } + if (vnew != vadj->value) { + vadj->value = vnew; + g_signal_emit_by_name (G_OBJECT (vadj), "value_changed"); + } +} + +void +sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height) +{ + *width = sheet->priv->width * sheet->priv->zoom; + *height = sheet->priv->height * sheet->priv->zoom; +} + +void +sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog) +{ +/* gtk_window_set_transient_for ( + GTK_WINDOW (dialog), + GTK_WINDOW (SCHEMATIC (sheet->schematic)->toplevel) + );*/ +} diff --git a/src/sheet/sheet.h b/src/sheet/sheet.h new file mode 100644 index 0000000..d880743 --- /dev/null +++ b/src/sheet/sheet.h @@ -0,0 +1,92 @@ +/* + * sheet.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __SHEET_H +#define __SHEET_H + +#include <libgnomecanvas/libgnomecanvas.h> +#include <gtk/gtkdialog.h> +#include "grid.h" + +typedef struct _Sheet Sheet; +typedef struct _SheetPriv SheetPriv; +typedef struct _SheetClass SheetClass; + +#define TYPE_SHEET (sheet_get_type()) +#define SHEET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_SHEET, Sheet)) +#define SHEET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, TYPE_SHEET, SheetClass)) +#define IS_SHEET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_SHEET)) + +typedef enum { + SHEET_STATE_NONE, + SHEET_STATE_DRAG_START, + SHEET_STATE_DRAG, + SHEET_STATE_FLOAT_START, + SHEET_STATE_FLOAT, + SHEET_STATE_WIRE, + SHEET_STATE_TEXTBOX_WAIT, + SHEET_STATE_TEXTBOX_START, + SHEET_STATE_TEXTBOX_CREATING +} SheetState; + +struct _Sheet { + GnomeCanvas parent_canvas; + SheetState state; + GnomeCanvasGroup *object_group; + Grid *grid; + SheetPriv *priv; +}; + +struct _SheetClass { + GnomeCanvasClass parent_class; + + void (*selection_changed) (Sheet *sheet); + gint (*button_press) (Sheet *sheet, GdkEventButton *event); + void (*context_click) (Sheet *sheet, + const char *name, gpointer data); + void (*cancel) (Sheet *sheet); + void (*reset_tool) (Sheet *sheet); +}; + +GType sheet_get_type (void); +GtkWidget *sheet_new (int width, int height); +void sheet_scroll (const Sheet *sheet, int dx, int dy); +void sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height); +int sheet_get_num_selected_items (const Sheet *sheet); +gpointer sheet_get_first_selected_item (const Sheet *sheet); +GSList *sheet_get_selected_items (const Sheet *sheet); +void sheet_change_zoom (const Sheet *sheet, double rate); +void sheet_get_zoom (const Sheet *sheet, gdouble *zoom); +void sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog); +void sheet_delete_selected_items (const Sheet *sheet); +void sheet_rotate_selected_items (const Sheet *sheet); +void sheet_rotate_floating_items (const Sheet *sheet); +void sheet_reset_floating_items (const Sheet *sheet); + +#endif diff --git a/src/sheet/textbox-item.c b/src/sheet/textbox-item.c new file mode 100644 index 0000000..2354c55 --- /dev/null +++ b/src/sheet/textbox-item.c @@ -0,0 +1,699 @@ +/* + * textbox-item.c + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* + * TextboxItem object: the graphical representation of a textbox. + * + * Author: + * Richard Hult (rhult@hem2.passagen.se) + * + * (C) 1999, 2000 Richard Hult, http://www.dtek.chalmers.se/~d4hult/oregano/ + */ + +#include <math.h> +#include <gnome.h> +#include <glade/glade.h> +#include "cursors.h" +#include "sheet-private.h" +#include "sheet-pos.h" +#include "textbox-item.h" +#include "textbox.h" +#include "dialogs.h" + +#define NORMAL_COLOR "black" +#define SELECTED_COLOR "green" +#define TEXTBOX_FONT "Arial 10" + +static void textbox_item_class_init (TextboxItemClass *klass); +static void textbox_item_init (TextboxItem *item); +static void textbox_item_destroy (GtkObject *object); +static void textbox_item_moved (SheetItem *object); + +static void textbox_rotated_callback (ItemData *data, int angle, + SheetItem *sheet_item); +static void textbox_flipped_callback (ItemData *data, gboolean horizontal, + SheetItem *sheet_item); +static void textbox_moved_callback (ItemData *data, SheetPos *pos, + SheetItem *item); +static void textbox_text_changed_callback (ItemData *data, gchar *new_text, + SheetItem *item); +static void textbox_font_changed_callback (ItemData *data, gchar *new_font, + SheetItem *item); + +static void textbox_item_paste (SchematicView *sv, ItemData *data); +static void selection_changed (TextboxItem *item, gboolean select, + gpointer user_data); +static int select_idle_callback (TextboxItem *item); +static int deselect_idle_callback (TextboxItem *item); +static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2); +inline static void get_cached_bounds (TextboxItem *item, SheetPos *p1, + SheetPos *p2); + +static void textbox_item_place (SheetItem *item, SchematicView *sv); +static void textbox_item_place_ghost (SheetItem *item, SchematicView *sv); + +static void edit_cmd (GtkWidget *widget, SchematicView *sv); +static void edit_textbox (SheetItem *sheet_item); + +typedef struct { + GtkDialog *dialog; + GtkFontSelection *font; + GtkEntry *entry; +} TextboxPropDialog; + +static TextboxPropDialog *prop_dialog = NULL; +static SheetItemClass *textbox_item_parent_class = NULL; + +/* Use EDIT!! */ +static const char *textbox_item_context_menu = +"<ui>" +" <popup name='ItemMenu'>" +" <menuitem action='EditText'/>" +" </popup>" +"</ui>"; + +static GtkActionEntry action_entries[] = { + {"EditText", GTK_STOCK_PROPERTIES, N_("_Edit the text..."), NULL, + N_("Edit the text"),G_CALLBACK (edit_cmd)} +}; + +enum { + TEXTBOX_ITEM_ARG_0, + TEXTBOX_ITEM_ARG_NAME +}; + +struct _TextboxItemPriv { + guint cache_valid : 1; + + guint highlight : 1; + + // FIXME: More members. + GnomeCanvasItem *text_canvas_item; + + /* + * Cached bounding box. This is used to make + * the rubberband selection a bit faster. + */ + SheetPos bbox_start; + SheetPos bbox_end; +}; + +GType +textbox_item_get_type () +{ + static GType textbox_item_type = 0; + + if (!textbox_item_type) { + static const GTypeInfo textbox_item_info = { + sizeof(TextboxItemClass), + NULL, + NULL, + (GClassInitFunc)textbox_item_class_init, + NULL, + NULL, + sizeof(TextboxItem), + 0, + (GInstanceInitFunc)textbox_item_init, + NULL + }; + + textbox_item_type = g_type_register_static(TYPE_SHEET_ITEM, + "TextboxItem", &textbox_item_info, 0); + } + return textbox_item_type; +} + +static void +textbox_item_class_init (TextboxItemClass *textbox_item_class) +{ + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + SheetItemClass *sheet_item_class; + + object_class = G_OBJECT_CLASS(textbox_item_class); + gtk_object_class = GTK_OBJECT_CLASS(textbox_item_class); + sheet_item_class = SHEET_ITEM_CLASS(textbox_item_class); + textbox_item_parent_class = + g_type_class_peek_parent(textbox_item_class); + + gtk_object_class->destroy = textbox_item_destroy; + + sheet_item_class->moved = textbox_item_moved; + sheet_item_class->paste = textbox_item_paste; + sheet_item_class->is_in_area = is_in_area; + sheet_item_class->selection_changed = (gpointer) selection_changed; + sheet_item_class->edit_properties = edit_textbox; + sheet_item_class->place = textbox_item_place; + sheet_item_class->place_ghost = textbox_item_place_ghost; +} + +static void +textbox_item_init (TextboxItem *item) +{ + TextboxItemPriv *priv; + + priv = g_new0 (TextboxItemPriv, 1); + item->priv = priv; + + priv->highlight = FALSE; + priv->cache_valid = FALSE; + + sheet_item_add_menu (SHEET_ITEM (item), textbox_item_context_menu, + action_entries, G_N_ELEMENTS (action_entries)); +} + +static void +textbox_item_destroy (GtkObject *object) +{ + TextboxItem *textbox; + TextboxItemPriv *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (object)); + + textbox = TEXTBOX_ITEM (object); + priv = textbox->priv; + + if (priv) { + if (priv->text_canvas_item) { + gtk_object_destroy(GTK_OBJECT(priv->text_canvas_item)); + } + textbox->priv = NULL; + g_free (priv); + } + + if (GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy){ + GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy(object); + } +} + +/* + * textbox_item_moved + * + * "moved" signal handler. Invalidates the bounding box cache. + */ +static void +textbox_item_moved (SheetItem *object) +{ + TextboxItem *item; + TextboxItemPriv *priv; + + item = TEXTBOX_ITEM (object); + priv = item->priv; + + priv->cache_valid = FALSE; +} + +TextboxItem * +textbox_item_new (Sheet *sheet, Textbox *textbox) +{ + TextboxItem *item; + TextboxItemPriv *priv; + SheetPos pos; + + g_return_val_if_fail(sheet != NULL, NULL); + g_return_val_if_fail(IS_SHEET(sheet), NULL); + + item_data_get_pos(ITEM_DATA(textbox), &pos); + + item = TEXTBOX_ITEM(gnome_canvas_item_new ( + sheet->object_group, + textbox_item_get_type(), + "data", textbox, + "x", (double) pos.x, + "y", (double) pos.y, + NULL)); + + priv = item->priv; + + priv->text_canvas_item = gnome_canvas_item_new ( + GNOME_CANVAS_GROUP (item), + gnome_canvas_text_get_type (), + "x", 0.0, + "y", 0.0, + "text", textbox_get_text (textbox), + "fill_color", NORMAL_COLOR, + "anchor", GTK_ANCHOR_SW, + "font", TEXTBOX_FONT, + NULL); + + g_signal_connect_object(G_OBJECT (textbox), + "rotated", G_CALLBACK(textbox_rotated_callback), + G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT (textbox), + "flipped", G_CALLBACK(textbox_flipped_callback), + G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT (textbox), + "moved", G_CALLBACK(textbox_moved_callback), + G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT (textbox), + "text_changed", G_CALLBACK(textbox_text_changed_callback), + G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT (textbox), + "font_changed", G_CALLBACK(textbox_font_changed_callback), + G_OBJECT(item), 0); + + textbox_update_bbox (textbox); + + return item; +} + +void +textbox_item_signal_connect_placed (TextboxItem *textbox_item, + SchematicView *sv) +{ + g_signal_connect ( + G_OBJECT (textbox_item), + "event", + G_CALLBACK(sheet_item_event), + sv); +} + +static void +textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +{ + TextboxItem *item; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item)); + + item = TEXTBOX_ITEM (sheet_item); + + item->priv->cache_valid = FALSE; +} + +static void +textbox_flipped_callback (ItemData *data, + gboolean horizontal, SheetItem *sheet_item) +{ + TextboxItem *item; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item)); + + item = TEXTBOX_ITEM (sheet_item); + + item->priv->cache_valid = FALSE; +} + +static int +select_idle_callback (TextboxItem *item) +{ + SheetPos bbox_start, bbox_end; + TextboxItemPriv *priv = item->priv; + + get_cached_bounds (item, &bbox_start, &bbox_end); + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item), + "fill_color", SELECTED_COLOR, NULL); + + priv->highlight = TRUE; + + return FALSE; +} + +static int +deselect_idle_callback (TextboxItem *item) +{ + TextboxItemPriv *priv = item->priv; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item), + "fill_color", NORMAL_COLOR, NULL); + + priv->highlight = FALSE; + + return FALSE; +} + +static void +selection_changed (TextboxItem *item, gboolean select, gpointer user_data) +{ + if (select) + gtk_idle_add ((gpointer) select_idle_callback, item); + else + gtk_idle_add ((gpointer) deselect_idle_callback, item); +} + +static gboolean +is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +{ + TextboxItem *item; + SheetPos bbox_start, bbox_end; + + item = TEXTBOX_ITEM (object); + + get_cached_bounds (item, &bbox_start, &bbox_end); + + if (p1->x < bbox_start.x && + p2->x > bbox_end.x && + p1->y < bbox_start.y && + p2->y > bbox_end.y) + return TRUE; + + return FALSE; +} + +/** + * Retrieves the bounding box. We use a caching scheme for this + * since it's too expensive to calculate it every time we need it. + */ +inline static void +get_cached_bounds (TextboxItem *item, SheetPos *p1, SheetPos *p2) +{ + PangoFontDescription *font; + PangoFontMetrics *font_metric; + int width; + int rbearing; + int lbearing; + int ascent, descent; + SheetPos pos; + + TextboxItemPriv *priv; + priv = item->priv; + + if (!priv->cache_valid) { + SheetPos start_pos, end_pos; + + font = pango_font_description_from_string(TEXTBOX_FONT); + + item_data_get_pos (sheet_item_get_data (SHEET_ITEM (item)), + &pos); + + start_pos.x = pos.x; + start_pos.y = pos.y-5;// - font->ascent; + end_pos.x = pos.x+5; // + rbearing; + end_pos.y = pos.y+5; // + font->descent; + + priv->bbox_start = start_pos; + priv->bbox_end = end_pos; + priv->cache_valid = TRUE; + pango_font_description_free(font); + } + + memcpy (p1, &priv->bbox_start, sizeof (SheetPos)); + memcpy (p2, &priv->bbox_end, sizeof (SheetPos)); + +} + +static void +textbox_item_paste (SchematicView *sv, ItemData *data) +{ + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_TEXTBOX (data)); + + schematic_view_add_ghost_item (sv, data); +} + +/** + * This is called when the textbox data was moved. Update the view accordingly. + */ +static void +textbox_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item) +{ + TextboxItem *textbox_item; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (item)); + + if (pos == NULL) + return; + + textbox_item = TEXTBOX_ITEM (item); + + /* + * Move the canvas item and invalidate the bbox cache. + */ + gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y); + textbox_item->priv->cache_valid = FALSE; +} + +static void +textbox_text_changed_callback (ItemData *data, + gchar *new_text, SheetItem *item) +{ + TextboxItem *textbox_item; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (item)); + + textbox_item = TEXTBOX_ITEM (item); + + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM ( textbox_item->priv->text_canvas_item ), + "text", new_text, NULL ); + +} + +static void +textbox_font_changed_callback (ItemData *data, + gchar *new_font, SheetItem *item) +{ + TextboxItem *textbox_item; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (item)); + g_return_if_fail (new_font != NULL); + + textbox_item = TEXTBOX_ITEM (item); + + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (textbox_item->priv->text_canvas_item), + "font", new_font, NULL); +} + +static void +textbox_item_place (SheetItem *item, SchematicView *sv) +{ + textbox_item_signal_connect_placed (TEXTBOX_ITEM (item), sv); + + g_signal_connect ( + G_OBJECT (item), + "double_clicked", + G_CALLBACK(edit_textbox), + item); +} + +static void +textbox_item_place_ghost (SheetItem *item, SchematicView *sv) +{ +// textbox_item_signal_connect_placed (TEXTBOX_ITEM (item)); +} + +static gboolean +create_textbox_event (Sheet *sheet, GdkEvent *event, SchematicView *sv) +{ + switch (event->type) { + case GDK_3BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + return TRUE; + + case GDK_BUTTON_PRESS: + if (event->button.button == 4 || event->button.button == 5) + return FALSE; + + if (event->button.button == 1) { + if (sheet->state == SHEET_STATE_TEXTBOX_WAIT) + sheet->state = SHEET_STATE_TEXTBOX_START; + + return TRUE; + } else + return FALSE; + + case GDK_BUTTON_RELEASE: + if (event->button.button == 4 || event->button.button == 5) + return FALSE; + + if (sheet->state == SHEET_STATE_TEXTBOX_START) { + Textbox *textbox; + SheetPos pos; + + sheet->state = SHEET_STATE_NONE; + + pos.x = event->button.x; + pos.y = event->button.y; + + textbox = textbox_new (NULL); + item_data_set_pos (ITEM_DATA (textbox), &pos); + textbox_set_text (textbox, _("Label")); + + schematic_add_item (schematic_view_get_schematic (sv), + ITEM_DATA (textbox)); + + g_signal_emit_by_name(G_OBJECT(sheet), + "reset_tool", NULL); + g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), + G_CALLBACK(create_textbox_event), sv); + } + + return TRUE; + + default: + return FALSE; + } + + return TRUE; +} + + void +textbox_item_cancel_listen (SchematicView *sv) +{ + Sheet *sheet; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + + sheet = schematic_view_get_sheet (sv); + + sheet->state = SHEET_STATE_NONE; + g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), G_CALLBACK(create_textbox_event), sv); +} + +void +textbox_item_listen (SchematicView *sv) +{ + Sheet *sheet; + + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + +// schematic_view_disconnect_handler (sv); + sheet = schematic_view_get_sheet (sv); + + /* + * Connect to a signal handler that will + * let the user create a new textbox. + */ + sheet->state = SHEET_STATE_TEXTBOX_WAIT; + g_signal_connect(G_OBJECT(sheet), + "event", G_CALLBACK(create_textbox_event), sv); +} + +/* + * Go through the properties and commit the changes. + */ +static void +edit_dialog_ok(TextboxItem *item) +{ + TextboxItemPriv *priv; + Textbox *textbox; + const gchar *value; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (item)); + + priv = item->priv; + textbox = TEXTBOX (sheet_item_get_data (SHEET_ITEM (item))); + + value = gtk_entry_get_text(GTK_ENTRY(prop_dialog->entry)); + + textbox_set_text(textbox, value); + textbox_set_font(textbox, + gtk_font_selection_get_font_name(prop_dialog->font)); +} + +static void +edit_textbox (SheetItem *sheet_item) +{ + Sheet *sheet; + TextboxItem *item; + TextboxItemPriv *priv; + Textbox *textbox; + char *msg, *value; + GladeXML *gui; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item)); + + item = TEXTBOX_ITEM (sheet_item); + priv = item->priv; + textbox = TEXTBOX (sheet_item_get_data (sheet_item)); + + if (!g_file_test (OREGANO_GLADEDIR "/textbox-properties-dialog.glade", + G_FILE_TEST_EXISTS)) { + msg = g_strdup_printf ( + _("The file %s could not be found. You might need to reinstall Oregano to fix this."), + OREGANO_GLADEDIR "/textbox-properties-dialog.glade"); + oregano_error (_("Could not create textbox properties dialog")); + g_free (msg); + return; + } + + gui = glade_xml_new ( + OREGANO_GLADEDIR "/textbox-properties-dialog.glade", + NULL, NULL); + if (!gui) { + oregano_error (_("Could not create textbox properties dialog")); + return; + } + + prop_dialog = g_new0 (TextboxPropDialog, 1); + prop_dialog->dialog = GTK_DIALOG ( + glade_xml_get_widget (gui, "textbox-properties-dialog")); + prop_dialog->font = GTK_FONT_SELECTION ( + glade_xml_get_widget (gui, "font_selector")); + prop_dialog->entry = GTK_ENTRY (glade_xml_get_widget (gui, "entry")); + + value = textbox_get_font (textbox); + gtk_font_selection_set_font_name ( + GTK_FONT_SELECTION (prop_dialog->font), value); + + value = textbox_get_text(textbox); + gtk_entry_set_text (GTK_ENTRY (prop_dialog->entry), value); + + sheet = sheet_item_get_sheet (SHEET_ITEM (item)); + sheet_dialog_set_parent (sheet, (GtkDialog*) prop_dialog->dialog); + + gtk_dialog_set_default_response ( + GTK_DIALOG (prop_dialog->dialog), GTK_RESPONSE_OK); + + gtk_dialog_run( GTK_DIALOG (prop_dialog->dialog)); + + edit_dialog_ok(item); + + /* Clean the dialog */ + gtk_widget_destroy(GTK_WIDGET(prop_dialog->dialog)); + prop_dialog = NULL; +} + +static void +edit_cmd (GtkWidget *widget, SchematicView *sv) +{ + GList *list; + + list = schematic_view_get_selection (sv); + if ((list != NULL) && IS_TEXTBOX_ITEM (list->data)) + edit_textbox (list->data); +} diff --git a/src/sheet/textbox-item.h b/src/sheet/textbox-item.h new file mode 100644 index 0000000..74edfff --- /dev/null +++ b/src/sheet/textbox-item.h @@ -0,0 +1,67 @@ +/* + * textbox-item.h + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __TEXTBOX_ITEM_H +#define __TEXTBOX_ITEM_H + +#include <gtk/gtk.h> +#include "schematic-view.h" +#include "sheet-item.h" +#include "textbox.h" + +#define TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, textbox_item_get_type (), TextboxItem) +#define TEXTBOX_ITEM_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, textbox_item_get_type (), TextboxItemClass) +#define IS_TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, textbox_item_get_type ()) + +typedef struct _TextboxItemPriv TextboxItemPriv; + +typedef enum { + TEXTBOX_DIR_NONE = 0, + TEXTBOX_DIR_HORIZ = 1, + TEXTBOX_DIR_VERT = 2, + TEXTBOX_DIR_DIAG = 3 +} TextboxDir; + +typedef struct { + SheetItem parent_object; + TextboxItemPriv *priv; +} TextboxItem; + +typedef struct { + SheetItemClass parent_class; +} TextboxItemClass; + +GType textbox_item_get_type (void); +TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox); +void textbox_item_signal_connect_placed (TextboxItem *textbox_item, SchematicView *sv); +void textbox_item_cancel_listen (SchematicView *sv); +void textbox_item_listen (SchematicView *sv); + + +#endif diff --git a/src/sheet/wire-item.c b/src/sheet/wire-item.c new file mode 100644 index 0000000..a5b542d --- /dev/null +++ b/src/sheet/wire-item.c @@ -0,0 +1,916 @@ +/* + * wire-item.c + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <math.h> +#include <gnome.h> +#include "cursors.h" +#include "sheet-private.h" +#include "sheet-pos.h" +#include "wire-item.h" +#include "node-store.h" +#include "wire.h" +#include "wire-private.h" + +#define NORMAL_COLOR "blue" +#define SELECTED_COLOR "green" +#define HIGHLIGHT_COLOR "yellow" + +#define RESIZER_SIZE 4.0f + +static void wire_item_class_init (WireItemClass *klass); +static void wire_item_init (WireItem *item); +static void wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id); +static void wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); +static void wire_item_destroy (GtkObject *object); +static void wire_item_moved (SheetItem *object); + +static void wire_rotated_callback (ItemData *data, int angle, + SheetItem *sheet_item); +static void wire_flipped_callback (ItemData *data, gboolean horizontal, + SheetItem *sheet_item); +static void wire_moved_callback (ItemData *data, SheetPos *pos, + SheetItem *item); +static void wire_changed_callback (Wire *, WireItem *item); +static void wire_delete_callback (Wire *, WireItem *item); + +static void wire_item_paste (SchematicView *sv, ItemData *data); +static void selection_changed (WireItem *item, gboolean select, + gpointer user_data); +static int select_idle_callback (WireItem *item); +static int deselect_idle_callback (WireItem *item); +static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2); +inline static void get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2); + +static void mouse_over_wire_cb (WireItem *item, SchematicView *sv); +static void highlight_wire_cb (Wire *wire, WireItem *item); +static int unhighlight_wire (WireItem *item); + +static void wire_item_place (SheetItem *item, SchematicView *sv); +static void wire_item_place_ghost (SheetItem *item, SchematicView *sv); + +static SheetItemClass *wire_item_parent_class = NULL; + +enum { + WIRE_ITEM_ARG_0, + WIRE_ITEM_ARG_NAME +}; + +enum { + WIRE_RESIZER_NONE, + WIRE_RESIZER_1, + WIRE_RESIZER_2 +}; + +struct _WireItemPriv { + guint cache_valid : 1; + guint resize_state; + guint highlight : 1; + WireDir direction; /* Direction of the wire. */ + + GnomeCanvasLine *line; + GnomeCanvasRect *resize1; + GnomeCanvasRect *resize2; + + /* + * Cached bounding box. This is used to make + * the rubberband selection a bit faster. + */ + SheetPos bbox_start; + SheetPos bbox_end; +}; + +GType +wire_item_get_type () +{ + static GType wire_item_type = 0; + + if (!wire_item_type) { + static const GTypeInfo wire_item_info = { + sizeof (WireItemClass), + NULL, + NULL, + (GClassInitFunc)wire_item_class_init, + NULL, + NULL, + sizeof (WireItem), + 0, + (GInstanceInitFunc)wire_item_init, + NULL + }; + + wire_item_type = g_type_register_static(TYPE_SHEET_ITEM, + "WireItem", &wire_item_info, 0); + } + return wire_item_type; +} + +static void +wire_item_class_init (WireItemClass *wire_item_class) +{ + GObjectClass *object_class; + GtkObjectClass *gtk_object_class; + SheetItemClass *sheet_item_class; + + object_class = G_OBJECT_CLASS(wire_item_class); + gtk_object_class = GTK_OBJECT_CLASS(wire_item_class); + sheet_item_class = SHEET_ITEM_CLASS(wire_item_class); + wire_item_parent_class = g_type_class_peek(TYPE_SHEET_ITEM); + + gtk_object_class->destroy = wire_item_destroy; + + sheet_item_class->moved = wire_item_moved; + sheet_item_class->paste = wire_item_paste; + sheet_item_class->is_in_area = is_in_area; + sheet_item_class->selection_changed = (gpointer) selection_changed; + sheet_item_class->place = wire_item_place; + sheet_item_class->place_ghost = wire_item_place_ghost; +} + +static void +wire_item_init (WireItem *item) +{ + WireItemPriv *priv; + + priv = g_new0 (WireItemPriv, 1); + + priv->direction = WIRE_DIR_NONE; + priv->highlight = FALSE; + priv->cache_valid = FALSE; + + item->priv = priv; +} + +static void +wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + WireItem *wire_item = WIRE_ITEM (object); + + wire_item = WIRE_ITEM (object); + + switch (arg_id) { + case WIRE_ITEM_ARG_NAME: + break; + } +} + +static void +wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + WireItem *wire_item = WIRE_ITEM (object); + + wire_item = WIRE_ITEM (object); + + switch (arg_id) { + case WIRE_ITEM_ARG_NAME: + break; + default: + //arg->type = G_TYPE_INVALID; + break; + } +} + +static void +wire_item_destroy (GtkObject *object) +{ + WireItem *wire; + WireItemPriv *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_WIRE_ITEM (object)); + + wire = WIRE_ITEM (object); + priv = wire->priv; + + if (priv) { + if (priv->line) { + /* TODO Check if destroy or unref have to be used for + * GnomeCanvasItem */ + gtk_object_destroy (GTK_OBJECT (priv->line)); + priv->line = NULL; + } + g_free (priv); + wire->priv = NULL; + } + + if (GTK_OBJECT_CLASS(wire_item_parent_class)->destroy){ + GTK_OBJECT_CLASS(wire_item_parent_class)->destroy(object); + } +} + +/** + * "moved" signal handler. Invalidates the bounding box cache. + */ +static void +wire_item_moved (SheetItem *object) +{ + WireItem *item; + WireItemPriv *priv; + + item = WIRE_ITEM (object); + priv = item->priv; + + priv->cache_valid = FALSE; +} + +WireItem * +wire_item_new (Sheet *sheet, Wire *wire) +{ + WireItem *item; + GnomeCanvasPoints *points; + WireItemPriv *priv; + SheetPos start_pos, length; + + g_return_val_if_fail (sheet != NULL, NULL); + g_return_val_if_fail (IS_SHEET (sheet), NULL); + + //g_object_ref (G_OBJECT(wire)); + /* XXX Ver si hay equivalente gtk_object_sink (GTK_OBJECT (wire)); */ + + wire_get_pos_and_length (wire, &start_pos, &length); + + /* + * Because of the GnomeCanvasGroup inheritance, a small hack is needed + * here. The group starts at the startpoint of the wire, and the line + * goes from (0,0) to (length.x, length.y). + */ + item = WIRE_ITEM (gnome_canvas_item_new ( + sheet->object_group, + wire_item_get_type (), + "data", wire, + "x", (double) start_pos.x, + "y", (double) start_pos.y, + NULL)); + + priv = item->priv; + + priv->resize1 = GNOME_CANVAS_RECT (gnome_canvas_item_new ( + GNOME_CANVAS_GROUP (item), + gnome_canvas_rect_get_type (), + "x1", -RESIZER_SIZE, + "y1", -RESIZER_SIZE, + "x2", RESIZER_SIZE, + "y2", RESIZER_SIZE, + "fill_color", "red", + "fill_color_rgba", 0x3cb37180, + "outline_color", "blue", + "width_pixels", 1, + NULL)); + + priv->resize2 = GNOME_CANVAS_RECT (gnome_canvas_item_new ( + GNOME_CANVAS_GROUP (item), + gnome_canvas_rect_get_type (), + "x1", length.x-RESIZER_SIZE, + "y1", length.y-RESIZER_SIZE, + "x2", length.x+RESIZER_SIZE, + "y2", length.y+RESIZER_SIZE, + "fill_color", "red", + "fill_color_rgba", 0x3cb37180, + "outline_color", "blue", + "width_pixels", 1, + NULL)); + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize1)); + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize2)); + + points = gnome_canvas_points_new (2); + points->coords[0] = 0; + points->coords[1] = 0; + points->coords[2] = length.x; + points->coords[3] = length.y; + + priv->line = GNOME_CANVAS_LINE (gnome_canvas_item_new ( + GNOME_CANVAS_GROUP (item), + gnome_canvas_line_get_type (), + "points", points, + "fill_color", "blue", + "width_pixels", 1, + NULL)); + + gnome_canvas_points_free (points); + + g_signal_connect_object(G_OBJECT(wire), "rotated", + G_CALLBACK(wire_rotated_callback), G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT(wire), "flipped", + G_CALLBACK(wire_flipped_callback), G_OBJECT(item), 0); + g_signal_connect_object(G_OBJECT(wire), "moved", + G_CALLBACK(wire_moved_callback), G_OBJECT(item), 0); + + g_signal_connect (G_OBJECT (wire), "changed", G_CALLBACK (wire_changed_callback), item); + g_signal_connect (G_OBJECT (wire), "delete", G_CALLBACK (wire_delete_callback), item); + wire_update_bbox (wire); + + return item; +} + +static +int wire_item_event (WireItem *wire_item, const GdkEvent *event, SchematicView *sv) +{ + SheetPos start_pos, length; + Wire *wire; + Sheet *sheet; + GnomeCanvas *canvas; + static double last_x, last_y; + double dx, dy, zoom; + /* The selected group's bounding box in window resp. canvas coordinates. */ + double x1, y1, x2, y2; + static double bb_x1, bb_y1, bb_x2, bb_y2; + int cx1, cy1, cx2, cy2; + double snapped_x, snapped_y; + int sheet_width, sheet_height; + SheetPos pos; + + sheet = schematic_view_get_sheet (sv); + canvas = GNOME_CANVAS (sheet); + g_object_get (G_OBJECT (wire_item), "data", &wire, NULL); + + wire_get_pos_and_length (WIRE (wire), &start_pos, &length); + sheet_get_zoom (sheet, &zoom); + + switch (event->type) { + case GDK_BUTTON_PRESS: + switch (event->button.button) { + case 1: { + g_signal_stop_emission_by_name (G_OBJECT (sheet), "event"); + double x, y; + x = event->button.x - start_pos.x; + y = event->button.y - start_pos.y; + if ((x > -RESIZER_SIZE) && (x < RESIZER_SIZE) && + (y > -RESIZER_SIZE) && (y < RESIZER_SIZE)) { + gtk_widget_grab_focus (GTK_WIDGET (sheet)); + sheet->state = SHEET_STATE_DRAG_START; + wire_item->priv->resize_state = WIRE_RESIZER_1; + + last_x = event->button.x; + last_y = event->button.y; + item_data_unregister (ITEM_DATA (wire)); + return TRUE; + } + if ((x > (length.x-RESIZER_SIZE)) && (x < (length.x+RESIZER_SIZE)) && + (y > (length.y-RESIZER_SIZE)) && (y < (length.y+RESIZER_SIZE))) { + gtk_widget_grab_focus (GTK_WIDGET (sheet)); + sheet->state = SHEET_STATE_DRAG_START; + wire_item->priv->resize_state = WIRE_RESIZER_2; + + last_x = event->button.x; + last_y = event->button.y; + item_data_unregister (ITEM_DATA (wire)); + return TRUE; + } + } + break; + } + break; + case GDK_MOTION_NOTIFY: + if (sheet->state != SHEET_STATE_DRAG && + sheet->state != SHEET_STATE_DRAG_START) + break; + + if (wire_item->priv->resize_state == WIRE_RESIZER_NONE) + break; + + if (sheet->state == SHEET_STATE_DRAG_START || sheet->state == SHEET_STATE_DRAG) { + sheet->state = SHEET_STATE_DRAG; + + snapped_x = event->motion.x; + snapped_y = event->motion.y; + snap_to_grid (sheet->grid, &snapped_x, &snapped_y); + + dx = snapped_x - last_x; + dy = snapped_y - last_y; + + + last_x = snapped_x; + last_y = snapped_y; + + wire_get_pos_and_length (wire, &pos, &length); + + if (wire_item->priv->resize_state == WIRE_RESIZER_1) { + switch (wire->priv->direction) { + case WIRE_DIR_VERT: + /* Vertical Wire */ + pos.y = last_y; + length.y -= dy; + break; + case WIRE_DIR_HORIZ: + /* Horizontal Wire */ + pos.x = last_x; + length.x -= dx; + break; + default: + pos.y = last_y; + length.y -= dy; + pos.x = last_x; + length.x -= dx; + } + } else { + switch (wire->priv->direction) { + case WIRE_DIR_VERT: + /* Vertical Wire */ + length.y += dy; + break; + case WIRE_DIR_HORIZ: + /* Horizontal Wire */ + length.x += dx; + break; + default: + length.y += dy; + length.x += dx; + } + } + snap_to_grid (sheet->grid, &length.x, &length.y); + item_data_set_pos (sheet_item_get_data (SHEET_ITEM (wire_item)), &pos); + wire_set_length (wire, &length); + return TRUE; + } + break; + case GDK_BUTTON_RELEASE: + switch (event->button.button) { + case 1: + if (sheet->state != SHEET_STATE_DRAG && + sheet->state != SHEET_STATE_DRAG_START) + break; + if (wire_item->priv->resize_state == WIRE_RESIZER_NONE) + break; + + g_signal_stop_emission_by_name (G_OBJECT (wire_item), "event"); + + //gtk_timeout_remove (priv->scroll_timeout_id); // Esto no esta bien. + + sheet->state = SHEET_STATE_NONE; + gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (wire_item), event->button.time); + + wire_item->priv->resize_state = WIRE_RESIZER_NONE; + sheet->state = SHEET_STATE_NONE; + item_data_register (ITEM_DATA (wire)); + return TRUE; + } + break; + default: + return sheet_item_event (SHEET_ITEM (wire_item), event, sv); + } + return sheet_item_event (SHEET_ITEM (wire_item), event, sv); +} + +void +wire_item_signal_connect_placed (WireItem *wire, SchematicView *sv) +{ + g_signal_connect ( + G_OBJECT (wire), + "event", + G_CALLBACK(wire_item_event), + sv); + + g_signal_connect ( + G_OBJECT (wire), + "mouse_over", + G_CALLBACK(mouse_over_wire_cb), + sv); + + g_signal_connect ( + G_OBJECT (sheet_item_get_data (SHEET_ITEM (wire))), + "highlight", + G_CALLBACK(highlight_wire_cb), + wire); +} + +static void +wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item) +{ + WireItem *wire_item; + GnomeCanvasPoints *points; + SheetPos start_pos, length; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_WIRE_ITEM (sheet_item)); + + wire_item = WIRE_ITEM (sheet_item); + + wire_get_pos_and_length (WIRE (data), &start_pos, &length); + + points = gnome_canvas_points_new (2); + points->coords[0] = 0; + points->coords[1] = 0; + points->coords[2] = length.x; + points->coords[3] = length.y; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item->priv->line), + "points", points, + NULL); + gnome_canvas_points_unref (points); + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item), + "x", start_pos.x, + "y", start_pos.y, + NULL); + + gnome_canvas_item_set ( + GNOME_CANVAS_ITEM (wire_item-> priv->resize2), + "x1", length.x-RESIZER_SIZE, + "y1", length.y-RESIZER_SIZE, + "x2", length.x+RESIZER_SIZE, + "y2", length.y+RESIZER_SIZE, + NULL + ); + + /* + * Invalidate the bounding box cache. + */ + wire_item->priv->cache_valid = FALSE; +} + +static void +wire_flipped_callback (ItemData *data, + gboolean horizontal, SheetItem *sheet_item) +{ + GnomeCanvasPoints *points; + WireItem *item; + WireItemPriv *priv; + SheetPos start_pos, length; + + g_return_if_fail (sheet_item != NULL); + g_return_if_fail (IS_WIRE_ITEM (sheet_item)); + + item = WIRE_ITEM (sheet_item); + priv = item->priv; + + wire_get_pos_and_length (WIRE (data), &start_pos, &length); + + points = gnome_canvas_points_new (2); + points->coords[0] = 0; + points->coords[1] = 0; + points->coords[2] = length.x; + points->coords[3] = length.y; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line), + "points", points, + NULL); + gnome_canvas_points_unref (points); + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (item), + "x", start_pos.x, + "y", start_pos.y, + NULL); + + /* + * Invalidate the bounding box cache. + */ + priv->cache_valid = FALSE; +} + +static int +select_idle_callback (WireItem *item) +{ + WireItemPriv *priv = item->priv; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line), + "fill_color", SELECTED_COLOR, NULL); + + priv->highlight = TRUE; + + g_object_unref (G_OBJECT (item)); + return FALSE; +} + +static int +deselect_idle_callback (WireItem *item) +{ + WireItemPriv *priv = item->priv; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line), + "fill_color", NORMAL_COLOR, NULL); + + priv->highlight = FALSE; + + g_object_unref(G_OBJECT (item)); + return FALSE; +} + +static void +selection_changed(WireItem *item, gboolean select, gpointer user_data) +{ + g_object_ref(G_OBJECT(item)); + if (select) { + gtk_idle_add ((gpointer) select_idle_callback, item); + gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize1)); + gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize2)); + } else { + gtk_idle_add ((gpointer) deselect_idle_callback, item); + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize1)); + gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize2)); + } +} + +/** + * This function returns the position of the canvas item. It has + * nothing to do with where the wire is stored in the sheet node store. + */ +void +wire_item_get_start_pos (WireItem *item, SheetPos *pos) +{ + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + g_return_if_fail (pos != NULL); + + g_object_get(G_OBJECT(item), "x", &pos->x, "y", &pos->y, NULL); +} + +/** + * This function returns the length of the canvas item. + */ +void +wire_item_get_length (WireItem *item, SheetPos *pos) +{ + WireItemPriv *priv; + GnomeCanvasPoints *points; + + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + g_return_if_fail (pos != NULL); + + priv = item->priv; + + g_object_get(G_OBJECT(priv->line), "points", &points, NULL); + + /* + * This is not strictly neccessary, since the first point is always + * (0,0) but it's more correct and good to have if this changes in the + * future. + */ + pos->x = points->coords[2] - points->coords[0]; + pos->y = points->coords[3] - points->coords[1]; + gnome_canvas_points_free (points); +} + +static gboolean +is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2) +{ + WireItem *item; + SheetPos bbox_start, bbox_end; + + item = WIRE_ITEM (object); + + get_bbox (item, &bbox_start, &bbox_end); + + if (p1->x < bbox_start.x && + p2->x > bbox_end.x && + p1->y < bbox_start.y && + p2->y > bbox_end.y) + return TRUE; + + return FALSE; +} + +/** + * Retrieves the bounding box. We use a caching scheme for this + * since it's too expensive to calculate it every time we need it. + */ +inline static void +get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2) +{ + WireItemPriv *priv; + priv = item->priv; + + if (!priv->cache_valid) { + SheetPos start_pos, end_pos; + + wire_item_get_start_pos (item, &start_pos); + wire_item_get_length (item, &end_pos); + end_pos.x += start_pos.x; + end_pos.y += start_pos.y; + + priv->bbox_start.x = MIN (start_pos.x, end_pos.x); + priv->bbox_start.y = MIN (start_pos.y, end_pos.y); + priv->bbox_end.x = MAX (start_pos.x, end_pos.x); + priv->bbox_end.y = MAX (start_pos.y, end_pos.y); + priv->cache_valid = TRUE; + } + + memcpy (p1, &priv->bbox_start, sizeof (SheetPos)); + memcpy (p2, &priv->bbox_end, sizeof (SheetPos)); +} + +static void +wire_item_paste (SchematicView *sv, ItemData *data) +{ + g_return_if_fail (sv != NULL); + g_return_if_fail (IS_SCHEMATIC_VIEW (sv)); + g_return_if_fail (data != NULL); + g_return_if_fail (IS_WIRE (data)); + + schematic_view_add_ghost_item (sv, data); +} + +static void wire_traverse (Wire *wire); + +static void +node_traverse (Node *node) +{ + GSList *wires; + + g_return_if_fail (node != NULL); + g_return_if_fail (IS_NODE (node)); + + if (node_is_visited (node)) + return; + + node_set_visited (node, TRUE); + + for (wires = node->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + wire_traverse (wire); + } +} + +static void +wire_traverse (Wire *wire) +{ + GSList *nodes; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + if (wire_is_visited (wire)) + return; + + wire_set_visited (wire, TRUE); + + g_signal_emit_by_name(G_OBJECT (wire), "highlight"); + + for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { + Node *node = nodes->data; + + node_traverse (node); + } +} + +static void +node_foreach_reset (gpointer key, gpointer value, gpointer user_data) +{ + Node *node = value; + + node_set_visited (node, FALSE); +} + +static void +mouse_over_wire_cb (WireItem *item, SchematicView *sv) +{ + GList *wires; + Wire *wire; + NodeStore *store; + Sheet *sheet; + + sheet = schematic_view_get_sheet (sv); + + if (sheet->state != SHEET_STATE_NONE) + return; + + store = schematic_get_store (schematic_view_get_schematic (sv)); + + node_store_node_foreach (store, (GHFunc *) node_foreach_reset, NULL); + for (wires = store->wires; wires; wires = wires->next) { + wire = wires->data; + wire_set_visited (wire, FALSE); + } + + wire = WIRE (sheet_item_get_data (SHEET_ITEM (item))); + wire_traverse (wire); +} + +static void +highlight_wire_cb (Wire *wire, WireItem *item) +{ + WireItemPriv *priv = item->priv; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line), + "fill_color", HIGHLIGHT_COLOR, NULL); + + /* + * Guard against removal during the highlighting. + */ + g_object_ref(G_OBJECT (item)); + + gtk_timeout_add (1000, (gpointer) unhighlight_wire, item); +} + +static int +unhighlight_wire (WireItem *item) +{ + char *color; + WireItemPriv *priv = item->priv; + + color = sheet_item_get_selected (SHEET_ITEM (item)) ? + SELECTED_COLOR : NORMAL_COLOR; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line), + "fill_color", color, NULL); + + g_object_unref (G_OBJECT (item)); + + return FALSE; +} + +/** + * This is called when the wire data was moved. Update the view accordingly. + */ +static void +wire_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item) +{ + WireItem *wire_item; + + g_return_if_fail (data != NULL); + g_return_if_fail (IS_ITEM_DATA (data)); + g_return_if_fail (item != NULL); + g_return_if_fail (IS_WIRE_ITEM (item)); + + if (pos == NULL) + return; + + wire_item = WIRE_ITEM (item); + + /* + * Move the canvas item and invalidate the bbox cache. + */ + gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y); + wire_item->priv->cache_valid = FALSE; +} + +static void +wire_item_place (SheetItem *item, SchematicView *sv) +{ + wire_item_signal_connect_placed (WIRE_ITEM (item), sv); +} + +static void +wire_item_place_ghost (SheetItem *item, SchematicView *sv) +{ +// wire_item_signal_connect_placed (WIRE_ITEM (item)); +} + + +static void +wire_changed_callback (Wire *wire, WireItem *item) +{ + SheetPos start_pos, length; + GnomeCanvasPoints *points; + + wire_get_pos_and_length (wire, &start_pos, &length); + + points = gnome_canvas_points_new (2); + points->coords[0] = 0; + points->coords[1] = 0; + points->coords[2] = length.x; + points->coords[3] = length.y; + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line), + "points", points, + NULL); + gnome_canvas_points_unref (points); + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize1), + "x1", -RESIZER_SIZE, + "y1", -RESIZER_SIZE, + "x2", RESIZER_SIZE, + "y2", RESIZER_SIZE, + NULL); + + gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize2), + "x1", length.x-RESIZER_SIZE, + "y1", length.y-RESIZER_SIZE, + "x2", length.x+RESIZER_SIZE, + "y2", length.y+RESIZER_SIZE, + NULL); +} + +static void +wire_delete_callback (Wire *wire, WireItem *item) +{ + gtk_object_destroy (GTK_OBJECT (item)); +} + + diff --git a/src/sheet/wire-item.h b/src/sheet/wire-item.h new file mode 100644 index 0000000..11124cc --- /dev/null +++ b/src/sheet/wire-item.h @@ -0,0 +1,60 @@ +/* + * wire-item.h + * + * + * Authors: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2004 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __WIRE_ITEM_H +#define __WIRE_ITEM_H + +#include <gnome.h> + +#include "schematic-view.h" +#include "sheet-item.h" +#include "wire.h" + +#define WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, wire_item_get_type (), WireItem)) +#define WIRE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, wire_item_get_type (), WireItemClass)) +#define IS_WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, wire_item_get_type ())) + +typedef struct _WireItemPriv WireItemPriv; + +typedef struct { + SheetItem parent_object; + WireItemPriv *priv; +} WireItem; + +typedef struct { + SheetItemClass parent_class; +} WireItemClass; + +GType wire_item_get_type (void); +WireItem *wire_item_new (Sheet *sheet, Wire *wire); +void wire_item_initiate (Sheet *sheet); +void wire_item_get_start_pos (WireItem *item, SheetPos *pos); +void wire_item_get_length (WireItem *item, SheetPos *pos); + +#endif |