From ef5d0e1b96f480075a8949a2597eef12478ac169 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 10 Jan 2025 01:52:54 +0100 Subject: [PATCH 1/3] str_replace(): add parameter to specify number of substituitions (or all); replaced atof_spice() with atof_eng() in various parts related to numbers that do not come from spice netlists; fix numerical setting of cursors if log scale is set (wrong preset was shown); make wave labels in graph scale with gr->magx as X-axis labels; fix scaling roundoff issues in dtoa_eng(); add new `@spice_get_node ` token (where spice_node may contain @variables) in symbol texts to display indicated spice node value. Does not use TCL, thus faster and less "quoting hell" problems --- XSchemWin/XSchemWix/Product.wxs | 6 +- XSchemWin/XSchemWix/doc.wxs | 4 + XSchemWin/XSchemWix/heat_doc.wxs | 780 +++++++++++++++-------------- doc/xschem_man/developer_info.html | 4 +- src/actions.c | 8 +- src/callback.c | 66 +-- src/draw.c | 21 +- src/editprop.c | 42 +- src/scheduler.c | 18 +- src/token.c | 111 +++- src/xinit.c | 2 +- src/xschem.h | 3 +- 12 files changed, 585 insertions(+), 480 deletions(-) diff --git a/XSchemWin/XSchemWix/Product.wxs b/XSchemWin/XSchemWix/Product.wxs index e911c39f..8a036a35 100644 --- a/XSchemWin/XSchemWix/Product.wxs +++ b/XSchemWin/XSchemWix/Product.wxs @@ -1,6 +1,6 @@ - + @@ -151,9 +151,6 @@ - - - @@ -361,7 +358,6 @@ - diff --git a/XSchemWin/XSchemWix/doc.wxs b/XSchemWin/XSchemWix/doc.wxs index 38aaea95..ad7f632e 100644 --- a/XSchemWin/XSchemWix/doc.wxs +++ b/XSchemWin/XSchemWix/doc.wxs @@ -5880,6 +5880,9 @@ + + + @@ -8370,6 +8373,7 @@ + diff --git a/XSchemWin/XSchemWix/heat_doc.wxs b/XSchemWin/XSchemWix/heat_doc.wxs index 79157470..6ba8de51 100644 --- a/XSchemWin/XSchemWix/heat_doc.wxs +++ b/XSchemWin/XSchemWix/heat_doc.wxs @@ -3,1171 +3,1174 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1386,6 +1389,7 @@ + diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html index e4f6ed33..8dd24c12 100644 --- a/doc/xschem_man/developer_info.html +++ b/doc/xschem_man/developer_info.html @@ -1342,6 +1342,8 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns" if x0, y0 not given use mouse coordinates
  • rotate_in_place
  •     Rotate selected objects around their 0,0 coordinate point 
    +
  • round_to_n_digits i n
  • +   round number 'i' to 'n' digits 
  • save [fast]
  •     Save schematic if modified. Does not ask confirmation!
        if 'fast' is given it is passed to save_schematic() to avoid
    @@ -1506,7 +1508,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        
  • snap_wire
  •     Start a GUI start snapped wire placement (click to start a
        wire to closest pin/net endpoint) 
    -
  • str_replace str rep with [escape]
  • +   
  • str_replace str rep with [escape] [count]
  •     replace 'rep' with 'with' in string 'str'
        if rep not preceeded by an 'escape' character 
  • subst_tok str tok newval
  • diff --git a/src/actions.c b/src/actions.c
    index ab8af011..3e4c95ae 100644
    --- a/src/actions.c
    +++ b/src/actions.c
    @@ -1482,7 +1482,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot
      /* remove tcleval( given in file selector, if any ... */
      if(strstr(name1, "tcleval(")) {
        tclev = 1;
    -   my_snprintf(name1, S(name1), "%s", str_replace(name1, "tcleval(", "", 0));
    +   my_snprintf(name1, S(name1), "%s", str_replace(name1, "tcleval(", "", 0, -1));
      }
      dbg(1, "place_symbol(): 2: name1=%s\n",name1);
     
    @@ -1788,7 +1788,7 @@ const char *get_sym_name(int inst, int ndir, int ext, int abs_path)
     
       /* instance based symbol selection */
       sch = tcl_hook2(str_replace(get_tok_value(xctx->inst[inst].prop_ptr,"schematic", 2), "@symname",
    -        get_cell(xctx->inst[inst].name, 0), '\\'));
    +        get_cell(xctx->inst[inst].name, 0), '\\', -1));
     
       if(xctx->tok_size) { /* token exists */ 
         if(abs_path)
    @@ -1967,7 +1967,7 @@ void get_additional_symbols(int what)
           my_strdup(_ALLOC_ID_, &vhdl_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"vhdl_sym_def",4));
           my_strdup2(_ALLOC_ID_, &sch, tcl_hook2(
              str_replace( get_tok_value(xctx->inst[i].prop_ptr,"schematic",2), "@symname",
    -           get_cell(xctx->inst[i].name, 0), '\\')));
    +           get_cell(xctx->inst[i].name, 0), '\\', -1)));
           dbg(1, "get_additional_symbols(): inst=%d sch=%s\n",i,  sch);
           /* schematic does not exist */
           if(sch[0] && stat(abs_sym_path(sch, ""), &buf)) {
    @@ -2086,7 +2086,7 @@ void get_sch_from_sym(char *filename, xSymbol *sym, int inst, int fallback)
       if(str_tmp[0]) { /* schematic attribute in symbol or instance was given */
         /* @symname in schematic attribute will be replaced with symbol name */
         my_strdup2(_ALLOC_ID_, &sch, tcl_hook2(str_replace(str_tmp, "@symname",
    -       get_cell(sym->name, 0), '\\')));
    +       get_cell(sym->name, 0), '\\', -1)));
         if(is_generator(sch)) { /* generator: return as is */
           my_strncpy(filename, sch, PATH_MAX);
           is_gen = 1;
    diff --git a/src/callback.c b/src/callback.c
    index 1835e307..b1ca8560 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -560,7 +560,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
             if(r->flags & 4) { /* private_cursor */
               const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0);
               if(s[0]) {
    -            cursor1 = atof(s);
    +            cursor1 = atof_eng(s);
               } else {
                 cursor1 = xctx->graph_cursor1_x;
               }
    @@ -579,7 +579,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
             if(r->flags & 4) { /* private_cursor */
               const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0);
               if(s[0]) {
    -            cursor2 = atof_spice(s);
    +            cursor2 = atof_eng(s);
               } else {
                 cursor2 = xctx->graph_cursor2_x;
               }
    @@ -597,27 +597,28 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
         else if(event == ButtonPress && button == Button3) {
           /* Numerically set cursor position */
           if(xctx->graph_flags & 2) {
    -        double cursor1;
    +        double logcursor, cursor;
             if(r->flags & 4) { /* private_cursor */
               const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0);
               if(s[0]) {
    -            cursor1 = atof_spice(s);
    +            cursor = atof_spice(s);
               } else {
    -            cursor1 = xctx->graph_cursor1_x;
    +            cursor = xctx->graph_cursor1_x;
               }
             } else {
    -          cursor1 = xctx->graph_cursor1_x;
    +          cursor = xctx->graph_cursor1_x;
             }
    +        logcursor = cursor;
             if(gr->logx ) {
    -          cursor1 = mylog10(cursor1);
    +          logcursor = mylog10(cursor);
             }
    -        if(fabs(xctx->mousex - W_X(cursor1)) < 10) {
    -          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor1), NULL);
    -          cursor1 = atof_spice(tclresult());
    +        if(fabs(xctx->mousex - W_X(logcursor)) < 10) {
    +          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    +          cursor = atof_eng(tclresult());
               if(r->flags & 4) {
    -            my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(cursor1)));
    +            my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(cursor)));
               } else {
    -            xctx->graph_cursor1_x = cursor1;
    +            xctx->graph_cursor1_x = cursor;
               }
               event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */
             }
    @@ -625,27 +626,28 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
           }
           /* Numerically set cursor position */
           if(xctx->graph_flags & 4) {
    -        double cursor2;
    +        double logcursor, cursor;
             if(r->flags & 4) { /* private_cursor */
               const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0);
               if(s[0]) {
    -            cursor2 = atof_spice(s);
    +            cursor = atof_spice(s);
               } else {
    -            cursor2 = xctx->graph_cursor2_x;
    +            cursor = xctx->graph_cursor2_x;
               }
             } else {
    -          cursor2 = xctx->graph_cursor2_x;
    +          cursor = xctx->graph_cursor2_x;
             }
    +        logcursor = cursor;
             if(gr->logx) {
    -          cursor2 = mylog10(cursor2);
    +          logcursor = mylog10(cursor);
             }
    -        if(fabs(xctx->mousex - W_X(cursor2)) < 10) {
    -          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor2), NULL);
    -          cursor2 = atof_spice(tclresult());
    +        if(fabs(xctx->mousex - W_X(logcursor)) < 10) {
    +          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    +          cursor = atof_eng(tclresult());
               if(r->flags & 4) {
    -            my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor2_x", dtoa(cursor2)));
    +            my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor2_x", dtoa(cursor)));
               } else {
    -            xctx->graph_cursor2_x = cursor2;
    +            xctx->graph_cursor2_x = cursor;
               }
               event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */
             }
    @@ -901,30 +903,30 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
         else if(event == ButtonPress && button == Button3) {
           /* Numerically set hcursor position */
           if(xctx->graph_flags & 128) {
    -        double cursor;
    -        cursor = gr->hcursor1_y;
    +        double logcursor, cursor;
    +        logcursor = cursor = gr->hcursor1_y;
             if(gr->logy ) {
    -          cursor = mylog10(cursor);
    +          logcursor = mylog10(cursor);
             }
    -        if(fabs(xctx->mousey - W_Y(cursor)) < 10) {
    +        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
               xctx->ui_state &= ~GRAPHPAN; /* we are setting a cursor so clear GRAPHPAN set before */
               tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    -          cursor = atof_spice(tclresult());
    +          cursor = atof_eng(tclresult());
               my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", dtoa(cursor)));
             }
             need_redraw = 1;
           }
           /* Numerically set hcursor position */
           if(xctx->graph_flags & 256) {
    -        double cursor;
    -        cursor = gr->hcursor2_y;
    +        double logcursor, cursor;
    +        logcursor = cursor = gr->hcursor2_y;
             if(gr->logy ) {
    -          cursor = mylog10(cursor);
    +          logcursor = mylog10(cursor);
             }
    -        if(fabs(xctx->mousey - W_Y(cursor)) < 10) {
    +        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
               xctx->ui_state &= ~GRAPHPAN; /* we are setting a cursor so clear GRAPHPAN set before */
               tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    -          cursor = atof_spice(tclresult());
    +          cursor = atof_eng(tclresult());
               my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", dtoa(cursor)));
             }
             need_redraw = 1;
    diff --git a/src/draw.c b/src/draw.c
    index 6693b3e4..37b9ff4f 100644
    --- a/src/draw.c
    +++ b/src/draw.c
    @@ -2915,21 +2915,21 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr)
       gr->hcursor1_y = gr->hcursor2_y = 0.0;
       val = get_tok_value(r->prop_ptr,"hcursor1_y", 0);
       if(val[0]) {
    -    gr->hcursor1_y = atof_spice(val);
    +    gr->hcursor1_y = atof_eng(val);
         xctx->graph_flags |= 128;
       }
       val = get_tok_value(r->prop_ptr,"hcursor2_y", 0);
       if(val[0]) {
    -    gr->hcursor2_y = atof_spice(val);
    +    gr->hcursor2_y = atof_eng(val);
         xctx->graph_flags |= 256;
       }
       if(!skip) {
         gr->gx1 = 0;
         gr->gx2 = 1e-6;
         val = get_tok_value(r->prop_ptr,"x1", 0);
    -    if(val[0]) gr->gx1 = atof_spice(val);
    +    if(val[0]) gr->gx1 = atof_eng(val);
         val = get_tok_value(r->prop_ptr,"x2", 0);
    -    if(val[0]) gr->gx2 = atof_spice(val);
    +    if(val[0]) gr->gx2 = atof_eng(val);
         if(gr->gx1 == gr->gx2) gr->gx2 += 1e-6;
         gr->gw = gr->gx2 - gr->gx1;
       }
    @@ -3012,17 +3012,17 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr)
       val = get_tok_value(r->prop_ptr,"logy", 0);
       if(val[0] == '1') gr->logy = 1;
       val = get_tok_value(r->prop_ptr,"y1", 0);
    -  if(val[0]) gr->gy1 = atof_spice(val);
    +  if(val[0]) gr->gy1 = atof_eng(val);
       val = get_tok_value(r->prop_ptr,"y2", 0);
    -  if(val[0]) gr->gy2 = atof_spice(val);
    +  if(val[0]) gr->gy2 = atof_eng(val);
       if(gr->gy1 == gr->gy2) gr->gy2 += 1.0;
       val = get_tok_value(r->prop_ptr,"digital", 0);
       if(val[0]) gr->digital = atoi(val);
       if(gr->digital) {
         val = get_tok_value(r->prop_ptr,"ypos1", 0);
    -    if(val[0]) gr->ypos1 = atof_spice(val);
    +    if(val[0]) gr->ypos1 = atof_eng(val);
         val = get_tok_value(r->prop_ptr,"ypos2", 0);
    -    if(val[0]) gr->ypos2 = atof_spice(val);
    +    if(val[0]) gr->ypos2 = atof_eng(val);
         if(gr->ypos2 == gr->ypos1) gr->ypos2 += 1.0;
       }
       gr->posh = gr->ypos2 - gr->ypos1;
    @@ -3057,6 +3057,7 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr)
         gr->digtxtsizelab = 0.000900 * fabs( gr->h / gr->posh * gr->gh ); 
       else
         gr->digtxtsizelab = 0.001200 * fabs( gr->h / gr->posh * gr->gh );
    +  gr->txtsizelab *= gr->magx;
     
       /* x axis, y axis text sizes */
       gr->txtsizey = gr->h / gr->divy * 0.0095;
    @@ -3783,7 +3784,7 @@ void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct)
       if(r->flags & 4) { /* private_cursor */
         const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0);
         if(s[0]) {
    -      cursor1 = atof_spice(s);
    +      cursor1 = atof_eng(s);
         } else { 
           cursor1 = xctx->graph_cursor1_x;
         }
    @@ -3794,7 +3795,7 @@ void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct)
       if(r->flags & 4) { /* private_cursor */
         const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0);
         if(s[0]) {
    -      cursor2 = atof_spice(s);
    +      cursor2 = atof_eng(s);
         } else {
           cursor2 = xctx->graph_cursor2_x;
         }
    diff --git a/src/editprop.c b/src/editprop.c
    index 277ad78d..7c96173b 100644
    --- a/src/editprop.c
    +++ b/src/editprop.c
    @@ -554,20 +554,20 @@ char *dtoa_eng(double i)
       size_t n;
       int suffix = 0;
       double absi = fabs(i);
    -
    -  if     (absi == 0.0)  { suffix = 0;}
    -  else if(absi < 1e-23) { i = 0; suffix = 0;}
    -  else if(absi >=1e12)  { i /= 1e12; suffix = 'T';}
    -  else if(absi >=1e9)   { i /= 1e9 ; suffix = 'G';}
    -  else if(absi >=1e6)   { i /= 1e6 ; suffix = 'M';}
    -  else if(absi >=1e3)   { i /= 1e3 ; suffix = 'k';}
    -  else if(absi >=0.1)   { suffix = 0;}
    -  else if(absi >=1e-3)  { i *= 1e3 ; suffix = 'm';}
    -  else if(absi >=1e-6)  { i *= 1e6 ; suffix = 'u';}
    -  else if(absi >=1e-9)  { i *= 1e9 ; suffix = 'n';}
    -  else if(absi >=1e-12) { i *= 1e12; suffix = 'p';}
    -  else if(absi >=1e-15) { i *= 1e15; suffix = 'f';}
    -  else                  { i *= 1e18; suffix = 'a';}
    +  dbg(1,  "dtoa_eng(): i=%.17g, absi=%.17g\n", i, absi);
    +  if     (absi == 0.0)        {            suffix =  0 ;}
    +  else if(absi < 0.999999e-23) { i  = 0.0 ; suffix =  0 ;}
    +  else if(absi > 0.999999e12)  { i /= 1e12; suffix = 'T';}
    +  else if(absi > 0.999999e9)   { i /= 1e9 ; suffix = 'G';}
    +  else if(absi > 0.999999e6)   { i /= 1e6 ; suffix = 'M';}
    +  else if(absi > 0.999999e3)   { i /= 1e3 ; suffix = 'k';}
    +  else if(absi > 0.999999e-1)  {            suffix = 0;  }
    +  else if(absi > 0.999999e-3)  { i *= 1e3 ; suffix = 'm';}
    +  else if(absi > 0.999999e-6)  { i *= 1e6 ; suffix = 'u';}
    +  else if(absi > 0.999999e-9)  { i *= 1e9 ; suffix = 'n';}
    +  else if(absi > 0.999999e-12) { i *= 1e12; suffix = 'p';}
    +  else if(absi > 0.999999e-15) { i *= 1e15; suffix = 'f';}
    +  else                        { i *= 1e18; suffix = 'a';}
       if(suffix) {
         n = my_snprintf(s, S(s), "%.5g%c", i, suffix);
       } else {
    @@ -1481,7 +1481,7 @@ int drc_check(int i)
             const char *result;
             const char *replace_res;
             
    -        replace_res = str_replace(res, "@symname", xctx->sym[xctx->inst[j].ptr].name, '\\');
    +        replace_res = str_replace(res, "@symname", xctx->sym[xctx->inst[j].ptr].name, '\\', -1);
             result = tcleval(replace_res);
             if(result && result[0]) {
               ret = 1;
    @@ -1799,8 +1799,10 @@ void change_elem_order(int n)
         if(modified) set_modify(1);
       }
     }
    -/* replace substring 'rep' in 'str' with 'with', if 'rep' not preceeded by an 'escape' char */
    -char *str_replace(const char *str, const char *rep, const char *with, int escape)
    +/* replace substring 'rep' in 'str' with 'with', if 'rep' not preceeded by an 'escape' char 
    + * 'count' indicates the number of replacements to do or all if -1
    + */
    +char *str_replace(const char *str, const char *rep, const char *with, int escape, int count)
     {
       static char *result = NULL;
       static size_t size=0;
    @@ -1809,6 +1811,7 @@ char *str_replace(const char *str, const char *rep, const char *with, int escape
       size_t with_len;
       const char *s = str;
       int cond;
    +  int replacements = 0;
     
       if(s==NULL || rep == NULL || with == NULL || rep[0] == '\0') {
         my_free(_ALLOC_ID_, &result);
    @@ -1825,11 +1828,14 @@ char *str_replace(const char *str, const char *rep, const char *with, int escape
       while(*s) {
         STR_ALLOC(&result, result_pos + with_len + 1, &size);
     
    -    cond = ((s == str) || ((*(s - 1) != escape))) && (!strncmp(s, rep, rep_len));
    +    cond = (count == -1 || replacements < count)  && 
    +           ((s == str) || ((*(s - 1) != escape))) &&
    +           (!strncmp(s, rep, rep_len));
         if(cond) {
           my_strncpy(result + result_pos, with, with_len + 1);
           result_pos += with_len;
           s += rep_len;
    +      replacements++;
         } else {
           result[result_pos++] = *s++;
         }
    diff --git a/src/scheduler.c b/src/scheduler.c
    index 38b1fca0..def55e4f 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -4589,6 +4589,17 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           Tcl_ResetResult(interp);
         }
     
    +    /* round_to_n_digits i n
    +     *   round number 'i' to 'n' digits */
    +    else if(!strcmp(argv[1], "round_to_n_digits"))
    +    {
    +      double r;
    +      if(argc > 3) {
    +        r = round_to_n_digits(atof(argv[2]), atoi(argv[3]));
    +        Tcl_SetResult(interp, dtoa(r), TCL_VOLATILE);
    +      }
    +    }
    +
         else { cmd_found = 0;}
         break;
         case 's': /*----------------------------------------------*/
    @@ -5538,15 +5549,16 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           xctx->ui_state2 = MENUSTARTSNAPWIRE;
         }
     
    -    /* str_replace str rep with [escape]
    +    /* str_replace str rep with [escape] [count]
          *   replace 'rep' with 'with' in string 'str'
          *   if rep not preceeded by an 'escape' character */
         else if(!strcmp(argv[1], "str_replace"))
         {
    -      int escape = 0;
    +      int escape = 0, count = -1;
           if(argc > 5) escape = argv[5][0];
    +      if(argc > 6) count = atoi(argv[6]);
           if(argc > 4) {
    -        Tcl_AppendResult(interp, str_replace(argv[2], argv[3], argv[4], escape), NULL);
    +        Tcl_AppendResult(interp, str_replace(argv[2], argv[3], argv[4], escape, count), NULL);
           } else {
             Tcl_SetResult(interp, "Missing arguments", TCL_STATIC);
             return TCL_ERROR;
    diff --git a/src/token.c b/src/token.c
    index d66831dd..d3aef7c6 100644
    --- a/src/token.c
    +++ b/src/token.c
    @@ -67,7 +67,7 @@ const char *tcl_hook2(const char *cmd)
         return empty;
       }
       if(strstr(cmd, "tcleval(") == cmd) {
    -    unescaped_res = str_replace(cmd, "\\}", "}", 0);
    +    unescaped_res = str_replace(cmd, "\\}", "}", 0, -1);
         tclvareval("tclpropeval2 {", unescaped_res, "}" , NULL);
         my_strdup2(_ALLOC_ID_, &result, tclresult());
         /* dbg(0, "tcl_hook2: return: %s\n", result);*/
    @@ -1972,7 +1972,7 @@ void print_spice_subckt_nodes(FILE *fd, int symbol)
     
      /* can not do this, since @symname is used as a token later in format parser */
      /* my_strdup(_ALLOC_ID_, &format1,
    -  * str_replace(format1, "@symname", get_cell(xctx->sym[symbol].name, 0), '\\')); */
    +  * str_replace(format1, "@symname", get_cell(xctx->sym[symbol].name, 0), '\\', -1)); */
     
      if(format1 && strstr(format1, "tcleval(") == format1) {
         tclres = tcl_hook2(format1);
    @@ -3645,6 +3645,57 @@ static char *get_pin_attr(const char *token, int inst, int engineering)
       return value;
     }
     
    +const char *spice_get_node(const char *token)
    +{
    +  const char *pos;
    +
    +  if((pos = strstr(token, "@spice_get_node "))) {
    +    char *node = NULL;
    +    char *token2 = NULL;
    +    int idx;
    +    char sp;
    +    int n;
    +    size_t len;
    +    double val = 0.0;
    +    const char *valstr;
    +    const char *s;
    +
    +    dbg(1, "token=%s\n", token);
    +    node = my_malloc(_ALLOC_ID_, strlen(token) + 1);
    +    n = sscanf(pos, "%*[^ ] %[^ ]%c", node, &sp);
    +    len = strlen(node);
    +    dbg(1, "node=%s, n=%d, sp=|%c|\n", node, n, sp);
    +    idx = get_raw_index(node, NULL);
    +    if(idx >= 0) {
    +      val = xctx->raw->cursor_b_val[idx];
    +    }
    +    if(!strcmp(node, "0") || !my_strcasecmp(node, "GND")) {
    +      valstr = "0.0";
    +    } else if(idx < 0) {
    +      valstr = "-";
    +    } else {
    +      /* always use engineering as these tokens are generated from single
    +       * @spice_get_node(...) patterns */
    +      valstr = dtoa_eng(val);
    +    }
    +    dbg(1, "valstr=%s\n", valstr);
    +    my_strdup2(_ALLOC_ID_, &token2, str_replace(token, "@spice_get_node ", "", 0, 1));
    +    dbg(1, "token2=%s\n", token2);
    +    if(n == 2 && sp == ' ') {
    +      node[len] = ' ';
    +      node[len + 1] = '\0';
    +    }
    +    s = str_replace(token2, node, valstr, 0, 1);
    +    dbg(1, "s=%s\n", s);
    +    my_free(_ALLOC_ID_, &token2);
    +    my_free(_ALLOC_ID_, &node);
    +    return s;
    +  } else {
    +    return token;
    +  }
    +}
    +
    +
     /* substitute given tokens in a string with their corresponding values */
     /* ex.: name=@name w=@w l=@l ---> name=m112 w=3e-6 l=0.8e-6 */
     /* if s==NULL return emty string */
    @@ -3902,6 +3953,31 @@ const char *translate(int inst, const char* s)
            }
          }
        }
    +
    +   /* copy as is: processed by spice_get_node() later
    +    * the format is "some_text@spice_get_node  some_additional_text"
    +    * Examples:
    +    *   Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id])
    +    *     will translate to: 
    +    *   Id=6.6177u
    +    *   Id=@spice_get_node i(\@m.@path@spiceprefix@name\.msky130_fd_pr__@model\[id]) A
    +    *     will translate to: 
    +    *   Id=6.6177uA
    +    * note the required separator spaces around the spice node. Spaces are used here as
    +    * separators since spice nodes don't allow spaces.
    +    * escapes are used for 2 reasons:
    +    * mark a @ as a literal character instead of a the start of a @var token to be substituted
    +    * mark the end of a @var, like for example @var\iable. In this case @var will
    +    * be substituted by xschem instead of @variable
    +    *
    +    * caveats: only one @spice_get_node is allowed in a string
    +    */
    +   else if(strcmp(token,"@spice_get_node")==0 )
    +   {
    +     STR_ALLOC(&result, 15 + result_pos, &size);
    +     memcpy(result+result_pos, token, 16);
    +     result_pos += 15;
    +   }
        else if(strncmp(token,"@spice_get_voltage(", 19)==0 )
        {
          int start_level; /* hierarchy level where waves were loaded */
    @@ -3914,7 +3990,7 @@ const char *translate(int inst, const char* s)
            char *global_net;
            size_t len;
            int idx, n, multip;
    -       double val;
    +       double val = 0.0;
            const char *valstr;
            tmp = strlen(token) + 1;
            if(path) {
    @@ -3956,8 +4032,8 @@ const char *translate(int inst, const char* s)
                  len = 3;
                } else if(idx < 0) {
                  valstr = "-";
    -             xctx->tok_size = 5;
    -             len = 5;
    +             xctx->tok_size = 1;
    +             len = 1;
                } else {
                  /* always use engineering as these tokens are generated from single
                   * @spice_get_voltage patterns */
    @@ -3986,7 +4062,7 @@ const char *translate(int inst, const char* s)
            char *dev = NULL;
            size_t len;
            int idx, n;
    -       double val;
    +       double val = 0.0;
            const char *valstr;
            tmp = strlen(token) + 1;
            if(path) {
    @@ -4029,9 +4105,9 @@ const char *translate(int inst, const char* s)
                  val = xctx->raw->cursor_b_val[idx];
                }
                if(idx < 0) {
    -             valstr = "undef";
    -             xctx->tok_size = 5;
    -             len = 5;
    +             valstr = "-";
    +             xctx->tok_size = 1;
    +             len = 1;
                } else {
                  /* always use engineering as these tokens are generated from single
                   * @spice_get_voltage patterns */
    @@ -4093,9 +4169,9 @@ const char *translate(int inst, const char* s)
                idx1 = get_raw_index(fqnet1, NULL);
                idx2 = get_raw_index(fqnet2, NULL);
                if( (!gnd1 && idx1 < 0) || (!gnd2 && idx2 < 0) ) {
    -             valstr = "";
    -             xctx->tok_size = 0;
    -             len = 0;
    +             valstr = "-";
    +             xctx->tok_size = 1;
    +             len = 1;
                } else {
                  double val1 = gnd1 ? 0.0 : xctx->raw->cursor_b_val[idx1];
                  double val2 = gnd2 ? 0.0 : xctx->raw->cursor_b_val[idx2];
    @@ -4126,7 +4202,7 @@ const char *translate(int inst, const char* s)
            char *dev = NULL;
            size_t len;
            int idx;
    -       double val;
    +       double val = 0.0;
            const char *valstr;
            if(path) {
              int skip = 0;
    @@ -4166,9 +4242,9 @@ const char *translate(int inst, const char* s)
                val = xctx->raw->cursor_b_val[idx];
              }
              if(idx < 0) {
    -           valstr = "";
    -           xctx->tok_size = 0;
    -           len = 0;
    +           valstr = "-";
    +           xctx->tok_size = 1;
    +           len = 1;
              } else {
                valstr = engineering ? dtoa_eng(val) : dtoa(val);
                len = xctx->tok_size;
    @@ -4286,7 +4362,8 @@ const char *translate(int inst, const char* s)
     
      /* if result is like: 'tcleval(some_string)' pass it thru tcl evaluation so expressions
       * can be calculated */
    - my_strdup2(_ALLOC_ID_, &translated_tok, tcl_hook2(result));
    + my_strdup2(_ALLOC_ID_, &translated_tok, spice_get_node(tcl_hook2(result)));
    + 
      return translated_tok;
     }
     
    diff --git a/src/xinit.c b/src/xinit.c
    index 5ec8745f..1fbc00a1 100644
    --- a/src/xinit.c
    +++ b/src/xinit.c
    @@ -715,7 +715,7 @@ static void delete_schematic_data(int delete_pixmap)
       /* delete instances, wires, lines, rects, arcs, polys, texts, hash_inst, hash_wire, 
        * inst & wire .node fields, instance name hash */
       remove_symbols();
    -  str_replace(NULL, NULL, NULL, 0);
    +  str_replace(NULL, NULL, NULL, 0, -1);
       escape_chars(NULL, "");
       sanitize(NULL);
       is_generator(NULL);
    diff --git a/src/xschem.h b/src/xschem.h
    index 884076d2..03163306 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -1601,6 +1601,7 @@ extern Ptr_hashentry *ptr_hash_lookup(Ptr_hashtable *hashtable,
     extern char *trim_chars(const char *str, const char *sep);
     extern char *find_nth(const char *str, const char *sep, const char *quote, int keep_quote, int n);
     extern int isonlydigit(const char *s);
    +extern const char *spice_get_node(const char *token);
     extern const char *translate(int inst, const char* s);
     extern const char* translate2(Lcc *lcc, int level, char* s);
     extern const char *translate3(const char* s, int eat_escapes, const char *s1, const char *s2, const char *s3);
    @@ -1728,7 +1729,7 @@ extern int drc_check(int i);
     extern void change_elem_order(int n);
     extern int is_generator(const char *name);
     extern char *str_chars_replace(const char *str, const char *replace_set, const char with);
    -extern char *str_replace(const char *str, const char *rep, const char *with, int escape);
    +extern char *str_replace(const char *str, const char *rep, const char *with, int escape, int count);
     extern char *escape_chars(const char *source, const char *charset);
     extern int set_different_token(char **s,const char *new, const char *old);
     extern void print_hilight_net(int show);
    
    From 2c1e1c1fe77999ee0f096bca37173b89abc05eb8 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Fri, 10 Jan 2025 02:35:50 +0100
    Subject: [PATCH 2/3] refactoring and cleanup of wave_callback() -8-
    
    ---
     src/callback.c | 66 ++++++++++++++++++++++++--------------------------
     1 file changed, 32 insertions(+), 34 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index b1ca8560..1e6b835f 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -622,9 +622,9 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
               }
               event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */
             }
    -        need_fullredraw = 1;
    +        need_all_redraw = 1;
           }
    -      /* Numerically set cursor position */
    +      /* Numerically set cursor position  *** DO NOT PUT AN `else if` BELOW *** */
           if(xctx->graph_flags & 4) {
             double logcursor, cursor;
             if(r->flags & 4) { /* private_cursor */
    @@ -653,6 +653,36 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
             }
             need_fullredraw = 1;
           }
    +      /* Numerically set hcursor position  *** DO NOT PUT AN `else if` BELOW *** */
    +      if(xctx->graph_flags & 128) {
    +        double logcursor, cursor;
    +        logcursor = cursor = gr->hcursor1_y;
    +        if(gr->logy ) {
    +          logcursor = mylog10(cursor);
    +        }
    +        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
    +          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    +          cursor = atof_eng(tclresult());
    +          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", dtoa(cursor)));
    +          event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */
    +        }
    +        need_redraw_master = 1;
    +      }
    +      /* Numerically set hcursor position *** DO NOT PUT AN `else if` BELOW *** */
    +      if(xctx->graph_flags & 256) {
    +        double logcursor, cursor;
    +        logcursor = cursor = gr->hcursor2_y;
    +        if(gr->logy ) {
    +          logcursor = mylog10(cursor);
    +        }
    +        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
    +          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    +          cursor = atof_eng(tclresult());
    +          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", dtoa(cursor)));
    +          event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */
    +        }
    +        need_redraw_master = 1;
    +      }
         }
         else if(event == -3 && button == Button1) {
           if(!edit_wave_attributes(1, i, gr)) {
    @@ -900,38 +930,6 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
             }
           }
         }
    -    else if(event == ButtonPress && button == Button3) {
    -      /* Numerically set hcursor position */
    -      if(xctx->graph_flags & 128) {
    -        double logcursor, cursor;
    -        logcursor = cursor = gr->hcursor1_y;
    -        if(gr->logy ) {
    -          logcursor = mylog10(cursor);
    -        }
    -        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
    -          xctx->ui_state &= ~GRAPHPAN; /* we are setting a cursor so clear GRAPHPAN set before */
    -          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    -          cursor = atof_eng(tclresult());
    -          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", dtoa(cursor)));
    -        }
    -        need_redraw = 1;
    -      }
    -      /* Numerically set hcursor position */
    -      if(xctx->graph_flags & 256) {
    -        double logcursor, cursor;
    -        logcursor = cursor = gr->hcursor2_y;
    -        if(gr->logy ) {
    -          logcursor = mylog10(cursor);
    -        }
    -        if(fabs(xctx->mousey - W_Y(logcursor)) < 10) {
    -          xctx->ui_state &= ~GRAPHPAN; /* we are setting a cursor so clear GRAPHPAN set before */
    -          tclvareval("input_line {Pos:} {} ", dtoa_eng(cursor), NULL);
    -          cursor = atof_eng(tclresult());
    -          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", dtoa(cursor)));
    -        }
    -        need_redraw = 1;
    -      }
    -    }
         else if(event == ButtonPress && button == Button5 && !(state & ShiftMask)) {
           double delta;
           /* vertical move of waveforms with mouse wheel */
    
    From a29814d72825c91bfa9f57c601fdfbe9018d0e97 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Fri, 10 Jan 2025 06:03:52 +0100
    Subject: [PATCH 3/3] get_additional_symbols(), get_sch_from_sym(): allow to
     resolve schematic calls like: schematic=generator.tcl( @n ) where n=11 is
     defined in instance attrs. some fixes in cursor movement in waves_callback()
    
    ---
     src/actions.c  | 20 ++++++++++--
     src/callback.c | 85 ++++++++++++++++++++++++++++++--------------------
     2 files changed, 69 insertions(+), 36 deletions(-)
    
    diff --git a/src/actions.c b/src/actions.c
    index 3e4c95ae..f726c945 100644
    --- a/src/actions.c
    +++ b/src/actions.c
    @@ -1965,9 +1965,17 @@ void get_additional_symbols(int what)
           my_strdup(_ALLOC_ID_, &spice_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"spice_sym_def",6));
           my_strdup(_ALLOC_ID_, &verilog_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"verilog_sym_def",4));
           my_strdup(_ALLOC_ID_, &vhdl_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"vhdl_sym_def",4));
    +
    +      dbg(1, "schematic=%s\n", get_tok_value(xctx->inst[i].prop_ptr,"schematic",2));
    +      /* resolve schematic=generator.tcl( @n ) where n=11 is defined in instance attrs */
    +      my_strdup2(_ALLOC_ID_, &sch,
    +          translate3(get_tok_value(xctx->inst[i].prop_ptr,"schematic",2), 1,
    +            xctx->inst[i].prop_ptr, NULL, NULL));
    +      dbg(1, "sch=%s\n", sch);
    +    
           my_strdup2(_ALLOC_ID_, &sch, tcl_hook2(
    -         str_replace( get_tok_value(xctx->inst[i].prop_ptr,"schematic",2), "@symname",
    -           get_cell(xctx->inst[i].name, 0), '\\', -1)));
    +         str_replace(sch, "@symname", get_cell(xctx->inst[i].name, 0), '\\', -1)));
    +
           dbg(1, "get_additional_symbols(): inst=%d sch=%s\n",i,  sch);
           /* schematic does not exist */
           if(sch[0] && stat(abs_sym_path(sch, ""), &buf)) {
    @@ -1982,6 +1990,7 @@ void get_additional_symbols(int what)
             char *symname_attr = NULL;
             int ignore_schematic = 0;
             xSymbol *symptr = xctx->inst[i].ptr + xctx->sym;
    +
             my_strdup2(_ALLOC_ID_, &default_schematic, get_tok_value(symptr->prop_ptr,"default_schematic",0));
             ignore_schematic = !strcmp(default_schematic, "ignore");
     
    @@ -2081,7 +2090,12 @@ void get_sch_from_sym(char *filename, xSymbol *sym, int inst, int fallback)
       }
       dbg(1, "get_sch_from_sym(): current_dirname= %s\n", xctx->current_dirname);
       dbg(1, "get_sch_from_sym(): symbol %s inst=%d web_url=%d\n", sym->name, inst, web_url);
    -  if(inst >= 0) my_strdup(_ALLOC_ID_, &str_tmp,  get_tok_value(xctx->inst[inst].prop_ptr, "schematic", 2));
    +  if(inst >= 0) {
    +     /* resolve schematic=generator.tcl( @n ) where n=11 is defined in instance attrs */
    +     my_strdup2(_ALLOC_ID_, &str_tmp,
    +        translate3(get_tok_value(xctx->inst[inst].prop_ptr,"schematic",2), 1,
    +          xctx->inst[inst].prop_ptr, NULL, NULL));
    +  }
       if(!str_tmp) my_strdup2(_ALLOC_ID_, &str_tmp,  get_tok_value(sym->prop_ptr, "schematic", 2));
       if(str_tmp[0]) { /* schematic attribute in symbol or instance was given */
         /* @symname in schematic attribute will be replaced with symbol name */
    diff --git a/src/callback.c b/src/callback.c
    index 1e6b835f..757b1963 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -481,41 +481,12 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
           need_redraw_master = 1;
         }   
     
    -    /* move cursor1 */
    -    /* set cursor position from master graph x-axis */
    -    else if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 16 )) {
    -      double c;
     
    -      c = G_X(xctx->mousex);
    -      if(gr->logx) c = pow(10, c);
    -      if(r->flags & 4) { /* private_cursor */
    -        my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(c)));
    -      } else {
    -        xctx->graph_cursor1_x = c;
    -      }
    -      need_all_redraw = 1;
    -    }
    -    /* move cursor2 */
    -    /* set cursor position from master graph x-axis */
    -    else if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 32 )) {
    -      double c;
    -      int floaters = there_are_floaters();
     
    -      c = G_X(xctx->mousex);
    -      if(gr->logx) c = pow(10, c);
    -      if(r->flags & 4) { /* private_cursor */
    -        my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor2_x", dtoa(c)));
    -      } else {
    -        xctx->graph_cursor2_x = c; 
    -      }       
    -      if(tclgetboolvar("live_cursor2_backannotate")) {
    -        backannotate_at_cursor_b_pos(r, gr);
    -        if(floaters) set_modify(-2); /* update floater caches to reflect actual backannotation */
    -        need_fullredraw = 1;
    -      } else {
    -        need_all_redraw = 1;
    -      }
    -    }
    +
    +
    +
    +
         if(xctx->ui_state & GRAPHPAN) goto finish; /* After GRAPHPAN only need to check Motion events for cursors */
         if(xctx->mousey_snap < W_Y(gr->gy2)) {
           xctx->graph_top = 1;
    @@ -930,6 +901,54 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int
             }
           }
         }
    +
    +
    +
    +    /* move cursor1 */
    +    /* set cursor position from master graph x-axis */
    +    else if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 16 )) {
    +      double c;
    +
    +      /* selected or locked or master */
    +      if( r->sel || !(r->flags & 2) || i == xctx->graph_master) {
    +        c = G_X(xctx->mousex);
    +        if(gr->logx) c = pow(10, c);
    +        if(r->flags & 4) { /* private_cursor */
    +          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(c)));
    +        } else {
    +          xctx->graph_cursor1_x = c;
    +        }
    +        need_all_redraw = 1;
    +      }
    +    }
    +    /* move cursor2 */
    +    /* set cursor position from master graph x-axis */
    +    else if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 32 )) {
    +      double c;
    +      int floaters = there_are_floaters();
    +
    +      /* selected or locked or master */
    +      if( r->sel || !(r->flags & 2) || i == xctx->graph_master) {
    +        c = G_X(xctx->mousex);
    +        if(gr->logx) c = pow(10, c);
    +        if(r->flags & 4) { /* private_cursor */
    +          my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor2_x", dtoa(c)));
    +        } else {
    +          xctx->graph_cursor2_x = c; 
    +        }       
    +        if(tclgetboolvar("live_cursor2_backannotate")) {
    +          backannotate_at_cursor_b_pos(r, gr);
    +          if(floaters) set_modify(-2); /* update floater caches to reflect actual backannotation */
    +          need_fullredraw = 1;
    +        } else {
    +          need_all_redraw = 1;
    +        }
    +      }
    +    }
    +
    +
    +
    +
         else if(event == ButtonPress && button == Button5 && !(state & ShiftMask)) {
           double delta;
           /* vertical move of waveforms with mouse wheel */