summaryrefslogtreecommitdiff
path: root/src/sheet
diff options
context:
space:
mode:
Diffstat (limited to 'src/sheet')
-rw-r--r--src/sheet/Makefile.am28
-rw-r--r--src/sheet/grid.c394
-rw-r--r--src/sheet/grid.h61
-rw-r--r--src/sheet/node-item.c112
-rw-r--r--src/sheet/node-item.h64
-rw-r--r--src/sheet/part-item.c1632
-rw-r--r--src/sheet/part-item.h70
-rw-r--r--src/sheet/sheet-item-factory.c70
-rw-r--r--src/sheet/sheet-item-factory.h38
-rw-r--r--src/sheet/sheet-item.c1222
-rw-r--r--src/sheet/sheet-item.h129
-rw-r--r--src/sheet/sheet-private.h54
-rw-r--r--src/sheet/sheet.c469
-rw-r--r--src/sheet/sheet.h92
-rw-r--r--src/sheet/textbox-item.c699
-rw-r--r--src/sheet/textbox-item.h67
-rw-r--r--src/sheet/wire-item.c916
-rw-r--r--src/sheet/wire-item.h60
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 = &GTK_WIDGET (sheet)->allocation;
+
+ if (priv->width > allocation->width)
+ hmax = (gfloat) (priv->width - allocation->width);
+ else
+ hmax = 0.0;
+
+ if (priv->height > allocation->height)
+ vmax = (gfloat) (priv->height - allocation->height);
+ else
+ vmax = 0.0;
+
+ hnew = CLAMP (hadj->value + (gfloat) delta_x, 0.0, hmax);
+ vnew = CLAMP (vadj->value + (gfloat) delta_y, 0.0, vmax);
+
+ if (hnew != hadj->value) {
+ hadj->value = hnew;
+ g_signal_emit_by_name (G_OBJECT (hadj), "value_changed");
+ }
+ if (vnew != vadj->value) {
+ vadj->value = vnew;
+ g_signal_emit_by_name (G_OBJECT (vadj), "value_changed");
+ }
+}
+
+void
+sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height)
+{
+ *width = sheet->priv->width * sheet->priv->zoom;
+ *height = sheet->priv->height * sheet->priv->zoom;
+}
+
+void
+sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog)
+{
+/* gtk_window_set_transient_for (
+ GTK_WINDOW (dialog),
+ GTK_WINDOW (SCHEMATIC (sheet->schematic)->toplevel)
+ );*/
+}
diff --git a/src/sheet/sheet.h b/src/sheet/sheet.h
new file mode 100644
index 0000000..d880743
--- /dev/null
+++ b/src/sheet/sheet.h
@@ -0,0 +1,92 @@
+/*
+ * sheet.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __SHEET_H
+#define __SHEET_H
+
+#include <libgnomecanvas/libgnomecanvas.h>
+#include <gtk/gtkdialog.h>
+#include "grid.h"
+
+typedef struct _Sheet Sheet;
+typedef struct _SheetPriv SheetPriv;
+typedef struct _SheetClass SheetClass;
+
+#define TYPE_SHEET (sheet_get_type())
+#define SHEET(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_SHEET, Sheet))
+#define SHEET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, TYPE_SHEET, SheetClass))
+#define IS_SHEET(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, TYPE_SHEET))
+
+typedef enum {
+ SHEET_STATE_NONE,
+ SHEET_STATE_DRAG_START,
+ SHEET_STATE_DRAG,
+ SHEET_STATE_FLOAT_START,
+ SHEET_STATE_FLOAT,
+ SHEET_STATE_WIRE,
+ SHEET_STATE_TEXTBOX_WAIT,
+ SHEET_STATE_TEXTBOX_START,
+ SHEET_STATE_TEXTBOX_CREATING
+} SheetState;
+
+struct _Sheet {
+ GnomeCanvas parent_canvas;
+ SheetState state;
+ GnomeCanvasGroup *object_group;
+ Grid *grid;
+ SheetPriv *priv;
+};
+
+struct _SheetClass {
+ GnomeCanvasClass parent_class;
+
+ void (*selection_changed) (Sheet *sheet);
+ gint (*button_press) (Sheet *sheet, GdkEventButton *event);
+ void (*context_click) (Sheet *sheet,
+ const char *name, gpointer data);
+ void (*cancel) (Sheet *sheet);
+ void (*reset_tool) (Sheet *sheet);
+};
+
+GType sheet_get_type (void);
+GtkWidget *sheet_new (int width, int height);
+void sheet_scroll (const Sheet *sheet, int dx, int dy);
+void sheet_get_size_pixels (const Sheet *sheet, guint *width, guint *height);
+int sheet_get_num_selected_items (const Sheet *sheet);
+gpointer sheet_get_first_selected_item (const Sheet *sheet);
+GSList *sheet_get_selected_items (const Sheet *sheet);
+void sheet_change_zoom (const Sheet *sheet, double rate);
+void sheet_get_zoom (const Sheet *sheet, gdouble *zoom);
+void sheet_dialog_set_parent (const Sheet *sheet, GtkDialog *dialog);
+void sheet_delete_selected_items (const Sheet *sheet);
+void sheet_rotate_selected_items (const Sheet *sheet);
+void sheet_rotate_floating_items (const Sheet *sheet);
+void sheet_reset_floating_items (const Sheet *sheet);
+
+#endif
diff --git a/src/sheet/textbox-item.c b/src/sheet/textbox-item.c
new file mode 100644
index 0000000..2354c55
--- /dev/null
+++ b/src/sheet/textbox-item.c
@@ -0,0 +1,699 @@
+/*
+ * textbox-item.c
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ * TextboxItem object: the graphical representation of a textbox.
+ *
+ * Author:
+ * Richard Hult (rhult@hem2.passagen.se)
+ *
+ * (C) 1999, 2000 Richard Hult, http://www.dtek.chalmers.se/~d4hult/oregano/
+ */
+
+#include <math.h>
+#include <gnome.h>
+#include <glade/glade.h>
+#include "cursors.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "textbox-item.h"
+#include "textbox.h"
+#include "dialogs.h"
+
+#define NORMAL_COLOR "black"
+#define SELECTED_COLOR "green"
+#define TEXTBOX_FONT "Arial 10"
+
+static void textbox_item_class_init (TextboxItemClass *klass);
+static void textbox_item_init (TextboxItem *item);
+static void textbox_item_destroy (GtkObject *object);
+static void textbox_item_moved (SheetItem *object);
+
+static void textbox_rotated_callback (ItemData *data, int angle,
+ SheetItem *sheet_item);
+static void textbox_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item);
+static void textbox_moved_callback (ItemData *data, SheetPos *pos,
+ SheetItem *item);
+static void textbox_text_changed_callback (ItemData *data, gchar *new_text,
+ SheetItem *item);
+static void textbox_font_changed_callback (ItemData *data, gchar *new_font,
+ SheetItem *item);
+
+static void textbox_item_paste (SchematicView *sv, ItemData *data);
+static void selection_changed (TextboxItem *item, gboolean select,
+ gpointer user_data);
+static int select_idle_callback (TextboxItem *item);
+static int deselect_idle_callback (TextboxItem *item);
+static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2);
+inline static void get_cached_bounds (TextboxItem *item, SheetPos *p1,
+ SheetPos *p2);
+
+static void textbox_item_place (SheetItem *item, SchematicView *sv);
+static void textbox_item_place_ghost (SheetItem *item, SchematicView *sv);
+
+static void edit_cmd (GtkWidget *widget, SchematicView *sv);
+static void edit_textbox (SheetItem *sheet_item);
+
+typedef struct {
+ GtkDialog *dialog;
+ GtkFontSelection *font;
+ GtkEntry *entry;
+} TextboxPropDialog;
+
+static TextboxPropDialog *prop_dialog = NULL;
+static SheetItemClass *textbox_item_parent_class = NULL;
+
+/* Use EDIT!! */
+static const char *textbox_item_context_menu =
+"<ui>"
+" <popup name='ItemMenu'>"
+" <menuitem action='EditText'/>"
+" </popup>"
+"</ui>";
+
+static GtkActionEntry action_entries[] = {
+ {"EditText", GTK_STOCK_PROPERTIES, N_("_Edit the text..."), NULL,
+ N_("Edit the text"),G_CALLBACK (edit_cmd)}
+};
+
+enum {
+ TEXTBOX_ITEM_ARG_0,
+ TEXTBOX_ITEM_ARG_NAME
+};
+
+struct _TextboxItemPriv {
+ guint cache_valid : 1;
+
+ guint highlight : 1;
+
+ // FIXME: More members.
+ GnomeCanvasItem *text_canvas_item;
+
+ /*
+ * Cached bounding box. This is used to make
+ * the rubberband selection a bit faster.
+ */
+ SheetPos bbox_start;
+ SheetPos bbox_end;
+};
+
+GType
+textbox_item_get_type ()
+{
+ static GType textbox_item_type = 0;
+
+ if (!textbox_item_type) {
+ static const GTypeInfo textbox_item_info = {
+ sizeof(TextboxItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)textbox_item_class_init,
+ NULL,
+ NULL,
+ sizeof(TextboxItem),
+ 0,
+ (GInstanceInitFunc)textbox_item_init,
+ NULL
+ };
+
+ textbox_item_type = g_type_register_static(TYPE_SHEET_ITEM,
+ "TextboxItem", &textbox_item_info, 0);
+ }
+ return textbox_item_type;
+}
+
+static void
+textbox_item_class_init (TextboxItemClass *textbox_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ SheetItemClass *sheet_item_class;
+
+ object_class = G_OBJECT_CLASS(textbox_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(textbox_item_class);
+ sheet_item_class = SHEET_ITEM_CLASS(textbox_item_class);
+ textbox_item_parent_class =
+ g_type_class_peek_parent(textbox_item_class);
+
+ gtk_object_class->destroy = textbox_item_destroy;
+
+ sheet_item_class->moved = textbox_item_moved;
+ sheet_item_class->paste = textbox_item_paste;
+ sheet_item_class->is_in_area = is_in_area;
+ sheet_item_class->selection_changed = (gpointer) selection_changed;
+ sheet_item_class->edit_properties = edit_textbox;
+ sheet_item_class->place = textbox_item_place;
+ sheet_item_class->place_ghost = textbox_item_place_ghost;
+}
+
+static void
+textbox_item_init (TextboxItem *item)
+{
+ TextboxItemPriv *priv;
+
+ priv = g_new0 (TextboxItemPriv, 1);
+ item->priv = priv;
+
+ priv->highlight = FALSE;
+ priv->cache_valid = FALSE;
+
+ sheet_item_add_menu (SHEET_ITEM (item), textbox_item_context_menu,
+ action_entries, G_N_ELEMENTS (action_entries));
+}
+
+static void
+textbox_item_destroy (GtkObject *object)
+{
+ TextboxItem *textbox;
+ TextboxItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (object));
+
+ textbox = TEXTBOX_ITEM (object);
+ priv = textbox->priv;
+
+ if (priv) {
+ if (priv->text_canvas_item) {
+ gtk_object_destroy(GTK_OBJECT(priv->text_canvas_item));
+ }
+ textbox->priv = NULL;
+ g_free (priv);
+ }
+
+ if (GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy){
+ GTK_OBJECT_CLASS(textbox_item_parent_class)->destroy(object);
+ }
+}
+
+/*
+ * textbox_item_moved
+ *
+ * "moved" signal handler. Invalidates the bounding box cache.
+ */
+static void
+textbox_item_moved (SheetItem *object)
+{
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+
+ item = TEXTBOX_ITEM (object);
+ priv = item->priv;
+
+ priv->cache_valid = FALSE;
+}
+
+TextboxItem *
+textbox_item_new (Sheet *sheet, Textbox *textbox)
+{
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+ SheetPos pos;
+
+ g_return_val_if_fail(sheet != NULL, NULL);
+ g_return_val_if_fail(IS_SHEET(sheet), NULL);
+
+ item_data_get_pos(ITEM_DATA(textbox), &pos);
+
+ item = TEXTBOX_ITEM(gnome_canvas_item_new (
+ sheet->object_group,
+ textbox_item_get_type(),
+ "data", textbox,
+ "x", (double) pos.x,
+ "y", (double) pos.y,
+ NULL));
+
+ priv = item->priv;
+
+ priv->text_canvas_item = gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_text_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ "text", textbox_get_text (textbox),
+ "fill_color", NORMAL_COLOR,
+ "anchor", GTK_ANCHOR_SW,
+ "font", TEXTBOX_FONT,
+ NULL);
+
+ g_signal_connect_object(G_OBJECT (textbox),
+ "rotated", G_CALLBACK(textbox_rotated_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "flipped", G_CALLBACK(textbox_flipped_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "moved", G_CALLBACK(textbox_moved_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "text_changed", G_CALLBACK(textbox_text_changed_callback),
+ G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT (textbox),
+ "font_changed", G_CALLBACK(textbox_font_changed_callback),
+ G_OBJECT(item), 0);
+
+ textbox_update_bbox (textbox);
+
+ return item;
+}
+
+void
+textbox_item_signal_connect_placed (TextboxItem *textbox_item,
+ SchematicView *sv)
+{
+ g_signal_connect (
+ G_OBJECT (textbox_item),
+ "event",
+ G_CALLBACK(sheet_item_event),
+ sv);
+}
+
+static void
+textbox_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
+{
+ TextboxItem *item;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+
+ item->priv->cache_valid = FALSE;
+}
+
+static void
+textbox_flipped_callback (ItemData *data,
+ gboolean horizontal, SheetItem *sheet_item)
+{
+ TextboxItem *item;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+
+ item->priv->cache_valid = FALSE;
+}
+
+static int
+select_idle_callback (TextboxItem *item)
+{
+ SheetPos bbox_start, bbox_end;
+ TextboxItemPriv *priv = item->priv;
+
+ get_cached_bounds (item, &bbox_start, &bbox_end);
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item),
+ "fill_color", SELECTED_COLOR, NULL);
+
+ priv->highlight = TRUE;
+
+ return FALSE;
+}
+
+static int
+deselect_idle_callback (TextboxItem *item)
+{
+ TextboxItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->text_canvas_item),
+ "fill_color", NORMAL_COLOR, NULL);
+
+ priv->highlight = FALSE;
+
+ return FALSE;
+}
+
+static void
+selection_changed (TextboxItem *item, gboolean select, gpointer user_data)
+{
+ if (select)
+ gtk_idle_add ((gpointer) select_idle_callback, item);
+ else
+ gtk_idle_add ((gpointer) deselect_idle_callback, item);
+}
+
+static gboolean
+is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2)
+{
+ TextboxItem *item;
+ SheetPos bbox_start, bbox_end;
+
+ item = TEXTBOX_ITEM (object);
+
+ get_cached_bounds (item, &bbox_start, &bbox_end);
+
+ if (p1->x < bbox_start.x &&
+ p2->x > bbox_end.x &&
+ p1->y < bbox_start.y &&
+ p2->y > bbox_end.y)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Retrieves the bounding box. We use a caching scheme for this
+ * since it's too expensive to calculate it every time we need it.
+ */
+inline static void
+get_cached_bounds (TextboxItem *item, SheetPos *p1, SheetPos *p2)
+{
+ PangoFontDescription *font;
+ PangoFontMetrics *font_metric;
+ int width;
+ int rbearing;
+ int lbearing;
+ int ascent, descent;
+ SheetPos pos;
+
+ TextboxItemPriv *priv;
+ priv = item->priv;
+
+ if (!priv->cache_valid) {
+ SheetPos start_pos, end_pos;
+
+ font = pango_font_description_from_string(TEXTBOX_FONT);
+
+ item_data_get_pos (sheet_item_get_data (SHEET_ITEM (item)),
+ &pos);
+
+ start_pos.x = pos.x;
+ start_pos.y = pos.y-5;// - font->ascent;
+ end_pos.x = pos.x+5; // + rbearing;
+ end_pos.y = pos.y+5; // + font->descent;
+
+ priv->bbox_start = start_pos;
+ priv->bbox_end = end_pos;
+ priv->cache_valid = TRUE;
+ pango_font_description_free(font);
+ }
+
+ memcpy (p1, &priv->bbox_start, sizeof (SheetPos));
+ memcpy (p2, &priv->bbox_end, sizeof (SheetPos));
+
+}
+
+static void
+textbox_item_paste (SchematicView *sv, ItemData *data)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_TEXTBOX (data));
+
+ schematic_view_add_ghost_item (sv, data);
+}
+
+/**
+ * This is called when the textbox data was moved. Update the view accordingly.
+ */
+static void
+textbox_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ if (pos == NULL)
+ return;
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ /*
+ * Move the canvas item and invalidate the bbox cache.
+ */
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y);
+ textbox_item->priv->cache_valid = FALSE;
+}
+
+static void
+textbox_text_changed_callback (ItemData *data,
+ gchar *new_text, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM ( textbox_item->priv->text_canvas_item ),
+ "text", new_text, NULL );
+
+}
+
+static void
+textbox_font_changed_callback (ItemData *data,
+ gchar *new_font, SheetItem *item)
+{
+ TextboxItem *textbox_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+ g_return_if_fail (new_font != NULL);
+
+ textbox_item = TEXTBOX_ITEM (item);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (textbox_item->priv->text_canvas_item),
+ "font", new_font, NULL);
+}
+
+static void
+textbox_item_place (SheetItem *item, SchematicView *sv)
+{
+ textbox_item_signal_connect_placed (TEXTBOX_ITEM (item), sv);
+
+ g_signal_connect (
+ G_OBJECT (item),
+ "double_clicked",
+ G_CALLBACK(edit_textbox),
+ item);
+}
+
+static void
+textbox_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+// textbox_item_signal_connect_placed (TEXTBOX_ITEM (item));
+}
+
+static gboolean
+create_textbox_event (Sheet *sheet, GdkEvent *event, SchematicView *sv)
+{
+ switch (event->type) {
+ case GDK_3BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ return TRUE;
+
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 4 || event->button.button == 5)
+ return FALSE;
+
+ if (event->button.button == 1) {
+ if (sheet->state == SHEET_STATE_TEXTBOX_WAIT)
+ sheet->state = SHEET_STATE_TEXTBOX_START;
+
+ return TRUE;
+ } else
+ return FALSE;
+
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 4 || event->button.button == 5)
+ return FALSE;
+
+ if (sheet->state == SHEET_STATE_TEXTBOX_START) {
+ Textbox *textbox;
+ SheetPos pos;
+
+ sheet->state = SHEET_STATE_NONE;
+
+ pos.x = event->button.x;
+ pos.y = event->button.y;
+
+ textbox = textbox_new (NULL);
+ item_data_set_pos (ITEM_DATA (textbox), &pos);
+ textbox_set_text (textbox, _("Label"));
+
+ schematic_add_item (schematic_view_get_schematic (sv),
+ ITEM_DATA (textbox));
+
+ g_signal_emit_by_name(G_OBJECT(sheet),
+ "reset_tool", NULL);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sheet),
+ G_CALLBACK(create_textbox_event), sv);
+ }
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+ void
+textbox_item_cancel_listen (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+ sheet = schematic_view_get_sheet (sv);
+
+ sheet->state = SHEET_STATE_NONE;
+ g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), G_CALLBACK(create_textbox_event), sv);
+}
+
+void
+textbox_item_listen (SchematicView *sv)
+{
+ Sheet *sheet;
+
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+
+// schematic_view_disconnect_handler (sv);
+ sheet = schematic_view_get_sheet (sv);
+
+ /*
+ * Connect to a signal handler that will
+ * let the user create a new textbox.
+ */
+ sheet->state = SHEET_STATE_TEXTBOX_WAIT;
+ g_signal_connect(G_OBJECT(sheet),
+ "event", G_CALLBACK(create_textbox_event), sv);
+}
+
+/*
+ * Go through the properties and commit the changes.
+ */
+static void
+edit_dialog_ok(TextboxItem *item)
+{
+ TextboxItemPriv *priv;
+ Textbox *textbox;
+ const gchar *value;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (item));
+
+ priv = item->priv;
+ textbox = TEXTBOX (sheet_item_get_data (SHEET_ITEM (item)));
+
+ value = gtk_entry_get_text(GTK_ENTRY(prop_dialog->entry));
+
+ textbox_set_text(textbox, value);
+ textbox_set_font(textbox,
+ gtk_font_selection_get_font_name(prop_dialog->font));
+}
+
+static void
+edit_textbox (SheetItem *sheet_item)
+{
+ Sheet *sheet;
+ TextboxItem *item;
+ TextboxItemPriv *priv;
+ Textbox *textbox;
+ char *msg, *value;
+ GladeXML *gui;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_TEXTBOX_ITEM (sheet_item));
+
+ item = TEXTBOX_ITEM (sheet_item);
+ priv = item->priv;
+ textbox = TEXTBOX (sheet_item_get_data (sheet_item));
+
+ if (!g_file_test (OREGANO_GLADEDIR "/textbox-properties-dialog.glade",
+ G_FILE_TEST_EXISTS)) {
+ msg = g_strdup_printf (
+ _("The file %s could not be found. You might need to reinstall Oregano to fix this."),
+ OREGANO_GLADEDIR "/textbox-properties-dialog.glade");
+ oregano_error (_("Could not create textbox properties dialog"));
+ g_free (msg);
+ return;
+ }
+
+ gui = glade_xml_new (
+ OREGANO_GLADEDIR "/textbox-properties-dialog.glade",
+ NULL, NULL);
+ if (!gui) {
+ oregano_error (_("Could not create textbox properties dialog"));
+ return;
+ }
+
+ prop_dialog = g_new0 (TextboxPropDialog, 1);
+ prop_dialog->dialog = GTK_DIALOG (
+ glade_xml_get_widget (gui, "textbox-properties-dialog"));
+ prop_dialog->font = GTK_FONT_SELECTION (
+ glade_xml_get_widget (gui, "font_selector"));
+ prop_dialog->entry = GTK_ENTRY (glade_xml_get_widget (gui, "entry"));
+
+ value = textbox_get_font (textbox);
+ gtk_font_selection_set_font_name (
+ GTK_FONT_SELECTION (prop_dialog->font), value);
+
+ value = textbox_get_text(textbox);
+ gtk_entry_set_text (GTK_ENTRY (prop_dialog->entry), value);
+
+ sheet = sheet_item_get_sheet (SHEET_ITEM (item));
+ sheet_dialog_set_parent (sheet, (GtkDialog*) prop_dialog->dialog);
+
+ gtk_dialog_set_default_response (
+ GTK_DIALOG (prop_dialog->dialog), GTK_RESPONSE_OK);
+
+ gtk_dialog_run( GTK_DIALOG (prop_dialog->dialog));
+
+ edit_dialog_ok(item);
+
+ /* Clean the dialog */
+ gtk_widget_destroy(GTK_WIDGET(prop_dialog->dialog));
+ prop_dialog = NULL;
+}
+
+static void
+edit_cmd (GtkWidget *widget, SchematicView *sv)
+{
+ GList *list;
+
+ list = schematic_view_get_selection (sv);
+ if ((list != NULL) && IS_TEXTBOX_ITEM (list->data))
+ edit_textbox (list->data);
+}
diff --git a/src/sheet/textbox-item.h b/src/sheet/textbox-item.h
new file mode 100644
index 0000000..74edfff
--- /dev/null
+++ b/src/sheet/textbox-item.h
@@ -0,0 +1,67 @@
+/*
+ * textbox-item.h
+ *
+ *
+ * Author:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __TEXTBOX_ITEM_H
+#define __TEXTBOX_ITEM_H
+
+#include <gtk/gtk.h>
+#include "schematic-view.h"
+#include "sheet-item.h"
+#include "textbox.h"
+
+#define TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, textbox_item_get_type (), TextboxItem)
+#define TEXTBOX_ITEM_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, textbox_item_get_type (), TextboxItemClass)
+#define IS_TEXTBOX_ITEM(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, textbox_item_get_type ())
+
+typedef struct _TextboxItemPriv TextboxItemPriv;
+
+typedef enum {
+ TEXTBOX_DIR_NONE = 0,
+ TEXTBOX_DIR_HORIZ = 1,
+ TEXTBOX_DIR_VERT = 2,
+ TEXTBOX_DIR_DIAG = 3
+} TextboxDir;
+
+typedef struct {
+ SheetItem parent_object;
+ TextboxItemPriv *priv;
+} TextboxItem;
+
+typedef struct {
+ SheetItemClass parent_class;
+} TextboxItemClass;
+
+GType textbox_item_get_type (void);
+TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox);
+void textbox_item_signal_connect_placed (TextboxItem *textbox_item, SchematicView *sv);
+void textbox_item_cancel_listen (SchematicView *sv);
+void textbox_item_listen (SchematicView *sv);
+
+
+#endif
diff --git a/src/sheet/wire-item.c b/src/sheet/wire-item.c
new file mode 100644
index 0000000..a5b542d
--- /dev/null
+++ b/src/sheet/wire-item.c
@@ -0,0 +1,916 @@
+/*
+ * wire-item.c
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <gnome.h>
+#include "cursors.h"
+#include "sheet-private.h"
+#include "sheet-pos.h"
+#include "wire-item.h"
+#include "node-store.h"
+#include "wire.h"
+#include "wire-private.h"
+
+#define NORMAL_COLOR "blue"
+#define SELECTED_COLOR "green"
+#define HIGHLIGHT_COLOR "yellow"
+
+#define RESIZER_SIZE 4.0f
+
+static void wire_item_class_init (WireItemClass *klass);
+static void wire_item_init (WireItem *item);
+static void wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+static void wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+static void wire_item_destroy (GtkObject *object);
+static void wire_item_moved (SheetItem *object);
+
+static void wire_rotated_callback (ItemData *data, int angle,
+ SheetItem *sheet_item);
+static void wire_flipped_callback (ItemData *data, gboolean horizontal,
+ SheetItem *sheet_item);
+static void wire_moved_callback (ItemData *data, SheetPos *pos,
+ SheetItem *item);
+static void wire_changed_callback (Wire *, WireItem *item);
+static void wire_delete_callback (Wire *, WireItem *item);
+
+static void wire_item_paste (SchematicView *sv, ItemData *data);
+static void selection_changed (WireItem *item, gboolean select,
+ gpointer user_data);
+static int select_idle_callback (WireItem *item);
+static int deselect_idle_callback (WireItem *item);
+static gboolean is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2);
+inline static void get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2);
+
+static void mouse_over_wire_cb (WireItem *item, SchematicView *sv);
+static void highlight_wire_cb (Wire *wire, WireItem *item);
+static int unhighlight_wire (WireItem *item);
+
+static void wire_item_place (SheetItem *item, SchematicView *sv);
+static void wire_item_place_ghost (SheetItem *item, SchematicView *sv);
+
+static SheetItemClass *wire_item_parent_class = NULL;
+
+enum {
+ WIRE_ITEM_ARG_0,
+ WIRE_ITEM_ARG_NAME
+};
+
+enum {
+ WIRE_RESIZER_NONE,
+ WIRE_RESIZER_1,
+ WIRE_RESIZER_2
+};
+
+struct _WireItemPriv {
+ guint cache_valid : 1;
+ guint resize_state;
+ guint highlight : 1;
+ WireDir direction; /* Direction of the wire. */
+
+ GnomeCanvasLine *line;
+ GnomeCanvasRect *resize1;
+ GnomeCanvasRect *resize2;
+
+ /*
+ * Cached bounding box. This is used to make
+ * the rubberband selection a bit faster.
+ */
+ SheetPos bbox_start;
+ SheetPos bbox_end;
+};
+
+GType
+wire_item_get_type ()
+{
+ static GType wire_item_type = 0;
+
+ if (!wire_item_type) {
+ static const GTypeInfo wire_item_info = {
+ sizeof (WireItemClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)wire_item_class_init,
+ NULL,
+ NULL,
+ sizeof (WireItem),
+ 0,
+ (GInstanceInitFunc)wire_item_init,
+ NULL
+ };
+
+ wire_item_type = g_type_register_static(TYPE_SHEET_ITEM,
+ "WireItem", &wire_item_info, 0);
+ }
+ return wire_item_type;
+}
+
+static void
+wire_item_class_init (WireItemClass *wire_item_class)
+{
+ GObjectClass *object_class;
+ GtkObjectClass *gtk_object_class;
+ SheetItemClass *sheet_item_class;
+
+ object_class = G_OBJECT_CLASS(wire_item_class);
+ gtk_object_class = GTK_OBJECT_CLASS(wire_item_class);
+ sheet_item_class = SHEET_ITEM_CLASS(wire_item_class);
+ wire_item_parent_class = g_type_class_peek(TYPE_SHEET_ITEM);
+
+ gtk_object_class->destroy = wire_item_destroy;
+
+ sheet_item_class->moved = wire_item_moved;
+ sheet_item_class->paste = wire_item_paste;
+ sheet_item_class->is_in_area = is_in_area;
+ sheet_item_class->selection_changed = (gpointer) selection_changed;
+ sheet_item_class->place = wire_item_place;
+ sheet_item_class->place_ghost = wire_item_place_ghost;
+}
+
+static void
+wire_item_init (WireItem *item)
+{
+ WireItemPriv *priv;
+
+ priv = g_new0 (WireItemPriv, 1);
+
+ priv->direction = WIRE_DIR_NONE;
+ priv->highlight = FALSE;
+ priv->cache_valid = FALSE;
+
+ item->priv = priv;
+}
+
+static void
+wire_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ WireItem *wire_item = WIRE_ITEM (object);
+
+ wire_item = WIRE_ITEM (object);
+
+ switch (arg_id) {
+ case WIRE_ITEM_ARG_NAME:
+ break;
+ }
+}
+
+static void
+wire_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ WireItem *wire_item = WIRE_ITEM (object);
+
+ wire_item = WIRE_ITEM (object);
+
+ switch (arg_id) {
+ case WIRE_ITEM_ARG_NAME:
+ break;
+ default:
+ //arg->type = G_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+wire_item_destroy (GtkObject *object)
+{
+ WireItem *wire;
+ WireItemPriv *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (object));
+
+ wire = WIRE_ITEM (object);
+ priv = wire->priv;
+
+ if (priv) {
+ if (priv->line) {
+ /* TODO Check if destroy or unref have to be used for
+ * GnomeCanvasItem */
+ gtk_object_destroy (GTK_OBJECT (priv->line));
+ priv->line = NULL;
+ }
+ g_free (priv);
+ wire->priv = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS(wire_item_parent_class)->destroy){
+ GTK_OBJECT_CLASS(wire_item_parent_class)->destroy(object);
+ }
+}
+
+/**
+ * "moved" signal handler. Invalidates the bounding box cache.
+ */
+static void
+wire_item_moved (SheetItem *object)
+{
+ WireItem *item;
+ WireItemPriv *priv;
+
+ item = WIRE_ITEM (object);
+ priv = item->priv;
+
+ priv->cache_valid = FALSE;
+}
+
+WireItem *
+wire_item_new (Sheet *sheet, Wire *wire)
+{
+ WireItem *item;
+ GnomeCanvasPoints *points;
+ WireItemPriv *priv;
+ SheetPos start_pos, length;
+
+ g_return_val_if_fail (sheet != NULL, NULL);
+ g_return_val_if_fail (IS_SHEET (sheet), NULL);
+
+ //g_object_ref (G_OBJECT(wire));
+ /* XXX Ver si hay equivalente gtk_object_sink (GTK_OBJECT (wire)); */
+
+ wire_get_pos_and_length (wire, &start_pos, &length);
+
+ /*
+ * Because of the GnomeCanvasGroup inheritance, a small hack is needed
+ * here. The group starts at the startpoint of the wire, and the line
+ * goes from (0,0) to (length.x, length.y).
+ */
+ item = WIRE_ITEM (gnome_canvas_item_new (
+ sheet->object_group,
+ wire_item_get_type (),
+ "data", wire,
+ "x", (double) start_pos.x,
+ "y", (double) start_pos.y,
+ NULL));
+
+ priv = item->priv;
+
+ priv->resize1 = GNOME_CANVAS_RECT (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_rect_get_type (),
+ "x1", -RESIZER_SIZE,
+ "y1", -RESIZER_SIZE,
+ "x2", RESIZER_SIZE,
+ "y2", RESIZER_SIZE,
+ "fill_color", "red",
+ "fill_color_rgba", 0x3cb37180,
+ "outline_color", "blue",
+ "width_pixels", 1,
+ NULL));
+
+ priv->resize2 = GNOME_CANVAS_RECT (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_rect_get_type (),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ "fill_color", "red",
+ "fill_color_rgba", 0x3cb37180,
+ "outline_color", "blue",
+ "width_pixels", 1,
+ NULL));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize1));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (priv->resize2));
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ priv->line = GNOME_CANVAS_LINE (gnome_canvas_item_new (
+ GNOME_CANVAS_GROUP (item),
+ gnome_canvas_line_get_type (),
+ "points", points,
+ "fill_color", "blue",
+ "width_pixels", 1,
+ NULL));
+
+ gnome_canvas_points_free (points);
+
+ g_signal_connect_object(G_OBJECT(wire), "rotated",
+ G_CALLBACK(wire_rotated_callback), G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT(wire), "flipped",
+ G_CALLBACK(wire_flipped_callback), G_OBJECT(item), 0);
+ g_signal_connect_object(G_OBJECT(wire), "moved",
+ G_CALLBACK(wire_moved_callback), G_OBJECT(item), 0);
+
+ g_signal_connect (G_OBJECT (wire), "changed", G_CALLBACK (wire_changed_callback), item);
+ g_signal_connect (G_OBJECT (wire), "delete", G_CALLBACK (wire_delete_callback), item);
+ wire_update_bbox (wire);
+
+ return item;
+}
+
+static
+int wire_item_event (WireItem *wire_item, const GdkEvent *event, SchematicView *sv)
+{
+ SheetPos start_pos, length;
+ Wire *wire;
+ Sheet *sheet;
+ GnomeCanvas *canvas;
+ static double last_x, last_y;
+ double dx, dy, zoom;
+ /* The selected group's bounding box in window resp. canvas coordinates. */
+ double x1, y1, x2, y2;
+ static double bb_x1, bb_y1, bb_x2, bb_y2;
+ int cx1, cy1, cx2, cy2;
+ double snapped_x, snapped_y;
+ int sheet_width, sheet_height;
+ SheetPos pos;
+
+ sheet = schematic_view_get_sheet (sv);
+ canvas = GNOME_CANVAS (sheet);
+ g_object_get (G_OBJECT (wire_item), "data", &wire, NULL);
+
+ wire_get_pos_and_length (WIRE (wire), &start_pos, &length);
+ sheet_get_zoom (sheet, &zoom);
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ switch (event->button.button) {
+ case 1: {
+ g_signal_stop_emission_by_name (G_OBJECT (sheet), "event");
+ double x, y;
+ x = event->button.x - start_pos.x;
+ y = event->button.y - start_pos.y;
+ if ((x > -RESIZER_SIZE) && (x < RESIZER_SIZE) &&
+ (y > -RESIZER_SIZE) && (y < RESIZER_SIZE)) {
+ gtk_widget_grab_focus (GTK_WIDGET (sheet));
+ sheet->state = SHEET_STATE_DRAG_START;
+ wire_item->priv->resize_state = WIRE_RESIZER_1;
+
+ last_x = event->button.x;
+ last_y = event->button.y;
+ item_data_unregister (ITEM_DATA (wire));
+ return TRUE;
+ }
+ if ((x > (length.x-RESIZER_SIZE)) && (x < (length.x+RESIZER_SIZE)) &&
+ (y > (length.y-RESIZER_SIZE)) && (y < (length.y+RESIZER_SIZE))) {
+ gtk_widget_grab_focus (GTK_WIDGET (sheet));
+ sheet->state = SHEET_STATE_DRAG_START;
+ wire_item->priv->resize_state = WIRE_RESIZER_2;
+
+ last_x = event->button.x;
+ last_y = event->button.y;
+ item_data_unregister (ITEM_DATA (wire));
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ break;
+
+ if (wire_item->priv->resize_state == WIRE_RESIZER_NONE)
+ break;
+
+ if (sheet->state == SHEET_STATE_DRAG_START || sheet->state == SHEET_STATE_DRAG) {
+ sheet->state = SHEET_STATE_DRAG;
+
+ snapped_x = event->motion.x;
+ snapped_y = event->motion.y;
+ snap_to_grid (sheet->grid, &snapped_x, &snapped_y);
+
+ dx = snapped_x - last_x;
+ dy = snapped_y - last_y;
+
+
+ last_x = snapped_x;
+ last_y = snapped_y;
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ if (wire_item->priv->resize_state == WIRE_RESIZER_1) {
+ switch (wire->priv->direction) {
+ case WIRE_DIR_VERT:
+ /* Vertical Wire */
+ pos.y = last_y;
+ length.y -= dy;
+ break;
+ case WIRE_DIR_HORIZ:
+ /* Horizontal Wire */
+ pos.x = last_x;
+ length.x -= dx;
+ break;
+ default:
+ pos.y = last_y;
+ length.y -= dy;
+ pos.x = last_x;
+ length.x -= dx;
+ }
+ } else {
+ switch (wire->priv->direction) {
+ case WIRE_DIR_VERT:
+ /* Vertical Wire */
+ length.y += dy;
+ break;
+ case WIRE_DIR_HORIZ:
+ /* Horizontal Wire */
+ length.x += dx;
+ break;
+ default:
+ length.y += dy;
+ length.x += dx;
+ }
+ }
+ snap_to_grid (sheet->grid, &length.x, &length.y);
+ item_data_set_pos (sheet_item_get_data (SHEET_ITEM (wire_item)), &pos);
+ wire_set_length (wire, &length);
+ return TRUE;
+ }
+ break;
+ case GDK_BUTTON_RELEASE:
+ switch (event->button.button) {
+ case 1:
+ if (sheet->state != SHEET_STATE_DRAG &&
+ sheet->state != SHEET_STATE_DRAG_START)
+ break;
+ if (wire_item->priv->resize_state == WIRE_RESIZER_NONE)
+ break;
+
+ g_signal_stop_emission_by_name (G_OBJECT (wire_item), "event");
+
+ //gtk_timeout_remove (priv->scroll_timeout_id); // Esto no esta bien.
+
+ sheet->state = SHEET_STATE_NONE;
+ gnome_canvas_item_ungrab (GNOME_CANVAS_ITEM (wire_item), event->button.time);
+
+ wire_item->priv->resize_state = WIRE_RESIZER_NONE;
+ sheet->state = SHEET_STATE_NONE;
+ item_data_register (ITEM_DATA (wire));
+ return TRUE;
+ }
+ break;
+ default:
+ return sheet_item_event (SHEET_ITEM (wire_item), event, sv);
+ }
+ return sheet_item_event (SHEET_ITEM (wire_item), event, sv);
+}
+
+void
+wire_item_signal_connect_placed (WireItem *wire, SchematicView *sv)
+{
+ g_signal_connect (
+ G_OBJECT (wire),
+ "event",
+ G_CALLBACK(wire_item_event),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT (wire),
+ "mouse_over",
+ G_CALLBACK(mouse_over_wire_cb),
+ sv);
+
+ g_signal_connect (
+ G_OBJECT (sheet_item_get_data (SHEET_ITEM (wire))),
+ "highlight",
+ G_CALLBACK(highlight_wire_cb),
+ wire);
+}
+
+static void
+wire_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
+{
+ WireItem *wire_item;
+ GnomeCanvasPoints *points;
+ SheetPos start_pos, length;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (sheet_item));
+
+ wire_item = WIRE_ITEM (sheet_item);
+
+ wire_get_pos_and_length (WIRE (data), &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (wire_item),
+ "x", start_pos.x,
+ "y", start_pos.y,
+ NULL);
+
+ gnome_canvas_item_set (
+ GNOME_CANVAS_ITEM (wire_item-> priv->resize2),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ NULL
+ );
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ wire_item->priv->cache_valid = FALSE;
+}
+
+static void
+wire_flipped_callback (ItemData *data,
+ gboolean horizontal, SheetItem *sheet_item)
+{
+ GnomeCanvasPoints *points;
+ WireItem *item;
+ WireItemPriv *priv;
+ SheetPos start_pos, length;
+
+ g_return_if_fail (sheet_item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (sheet_item));
+
+ item = WIRE_ITEM (sheet_item);
+ priv = item->priv;
+
+ wire_get_pos_and_length (WIRE (data), &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item),
+ "x", start_pos.x,
+ "y", start_pos.y,
+ NULL);
+
+ /*
+ * Invalidate the bounding box cache.
+ */
+ priv->cache_valid = FALSE;
+}
+
+static int
+select_idle_callback (WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", SELECTED_COLOR, NULL);
+
+ priv->highlight = TRUE;
+
+ g_object_unref (G_OBJECT (item));
+ return FALSE;
+}
+
+static int
+deselect_idle_callback (WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", NORMAL_COLOR, NULL);
+
+ priv->highlight = FALSE;
+
+ g_object_unref(G_OBJECT (item));
+ return FALSE;
+}
+
+static void
+selection_changed(WireItem *item, gboolean select, gpointer user_data)
+{
+ g_object_ref(G_OBJECT(item));
+ if (select) {
+ gtk_idle_add ((gpointer) select_idle_callback, item);
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize1));
+ gnome_canvas_item_show (GNOME_CANVAS_ITEM (item->priv->resize2));
+ } else {
+ gtk_idle_add ((gpointer) deselect_idle_callback, item);
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize1));
+ gnome_canvas_item_hide (GNOME_CANVAS_ITEM (item->priv->resize2));
+ }
+}
+
+/**
+ * This function returns the position of the canvas item. It has
+ * nothing to do with where the wire is stored in the sheet node store.
+ */
+void
+wire_item_get_start_pos (WireItem *item, SheetPos *pos)
+{
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+ g_return_if_fail (pos != NULL);
+
+ g_object_get(G_OBJECT(item), "x", &pos->x, "y", &pos->y, NULL);
+}
+
+/**
+ * This function returns the length of the canvas item.
+ */
+void
+wire_item_get_length (WireItem *item, SheetPos *pos)
+{
+ WireItemPriv *priv;
+ GnomeCanvasPoints *points;
+
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+ g_return_if_fail (pos != NULL);
+
+ priv = item->priv;
+
+ g_object_get(G_OBJECT(priv->line), "points", &points, NULL);
+
+ /*
+ * This is not strictly neccessary, since the first point is always
+ * (0,0) but it's more correct and good to have if this changes in the
+ * future.
+ */
+ pos->x = points->coords[2] - points->coords[0];
+ pos->y = points->coords[3] - points->coords[1];
+ gnome_canvas_points_free (points);
+}
+
+static gboolean
+is_in_area (SheetItem *object, SheetPos *p1, SheetPos *p2)
+{
+ WireItem *item;
+ SheetPos bbox_start, bbox_end;
+
+ item = WIRE_ITEM (object);
+
+ get_bbox (item, &bbox_start, &bbox_end);
+
+ if (p1->x < bbox_start.x &&
+ p2->x > bbox_end.x &&
+ p1->y < bbox_start.y &&
+ p2->y > bbox_end.y)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * Retrieves the bounding box. We use a caching scheme for this
+ * since it's too expensive to calculate it every time we need it.
+ */
+inline static void
+get_bbox (WireItem *item, SheetPos *p1, SheetPos *p2)
+{
+ WireItemPriv *priv;
+ priv = item->priv;
+
+ if (!priv->cache_valid) {
+ SheetPos start_pos, end_pos;
+
+ wire_item_get_start_pos (item, &start_pos);
+ wire_item_get_length (item, &end_pos);
+ end_pos.x += start_pos.x;
+ end_pos.y += start_pos.y;
+
+ priv->bbox_start.x = MIN (start_pos.x, end_pos.x);
+ priv->bbox_start.y = MIN (start_pos.y, end_pos.y);
+ priv->bbox_end.x = MAX (start_pos.x, end_pos.x);
+ priv->bbox_end.y = MAX (start_pos.y, end_pos.y);
+ priv->cache_valid = TRUE;
+ }
+
+ memcpy (p1, &priv->bbox_start, sizeof (SheetPos));
+ memcpy (p2, &priv->bbox_end, sizeof (SheetPos));
+}
+
+static void
+wire_item_paste (SchematicView *sv, ItemData *data)
+{
+ g_return_if_fail (sv != NULL);
+ g_return_if_fail (IS_SCHEMATIC_VIEW (sv));
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_WIRE (data));
+
+ schematic_view_add_ghost_item (sv, data);
+}
+
+static void wire_traverse (Wire *wire);
+
+static void
+node_traverse (Node *node)
+{
+ GSList *wires;
+
+ g_return_if_fail (node != NULL);
+ g_return_if_fail (IS_NODE (node));
+
+ if (node_is_visited (node))
+ return;
+
+ node_set_visited (node, TRUE);
+
+ for (wires = node->wires; wires; wires = wires->next) {
+ Wire *wire = wires->data;
+ wire_traverse (wire);
+ }
+}
+
+static void
+wire_traverse (Wire *wire)
+{
+ GSList *nodes;
+
+ g_return_if_fail (wire != NULL);
+ g_return_if_fail (IS_WIRE (wire));
+
+ if (wire_is_visited (wire))
+ return;
+
+ wire_set_visited (wire, TRUE);
+
+ g_signal_emit_by_name(G_OBJECT (wire), "highlight");
+
+ for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) {
+ Node *node = nodes->data;
+
+ node_traverse (node);
+ }
+}
+
+static void
+node_foreach_reset (gpointer key, gpointer value, gpointer user_data)
+{
+ Node *node = value;
+
+ node_set_visited (node, FALSE);
+}
+
+static void
+mouse_over_wire_cb (WireItem *item, SchematicView *sv)
+{
+ GList *wires;
+ Wire *wire;
+ NodeStore *store;
+ Sheet *sheet;
+
+ sheet = schematic_view_get_sheet (sv);
+
+ if (sheet->state != SHEET_STATE_NONE)
+ return;
+
+ store = schematic_get_store (schematic_view_get_schematic (sv));
+
+ node_store_node_foreach (store, (GHFunc *) node_foreach_reset, NULL);
+ for (wires = store->wires; wires; wires = wires->next) {
+ wire = wires->data;
+ wire_set_visited (wire, FALSE);
+ }
+
+ wire = WIRE (sheet_item_get_data (SHEET_ITEM (item)));
+ wire_traverse (wire);
+}
+
+static void
+highlight_wire_cb (Wire *wire, WireItem *item)
+{
+ WireItemPriv *priv = item->priv;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", HIGHLIGHT_COLOR, NULL);
+
+ /*
+ * Guard against removal during the highlighting.
+ */
+ g_object_ref(G_OBJECT (item));
+
+ gtk_timeout_add (1000, (gpointer) unhighlight_wire, item);
+}
+
+static int
+unhighlight_wire (WireItem *item)
+{
+ char *color;
+ WireItemPriv *priv = item->priv;
+
+ color = sheet_item_get_selected (SHEET_ITEM (item)) ?
+ SELECTED_COLOR : NORMAL_COLOR;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (priv->line),
+ "fill_color", color, NULL);
+
+ g_object_unref (G_OBJECT (item));
+
+ return FALSE;
+}
+
+/**
+ * This is called when the wire data was moved. Update the view accordingly.
+ */
+static void
+wire_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item)
+{
+ WireItem *wire_item;
+
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (IS_ITEM_DATA (data));
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (IS_WIRE_ITEM (item));
+
+ if (pos == NULL)
+ return;
+
+ wire_item = WIRE_ITEM (item);
+
+ /*
+ * Move the canvas item and invalidate the bbox cache.
+ */
+ gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), pos->x, pos->y);
+ wire_item->priv->cache_valid = FALSE;
+}
+
+static void
+wire_item_place (SheetItem *item, SchematicView *sv)
+{
+ wire_item_signal_connect_placed (WIRE_ITEM (item), sv);
+}
+
+static void
+wire_item_place_ghost (SheetItem *item, SchematicView *sv)
+{
+// wire_item_signal_connect_placed (WIRE_ITEM (item));
+}
+
+
+static void
+wire_changed_callback (Wire *wire, WireItem *item)
+{
+ SheetPos start_pos, length;
+ GnomeCanvasPoints *points;
+
+ wire_get_pos_and_length (wire, &start_pos, &length);
+
+ points = gnome_canvas_points_new (2);
+ points->coords[0] = 0;
+ points->coords[1] = 0;
+ points->coords[2] = length.x;
+ points->coords[3] = length.y;
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->line),
+ "points", points,
+ NULL);
+ gnome_canvas_points_unref (points);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize1),
+ "x1", -RESIZER_SIZE,
+ "y1", -RESIZER_SIZE,
+ "x2", RESIZER_SIZE,
+ "y2", RESIZER_SIZE,
+ NULL);
+
+ gnome_canvas_item_set (GNOME_CANVAS_ITEM (item->priv->resize2),
+ "x1", length.x-RESIZER_SIZE,
+ "y1", length.y-RESIZER_SIZE,
+ "x2", length.x+RESIZER_SIZE,
+ "y2", length.y+RESIZER_SIZE,
+ NULL);
+}
+
+static void
+wire_delete_callback (Wire *wire, WireItem *item)
+{
+ gtk_object_destroy (GTK_OBJECT (item));
+}
+
+
diff --git a/src/sheet/wire-item.h b/src/sheet/wire-item.h
new file mode 100644
index 0000000..11124cc
--- /dev/null
+++ b/src/sheet/wire-item.h
@@ -0,0 +1,60 @@
+/*
+ * wire-item.h
+ *
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2004 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __WIRE_ITEM_H
+#define __WIRE_ITEM_H
+
+#include <gnome.h>
+
+#include "schematic-view.h"
+#include "sheet-item.h"
+#include "wire.h"
+
+#define WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, wire_item_get_type (), WireItem))
+#define WIRE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST (klass, wire_item_get_type (), WireItemClass))
+#define IS_WIRE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, wire_item_get_type ()))
+
+typedef struct _WireItemPriv WireItemPriv;
+
+typedef struct {
+ SheetItem parent_object;
+ WireItemPriv *priv;
+} WireItem;
+
+typedef struct {
+ SheetItemClass parent_class;
+} WireItemClass;
+
+GType wire_item_get_type (void);
+WireItem *wire_item_new (Sheet *sheet, Wire *wire);
+void wire_item_initiate (Sheet *sheet);
+void wire_item_get_start_pos (WireItem *item, SheetPos *pos);
+void wire_item_get_length (WireItem *item, SheetPos *pos);
+
+#endif