/* * COPYRIGHT * * cschem - modular/flexible schematics editor - sch-rnd (executable) * Copyright (C) 2020,2022 Tibor 'Igor2' Palinkas * * (Supported by NLnet NGI0 PET Fund in 2022) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ #include "config.h" #include #include #include #include #include "concrete.h" #include "cnc_grp.h" #include "cnc_pen.h" #include "cnc_text.h" #include "cnc_any_obj.h" #include "cnc_obj.h" #include "cnc_conn.h" #include "operation.h" #include "op_common.h" #include "util_wirenet.h" #include "util_grp.h" /* cschem convention for minimum terminal grid */ #define DEF_TERM_GRID 4000.0 /* Find and return the bottom floater text object */ static csch_text_t *bottom_floater(const csch_cgrp_t *grp) { htip_entry_t *e; csch_text_t *best = NULL; csch_coord_t besty = CSCH_COORD_MAX; for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) { csch_text_t *t = e->value; if ((t->hdr.type != CSCH_CTYPE_TEXT) || !t->hdr.floater) continue; if (t->spec1.y < besty) { besty = t->spec1.y; best = t; } } return best; } /* look at grp and fill in x1;y1 where the next floater dyntext should be placed */ static void floater_placement_below(const csch_cgrp_t *grp, const csch_cpen_t *pen, csch_coord_t *x1, csch_coord_t *y1) { csch_text_t *bottom; bottom = bottom_floater(grp); if (bottom != NULL) { csch_coord_t step = pen->font_height; step = ceil(step / DEF_TERM_GRID) * DEF_TERM_GRID; *x1 = bottom->spec1.x; *y1 = bottom->spec1.y - step; } else { /* first floater; start placing at the center */ *x1 = floor(((grp->hdr.bbox.x1 + grp->hdr.bbox.x2) / 2.0 - grp->x) / DEF_TERM_GRID) * DEF_TERM_GRID; *y1 = floor(((grp->hdr.bbox.y1 + grp->hdr.bbox.y2) / 2.0 - grp->y) / DEF_TERM_GRID) * DEF_TERM_GRID; } } static void floater_placement(const csch_cgrp_t *grp, const csch_cpen_t *pen, csch_coord_t *x1, csch_coord_t *y1, csch_coord_t *hint_xy) { switch(grp->role) { case CSCH_ROLE_WIRE_NET: if (hint_xy != NULL) { *x1 = hint_xy[0]; *y1 = hint_xy[1]; return; } break; default: break; } /* fallback: place at grp center or below last */ floater_placement_below(grp, pen, x1, y1); } /* Heuristics to decide default pen preferences */ static const char **def_pen_names(csch_cgrp_t *grp, const char *key) { switch(grp->role) { case CSCH_ROLE_SYMBOL: case CSCH_ROLE_EXTOBJ_GFX: { static const char *def_pen_sym[] = {"sym-decor", "sheet-decor", NULL}; return def_pen_sym; } break; case CSCH_ROLE_WIRE_NET: case CSCH_ROLE_BUS_NET: case CSCH_ROLE_HUB_POINT: { static const char *def_pen_wire[] = {"wire", "sheet-decor", NULL}; return def_pen_wire; } break; case CSCH_ROLE_JUNCTION: { static const char *def_pen_junc[] = {"junction", "wire", "sheet-decor", NULL}; return def_pen_junc; } break; case CSCH_ROLE_BUS_TERMINAL: case CSCH_ROLE_TERMINAL: { static const char *def_pen_term[] = {"term-primary", "sym-decor", "sheet-decor", NULL}; return def_pen_term; } break; case CSCH_ROLE_invalid: case CSCH_ROLE_empty: break; } /* final fallback */ { static const char *def_pen_any[] = {"sheet-decor", NULL}; return def_pen_any; } } csch_text_t *csch_auto_attr_place(csch_sheet_t *sheet, csch_cgrp_t *grp, const char *key, const char *stroke, const char *atempl, csch_coord_t *hint_xy) { csch_text_t *text; csch_cpen_t *pen = NULL; const char **pn, **opn; csch_coord_t x1, y1; if (stroke != NULL) pen = csch_pen_resolve(grp, stroke); if (pen == NULL) { for(opn = pn = def_pen_names(grp, key); (pen == NULL) && (*pn != NULL); pn++) { pen = csch_pen_resolve(grp, *pn); if ((pen != NULL) && (pen->font_height <= 0)) pen = NULL; } } if (pen == NULL) { rnd_message(RND_MSG_ERROR, "Can't place floater: no pen found\n; please define one of the following pens:"); for(pn = opn; *pn != NULL; pn++) rnd_message(RND_MSG_ERROR, " %s", *pn); rnd_message(RND_MSG_ERROR, "\n"); return 0; } if (hint_xy != NULL) csch_cgrp_inverse_xform(grp, &hint_xy[0], &hint_xy[1], 1); floater_placement(grp, pen, &x1, &y1, hint_xy); text = (csch_text_t *)csch_op_create(sheet, grp, CSCH_CTYPE_TEXT); text->spec1.x = x1; text->spec1.y = y1; if (atempl != NULL) text->text = rnd_strdup(atempl); else text->text = rnd_strdup_printf("%%../A.%s%%", key); text->hdr.stroke_name = csch_comm_str(sheet, pen->name.str, 1); text->hdr.floater = 1; text->dyntext = 1; csch_text_update(sheet, text, 1); csch_cobj_redraw(text); return text; } csch_text_t *csch_auto_attr_create(csch_sheet_t *sheet, csch_cgrp_t *grp, const char *key, const char *val, const char *stroke, const char *atempl) { csch_source_arg_t *src; src = csch_attrib_src_c(NULL, 0, 0, NULL); csch_cobj_attrib_set(sheet, grp, CSCH_ATP_USER_DEFAULT, key, val, src); if (stroke != NULL) return csch_auto_attr_place(sheet, grp, key, stroke, atempl, NULL); return NULL; } /* Rename object pen references within the group using regex subst; this can be used to rename sheet-decor pens to sym-decor. */ static void grp_pen_rename(csch_sheet_t *sheet, csch_cgrp_t *grp, const char *regex, const char *subst) { htip_entry_t *e; re_sei_t *rx = re_sei_comp(regex); for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) { csch_chdr_t *obj = e->value; if ((obj->stroke_name.str != NULL) && re_sei_exec(rx, obj->stroke_name.str)) { char *dst; if (re_sei_subst(rx, &dst, obj->stroke_name.str, subst, 0)) { csch_comm_str_t stroke_name = csch_comm_str(sheet, dst, 1); csch_chdr_pen_name_modify(sheet, obj, &stroke_name, NULL, 1); } } if ((obj->fill_name.str != NULL) && re_sei_exec(rx, obj->fill_name.str)) { char *dst; if (re_sei_subst(rx, &dst, obj->fill_name.str, subst, 0)) { csch_comm_str_t fill_name = csch_comm_str(sheet, dst, 1); csch_chdr_pen_name_modify(sheet, obj, NULL, &fill_name, 1); } } } re_sei_free(rx); } void csch_cgrp_update_dsply_recurse(csch_sheet_t *sheet, csch_cgrp_t *grp) { htip_entry_t *e; csch_cobj_update_dsply(sheet, &grp->hdr); for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) { csch_chdr_t *obj = e->value; if (csch_obj_is_grp(obj)) csch_cgrp_update_dsply_recurse(sheet, (csch_cgrp_t *)obj); else csch_cobj_update_dsply(sheet, obj); } } void csch_cgrp_role_side_effects(csch_sheet_t *sheet, csch_cgrp_t *grp, csch_role_t role) { csch_text_t *t; static int zero = 0; /* side effects for advanced groups */ switch(role) { case CSCH_ROLE_SYMBOL: csch_auto_attr_create(sheet, grp, "role", "symbol", NULL, NULL); csch_auto_attr_create(sheet, grp, "name", "REFDES", "sym-primary", NULL); grp_pen_rename(sheet, grp, "^sheet-", "sym-"); break; case CSCH_ROLE_TERMINAL: csch_auto_attr_create(sheet, grp, "role", "terminal", NULL, NULL); t = csch_auto_attr_create(sheet, grp, "name", "TERMNAME", "term-primary", "%../a.display/name%"); grp_pen_rename(sheet, grp, "^sheet-", "term-"); csch_commprp_modify(sheet, &t->hdr, NULL, &zero, 1); break; default: break; } csch_cgrp_update_dsply_recurse(sheet, grp); } static void csch_cgrp_replace_attr(csch_sheet_t *sheet, csch_cgrp_t *new, csch_cgrp_t *dst, csch_cgrp_t *src, csch_cgrp_replace_how_t how) { if (how & CSCH_REPLGRP_KEEP_DST_ATTR) { htsp_entry_t *e, *already; for(e = htsp_first(&dst->attr); e != NULL; e = htsp_next(&dst->attr, e)) { if (how & CSCH_REPLGRP_PREFER_DST_ATTR) already = NULL; else already = htsp_getentry(&new->attr, e->key); if (already == NULL) { csch_attrib_t *newa = csch_attrib_dup(e->value); htsp_set(&new->attr, newa->key, newa); } } } } int csch_cgrp_replace(csch_sheet_t *sheet, csch_cgrp_t *dst, csch_cgrp_t *src, csch_cgrp_replace_how_t how) { csch_cgrp_t *newg; csch_chdr_t *newo; uundo_freeze_serial(&sheet->undo); csch_wirenet_recalc_freeze(sheet); newo = csch_cobj_dup(sheet, dst->hdr.parent, &src->hdr, 0, 0); if (newo == NULL) { csch_wirenet_recalc_unfreeze(sheet); uundo_unfreeze_serial(&sheet->undo); return -1; } newg = ((csch_cgrp_t *)newo); newg->sym_prefer_loclib = 0; /* lib buffer paste related special casing */ /* copy transformation */ newg->spec_rot = dst->spec_rot; newg->x = dst->x; newg->y = dst->y; newg->mirx = dst->mirx; newg->miry = dst->miry; csch_cgrp_replace_attr(sheet, newg, dst, src, how); csch_op_remove(sheet, &dst->hdr); csch_cgrp_update(sheet, newg, 1); csch_op_inserted(sheet, newo->parent, newo); csch_conn_auto_recalc(sheet, newo); csch_wirenet_recalc_unfreeze(sheet); uundo_unfreeze_serial(&sheet->undo); uundo_inc_serial(&sheet->undo); return 0; }