diff --git a/src/actions.c b/src/actions.c index c2c7c0ac..ef4a537b 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1392,7 +1392,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot xctx->inst[n].prop_ptr=NULL; xctx->inst[n].instname=NULL; dbg(1, "place_symbol() :all inst_ptr members set\n"); /* 03-02-2000 */ - if(first_call) hash_all_names(); + if(first_call) hash_all_names(-1, XINSERT); if(inst_props) { new_prop_string(n, inst_props,!first_call, tclgetboolvar("disable_unique_names")); /* 20171214 first_call */ } diff --git a/src/callback.c b/src/callback.c index 078035de..540f76c9 100644 --- a/src/callback.c +++ b/src/callback.c @@ -2228,6 +2228,8 @@ int draw_xhair = tclgetboolvar("draw_crosshair"); } xctx->mx_double_save=xctx->mousex_snap; xctx->my_double_save=xctx->mousey_snap; + if(tclgetboolvar("enable_stretch")) + select_attached_nets(); /* stretch nets that land on selected instance pins */ move_objects(START,0,0,0); break; } @@ -2244,7 +2246,8 @@ int draw_xhair = tclgetboolvar("draw_crosshair"); { xctx->mx_double_save=xctx->mousex_snap; xctx->my_double_save=xctx->mousey_snap; - select_attached_nets(); /* stretch nets that land on selected instance pins */ + if(!tclgetboolvar("enable_stretch")) + select_attached_nets(); /* stretch nets that land on selected instance pins */ move_objects(START,0,0,0); break; } @@ -2927,7 +2930,7 @@ int draw_xhair = tclgetboolvar("draw_crosshair"); dbg(1, "callback(): DoubleClick ui_state=%d state=%d\n",xctx->ui_state,state); if(button==Button1) { int sel; - if(!xctx->lastsel) { + if(!xctx->lastsel && xctx->ui_state == 0) { /* Following 5 lines do again a selection overriding lock, * so locked instance attrs can be edited */ sel = select_object(xctx->mousex, xctx->mousey, SELECTED, 1); diff --git a/src/editprop.c b/src/editprop.c index 07ed4549..f866ef6c 100644 --- a/src/editprop.c +++ b/src/editprop.c @@ -1430,7 +1430,7 @@ static int update_symbol(const char *result, int x, int first_sel) } /* set unique name of current inst */ if(!pushed) { xctx->push_undo(); pushed=1;} - if(!k) hash_all_names(); + if(!k) hash_all_names(-1, XINSERT); new_prop_string(*ii, ptr, k, tclgetboolvar("disable_unique_names")); /* set new prop_ptr */ } diff --git a/src/move.c b/src/move.c index 8137e9ab..1a7f2f7c 100644 --- a/src/move.c +++ b/src/move.c @@ -1027,7 +1027,7 @@ void copy_objects(int what) xctx->inst[xctx->instances].flip = (xctx->move_flip? !xctx->inst[n].flip:xctx->inst[n].flip); /* the newpropcnt argument is zero for the 1st call and used in */ /* new_prop_string() for cleaning some internal caches. */ - if(!newpropcnt) hash_all_names(); + if(!newpropcnt) hash_all_names(-1, XINSERT); new_prop_string(xctx->instances, xctx->inst[n].prop_ptr,newpropcnt++, tclgetboolvar("disable_unique_names")); my_strdup(_ALLOC_ID_, &xctx->inst[xctx->instances].instname, diff --git a/src/paste.c b/src/paste.c index 32e56b9f..292df10d 100644 --- a/src/paste.c +++ b/src/paste.c @@ -273,11 +273,12 @@ static void merge_inst(int k,FILE *fd) xctx->inst[i].lab=NULL; /* assigned in link_symbols_to_instances */ xctx->inst[i].node=NULL; load_ascii_string(&prop_ptr,fd); - if(!k) hash_all_names(); + my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, prop_ptr); + set_inst_flags(&xctx->inst[i]); + if(!k) hash_all_names(-1, XINSERT); new_prop_string(i, prop_ptr, k, tclgetboolvar("disable_unique_names")); /* will also assign .instname */ /* the final tmp argument is zero for the 1st call and used in */ /* new_prop_string() for cleaning some internal caches. */ - set_inst_flags(&xctx->inst[i]); my_free(_ALLOC_ID_, &prop_ptr); xctx->instances++; } diff --git a/src/scheduler.c b/src/scheduler.c index 2886ffaf..52e8703b 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -3265,7 +3265,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg /* 20110325 only modify prefix if prefix not NUL */ if(prefix) name[0]=(char)prefix; /* change prefix if changing symbol type; */ my_strdup(_ALLOC_ID_, &ptr,subst_token(xctx->inst[inst].prop_ptr, "name", name) ); - if(!fast) hash_all_names(); + if(!fast) hash_all_names(-1, XINSERT); new_prop_string(inst, ptr, fast, tclgetboolvar("disable_unique_names")); /* set new prop_ptr */ my_strdup(_ALLOC_ID_, &xctx->inst[inst].instname, get_tok_value(xctx->inst[inst].prop_ptr, "name", 0)); @@ -3923,6 +3923,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } else { char *type; int cond; + char *subst = NULL; if(!fast) { bbox(START,0.0,0.0,0.0,0.0); symbol_bbox(inst, &xctx->inst[inst].x1, &xctx->inst[inst].y1, &xctx->inst[inst].x2, &xctx->inst[inst].y2); @@ -3932,14 +3933,14 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; - if(!strcmp(argv[4], "name")) hash_all_names(); + if(!strcmp(argv[4], "name")) hash_all_names(-1, XINSERT); if(argc > 5) { - new_prop_string(inst, subst_token(xctx->inst[inst].prop_ptr, argv[4], argv[5]),fast, - tclgetboolvar("disable_unique_names")); + my_strdup2(_ALLOC_ID_, &subst, subst_token(xctx->inst[inst].prop_ptr, argv[4], argv[5])); } else {/* assume argc == 5 , delete attribute */ - new_prop_string(inst, subst_token(xctx->inst[inst].prop_ptr, argv[4], NULL),fast, - tclgetboolvar("disable_unique_names")); + my_strdup2(_ALLOC_ID_, &subst, subst_token(xctx->inst[inst].prop_ptr, argv[4], NULL)); } + new_prop_string(inst, subst, fast, tclgetboolvar("disable_unique_names")); + my_free(_ALLOC_ID_, &subst); set_inst_flags(&xctx->inst[inst]); type=xctx->sym[xctx->inst[inst].ptr].type; diff --git a/src/token.c b/src/token.c index 1afa9d12..c25dea56 100644 --- a/src/token.c +++ b/src/token.c @@ -54,24 +54,65 @@ void floater_hash_all_names(void) } } -void hash_all_names(void) +/* if inst == -1 hash all instance names, else do only given instance + * action can be XINSERT or XDELETE to insert or remove items */ +void hash_all_names(int inst, int action) { - int i; - char *upinst = NULL, *type = NULL; - int_hash_free(&xctx->inst_name_table); - int_hash_init(&xctx->inst_name_table, HASHSIZE); - for(i=0; iinstances; ++i) { + int i, mult, start, stop; + char *upinst = NULL; + char *upinst_ptr, *upinst_state, *single_name; + if(inst == -1) { + int_hash_free(&xctx->inst_name_table); + int_hash_init(&xctx->inst_name_table, HASHSIZE); + } + if(inst == -1) { + start = 0; + stop = xctx->instances; + } else { + start = inst; + stop = inst + 1; + } + if(inst != -1) dbg(1, "hash_all_names(): start=%d, stop=%d, instname=%s\n", + start, stop, xctx->inst[inst].instname? xctx->inst[inst].instname : "NULL"); + for(i = start; i < stop; ++i) { if(xctx->inst[i].instname && xctx->inst[i].instname[0]) { - if(xctx->inst[i].ptr == -1) continue; - my_strdup(_ALLOC_ID_, &type,(xctx->inst[i].ptr+ xctx->sym)->type); - if(!type) continue; - my_strdup(_ALLOC_ID_, &upinst, xctx->inst[i].instname); + my_strdup(_ALLOC_ID_, &upinst, expandlabel(xctx->inst[i].instname, &mult)); strtoupper(upinst); - int_hash_lookup(&xctx->inst_name_table, upinst, i, XINSERT); + + upinst_ptr = upinst; + while( (single_name = my_strtok_r(upinst_ptr, ",", "", &upinst_state)) ) { + upinst_ptr = NULL; + dbg(1, "hash_all_names(): inst %d, name %s --> %d\n", i, single_name, action); + int_hash_lookup(&xctx->inst_name_table, single_name, i, action); + dbg(1, "hash_all_names(): hashing %s from %s\n", single_name, xctx->inst[i].instname); + } } } my_free(_ALLOC_ID_, &upinst); - my_free(_ALLOC_ID_, &type); +} + + +/* return -1 if name is not used, else return instance number with same name found */ +int name_is_used(char *name) +{ + int mult, used = -1; + char *upinst = NULL; + char *upinst_ptr, *upinst_state, *single_name; + Int_hashentry *entry; + my_strdup(_ALLOC_ID_, &upinst, expandlabel(name, &mult)); + strtoupper(upinst); + upinst_ptr = upinst; + while( (single_name = my_strtok_r(upinst_ptr, ",", "", &upinst_state)) ) { + upinst_ptr = NULL; + entry = int_hash_lookup(&xctx->inst_name_table, single_name, 1, XLOOKUP); + if(entry) { + used = entry->value; + break; + } + } + my_free(_ALLOC_ID_, &upinst); + dbg(1, "name_is_used(%s): return inst %d\n", name, used); + return used; } /* if cmd is wrapped inside tcleval(...) pass the content to tcl @@ -107,8 +148,7 @@ void check_unique_names(int rename) int i, first = 1, modified = 0; int newpropcnt = 0; char *tmp = NULL; - Int_hashentry *entry; - char *upinst = NULL; + int used; if(xctx->hilight_nets) { xctx->enable_drill=0; @@ -124,10 +164,9 @@ void check_unique_names(int rename) if(xctx->inst[i].instname && xctx->inst[i].instname[0]) { if(xctx->inst[i].ptr == -1) continue; if(!(xctx->inst[i].ptr+ xctx->sym)->type) continue; - my_strdup(_ALLOC_ID_, &upinst, xctx->inst[i].instname); - strtoupper(upinst); - if( (entry = int_hash_lookup(&xctx->inst_name_table, upinst, i, XINSERT_NOREPLACE) ) && - entry->value != i) { + used = name_is_used(xctx->inst[i].instname); + hash_all_names(i, XINSERT_NOREPLACE); + if( used != -1 && used != i) { dbg(0, "check_unique_names(): found duplicate: i=%d name=%s\n", i, xctx->inst[i].instname); xctx->inst[i].color = -PINLAYER; inst_hilight_hash_lookup(i, -PINLAYER, XINSERT_NOREPLACE); @@ -153,15 +192,12 @@ void check_unique_names(int rename) my_strdup(_ALLOC_ID_, &tmp, xctx->inst[i].prop_ptr); new_prop_string(i, tmp, newpropcnt++, 0); my_strdup(_ALLOC_ID_, &xctx->inst[i].instname, get_tok_value(xctx->inst[i].prop_ptr, "name", 0)); - my_strdup(_ALLOC_ID_, &upinst, xctx->inst[i].instname); - strtoupper(upinst); - int_hash_lookup(&xctx->inst_name_table, upinst, i, XINSERT); + hash_all_names(i, XINSERT); symbol_bbox(i, &xctx->inst[i].x1, &xctx->inst[i].y1, &xctx->inst[i].x2, &xctx->inst[i].y2); bbox(ADD, xctx->inst[i].x1, xctx->inst[i].y1, xctx->inst[i].x2, xctx->inst[i].y2); my_free(_ALLOC_ID_, &tmp); } } /* for(i...) */ - my_free(_ALLOC_ID_, &upinst); if(modified) set_modify(1); if(rename == 1 && xctx->hilight_nets) { bbox(SET,0.0,0.0,0.0,0.0); @@ -730,87 +766,72 @@ static char *get_pin_attr_from_inst(int inst, int pin, const char *attr) return pin_attr_value; /* caller is responsible for freeing up storage for pin_attr_value */ } -void new_prop_string(int i, const char *old_prop, int fast, int dis_uniq_names) -{ /* given a old_prop property string, return a new */ /* property string in xctx->inst[i].prop_ptr such that the element name is */ /* unique in current design (that is, element name is changed */ /* if necessary) */ /* if old_prop=NULL return NULL */ /* if old_prop does not contain a valid "name" or empty return old_prop */ - char *old_name=NULL, *new_name=NULL; - const char *tmp; - const char *tmp2; - int q,qq; - static int last[1 << 8 * sizeof(char) ]; /* safe to keep with multiple schematics, reset on 1st invocation */ - size_t old_name_len; - int n; - char *old_name_base = NULL; - Int_hashentry *entry; - char *up_old_name = NULL; - char *up_new_name = NULL; - - dbg(1, "new_prop_string(): i=%d, old_prop=%s, fast=%d\n", i, old_prop, fast); - if(!fast) { /* on 1st invocation of new_prop_string */ - for(q=1;q<=255;q++) last[q]=1; - } - if(old_prop==NULL) - { - my_free(_ALLOC_ID_, &xctx->inst[i].prop_ptr); - return; - } - old_name_len = my_strdup(_ALLOC_ID_, &old_name,get_tok_value(old_prop,"name",0) ); /* added old_name_len */ - my_strdup(_ALLOC_ID_, &up_old_name, old_name); - strtoupper(up_old_name); - - if(old_name==NULL) - { - my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, old_prop); /* changed to copy old props if no name */ - my_free(_ALLOC_ID_, &up_old_name); - return; - } - xctx->prefix=old_name[0]; - /* don't change old_prop if name does not conflict. */ - /* if no hash_all_names() is done and inst_table uninitialized --> use old_prop */ - if(dis_uniq_names || (entry = int_hash_lookup(&xctx->inst_name_table, up_old_name, i, XLOOKUP))==NULL || - entry->value == i) - { - my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, old_prop); - int_hash_lookup(&xctx->inst_name_table, up_old_name, i, XINSERT); - my_free(_ALLOC_ID_, &old_name); - my_free(_ALLOC_ID_, &up_old_name); - return; - } - old_name_base = my_malloc(_ALLOC_ID_, old_name_len+1); - n = sscanf(old_name, "%[^[0-9]",old_name_base); - tmp=find_bracket(old_name); - my_realloc(_ALLOC_ID_, &new_name, old_name_len + 40); /* strlen(old_name)+40); */ - qq=fast ? last[(int)xctx->prefix] : 1; - for(q=qq;;q++) - { - if(n >= 1 ) { - my_snprintf(new_name, old_name_len + 40, "%s%d%s", old_name_base, q, tmp); +void new_prop_string(int i, const char *old_prop, int fast, int dis_uniq_names) +{ + char *old_name=NULL, *new_name=NULL; + const char *brkt; + const char *new_prop; + size_t old_name_len; + int n, q; + char *old_name_base = NULL; + char *up_new_name = NULL; + int is_used; + + dbg(1, "new_prop_string(): i=%d, old_prop=%s, fast=%d\n", i, old_prop, fast); + if(old_prop==NULL) { + my_free(_ALLOC_ID_, &xctx->inst[i].prop_ptr); + return; + } + old_name_len = my_strdup(_ALLOC_ID_, &old_name,get_tok_value(old_prop,"name",0) ); /* added old_name_len */ + + if(old_name==NULL) { + my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, old_prop); /* changed to copy old props if no name */ + return; + } + /* don't change old_prop if name does not conflict. */ + /* if no hash_all_names() is done and inst_table uninitialized --> use old_prop */ + is_used = name_is_used(old_name); + if(dis_uniq_names || is_used == -1 || is_used == i) { + my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, old_prop); + hash_all_names(i, XINSERT); /* add instance 'i' to xctx->inst_name_table */ + my_free(_ALLOC_ID_, &old_name); + return; + } + /* old_name is not unique. Find another unique name */ + old_name_base = my_malloc(_ALLOC_ID_, old_name_len+1); + n = sscanf(old_name, "%[^[0-9]",old_name_base); + brkt=find_bracket(old_name); + my_realloc(_ALLOC_ID_, &new_name, old_name_len + 40); /* strlen(old_name)+40); */ + + for(q = 1 ;; ++q) { + if(n) { + my_snprintf(new_name, old_name_len + 40, "%s%d%s", old_name_base, q, brkt); } else { /* goes here if weird name set for example to name=[3:0] or name=12 */ - my_snprintf(new_name, old_name_len + 40, "%c%d%s", xctx->prefix,q, tmp); + my_snprintf(new_name, old_name_len + 40, "%d%s", q, brkt); } - my_strdup(_ALLOC_ID_, &up_new_name, new_name); - strtoupper(up_new_name); - if((entry = int_hash_lookup(&xctx->inst_name_table, up_new_name, i, XLOOKUP)) == NULL || entry->value == i) - { - last[(int)xctx->prefix]=q+1; - break; - } - } - my_free(_ALLOC_ID_, &old_name_base); - tmp2 = subst_token(old_prop, "name", new_name); - if(strcmp(tmp2, old_prop) ) { - my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, tmp2); - int_hash_lookup(&xctx->inst_name_table, up_new_name, i, XINSERT); /* reinsert in hash */ - } - my_free(_ALLOC_ID_, &old_name); - my_free(_ALLOC_ID_, &up_old_name); - my_free(_ALLOC_ID_, &new_name); - my_free(_ALLOC_ID_, &up_new_name); + is_used = name_is_used(new_name); + if(is_used == -1 ) break; + } + + my_free(_ALLOC_ID_, &old_name_base); + dbg(1, "new_prop_string(): new_name=%s\n", new_name); + new_prop = subst_token(old_prop, "name", new_name); + dbg(1, "new_prop_string(): old_prop=|%s|\n", old_prop); + dbg(1, "new_prop_string(): new_prop=|%s|\n", new_prop); + if(strcmp(new_prop, old_prop) ) { + my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, new_prop); + my_strdup2(_ALLOC_ID_, &xctx->inst[i].instname, new_name); + hash_all_names(i, XINSERT); + } + my_free(_ALLOC_ID_, &old_name); + my_free(_ALLOC_ID_, &new_name); + my_free(_ALLOC_ID_, &up_new_name); } @@ -1108,6 +1129,7 @@ const char *subst_token(const char *s, const char *tok, const char *new_val) /* if tok not found in s and new_val!=NULL add tok=new_val at end.*/ /* if new_val is empty ('\0') set token value to "" (token="") */ /* if new_val is NULL *remove* 'token (and =val if any)' from s */ +/* return the updated string */ { static char *result=NULL; size_t size=0; diff --git a/src/xschem.h b/src/xschem.h index 7c9f906e..43d0c10f 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -989,7 +989,6 @@ typedef struct { int bbox_set; /* set to 1 if a clipping bbox is set (void bbox() ) */ XRectangle savexrect; /* new_prop_string */ - char prefix; /* edit_symbol_property, update_symbol */ char *old_prop; int edit_sym_i; @@ -1503,7 +1502,8 @@ extern float my_atof(const char *p); extern const char *subst_token(const char *s, const char *tok, const char *new_val); extern void new_prop_string(int i, const char *old_prop,int fast, int dis_uniq_names); extern void hash_name(char *token, int remove); -extern void hash_all_names(void); +extern void hash_all_names(int inst, int action); /* if i == -1 hash all instances, else do only inst */ +extern int name_is_used(char *name); extern void floater_hash_all_names(void); extern void symbol_bbox(int i, double *x1,double *y1, double *x2, double *y2); /* extern char *escape_chars(char *dest, const char *source, int size); */ diff --git a/src/xschem.tcl b/src/xschem.tcl index afe21ba2..b15b28e8 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -5983,6 +5983,15 @@ proc build_widgets { {topwin {} } } { -onvalue disk -offvalue memory -command {switch_undo} $topwin.menubar.option.menu add checkbutton -label "Enable stretch" -variable enable_stretch \ -accelerator Y + + $topwin.menubar.option.menu add checkbutton -label "Auto Join/Trim Wires" -variable autotrim_wires \ + -command { + if {$autotrim_wires == 1} { + xschem trim_wires + xschem redraw + } + } + $topwin.menubar.option.menu add checkbutton -label "Show netlist win" -variable netlist_show \ -accelerator {Shift+A} $topwin.menubar.option.menu add checkbutton -label "Flat netlist" -variable flat_netlist \ @@ -6248,13 +6257,6 @@ proc build_widgets { {topwin {} } } { $topwin.menubar.tools.menu add command -label "Break wires at mouse position, align cut point" \ -command "xschem wire_cut" -accelerator {Alt-Right Butt.} toolbar_add ToolBreak "xschem break_wires" "Break wires at selected\ninstance pin intersections" $topwin - $topwin.menubar.tools.menu add checkbutton -label "Auto Join/Trim Wires" -variable autotrim_wires \ - -command { - if {$autotrim_wires == 1} { - xschem trim_wires - xschem redraw - } - } $topwin.menubar.tools.menu add command -label "Select all connected wires/labels/pins" \ -accelerator {Shift-Right Butt.} \ -command { xschem connected_nets}