1 /*
2  * part-item.c
3  *
4  *
5  * Authors:
6  *  Richard Hult <rhult@hem.passagen.se>
7  *  Ricardo Markiewicz <rmarkie@fi.uba.ar>
8  *  Andres de Barbara <adebarbara@fi.uba.ar>
9  *  Marc Lorber <lorber.marc@wanadoo.fr>
10  *  Bernhard Schuster <bernhard@ahoi.io>
11  *
12  * Web page: https://ahoi.io/project/oregano
13  *
14  * Copyright (C) 1999-2001  Richard Hult
15  * Copyright (C) 2003,2006  Ricardo Markiewicz
16  * Copyright (C) 2009-2012  Marc Lorber
17  * Copyright (C) 2013-2014  Bernhard Schuster
18  * Copyright (C) 2018       Guido Trentalancia
19  *
20  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License as
22  * published by the Free Software Foundation; either version 2 of the
23  * License, or (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  * General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public
31  * License along with this program; if not, write to the
32  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
33  * Boston, MA 02110-1301, USA.
34  */
35 
36 #include <glib/gi18n.h>
37 #include <string.h>
38 #include <goocanvas.h>
39 #include <math.h>
40 
41 #include "oregano.h"
42 #include "sheet-item.h"
43 #include "part-item.h"
44 #include "part-private.h"
45 #include "part-property.h"
46 #include "load-library.h"
47 #include "load-common.h"
48 #include "part-label.h"
49 #include "stock.h"
50 #include "dialogs.h"
51 #include "sheet.h"
52 #include "oregano-utils.h"
53 #include "options.h"
54 
55 #define NORMAL_COLOR "red"
56 #define LABEL_COLOR "dark cyan"
57 #define SELECTED_COLOR "green"
58 
59 #include "debug.h"
60 
61 static void part_item_class_init (PartItemClass *klass);
62 static void part_item_init (PartItem *gspart);
63 static void part_item_finalize (GObject *object);
64 static void part_item_moved (SheetItem *sheet_item);
65 static void edit_properties (SheetItem *object);
66 static void selection_changed (PartItem *item, gboolean select, gpointer user_data);
67 static int select_idle_callback (PartItem *item);
68 static int deselect_idle_callback (PartItem *item);
69 static void update_canvas_labels (PartItem *part_item);
70 static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2);
71 inline static void get_cached_bounds (PartItem *item, Coords *p1, Coords *p2);
72 static void show_labels (SheetItem *sheet_item, gboolean show);
73 static void part_item_paste (Sheet *sheet, ItemData *data);
74 static void part_rotated_callback (ItemData *data, int angle, SheetItem *item);
75 static void part_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item);
76 static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item);
77 static void part_changed_callback (ItemData *data, SheetItem *sheet_item);
78 
79 static void part_item_place (SheetItem *item, Sheet *sheet);
80 static void part_item_place_ghost (SheetItem *item, Sheet *sheet);
81 static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part);
82 static void create_canvas_labels (PartItem *item, Part *part);
83 static void create_canvas_label_nodes (PartItem *item, Part *part);
84 static PartItem *part_item_canvas_new (Sheet *sheet, Part *part);
85 static void part_item_get_property (GObject *object, guint prop_id, GValue *value,
86                                     GParamSpec *spec);
87 static void part_item_set_property (GObject *object, guint prop_id, const GValue *value,
88                                     GParamSpec *spec);
89 static void part_item_dispose (GObject *object);
90 static GooCanvasAnchorType part_item_get_anchor_from_part (Part *part);
91 
92 enum {
93 	ARG_0,
94 	ARG_DATA,
95 	ARG_SHEET,
96 	ARG_ACTION_GROUP,
97 	ARG_NAME,
98 	ARG_SYMNAME,
99 	ARG_LIBNAME,
100 	ARG_REFDES,
101 	ARG_TEMPLATE,
102 	ARG_MODEL
103 };
104 
105 struct _PartItemPriv
106 {
107 	guint cache_valid : 1;
108 	GooCanvasItem *label_group;
109 	GSList *label_items;
110 	GooCanvasItem *node_group;
111 	GSList *label_nodes;
112 
113 	GooCanvasItem *rect;
114 	// Cached bounding box. This is used to make
115 	// the rubberband selection a bit faster.
116 	Coords bbox_start;
117 	Coords bbox_end;
118 };
119 
120 typedef struct
121 {
122 	GtkDialog *dialog;
123 	PartItem *part_item;
124 	// List of GtkEntry's
125 	GList *widgets;
126 } PartPropDialog;
127 
128 static PartPropDialog *prop_dialog = NULL;
129 static SheetItemClass *parent_class = NULL;
130 
131 static const char *part_item_context_menu = "<ui>"
132                                             "  <popup name='ItemMenu'>"
133                                             "    <menuitem action='ObjectProperties'/>"
134                                             "  </popup>"
135                                             "</ui>";
136 
137 static GtkActionEntry action_entries[] = {{"ObjectProperties", GTK_STOCK_PROPERTIES,
138                                            N_ ("_Object Properties..."), NULL,
139                                            N_ ("Modify object properties"), NULL}};
140 
141 enum { ANCHOR_NORTH, ANCHOR_SOUTH, ANCHOR_WEST, ANCHOR_EAST };
142 
G_DEFINE_TYPE(PartItem,part_item,TYPE_SHEET_ITEM)143 G_DEFINE_TYPE (PartItem, part_item, TYPE_SHEET_ITEM)
144 
145 static void part_item_class_init (PartItemClass *part_item_class)
146 {
147 	GObjectClass *object_class;
148 	SheetItemClass *sheet_item_class;
149 
150 	object_class = G_OBJECT_CLASS (part_item_class);
151 	sheet_item_class = SHEET_ITEM_CLASS (part_item_class);
152 	parent_class = g_type_class_peek_parent (part_item_class);
153 
154 	object_class->finalize = part_item_finalize;
155 	object_class->dispose = part_item_dispose;
156 	object_class->set_property = part_item_set_property;
157 	object_class->get_property = part_item_get_property;
158 
159 	sheet_item_class->moved = part_item_moved;
160 	sheet_item_class->is_in_area = is_in_area;
161 	sheet_item_class->show_labels = show_labels;
162 	sheet_item_class->paste = part_item_paste;
163 	sheet_item_class->edit_properties = edit_properties;
164 	sheet_item_class->selection_changed = (gpointer)selection_changed;
165 
166 	sheet_item_class->place = part_item_place;
167 	sheet_item_class->place_ghost = part_item_place_ghost;
168 }
169 
part_item_init(PartItem * item)170 static void part_item_init (PartItem *item)
171 {
172 	PartItemPriv *priv;
173 
174 	priv = g_slice_new0 (PartItemPriv);
175 	priv->rect = NULL;
176 	priv->cache_valid = FALSE;
177 
178 	item->priv = priv;
179 
180 	sheet_item_add_menu (SHEET_ITEM (item), part_item_context_menu, action_entries,
181 	                     G_N_ELEMENTS (action_entries));
182 }
183 
part_item_set_property(GObject * object,guint propety_id,const GValue * value,GParamSpec * pspec)184 static void part_item_set_property (GObject *object, guint propety_id, const GValue *value,
185                                     GParamSpec *pspec)
186 {
187 	g_return_if_fail (object != NULL);
188 	g_return_if_fail (IS_PART_ITEM (object));
189 
190 	switch (propety_id) {
191 	default:
192 		g_warning ("PartItem: Invalid argument.\n");
193 	}
194 }
195 
part_item_get_property(GObject * object,guint propety_id,GValue * value,GParamSpec * pspec)196 static void part_item_get_property (GObject *object, guint propety_id, GValue *value,
197                                     GParamSpec *pspec)
198 {
199 	g_return_if_fail (object != NULL);
200 	g_return_if_fail (IS_PART_ITEM (object));
201 
202 	switch (propety_id) {
203 	default:
204 		pspec->value_type = G_TYPE_INVALID;
205 		break;
206 	}
207 }
208 
part_item_dispose(GObject * object)209 static void part_item_dispose (GObject *object) { G_OBJECT_CLASS (parent_class)->dispose (object); }
210 
part_item_finalize(GObject * object)211 static void part_item_finalize (GObject *object)
212 {
213 	PartItemPriv *priv;
214 
215 	priv = PART_ITEM (object)->priv;
216 
217 	g_slist_free (priv->label_nodes);
218 	g_slist_free (priv->label_items);
219 	g_slice_free (PartItemPriv, priv);
220 	priv = NULL;
221 	G_OBJECT_CLASS (parent_class)->finalize (object);
222 }
223 
224 ////////////////////////////////////////////////////////////////////////////////
225 // END BOILER PLATE
226 ////////////////////////////////////////////////////////////////////////////////
227 
part_item_set_label_items(PartItem * item,GSList * item_list)228 static void part_item_set_label_items (PartItem *item, GSList *item_list)
229 {
230 	PartItemPriv *priv;
231 
232 	g_return_if_fail (item != NULL);
233 	g_return_if_fail (IS_PART_ITEM (item));
234 
235 	priv = item->priv;
236 
237 	if (priv->label_items)
238 		g_slist_free (priv->label_items);
239 
240 	priv->label_items = item_list;
241 }
242 
part_item_moved(SheetItem * sheet_item)243 static void part_item_moved (SheetItem *sheet_item)
244 {
245 	//	g_warning ("part MOVED callback called - LEGACY");
246 }
247 
part_item_canvas_new(Sheet * sheet,Part * part)248 PartItem *part_item_canvas_new (Sheet *sheet, Part *part)
249 {
250 	PartItem *part_item;
251 	PartItemPriv *priv;
252 	GooCanvasItem *goo_item;
253 	ItemData *item_data;
254 
255 	g_return_val_if_fail (sheet != NULL, NULL);
256 	g_return_val_if_fail (IS_SHEET (sheet), NULL);
257 	g_return_val_if_fail (part != NULL, NULL);
258 	g_return_val_if_fail (IS_PART (part), NULL);
259 
260 	part_item = g_object_new (TYPE_PART_ITEM, NULL);
261 	goo_item = GOO_CANVAS_ITEM (part_item);
262 
263 	g_object_set (part_item, "parent", sheet->object_group, NULL);
264 
265 	g_object_set (part_item, "data", part, NULL);
266 
267 	priv = part_item->priv;
268 
269 	Coords b1, b2;
270 	item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2);
271 
272 	priv->rect = goo_canvas_rect_new (
273 	    goo_item, b1.x, b1.y, b2.x - b1.x, b2.y - b1.y, "stroke-color", "green", "line-width", .0,
274 	    "fill-color-rgba", 0x7733aa66, "radius-x", 1.0, "radius-y", 1.0, "visibility",
275 	    oregano_options_debug_boxes () ? GOO_CANVAS_ITEM_VISIBLE : GOO_CANVAS_ITEM_INVISIBLE, NULL);
276 
277 	priv->label_group = goo_canvas_group_new (goo_item, "width", -1.0, "height", -1.0, NULL);
278 
279 	priv->node_group = goo_canvas_group_new (goo_item, NULL);
280 
281 	g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
282 
283 	item_data = ITEM_DATA (part);
284 	item_data->rotated_handler_id =
285 	    g_signal_connect_object (part, "rotated", G_CALLBACK (part_rotated_callback), part_item, 0);
286 	item_data->flipped_handler_id =
287 	    g_signal_connect_object (part, "flipped", G_CALLBACK (part_flipped_callback), part_item, 0);
288 	item_data->moved_handler_id =
289 	    g_signal_connect_object (part, "moved", G_CALLBACK (part_moved_callback), part_item, 0);
290 	item_data->changed_handler_id =
291 	    g_signal_connect_object (part, "changed", G_CALLBACK (part_changed_callback), part_item, 0);
292 
293 	return part_item;
294 }
295 
update_canvas_labels(PartItem * item)296 static void update_canvas_labels (PartItem *item)
297 {
298 	PartItemPriv *priv;
299 	Part *part;
300 	GSList *labels, *label_items;
301 	GooCanvasItem *canvas_item;
302 
303 	g_return_if_fail (item != NULL);
304 	g_return_if_fail (IS_PART_ITEM (item));
305 
306 	priv = item->priv;
307 	part = PART (sheet_item_get_data (SHEET_ITEM (item)));
308 
309 	label_items = priv->label_items;
310 
311 	// Put the label of each item
312 	for (labels = part_get_labels (part); labels;
313 	     labels = labels->next, label_items = label_items->next) {
314 		char *text;
315 		PartLabel *label = (PartLabel *)labels->data;
316 		g_assert (label_items != NULL);
317 		canvas_item = label_items->data;
318 
319 		text = part_property_expand_macros (part, label->text);
320 		g_object_set (canvas_item, "text", text, NULL);
321 		g_free (text);
322 	}
323 }
324 
part_item_update_node_label(PartItem * item)325 void part_item_update_node_label (PartItem *item)
326 {
327 	PartItemPriv *priv;
328 	Part *part;
329 	GSList *labels;
330 	GooCanvasItem *canvas_item;
331 	Pin *pins;
332 	gint num_pins;
333 
334 	g_return_if_fail (item != NULL);
335 	g_return_if_fail (IS_PART_ITEM (item));
336 	priv = item->priv;
337 	part = PART (sheet_item_get_data (SHEET_ITEM (item)));
338 
339 	g_return_if_fail (IS_PART (part));
340 
341 	// Put the label of each node
342 	num_pins = part_get_num_pins (part);
343 
344 	if (num_pins == 1) {
345 		pins = part_get_pins (part);
346 		labels = priv->label_nodes;
347 		for (labels = priv->label_nodes; labels; labels = labels->next) {
348 			char *txt;
349 
350 			txt = g_strdup_printf ("V(%d)", pins[0].node_nr);
351 			canvas_item = labels->data;
352 			if (pins[0].node_nr != 0)
353 				g_object_set (canvas_item, "text", txt, "fill_color", LABEL_COLOR, "font", "Sans 8",
354 				              NULL);
355 			else
356 				g_object_set (canvas_item, "text", "", NULL);
357 
358 			g_free (txt);
359 		}
360 	}
361 }
362 
prop_dialog_destroy(GtkWidget * widget,PartPropDialog * prop_dialog)363 static void prop_dialog_destroy (GtkWidget *widget, PartPropDialog *prop_dialog)
364 {
365 	g_free (prop_dialog);
366 }
367 
prop_dialog_response(GtkWidget * dialog,gint response,PartPropDialog * prop_dialog)368 static void prop_dialog_response (GtkWidget *dialog, gint response, PartPropDialog *prop_dialog)
369 {
370 	GSList *props = NULL;
371 	GList *widget;
372 	Property *prop;
373 	PartItem *item;
374 	Part *part;
375 	gchar *prop_name;
376 	const gchar *prop_value;
377 	guint prop_value_length;
378 	gboolean valid_entry;
379 	GtkWidget *w;
380 
381 	item = prop_dialog->part_item;
382 
383 	part = PART (sheet_item_get_data (SHEET_ITEM (item)));
384 
385 	for (widget = prop_dialog->widgets; widget; widget = widget->next) {
386 		w = widget->data;
387 
388 		prop_name = g_object_get_data (G_OBJECT (w), "user");
389 		prop_value = gtk_entry_get_text (GTK_ENTRY (w));
390 
391 		for (props = part_get_properties (part); props; props = props->next) {
392 			prop = props->data;
393 			if (g_ascii_strcasecmp (prop->name, prop_name) == 0) {
394 				valid_entry = TRUE;
395 
396 				// Prevent invalid properties name entries
397 				if (g_ascii_strcasecmp (prop->name, "Refdes") == 0) {
398 					// Prevent invalid voltage source name entries
399 					if (prop->value[0] == 'V') {
400 						if (prop_value[0] != 'V')
401 							valid_entry = FALSE;
402 						prop_value_length = strlen (prop_value);
403 						for (int i = 1; i < prop_value_length; i++)
404 							if (prop_value[i] < '0' || prop_value[i] > '9')
405 								valid_entry = FALSE;
406 					}
407 				}
408 
409 				if (valid_entry) {
410 					g_free (prop->value);
411 					prop->value = g_strdup (prop_value);
412 				}
413 			}
414 		}
415 		g_free (prop_name);
416 	}
417 
418 	update_canvas_labels (item);
419 }
420 
edit_properties_point(PartItem * item)421 static void edit_properties_point (PartItem *item)
422 {
423 	GSList *properties;
424 	Part *part;
425 	GtkBuilder *gui;
426 	GError *error = NULL;
427 	GtkRadioButton *radio_v, *radio_c;
428 	GtkRadioButton *ac_r, *ac_m, *ac_i, *ac_p;
429 	GtkCheckButton *chk_db;
430 
431 	part = PART (sheet_item_get_data (SHEET_ITEM (item)));
432 
433 	if ((gui = gtk_builder_new ()) == NULL) {
434 		oregano_error (_ ("Could not create part properties dialog."));
435 		return;
436 	}
437 	gtk_builder_set_translation_domain (gui, NULL);
438 
439 	if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/clamp-properties-dialog.ui", &error) <= 0) {
440 		oregano_error_with_title (_ ("Could not create part properties dialog."), error->message);
441 		g_error_free (error);
442 		return;
443 	}
444 
445 	prop_dialog = g_new0 (PartPropDialog, 1);
446 
447 	prop_dialog->part_item = item;
448 
449 	prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, "clamp-properties-dialog"));
450 
451 	radio_v = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_v"));
452 	radio_c = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_c"));
453 
454 	gtk_widget_set_sensitive (GTK_WIDGET (radio_c), FALSE);
455 
456 	ac_r = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_r"));
457 	ac_m = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_m"));
458 	ac_p = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_p"));
459 	ac_i = GTK_RADIO_BUTTON (gtk_builder_get_object (gui, "radio_i"));
460 
461 	chk_db = GTK_CHECK_BUTTON (gtk_builder_get_object (gui, "check_db"));
462 
463 	// Setup GUI from properties
464 	for (properties = part_get_properties (part); properties; properties = properties->next) {
465 		Property *prop;
466 		prop = properties->data;
467 		if (prop->name) {
468 			if (!g_ascii_strcasecmp (prop->name, "internal"))
469 				continue;
470 
471 			if (!g_ascii_strcasecmp (prop->name, "type")) {
472 				if (!g_ascii_strcasecmp (prop->value, "v")) {
473 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_v), TRUE);
474 				} else {
475 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_c), TRUE);
476 				}
477 			} else if (!g_ascii_strcasecmp (prop->name, "ac_type")) {
478 				if (!g_ascii_strcasecmp (prop->value, "m")) {
479 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_m), TRUE);
480 				} else if (!g_ascii_strcasecmp (prop->value, "i")) {
481 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_i), TRUE);
482 				} else if (!g_ascii_strcasecmp (prop->value, "p")) {
483 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_p), TRUE);
484 				} else if (!g_ascii_strcasecmp (prop->value, "r")) {
485 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ac_r), TRUE);
486 				}
487 			} else if (!g_ascii_strcasecmp (prop->name, "ac_db")) {
488 				if (!g_ascii_strcasecmp (prop->value, "true"))
489 					gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (chk_db), TRUE);
490 			}
491 		}
492 	}
493 
494 	gtk_dialog_run (prop_dialog->dialog);
495 
496 	// Save properties from GUI
497 	for (properties = part_get_properties (part); properties; properties = properties->next) {
498 		Property *prop;
499 		prop = properties->data;
500 
501 		if (prop->name) {
502 			if (!g_ascii_strcasecmp (prop->name, "internal"))
503 				continue;
504 
505 			if (!g_ascii_strcasecmp (prop->name, "type")) {
506 				g_free (prop->value);
507 				if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_v))) {
508 					prop->value = g_strdup ("v");
509 				} else {
510 					prop->value = g_strdup ("i");
511 				}
512 			} else if (!g_ascii_strcasecmp (prop->name, "ac_type")) {
513 				g_free (prop->value);
514 				if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_m))) {
515 					prop->value = g_strdup ("m");
516 				} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_i))) {
517 					prop->value = g_strdup ("i");
518 				} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_p))) {
519 					prop->value = g_strdup ("p");
520 				} else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ac_r))) {
521 					prop->value = g_strdup ("r");
522 				}
523 			} else if (!g_ascii_strcasecmp (prop->name, "ac_db")) {
524 				g_free (prop->value);
525 				if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (chk_db)))
526 					prop->value = g_strdup ("true");
527 				else
528 					prop->value = g_strdup ("false");
529 			}
530 		}
531 	}
532 	gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog));
533 }
534 
edit_properties(SheetItem * object)535 static void edit_properties (SheetItem *object)
536 {
537 	GSList *properties;
538 	PartItem *item;
539 	Part *part;
540 	char *internal, *msg;
541 	GtkBuilder *gui;
542 	GError *error = NULL;
543 	GtkGrid *prop_grid;
544 	GtkNotebook *notebook;
545 	gint response, y = 0;
546 	gboolean has_model;
547 	gchar *model_name = NULL;
548 
549 	g_return_if_fail (object != NULL);
550 	g_return_if_fail (IS_PART_ITEM (object));
551 
552 	item = PART_ITEM (object);
553 	part = PART (sheet_item_get_data (SHEET_ITEM (item)));
554 
555 	internal = part_get_property (part, "internal");
556 	if (internal) {
557 		if (g_ascii_strcasecmp (internal, "ground") == 0) {
558 			g_free (internal);
559 			return;
560 		}
561 		if (g_ascii_strcasecmp (internal, "point") == 0) {
562 			edit_properties_point (item);
563 			return;
564 		}
565 	}
566 
567 	g_free (internal);
568 
569 	if ((gui = gtk_builder_new ()) == NULL) {
570 		oregano_error (_ ("Could not create part properties dialog."));
571 		return;
572 	} else
573 		gtk_builder_set_translation_domain (gui, NULL);
574 
575 	if (gtk_builder_add_from_file (gui, OREGANO_UIDIR "/part-properties-dialog.ui", &error) <= 0) {
576 		msg = error->message;
577 		oregano_error_with_title (_ ("Could not create part properties dialog."), msg);
578 		g_error_free (error);
579 		return;
580 	}
581 
582 	prop_dialog = g_new0 (PartPropDialog, 1);
583 
584 	prop_dialog->part_item = item;
585 
586 	prop_dialog->dialog = GTK_DIALOG (gtk_builder_get_object (gui, "part-properties-dialog"));
587 
588 	prop_grid = GTK_GRID (gtk_builder_get_object (gui, "prop_grid"));
589 	notebook = GTK_NOTEBOOK (gtk_builder_get_object (gui, "notebook"));
590 
591 	g_signal_connect (prop_dialog->dialog, "destroy", G_CALLBACK (prop_dialog_destroy),
592 	                  prop_dialog);
593 
594 	prop_dialog->widgets = NULL;
595 	has_model = FALSE;
596 
597 	for (properties = part_get_properties (part); properties; properties = properties->next) {
598 		Property *prop;
599 
600 		prop = properties->data;
601 
602 		if (prop->name) {
603 			GtkWidget *entry;
604 			GtkWidget *label;
605 			gchar *temp = NULL;
606 
607 			if (!g_ascii_strcasecmp (prop->name, "internal"))
608 				continue;
609 
610 			if (!g_ascii_strcasecmp (prop->name, "model")) {
611 				has_model = TRUE;
612 				model_name = g_strdup (prop->value);
613 			}
614 
615 			// Find the Refdes and replace by their real value
616 			temp = prop->name;
617 			if (!g_ascii_strcasecmp (temp, "Refdes"))
618 				temp = _ ("Designation");
619 			if (!g_ascii_strcasecmp (temp, "Template"))
620 				temp = _ ("Template");
621 			if (!g_ascii_strcasecmp (temp, "Res"))
622 				temp = _ ("Resistor");
623 			if (!g_ascii_strcasecmp (temp, "Cap"))
624 				temp = _ ("Capacitor");
625 			if (!g_ascii_strcasecmp (temp, "Ind"))
626 				temp = _ ("Inductor");
627 			label = gtk_label_new (temp);
628 
629 			entry = gtk_entry_new ();
630 			gtk_entry_set_text (GTK_ENTRY (entry), prop->value);
631 			g_object_set_data (G_OBJECT (entry), "user", g_strdup (prop->name));
632 
633 			gtk_grid_attach (prop_grid, label, 0, y, 1, 1);
634 
635 			gtk_grid_attach (prop_grid, entry, 1, y, 1, 1);
636 
637 			y++;
638 			gtk_widget_show (label);
639 			gtk_widget_show (entry);
640 
641 			prop_dialog->widgets = g_list_prepend (prop_dialog->widgets, entry);
642 		}
643 	}
644 
645 	if (!has_model) {
646 		gtk_notebook_remove_page (notebook, 1);
647 	} else {
648 		GtkTextBuffer *txtbuffer;
649 		GtkTextView *txtmodel;
650 		gchar *filename, *str;
651 		GError *read_error = NULL;
652 
653 		txtmodel = GTK_TEXT_VIEW (gtk_builder_get_object (gui, "txtmodel"));
654 		txtbuffer = gtk_text_buffer_new (NULL);
655 
656 		filename = g_strdup_printf ("%s/%s.model", OREGANO_MODELDIR, model_name);
657 		if (g_file_get_contents (filename, &str, NULL, &read_error)) {
658 			gtk_text_buffer_set_text (txtbuffer, str, -1);
659 			g_free (str);
660 		} else {
661 			gtk_text_buffer_set_text (txtbuffer, read_error->message, -1);
662 			g_error_free (read_error);
663 		}
664 
665 		g_free (filename);
666 		g_free (model_name);
667 
668 		gtk_text_view_set_buffer (txtmodel, txtbuffer);
669 	}
670 
671 	gtk_dialog_set_default_response (prop_dialog->dialog, 1);
672 
673 	response = gtk_dialog_run (prop_dialog->dialog);
674 
675 	prop_dialog_response (GTK_WIDGET (prop_dialog->dialog), response, prop_dialog);
676 
677 	gtk_widget_destroy (GTK_WIDGET (prop_dialog->dialog));
678 }
679 
angle_to_anchor(int angle)680 inline static GooCanvasAnchorType angle_to_anchor (int angle)
681 {
682 	GooCanvasAnchorType anchor;
683 	// Get the right anchor for the labels. This is needed since the
684 	// canvas doesn't know how to rotate text and since we rotate the
685 	// label_group instead of the labels directly.
686 
687 	while (angle < 0)
688 		angle += 360;
689 	angle %= 360;
690 
691 	if (90 - 45 < angle && angle < 90 + 45) {
692 		anchor = GOO_CANVAS_ANCHOR_NORTH_WEST;
693 	} else if (180 - 45 < angle && angle < 180 + 45) {
694 		anchor = GOO_CANVAS_ANCHOR_NORTH_EAST;
695 	} else if (270 - 45 < angle && angle < 270 + 45) {
696 		anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST;
697 	} else /* if (360-45 < angle && angle < 0+45) */ {
698 		anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST;
699 	}
700 
701 	return anchor;
702 }
703 
704 /**
705  * whenever the model changes, this one gets called to update the view
706  * representation
707  * @attention this recalculates the matrix every time, this makes sure no errors
708  * stack up
709  * @attention further reading on matrix manipulations
710  * @attention http://www.cairographics.org/matrix_transform/
711  * @param data the model item, a bare C struct derived from ItemData
712  * @param sheet_item the view item, derived from goo_canvas_group/item
713  */
part_changed_callback(ItemData * data,SheetItem * sheet_item)714 static void part_changed_callback (ItemData *data, SheetItem *sheet_item)
715 {
716 	g_return_if_fail (sheet_item != NULL);
717 	g_return_if_fail (IS_PART_ITEM (sheet_item));
718 
719 	// TODO add static vars in order to skip the redraw if nothing changed
720 	// TODO may happen once in a while and the check is really cheap
721 	PartItem *item = PART_ITEM (sheet_item);
722 	PartItemPriv *priv = item->priv;
723 
724 	// init the states
725 
726 	cairo_matrix_t morph, inv;
727 	cairo_status_t done;
728 
729 	inv = *(item_data_get_rotate (data)); // copy
730 	cairo_matrix_multiply (&morph, &inv, item_data_get_translate (data));
731 
732 	done = cairo_matrix_invert (&inv);
733 	if (done != CAIRO_STATUS_SUCCESS) {
734 		g_warning ("Failed to invert matrix. This should never happen. Ever!");
735 		return;
736 	}
737 	// no translations
738 	inv.y0 = inv.x0 = 0.;
739 
740 	goo_canvas_item_set_transform (GOO_CANVAS_ITEM (sheet_item), &(morph));
741 
742 	priv->cache_valid = FALSE;
743 	return; /* FIXME */
744 #if 0
745 	GooCanvasGroup *group = GOO_CANVAS_GROUP (item);
746 
747 	// rotate all items in the canvas group
748 	for (int index = 0; index < group->items->len; index++) {
749 		GooCanvasItem *canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]);
750 		goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), &morph);
751 	}
752 
753 	// revert the rotation of all labels and change their anchor to not overlap too badly
754 	// this assures that the text is always horizontal and properly aligned
755 	GooCanvasAnchorType anchor = angle_to_anchor (rotation);
756 
757 	for (GSList *iter = priv->label_items; iter; iter = iter->next) {
758 		g_object_set (iter->data,
759 		              "anchor", anchor,
760 		              NULL);
761 
762 		goo_canvas_item_set_transform (iter->data, &inv);
763 
764 	}
765 	// same for label nodes
766 	for (GSList *iter = priv->label_nodes; iter; iter = iter->next) {
767 		g_object_set (iter->data,
768 		              "anchor", anchor,
769 		              NULL);
770 
771 		goo_canvas_item_set_transform (iter->data, &inv);
772 	}
773 
774 
775 	// Invalidate the bounding box cache.
776 	priv->cache_valid = FALSE;
777 #endif
778 }
779 
780 /**
781  * a part got rotated
782  *
783  * @angle the angle the item is rotated towards the default (0) rotation
784  *
785  */
part_rotated_callback(ItemData * data,int angle,SheetItem * sheet_item)786 static void part_rotated_callback (ItemData *data, int angle, SheetItem *sheet_item)
787 {
788 	//	g_warning ("ROTATED callback called - LEGACY\n");
789 }
790 
791 /**
792  * handles the update of the canvas item when a part gets flipped (within the
793  * backend alias model)
794  * @data the part in form of a ItemData pointer
795  * @direction the new flip state
796  * @sheet_item the corresponding sheet_item to the model item @data
797  */
part_flipped_callback(ItemData * data,IDFlip direction,SheetItem * sheet_item)798 static void part_flipped_callback (ItemData *data, IDFlip direction, SheetItem *sheet_item)
799 {
800 	//	g_warning ("FLIPPED callback called - LEGACY\n");
801 }
802 
part_item_signal_connect_floating(PartItem * item)803 void part_item_signal_connect_floating (PartItem *item)
804 {
805 	Sheet *sheet;
806 
807 	sheet = sheet_item_get_sheet (SHEET_ITEM (item));
808 	sheet->state = SHEET_STATE_FLOAT_START;
809 
810 	g_signal_connect (G_OBJECT (item), "double_clicked", G_CALLBACK (edit_properties), item);
811 }
812 
selection_changed(PartItem * item,gboolean select,gpointer user_data)813 static void selection_changed (PartItem *item, gboolean select, gpointer user_data)
814 {
815 	g_object_ref (G_OBJECT (item));
816 	if (select)
817 		g_idle_add ((gpointer)select_idle_callback, item);
818 	else
819 		g_idle_add ((gpointer)deselect_idle_callback, item);
820 }
821 
select_idle_callback(PartItem * item)822 static int select_idle_callback (PartItem *item)
823 {
824 	GooCanvasItem *canvas_item = NULL;
825 	int index;
826 
827 	g_return_val_if_fail (item != NULL, FALSE);
828 
829 	for (index = 0; index < GOO_CANVAS_GROUP (item)->items->len; index++) {
830 		canvas_item = GOO_CANVAS_ITEM (GOO_CANVAS_GROUP (item)->items->pdata[index]);
831 		g_object_set (canvas_item, "stroke-color", SELECTED_COLOR, NULL);
832 	}
833 	g_object_unref (G_OBJECT (item));
834 	return FALSE;
835 }
836 
deselect_idle_callback(PartItem * item)837 static int deselect_idle_callback (PartItem *item)
838 {
839 	GooCanvasItem *canvas_item = NULL;
840 	int index;
841 
842 	for (index = 0; index < GOO_CANVAS_GROUP (item)->items->len; index++) {
843 		canvas_item = GOO_CANVAS_ITEM (GOO_CANVAS_GROUP (item)->items->pdata[index]);
844 
845 		if (GOO_IS_CANVAS_TEXT (canvas_item)) {
846 			g_object_set (canvas_item, "stroke-color", LABEL_COLOR, NULL);
847 		} else {
848 			g_object_set (canvas_item, "stroke-color", NORMAL_COLOR, NULL);
849 		}
850 	}
851 	g_object_unref (G_OBJECT (item));
852 	return FALSE;
853 }
854 
is_in_area(SheetItem * object,Coords * p1,Coords * p2)855 static gboolean is_in_area (SheetItem *object, Coords *p1, Coords *p2)
856 {
857 	PartItem *item;
858 	Coords bbox_start, bbox_end;
859 
860 	item = PART_ITEM (object);
861 
862 	get_cached_bounds (item, &bbox_start, &bbox_end);
863 
864 	if ((p1->x < bbox_start.x) && (p2->x > bbox_end.x) && (p1->y < bbox_start.y) &&
865 	    (p2->y > bbox_end.y)) {
866 		return TRUE;
867 	}
868 	return FALSE;
869 }
870 
show_labels(SheetItem * sheet_item,gboolean show)871 static void show_labels (SheetItem *sheet_item, gboolean show)
872 {
873 	PartItem *item;
874 	PartItemPriv *priv;
875 
876 	g_return_if_fail (sheet_item != NULL);
877 	g_return_if_fail (IS_PART_ITEM (sheet_item));
878 
879 	item = PART_ITEM (sheet_item);
880 	priv = item->priv;
881 
882 	if (show)
883 		g_object_set (priv->label_group, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
884 	else
885 		g_object_set (priv->label_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
886 }
887 
888 // Retrieves the bounding box. We use a caching scheme for this
889 // since it's too expensive to calculate it every time we need it.
get_cached_bounds(PartItem * item,Coords * p1,Coords * p2)890 inline static void get_cached_bounds (PartItem *item, Coords *p1, Coords *p2)
891 {
892 	PartItemPriv *priv;
893 	priv = item->priv;
894 
895 	if (G_LIKELY (priv->cache_valid)) {
896 		*p1 = priv->bbox_start;
897 		*p2 = priv->bbox_end;
898 	} else {
899 		Coords start_pos, end_pos;
900 		GooCanvasBounds bounds;
901 
902 		goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (item), &bounds);
903 
904 		start_pos.x = bounds.x1;
905 		start_pos.y = bounds.y1;
906 		end_pos.x = bounds.x2;
907 		end_pos.y = bounds.y2;
908 
909 		*p1 = priv->bbox_start = start_pos;
910 		*p2 = priv->bbox_end = end_pos;
911 		priv->cache_valid = TRUE;
912 	}
913 }
914 
part_item_paste(Sheet * sheet,ItemData * data)915 static void part_item_paste (Sheet *sheet, ItemData *data)
916 {
917 	g_return_if_fail (sheet != NULL);
918 	g_return_if_fail (IS_SHEET (sheet));
919 	g_return_if_fail (data != NULL);
920 	g_return_if_fail (IS_PART (data));
921 
922 	sheet_add_ghost_item (sheet, data);
923 }
924 
part_item_new(Sheet * sheet,Part * part)925 PartItem *part_item_new (Sheet *sheet, Part *part)
926 {
927 	Library *library;
928 	LibraryPart *library_part;
929 	PartPriv *priv;
930 	PartItem *item;
931 
932 	priv = part->priv;
933 
934 	library = priv->library;
935 	library_part = library_get_part (library, priv->name);
936 
937 	// Create the PartItem canvas item
938 	item = part_item_canvas_new (sheet, part);
939 	create_canvas_items (GOO_CANVAS_GROUP (item), library_part);
940 	create_canvas_labels (item, part);
941 	create_canvas_label_nodes (item, part);
942 	goo_canvas_item_ensure_updated (GOO_CANVAS_ITEM (item));
943 	return item;
944 }
945 
part_item_create_canvas_items_for_preview(GooCanvasGroup * group,LibraryPart * library_part)946 void part_item_create_canvas_items_for_preview (GooCanvasGroup *group, LibraryPart *library_part)
947 {
948 	g_return_if_fail (group != NULL);
949 	g_return_if_fail (library_part != NULL);
950 
951 	create_canvas_items (group, library_part);
952 }
953 
create_canvas_items(GooCanvasGroup * group,LibraryPart * library_part)954 static void create_canvas_items (GooCanvasGroup *group, LibraryPart *library_part)
955 {
956 	GooCanvasItem *item;
957 	GooCanvasPoints *points;
958 	GSList *objects;
959 	LibrarySymbol *symbol;
960 	SymbolObject *object;
961 	gdouble height, width;
962 	GooCanvasBounds bounds, group_bounds = {0, 0, 0, 0};
963 
964 	g_return_if_fail (group != NULL);
965 	g_return_if_fail (library_part != NULL);
966 
967 	symbol = library_get_symbol (library_part->symbol_name);
968 	if (symbol == NULL) {
969 		g_warning ("Couldn't find the requested symbol %s for part %s in "
970 		           "library.\n",
971 		           library_part->symbol_name, library_part->name);
972 		return;
973 	}
974 
975 	for (objects = symbol->symbol_objects; objects; objects = objects->next) {
976 		object = (SymbolObject *)(objects->data);
977 		switch (object->type) {
978 		case SYMBOL_OBJECT_LINE:
979 			points = object->u.uline.line;
980 			item = goo_canvas_polyline_new (GOO_CANVAS_ITEM (group), FALSE, 0, "points", points,
981 			                                "stroke-color", NORMAL_COLOR, "line-width", 0.5, NULL);
982 			if (object->u.uline.spline) {
983 				g_object_set (item, "smooth", TRUE, "spline_steps", 5, NULL);
984 			}
985 			break;
986 		case SYMBOL_OBJECT_ARC:
987 			item = goo_canvas_ellipse_new (GOO_CANVAS_ITEM (group),
988 			                               (object->u.arc.x2 + object->u.arc.x1) / 2.0,
989 			                               (object->u.arc.y1 + object->u.arc.y2) / 2.0,
990 			                               (object->u.arc.x2 - object->u.arc.x1) / 2.0,
991 			                               (object->u.arc.y1 - object->u.arc.y2) / 2.0,
992 			                               "stroke-color", NORMAL_COLOR, "line_width", 1.0, NULL);
993 			break;
994 		case SYMBOL_OBJECT_TEXT:
995 			item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), object->u.text.str,
996 			                            (double)object->u.text.x, (double)object->u.text.y, -1,
997 			                            GOO_CANVAS_ANCHOR_NORTH_EAST, "fill_color", LABEL_COLOR,
998 			                            "font", "Sans 8", NULL);
999 			break;
1000 		default:
1001 			g_warning ("Unknown symbol object.\n");
1002 			continue;
1003 		}
1004 		goo_canvas_item_get_bounds (item, &bounds);
1005 		if (group_bounds.x1 > bounds.x1)
1006 			group_bounds.x1 = bounds.x1;
1007 		if (group_bounds.x2 < bounds.x2)
1008 			group_bounds.x2 = bounds.x2;
1009 		if (group_bounds.y1 > bounds.y1)
1010 			group_bounds.y1 = bounds.y1;
1011 		if (group_bounds.y2 < bounds.y2)
1012 			group_bounds.y2 = bounds.y2;
1013 	}
1014 
1015 	g_object_get (group, "width", &width, "height", &height, NULL);
1016 	width = group_bounds.x2 - group_bounds.x1;
1017 	height = group_bounds.y2 - group_bounds.y1;
1018 
1019 	g_object_set (group, "width", width, "height", height, NULL);
1020 }
1021 
create_canvas_labels(PartItem * item,Part * part)1022 static void create_canvas_labels (PartItem *item, Part *part)
1023 {
1024 	GooCanvasItem *canvas_item;
1025 	GSList *list, *item_list;
1026 	GooCanvasGroup *group;
1027 
1028 	g_return_if_fail (item != NULL);
1029 	g_return_if_fail (IS_PART_ITEM (item));
1030 	g_return_if_fail (part != NULL);
1031 	g_return_if_fail (IS_PART (part));
1032 
1033 	group = GOO_CANVAS_GROUP (item->priv->label_group);
1034 	item_list = NULL;
1035 
1036 	for (list = part_get_labels (part); list; list = list->next) {
1037 		PartLabel *label = list->data;
1038 		char *text;
1039 
1040 		text = part_property_expand_macros (part, label->text);
1041 
1042 		canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), text, (double)label->pos.x,
1043 		                                   (double)label->pos.y, 0, GOO_CANVAS_ANCHOR_SOUTH_WEST,
1044 		                                   "fill_color", LABEL_COLOR, "font", "Sans 8", NULL);
1045 
1046 		item_list = g_slist_prepend (item_list, canvas_item);
1047 		g_free (text);
1048 	}
1049 
1050 	item_list = g_slist_reverse (item_list);
1051 	part_item_set_label_items (item, item_list);
1052 }
1053 
create_canvas_label_nodes(PartItem * item,Part * part)1054 static void create_canvas_label_nodes (PartItem *item, Part *part)
1055 {
1056 	GooCanvasItem *canvas_item;
1057 	GSList *item_list;
1058 	GooCanvasItem *group;
1059 	Pin *pins;
1060 	int num_pins, i;
1061 	Coords p1, p2;
1062 	GooCanvasAnchorType anchor;
1063 
1064 	g_return_if_fail (item != NULL);
1065 	g_return_if_fail (IS_PART_ITEM (item));
1066 	g_return_if_fail (part != NULL);
1067 	g_return_if_fail (IS_PART (part));
1068 
1069 	num_pins = part_get_num_pins (part);
1070 	pins = part_get_pins (part);
1071 	group = item->priv->node_group;
1072 	item_list = NULL;
1073 
1074 	get_cached_bounds (item, &p1, &p2);
1075 
1076 	switch (part_get_rotation (part)) {
1077 	case 0:
1078 		anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST;
1079 		break;
1080 	case 90:
1081 		anchor = GOO_CANVAS_ANCHOR_NORTH_WEST;
1082 		break;
1083 	case 180:
1084 		anchor = GOO_CANVAS_ANCHOR_NORTH_EAST;
1085 		break;
1086 	case 270:
1087 		anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST;
1088 		break;
1089 	default:
1090 		anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST;
1091 	}
1092 
1093 	for (i = 0; i < num_pins; i++) {
1094 		int x, y;
1095 		char *text;
1096 		x = pins[i].offset.x;
1097 		y = pins[i].offset.y;
1098 
1099 		text = g_strdup_printf ("%d", pins[i].node_nr);
1100 		canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), text, (double)x, (double)y, 0,
1101 		                                   anchor, "fill_color", "black", "font", "Sans 8", NULL);
1102 		// Shift slightly the label for a Voltmeter
1103 		if (i == 0)
1104 			goo_canvas_item_translate (canvas_item, -15.0, -10.0);
1105 
1106 		item_list = g_slist_prepend (item_list, canvas_item);
1107 		g_free (text);
1108 	}
1109 	item_list = g_slist_reverse (item_list);
1110 	item->priv->label_nodes = item_list;
1111 }
1112 
1113 // This is called when the part data was moved. Update the view accordingly.
part_moved_callback(ItemData * data,Coords * pos,SheetItem * item)1114 static void part_moved_callback (ItemData *data, Coords *pos, SheetItem *item) {}
1115 
part_item_place(SheetItem * item,Sheet * sheet)1116 static void part_item_place (SheetItem *item, Sheet *sheet)
1117 {
1118 	g_signal_connect (G_OBJECT (item), "button_press_event", G_CALLBACK (sheet_item_event), sheet);
1119 
1120 	g_signal_connect (G_OBJECT (item), "button_release_event", G_CALLBACK (sheet_item_event),
1121 	                  sheet);
1122 
1123 	g_signal_connect (G_OBJECT (item), "motion_notify_event", G_CALLBACK (sheet_item_event), sheet);
1124 
1125 	g_signal_connect (G_OBJECT (item), "key_press_event", G_CALLBACK (sheet_item_event), sheet);
1126 
1127 	g_signal_connect (G_OBJECT (item), "double_clicked", G_CALLBACK (edit_properties), item);
1128 }
1129 
part_item_place_ghost(SheetItem * item,Sheet * sheet)1130 static void part_item_place_ghost (SheetItem *item, Sheet *sheet)
1131 {
1132 	//	part_item_signal_connect_placed (PART_ITEM (item));
1133 }
1134 
part_item_show_node_labels(PartItem * part,gboolean show)1135 void part_item_show_node_labels (PartItem *part, gboolean show)
1136 {
1137 	PartItemPriv *priv;
1138 
1139 	priv = part->priv;
1140 
1141 	if (show)
1142 		g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
1143 	else
1144 		g_object_set (priv->node_group, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
1145 }
1146 
part_item_get_anchor_from_part(Part * part)1147 static GooCanvasAnchorType part_item_get_anchor_from_part (Part *part)
1148 {
1149 	int anchor_h, anchor_v;
1150 	int angle;
1151 	IDFlip flip;
1152 
1153 	flip = part_get_flip (part);
1154 	angle = part_get_rotation (part);
1155 
1156 	switch (angle) {
1157 	case 0:
1158 		anchor_h = ANCHOR_SOUTH;
1159 		anchor_v = ANCHOR_WEST;
1160 		break;
1161 	case 90:
1162 		anchor_h = ANCHOR_NORTH;
1163 		anchor_v = ANCHOR_WEST;
1164 		// Invert Rotation
1165 		if (flip & ID_FLIP_HORIZ)
1166 			flip = ID_FLIP_VERT;
1167 		else if (flip & ID_FLIP_VERT)
1168 			flip = ID_FLIP_HORIZ;
1169 		break;
1170 	}
1171 
1172 	if (flip & ID_FLIP_HORIZ) {
1173 		anchor_v = ANCHOR_EAST;
1174 	}
1175 	if (flip & ID_FLIP_VERT) {
1176 		anchor_h = ANCHOR_NORTH;
1177 	}
1178 
1179 	if ((anchor_v == ANCHOR_EAST) && (anchor_h == ANCHOR_NORTH))
1180 		return GOO_CANVAS_ANCHOR_NORTH_EAST;
1181 	if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_NORTH))
1182 		return GOO_CANVAS_ANCHOR_NORTH_WEST;
1183 	if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_SOUTH))
1184 		return GOO_CANVAS_ANCHOR_SOUTH_WEST;
1185 
1186 	return GOO_CANVAS_ANCHOR_SOUTH_EAST;
1187 }
1188