static GooCanvasAnchorType 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 GOO_CANVAS_ANCHOR_NORTH_EAST; if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_NORTH)) return GOO_CANVAS_ANCHOR_NORTH_WEST; if ((anchor_v == ANCHOR_WEST) && (anchor_h == ANCHOR_SOUTH)) return GOO_CANVAS_ANCHOR_SOUTH_WEST; return GOO_CANVAS_ANCHOR_SOUTH_EAST; }
static void part_changed (ItemData *data) { Part *part; Coords loc = {0., 0.}; int angle = 0; IDFlip flip = ID_FLIP_NONE; g_return_if_fail (IS_PART (data)); part = (Part *)data; flip = part_get_flip (part); angle = part_get_rotation (part); item_data_get_pos (data, &loc); #if 0 //FIXME isn't it more sane to just emit the changed? g_signal_emit_by_name (data, "moved", &loc); g_signal_emit_by_name (data, "flipped", flip); g_signal_emit_by_name (data, "rotated", angle); #endif g_signal_emit_by_name (data, "changed"); }
static void part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) { GSList *objects, *labels; SymbolObject *object; LibrarySymbol *symbol; double x0, y0; int i, rotation; Part *part; PartPriv *priv; Coords pos; IDFlip flip; GooCanvasPoints *line; g_return_if_fail (data != NULL); g_return_if_fail (IS_PART (data)); part = PART (data); priv = part->priv; symbol = library_get_symbol (priv->symbol_name); if (symbol == NULL) { return; } item_data_get_pos (ITEM_DATA (part), &pos); x0 = pos.x; y0 = pos.y; cairo_save (cr); gdk_cairo_set_source_rgba (cr, &ctx->colors.components); rotation = part_get_rotation (part); flip = part_get_flip (part); if ((flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT)) rotation += 180; else if (flip == ID_FLIP_HORIZ) cairo_scale (cr, -1, 1); else if (flip == ID_FLIP_VERT) cairo_scale (cr, 1, -1); if (rotation %= 360) cairo_rotate (cr, rotation*M_PI/180); for (objects = symbol->symbol_objects; objects; objects = objects->next) { object = (SymbolObject *)(objects->data); switch (object->type) { case SYMBOL_OBJECT_LINE: line = object->u.uline.line; for (i = 0; i < line->num_points; i++) { double x, y; x = line->coords[i * 2]; y = line->coords[i * 2 + 1]; if (i == 0) cairo_move_to (cr, x0 + x, y0 + y); else cairo_line_to (cr, x0 + x, y0 + y); } break; case SYMBOL_OBJECT_ARC: { gdouble x1 = object->u.arc.x1; gdouble y1 = object->u.arc.y1; gdouble x2 = object->u.arc.x2; gdouble y2 = object->u.arc.y2; gdouble width, height, x, y; x = (x2 + x1) / 2; y = (y2 + y1) / 2; width = x2 - x1; height = y2 - y1; cairo_save (cr); cairo_translate (cr, x0 + x, y0 + y); cairo_scale (cr, width / 2.0, height / 2.0); cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI); cairo_restore (cr); } break; default: g_warning ( "Print part: Part %s contains unknown object.", priv->name ); continue; } cairo_stroke (cr); } // We don't want to rotate labels text, only the (x,y) coordinate gdk_cairo_set_source_rgba (cr, &ctx->colors.labels); for (labels = part_get_labels (part); labels; labels = labels->next) { gdouble x, y; PartLabel *label = (PartLabel *)labels->data; gchar *text; /* gint text_width, text_height; */ x = label->pos.x + x0; y = label->pos.y + y0; text = part_property_expand_macros (part, label->text); /* Align the label. switch (rotation) { case 90: y += text_height*opc->scale; break; case 180: break; case 270: x -= text_width*opc->scale; break; case 0: default: break; } */ cairo_save (cr); cairo_move_to (cr, x, y); cairo_show_text (cr, text); cairo_restore (cr); g_free (text); } cairo_restore (cr); }
/** * whenever the model changes, this one gets called to update the view representation * @attention this recalculates the matrix every time, this makes sure no errors stack up * @attention further reading on matrix manipulations * @attention http://www.cairographics.org/matrix_transform/ * @param data the model item, a bare C struct derived from ItemData * @param sheet_item the view item, derived from goo_canvas_group/item */ static void part_changed_callback (ItemData *data, SheetItem *sheet_item) { //TODO add static vars in order to skip the redraw if nothing changed //TODO may happen once in a while and the check is really cheap GSList *iter; GooCanvasAnchorType anchor; GooCanvasGroup *group; GooCanvasItem *canvas_item; PartItem *item; PartItemPriv *priv; Part *part; int index = 0; Coords pos; double scale_h, scale_v; // states int rotation; IDFlip flip; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_PART_ITEM (sheet_item)); item = PART_ITEM (sheet_item); group = GOO_CANVAS_GROUP (item); part = PART (data); priv = item->priv; // init the states flip = part_get_flip (part); rotation = part_get_rotation (part); DEGSANITY (rotation); scale_h = (flip & ID_FLIP_HORIZ) ? -1. : 1.; scale_v = (flip & ID_FLIP_VERT) ? -1. : 1.; item_data_get_pos (data, &pos); // Move the canvas item and invalidate the bbox cache. goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (sheet_item), pos.x, pos.y, 1.0, 0.0); cairo_matrix_t morph, inv; cairo_status_t done; cairo_matrix_init_rotate (&morph, DEG2RAD (rotation)); cairo_matrix_scale (&morph, scale_h, scale_v); inv = morph; done = cairo_matrix_invert (&inv); if (done != CAIRO_STATUS_SUCCESS) { g_warning ("Failed to invert matrix. This should never happen. Never!"); return; } // rotate all items in the canvas group for (index = 0; index < group->items->len; index++) { canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), &morph); } // revert the rotation of all labels and change their anchor to not overlap too badly // this assures that the text is always horizontal and properly aligned anchor = angle_to_anchor (rotation); for (iter = priv->label_items; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // same for label nodes for (iter = priv->label_nodes; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // Invalidate the bounding box cache. priv->cache_valid = FALSE; }
static void create_canvas_label_nodes (PartItem *item, Part *part) { GooCanvasItem *canvas_item; GSList *item_list; GooCanvasItem *group; Pin *pins; int num_pins, i; Coords p1, p2; GooCanvasAnchorType anchor; g_return_if_fail (item != NULL); g_return_if_fail (IS_PART_ITEM (item)); g_return_if_fail (part != NULL); 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); switch (part_get_rotation (part)) { case 0: anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; break; case 90: anchor = GOO_CANVAS_ANCHOR_NORTH_WEST; break; case 180: anchor = GOO_CANVAS_ANCHOR_NORTH_EAST; break; case 270: anchor = GOO_CANVAS_ANCHOR_SOUTH_EAST; break; default: anchor = GOO_CANVAS_ANCHOR_SOUTH_WEST; } for (i = 0; i < num_pins; i++) { int x, y; char *text; x = pins[i].offset.x; y = pins[i].offset.y; text = g_strdup_printf ("%d", pins[i].node_nr); canvas_item = goo_canvas_text_new (GOO_CANVAS_ITEM (group), text, (double) x, (double) y, 0, anchor, "fill_color", "black", "font", "Sans 8", NULL); // Shift slightly the label for a Voltmeter if (i == 0) goo_canvas_item_translate (canvas_item, -15.0, -10.0); item_list = g_slist_prepend (item_list, canvas_item); g_free (text); } item_list = g_slist_reverse (item_list); item->priv->label_nodes = item_list; }
static void write_xml_part (Part *part, parseXmlContext *ctxt) { PartPriv *priv; xmlNodePtr node_part; gchar *str; Coords pos; g_return_if_fail (part!=NULL); g_return_if_fail (IS_PART(part)); priv = part->priv; // Create a node for the part. node_part = xmlNewChild (ctxt->node_parts, ctxt->ns, BAD_CAST "part", NULL); if (!node_part) { g_warning ("Failed during save of part %s.\n", priv->name); return; } str = g_strdup_printf ("%d", part_get_rotation (part)); xmlNewChild (node_part, ctxt->ns, BAD_CAST "rotation", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str)); g_free (str); if (priv->flip & ID_FLIP_HORIZ) xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "horizontal")); if (priv->flip & ID_FLIP_VERT) xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "vertical")); // Store the name. xmlNewChild (node_part, ctxt->ns, BAD_CAST "name", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->name)); // Store the name of the library the part resides in. xmlNewChild (node_part, ctxt->ns, BAD_CAST "library", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->library->name)); // Which symbol to use. xmlNewChild (node_part, ctxt->ns, BAD_CAST "symbol", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->symbol_name)); // Position. item_data_get_pos (ITEM_DATA (part), &pos); str = g_strdup_printf ("(%g %g)", pos.x, pos.y); xmlNewChild (node_part, ctxt->ns, BAD_CAST "position", BAD_CAST str); g_free (str); // Create a node for the properties. ctxt->node_props = xmlNewChild (node_part, ctxt->ns, BAD_CAST "properties", NULL); if (!ctxt->node_props) { g_warning ("Failed during save of part %s.\n", priv->name); return; } else{ g_slist_foreach (priv->properties, (GFunc) write_xml_property, ctxt); } // Create a node for the labels. ctxt->node_labels = xmlNewChild (node_part, ctxt->ns, BAD_CAST "labels", NULL); if (!ctxt->node_labels) { g_warning ("Failed during save of part %s.\n", priv->name); return; } else{ g_slist_foreach (priv->labels, (GFunc) write_xml_label, ctxt); } }