From 9dff3e1e3ec2def1aa2ae9f7153882b9346503b8 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 20 Feb 2025 11:42:26 +0100 Subject: [PATCH 01/67] refactor and fix small graphic glitches in draw_snap_cursor() --- src/callback.c | 36 +++++++++++++++++++++--------------- src/xschem.h | 1 - 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/callback.c b/src/callback.c index 92e82414..a2e0240d 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1568,8 +1568,11 @@ void erase_snap_cursor(double prev_x, double prev_y, int snapcursor_size) { } } -/* action = 1 => erase */ -void draw_snap_cursor(int action) { +/* action == 3 : delete and draw + * action == 1 : delete + * action == 2 : draw + */ +static void draw_snap_cursor(int action) { int snapcursor_size; int pos_changed; int prev_draw_window = xctx->draw_window; @@ -1581,21 +1584,22 @@ void draw_snap_cursor(int action) { /* Save current drawing context */ xctx->draw_pixmap = 0; xctx->draw_window = 1; - /* Erase and redraw the cursor if needed */ + /* Erase the cursor */ if (action & 1) { - double new_x, new_y; erase_snap_cursor(xctx->prev_snapx, xctx->prev_snapy, snapcursor_size); - + draw_selection(xctx->gc[SELLAYER], 0); + } + /* Redraw the cursor */ + if (action & 2) { + double new_x, new_y; find_snap_position(&new_x, &new_y, pos_changed); draw_snap_cursor_shape(xctx->gc[xctx->crosshair_layer],new_x, new_y, snapcursor_size); - /* Update previous position tracking */ xctx->prev_gridx = xctx->mousex_snap; xctx->prev_gridy = xctx->mousey_snap; xctx->prev_snapx = new_x; xctx->prev_snapy = new_y; } - draw_selection(xctx->gc[SELLAYER], 0); /* Restore previous drawing context */ xctx->draw_window = prev_draw_window; xctx->draw_pixmap = prev_draw_pixmap; @@ -2446,6 +2450,7 @@ static void handle_enter_notify(int draw_xhair, int crosshair_size) xctx->mousey_snap = -340; merge_file(1, ".sch"); } + return; } static void handle_motion_notify(int event, KeySym key, int state, int rstate, int button, @@ -2458,8 +2463,8 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i } if(draw_xhair) { draw_crosshair(1, state); /* when moving mouse: first action is delete crosshair, will be drawn later */ - if(snap_cursor && wire_draw_active) draw_snap_cursor(1); } + if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* clear */ /* pan schematic */ if(xctx->ui_state & STARTPAN) pan(RUBBER, mx, my); @@ -2467,7 +2472,7 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i if(draw_xhair) { draw_crosshair(2, state); /* locked UI: draw new crosshair and break out */ } - if(snap_cursor && wire_draw_active) draw_snap_cursor(2); + if(snap_cursor && wire_draw_active) draw_snap_cursor(2); /* redraw */ return; } @@ -2568,11 +2573,12 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i if(draw_xhair) { draw_crosshair(2, state); /* what = 2(draw) */ } - if(snap_cursor && wire_draw_active) draw_snap_cursor(2); + if(snap_cursor && wire_draw_active) draw_snap_cursor(2); /* redraw */ } static void handle_key_press(int event, KeySym key, int state, int rstate, int mx, int my, - int button, int aux, int infix_interface, int enable_stretch, const char *win_path, double c_snap) + int button, int aux, int infix_interface, int enable_stretch, + int wire_draw_active, const char *win_path, double c_snap) { char str[PATH_MAX + 100]; int cadence_compat = tclgetboolvar("cadence_compat"); @@ -2858,9 +2864,9 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m if(xctx->ui_state2 & MENUSTARTWIRE) { xctx->ui_state2 &= ~MENUSTARTWIRE; } + if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* erase */ if(tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE) && cadence_compat) { xctx->last_command &= ~STARTWIRE; - if(snap_cursor) draw_snap_cursor(1); } return; } @@ -4408,7 +4414,7 @@ static void handle_button_release(int event, KeySym key, int state, int button, return; } if(draw_xhair) draw_crosshair(3, state); /* restore crosshair when selecting / unselecting */ - if(snap_cursor && wire_draw_active) draw_snap_cursor(3); + if(snap_cursor && wire_draw_active) draw_snap_cursor(3); /* erase & redraw */ } static void handle_double_click(int event, int state, KeySym key, int button, @@ -4595,7 +4601,7 @@ int wire_draw_active = (xctx->ui_state & STARTWIRE) || case LeaveNotify: if(draw_xhair) draw_crosshair(1, state); /* clear crosshair when exiting window */ - if(snap_cursor && wire_draw_active) draw_snap_cursor(1); + if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* erase */ tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); xctx->mouse_inside = 0; break; @@ -4642,7 +4648,7 @@ int wire_draw_active = (xctx->ui_state & STARTWIRE) || case KeyPress: handle_key_press(event, key, state, rstate, mx, my, button, aux, - infix_interface, enable_stretch, win_path, c_snap); + infix_interface, enable_stretch, wire_draw_active, win_path, c_snap); break; case ButtonPress: diff --git a/src/xschem.h b/src/xschem.h index 27700e9f..12e1c069 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1400,7 +1400,6 @@ extern void tclmainloop(void); extern int Tcl_AppInit(Tcl_Interp *interp); extern void abort_operation(void); extern void draw_crosshair(int what, int state); -extern void draw_snap_cursor(int what); extern void backannotate_at_cursor_b_pos(xRect *r, Graph_ctx *gr); /* extern void snapped_wire(double c_snap); */ extern int callback(const char *win_path, int event, int mx, int my, KeySym key, From f15520d01afd3a7ad538de6e24d76c8366793c8f Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 20 Feb 2025 11:52:22 +0100 Subject: [PATCH 02/67] start snapped wires in manhattan mode as done woth normal wires --- src/callback.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/callback.c b/src/callback.c index a2e0240d..48c7b75e 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1703,6 +1703,7 @@ void snapped_wire(double c_snap) find_closest_net_or_symbol_pin(xctx->mousex, xctx->mousey, &x, &y); xctx->mx_double_save = my_round(x / c_snap) * c_snap; xctx->my_double_save = my_round(y / c_snap) * c_snap; + xctx->manhattan_lines = 1; new_wire(PLACE, x, y); new_wire(RUBBER, xctx->mousex_snap,xctx->mousey_snap); } @@ -2812,12 +2813,14 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m draw(); return; } - if(key == 'X' && rstate == 0) /* highlight discrepanciens between selected instance pin and net names */ + /* highlight discrepanciens between selected instance pin and net names */ + if(key == 'X' && rstate == 0) { hilight_net_pin_mismatches(); return; } - if(key== 'W' /* && !xctx->ui_state */ && rstate == 0 && !cadence_compat) { /* create wire snapping to closest instance pin */ + /* create wire snapping to closest instance pin */ + if(key== 'W' /* && !xctx->ui_state */ && rstate == 0 && !cadence_compat) { if(xctx->semaphore >= 2) return; if(infix_interface) { snapped_wire(c_snap); @@ -2827,7 +2830,8 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m } return; } - if(key== 's' /* && !xctx->ui_state */ && rstate == 0 && cadence_compat) { /* create wire snapping to closest instance pin (cadence keybind) */ + /* create wire snapping to closest instance pin (cadence keybind) */ + if(key== 's' /* && !xctx->ui_state */ && rstate == 0 && cadence_compat) { if(xctx->semaphore >= 2) return; snapped_wire(c_snap); return; From 62a2e6a36713b4d5ae1801dc5101ca006d0a6fab Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 20 Feb 2025 13:56:53 +0100 Subject: [PATCH 03/67] refactored draw_crosshair() into smaller pieces. draw_snap_cursor executed much less frequently (only on snapped ouse change). Zooming with mouse wheel will not clear MENUSTART ui_state --- src/callback.c | 418 ++++++++++++++++++++++--------------------------- 1 file changed, 190 insertions(+), 228 deletions(-) diff --git a/src/callback.c b/src/callback.c index 48c7b75e..d2ca603d 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1379,232 +1379,6 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int return 0; } -/* what == 3 (+4) : delete and draw (force) - * what == 1 (+4) : delete (force) - * what == 2 (+4) : draw (force) - * what == 4 : force (re)clear and/or (re)draw even if on same point */ -void draw_crosshair(int what, int state) -{ - int sdw, sdp; - int xhair_size = tclgetintvar("crosshair_size"); - double mx, my; - int changed = 0; - dbg(1, "draw_crosshair(): what=%d\n", what); - sdw = xctx->draw_window; - sdp = xctx->draw_pixmap; - - if(!xctx->mouse_inside) return; - mx = xctx->mousex_snap; - my = xctx->mousey_snap; - if( ( (xctx->ui_state & (MENUSTART | STARTWIRE) ) || xctx->ui_state == 0 ) && - (state == ShiftMask) ) { - /* mouse not changed so closest net or symbol pin unchanged too */ - if(mx == xctx->prev_m_crossx && my == xctx->prev_m_crossy) { - mx = xctx->prev_crossx; /* get previous one */ - my = xctx->prev_crossy; - } else { - /* mouse position changed, so find new closest net or pin */ - find_closest_net_or_symbol_pin(xctx->mousex_snap, xctx->mousey_snap, &mx, &my); - changed = 1; /* we force a cursor redraw */ - dbg(1, "find\n"); - } - } - - /* no changed closest pin/net, no force, mx,my is not changed. --> do nothing - | _____________| | - | | _____________________|____________________________ */ - if(!changed && !(what & 4) && mx == xctx->prev_crossx && my == xctx->prev_crossy) { - return; - } - dbg(1, "draw %d\n", what); - xctx->draw_pixmap = 0; - xctx->draw_window = 1; - if(what & 1) { /* delete previous */ - if(fix_broken_tiled_fill || !_unix) { - if(xhair_size) { - MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - 2 * INT_WIDTH(xctx->lw) + 2 * xhair_size, - 2 * INT_WIDTH(xctx->lw) + 2 * xhair_size, - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw) - xhair_size); - MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - 2 * INT_WIDTH(xctx->lw) + 2 * xhair_size, - 2 * INT_WIDTH(xctx->lw) + 2 * xhair_size, - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw) - xhair_size, - (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw) - xhair_size); - } else { /* full screen span xhair */ - MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], - 0, (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw), - xctx->xrect[0].width, 2 * INT_WIDTH(xctx->lw), - 0, (int)Y_TO_SCREEN(xctx->prev_crossy) - 1 * INT_WIDTH(xctx->lw)); - MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw), 0, - 2 * INT_WIDTH(xctx->lw), xctx->xrect[0].height, - (int)X_TO_SCREEN(xctx->prev_crossx) - 1 * INT_WIDTH(xctx->lw), 0); - } - - } else { - if(xhair_size) { - draw_xhair_line(xctx->gctiled, xhair_size, - X_TO_SCREEN(xctx->prev_crossx) - xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) - xhair_size, - X_TO_SCREEN(xctx->prev_crossx) + xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) - xhair_size); - draw_xhair_line(xctx->gctiled, xhair_size, - X_TO_SCREEN(xctx->prev_crossx) - xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) + xhair_size, - X_TO_SCREEN(xctx->prev_crossx) + xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) + xhair_size); - draw_xhair_line(xctx->gctiled, xhair_size, - X_TO_SCREEN(xctx->prev_crossx) - xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) - xhair_size, - X_TO_SCREEN(xctx->prev_crossx) - xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) + xhair_size); - draw_xhair_line(xctx->gctiled, xhair_size, - X_TO_SCREEN(xctx->prev_crossx) + xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) - xhair_size, - X_TO_SCREEN(xctx->prev_crossx) + xhair_size, - Y_TO_SCREEN(xctx->prev_crossy) + xhair_size); - } else { /* full screen span xhair */ - drawtempline(xctx->gctiled, NOW, X_TO_XSCHEM(xctx->areax1), - xctx->prev_crossy, X_TO_XSCHEM(xctx->areax2), xctx->prev_crossy); - drawtempline(xctx->gctiled, NOW, xctx->prev_crossx, Y_TO_XSCHEM(xctx->areay1), - xctx->prev_crossx, Y_TO_XSCHEM(xctx->areay2)); - } - } - } - if(what & 2) { /* draw new */ - if(xhair_size) { - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - X_TO_SCREEN(mx) - xhair_size, - Y_TO_SCREEN(my) - xhair_size, - X_TO_SCREEN(mx) + xhair_size, - Y_TO_SCREEN(my) - xhair_size); - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - X_TO_SCREEN(mx) - xhair_size, - Y_TO_SCREEN(my) + xhair_size, - X_TO_SCREEN(mx) + xhair_size, - Y_TO_SCREEN(my) + xhair_size); - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - X_TO_SCREEN(mx) - xhair_size, - Y_TO_SCREEN(my) - xhair_size, - X_TO_SCREEN(mx) - xhair_size, - Y_TO_SCREEN(my) + xhair_size); - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - X_TO_SCREEN(mx) + xhair_size, - Y_TO_SCREEN(my) - xhair_size, - X_TO_SCREEN(mx) + xhair_size, - Y_TO_SCREEN(my) + xhair_size); - } else { /* full screen span xhair */ - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - xctx->areax1, Y_TO_SCREEN(my), - xctx->areax2, Y_TO_SCREEN(my)); - draw_xhair_line(xctx->gc[xctx->crosshair_layer], xhair_size, - X_TO_SCREEN(mx), xctx->areay1, - X_TO_SCREEN(mx), xctx->areay2); - } - } - if(what) draw_selection(xctx->gc[SELLAYER], 0); - - if(what & 2) { - /* previous closest pin or net position (if snap wire or Shift pressed) */ - xctx->prev_crossx = mx; - xctx->prev_crossy = my; - /* previous mouse_snap position */ - xctx->prev_m_crossx = xctx->mousex_snap; - xctx->prev_m_crossy = xctx->mousey_snap; - } - - xctx->draw_window = sdw; - xctx->draw_pixmap = sdp; -} - -void find_snap_position(double *x, double *y, int pos_changed) { - if (!pos_changed) { - *x = xctx->prev_snapx; - *y = xctx->prev_snapy; - } else { - xctx->closest_pin_found = find_closest_net_or_symbol_pin( - xctx->mousex, xctx->mousey, x, y); - } -} - -void draw_snap_cursor_shape(GC gc, double x, double y, int snapcursor_size) { - /* Convert coordinates to screen space */ - double screen_x = X_TO_SCREEN(x); - double screen_y = Y_TO_SCREEN(y); - double left = screen_x - snapcursor_size; - double right = screen_x + snapcursor_size; - double top = screen_y - snapcursor_size; - double bottom = screen_y + snapcursor_size; - int i; - /* Define crosshair lines */ - double lines[4][4]; - lines[0][0] = screen_x; lines[0][1] = top; lines[0][2] = right; lines[0][3] = screen_y; - lines[1][0] = right; lines[1][1] = screen_y; lines[1][2] = screen_x; lines[1][3] = bottom; - lines[2][0] = screen_x; lines[2][1] = bottom; lines[2][2] = left; lines[2][3] = screen_y; - lines[3][0] = left; lines[3][1] = screen_y; lines[3][2] = screen_x; lines[3][3] = top; - /* Draw crosshair lines */ - for (i = 0; i < 4; i++) { - draw_xhair_line(gc, snapcursor_size, lines[i][0], lines[i][1], lines[i][2], lines[i][3]); - } -} - -void erase_snap_cursor(double prev_x, double prev_y, int snapcursor_size) { - if (fix_broken_tiled_fill || !_unix) { - MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], - (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, - (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size, - 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, - 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, - (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, - (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size); - } else { - draw_snap_cursor_shape(xctx->gctiled, prev_x, prev_y, snapcursor_size); - } -} - -/* action == 3 : delete and draw - * action == 1 : delete - * action == 2 : draw - */ -static void draw_snap_cursor(int action) { - int snapcursor_size; - int pos_changed; - int prev_draw_window = xctx->draw_window; - int prev_draw_pixmap = xctx->draw_pixmap; - - if (!xctx->mouse_inside) return; /* Early exit if mouse is outside */ - snapcursor_size = tclgetintvar("snap_cursor_size"); - pos_changed = (xctx->mousex_snap != xctx->prev_gridx) || (xctx->mousey_snap != xctx->prev_gridy); - /* Save current drawing context */ - xctx->draw_pixmap = 0; - xctx->draw_window = 1; - /* Erase the cursor */ - if (action & 1) { - erase_snap_cursor(xctx->prev_snapx, xctx->prev_snapy, snapcursor_size); - draw_selection(xctx->gc[SELLAYER], 0); - } - /* Redraw the cursor */ - if (action & 2) { - double new_x, new_y; - find_snap_position(&new_x, &new_y, pos_changed); - draw_snap_cursor_shape(xctx->gc[xctx->crosshair_layer],new_x, new_y, snapcursor_size); - /* Update previous position tracking */ - xctx->prev_gridx = xctx->mousex_snap; - xctx->prev_gridy = xctx->mousey_snap; - xctx->prev_snapx = new_x; - xctx->prev_snapy = new_y; - } - /* Restore previous drawing context */ - xctx->draw_window = prev_draw_window; - xctx->draw_pixmap = prev_draw_pixmap; -} - /* complete the STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */ static int end_place_move_copy_zoom() { @@ -1686,6 +1460,194 @@ static int end_place_move_copy_zoom() return 0; } +static void draw_snap_cursor_shape(GC gc, double x, double y, int snapcursor_size) { + /* Convert coordinates to screen space */ + double screen_x = X_TO_SCREEN(x); + double screen_y = Y_TO_SCREEN(y); + double left = screen_x - snapcursor_size; + double right = screen_x + snapcursor_size; + double top = screen_y - snapcursor_size; + double bottom = screen_y + snapcursor_size; + int i; + /* Define crosshair lines */ + double lines[4][4]; + lines[0][0] = screen_x; lines[0][1] = top; lines[0][2] = right; lines[0][3] = screen_y; + lines[1][0] = right; lines[1][1] = screen_y; lines[1][2] = screen_x; lines[1][3] = bottom; + lines[2][0] = screen_x; lines[2][1] = bottom; lines[2][2] = left; lines[2][3] = screen_y; + lines[3][0] = left; lines[3][1] = screen_y; lines[3][2] = screen_x; lines[3][3] = top; + /* Draw crosshair lines */ + for (i = 0; i < 4; i++) { + draw_xhair_line(gc, snapcursor_size, lines[i][0], lines[i][1], lines[i][2], lines[i][3]); + } +} + +static void erase_snap_cursor(double prev_x, double prev_y, int snapcursor_size) { + if (fix_broken_tiled_fill || !_unix) { + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, + (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size, + 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, + 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, + (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, + (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size); + } else { + draw_snap_cursor_shape(xctx->gctiled, prev_x, prev_y, snapcursor_size); + } +} + +static void find_snap_position(double *x, double *y, int pos_changed) { + if (!pos_changed) { + *x = xctx->prev_snapx; + *y = xctx->prev_snapy; + } else { + xctx->closest_pin_found = find_closest_net_or_symbol_pin( + xctx->mousex, xctx->mousey, x, y); + } +} + +/* action == 3 : delete and draw + * action == 1 : delete + * action == 2 : draw + */ +static void draw_snap_cursor(int action) { + int snapcursor_size; + int pos_changed; + int prev_draw_window = xctx->draw_window; + int prev_draw_pixmap = xctx->draw_pixmap; + + if (!xctx->mouse_inside) return; /* Early exit if mouse is outside */ + snapcursor_size = tclgetintvar("snap_cursor_size"); + pos_changed = (xctx->mousex_snap != xctx->prev_gridx) || (xctx->mousey_snap != xctx->prev_gridy); + /* Save current drawing context */ + xctx->draw_pixmap = 0; + xctx->draw_window = 1; + if(pos_changed) { + /* Erase the cursor */ + if (action & 1) { + erase_snap_cursor(xctx->prev_snapx, xctx->prev_snapy, snapcursor_size); + draw_selection(xctx->gc[SELLAYER], 0); + } + /* Redraw the cursor */ + if (action & 2) { + double new_x, new_y; + find_snap_position(&new_x, &new_y, pos_changed); + draw_snap_cursor_shape(xctx->gc[xctx->crosshair_layer],new_x, new_y, snapcursor_size); + /* Update previous position tracking */ + xctx->prev_gridx = xctx->mousex_snap; + xctx->prev_gridy = xctx->mousey_snap; + xctx->prev_snapx = new_x; + xctx->prev_snapy = new_y; + } + } + /* Restore previous drawing context */ + xctx->draw_window = prev_draw_window; + xctx->draw_pixmap = prev_draw_pixmap; +} + +static void erase_crosshair(int size) { + + int prev_cr_x = (int)X_TO_SCREEN(xctx->prev_crossx); + int prev_cr_y = (int)Y_TO_SCREEN(xctx->prev_crossy); + int lw = INT_WIDTH(xctx->lw); + if(size) { + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size, 2 * lw + 2 * size, 2 * lw + 2 * size, + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size); + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size, 2 * lw + 2 * size, 2 * lw + 2 * size, + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size); + } else { /* full screen span xhair */ + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + 0, prev_cr_y - 1 * lw, xctx->xrect[0].width, 2 * lw, 0, prev_cr_y - 1 * lw); + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw, 0, 2 * lw, xctx->xrect[0].height, prev_cr_x - 1 * lw, 0); + } +} + +static void draw_crosshair_shape(GC gc, double x, double y, int size) +{ + double screen_x = X_TO_SCREEN(x); + double screen_y = Y_TO_SCREEN(y); + if(size) { + draw_xhair_line(gc, size, screen_x - size, screen_y - size, screen_x + size, screen_y - size); + draw_xhair_line(gc, size, screen_x - size, screen_y + size, screen_x + size, screen_y + size); + draw_xhair_line(gc, size, screen_x - size, screen_y - size, screen_x - size, screen_y + size); + draw_xhair_line(gc, size, screen_x + size, screen_y - size, screen_x + size, screen_y + size); + } else { /* full screen span xhair */ + draw_xhair_line(gc, size, xctx->areax1, screen_y, xctx->areax2, screen_y); + draw_xhair_line(gc, size, screen_x, xctx->areay1, screen_x, xctx->areay2); + } +} + +/* what == 3 (+4) : delete and draw (force) + * what == 1 (+4) : delete (force) + * what == 2 (+4) : draw (force) + * what == 4 : force (re)clear and/or (re)draw even if on same point */ +void draw_crosshair(int what, int state) +{ + int sdw, sdp; + int xhair_size = tclgetintvar("crosshair_size"); + int snap_cursor = tclgetintvar("snap_cursor"); + double mx, my; + int changed = 0; + dbg(1, "draw_crosshair(): what=%d\n", what); + sdw = xctx->draw_window; + sdp = xctx->draw_pixmap; + + if(!xctx->mouse_inside) return; + mx = xctx->mousex_snap; + my = xctx->mousey_snap; + if( ( (xctx->ui_state & (MENUSTART | STARTWIRE) ) || xctx->ui_state == 0 ) && + (state == ShiftMask)) { + if(!snap_cursor) { + /* mouse not changed so closest net or symbol pin unchanged too */ + if(mx == xctx->prev_m_crossx && my == xctx->prev_m_crossy) { + mx = xctx->prev_crossx; /* get previous one */ + my = xctx->prev_crossy; + } else { + /* mouse position changed, so find new closest net or pin */ + find_closest_net_or_symbol_pin(xctx->mousex_snap, xctx->mousey_snap, &mx, &my); + changed = 1; /* we force a cursor redraw */ + dbg(1, "find\n"); + } + } else { + draw_snap_cursor(what); + } + } + + /* no changed closest pin/net, no force, mx,my is not changed. --> do nothing + | _____________| | + | | _____________________|____________________________ */ + if(!changed && !(what & 4) && mx == xctx->prev_crossx && my == xctx->prev_crossy) { + return; + } + dbg(1, "draw %d\n", what); + xctx->draw_pixmap = 0; + xctx->draw_window = 1; + if(what & 1) { /* delete previous */ + if(fix_broken_tiled_fill || !_unix) { + erase_crosshair(xhair_size); + } else { + draw_crosshair_shape(xctx->gctiled, xctx->prev_crossx, xctx->prev_crossy, xhair_size); + } + } + if(what & 2) { /* draw new */ + draw_crosshair_shape(xctx->gc[xctx->crosshair_layer], mx, my, xhair_size); + } + if(what) draw_selection(xctx->gc[SELLAYER], 0); + + if(what & 2) { + /* previous closest pin or net position (if snap wire or Shift pressed) */ + xctx->prev_crossx = mx; + xctx->prev_crossy = my; + /* previous mouse_snap position */ + xctx->prev_m_crossx = xctx->mousex_snap; + xctx->prev_m_crossy = xctx->mousey_snap; + } + xctx->draw_window = sdw; + xctx->draw_pixmap = sdp; +} + static void unselect_at_mouse_pos(int mx, int my) { xctx->last_command = 0; @@ -1696,7 +1658,7 @@ static void unselect_at_mouse_pos(int mx, int my) rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */ } -void snapped_wire(double c_snap) +static void snapped_wire(double c_snap) { double x, y; if(!(xctx->ui_state & STARTWIRE)){ @@ -4413,7 +4375,7 @@ static void handle_button_release(int event, KeySym key, int state, int button, } /* clear start from menu flag or infix_interface=0 start commands */ - if(xctx->ui_state & MENUSTART) { + if( state == Button1Mask && xctx->ui_state & MENUSTART) { xctx->ui_state &= ~MENUSTART; return; } From afe058a25d87feca706246555d4906967f541adb Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 20 Feb 2025 16:30:40 +0100 Subject: [PATCH 04/67] comments typos --- src/select.c | 2 +- src/xschem.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/select.c b/src/select.c index 620bac21..c3ae79c0 100644 --- a/src/select.c +++ b/src/select.c @@ -735,7 +735,7 @@ void bbox(int what,double x1,double y1, double x2, double y2) /* n = -1 : clear first selected info * n = -2 : return first selected element if still selected, or get first from - * selected list. Id no elements selected return first selected item (j = 0) + * selected list. If no elements selected return first selected item (j = 0) * n >= 0 : store indicated element as first selected */ int set_first_sel(unsigned short type, int n, unsigned int col) diff --git a/src/xschem.h b/src/xschem.h index 12e1c069..5dae2b9f 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -954,7 +954,7 @@ typedef struct { int lastsel; int maxsel; Selected *sel_array; - Selected first_sel; /* first selected instance (used as master when editing multile pbjects) */ + Selected first_sel; /* first selected instance (used as master when editing multiple objects) */ int prep_net_structs; int prep_hi_structs; int prep_hash_inst; From 90bb42e7fa15bb4e050227b88a648653c2e5539f Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 20 Feb 2025 16:52:23 +0100 Subject: [PATCH 05/67] fix regression: no dashed graph bbox --- src/draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.c b/src/draw.c index bee7b42b..92149469 100644 --- a/src/draw.c +++ b/src/draw.c @@ -2838,7 +2838,7 @@ static void draw_graph_grid(Graph_ctx *gr, void *ct) /* background */ filledrect(0, NOW, gr->rx1, gr->ry1, gr->rx2, gr->ry2, 2, -1, -1); /* graph bounding box */ - drawrect(GRIDLAYER, NOW, gr->rx1, gr->ry1, gr->rx2, gr->ry2, 0, -1, -1); + drawrect(GRIDLAYER, NOW, gr->rx1, gr->ry1, gr->rx2, gr->ry2, 2, -1, -1); bbox(START, 0.0, 0.0, 0.0, 0.0); bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2); From 34fd430c13b9d4662197cad16dc42aa1d3d68496 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 21 Feb 2025 11:12:27 +0100 Subject: [PATCH 06/67] svg_embedded_graph() and ps_embedded_graph() do not do a full redraw prior to graph rasterizing, draw only the current graph area. This avoid `overstrike` problems with superimposed text objects --- src/draw.c | 18 ++++++++++-------- src/psprint.c | 28 +++++++++++++--------------- src/svgdraw.c | 2 +- src/xschem.h | 2 +- src/xschem.tcl | 15 +++++++++++++-- 5 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/draw.c b/src/draw.c index 92149469..457d7ed7 100644 --- a/src/draw.c +++ b/src/draw.c @@ -4770,8 +4770,11 @@ static void draw_images_all(void) #endif } -void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, double ry2) +void svg_embedded_graph(FILE *fd, int i, double rx1, double ry1, double rx2, double ry2) { + #ifndef __unix__ + xRect *r = &xctx->rect[GRIDLAYER][i]; + #endif #if HAS_CAIRO==1 Zoom_info zi; char *ptr = NULL; @@ -4821,7 +4824,9 @@ void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, xctx->draw_pixmap=1; save = xctx->do_copy_area; xctx->do_copy_area=0; - draw(); + setup_graph_data(i, 0, &xctx->graph_struct); + draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, NULL); + #ifdef __unix__ png_sfc = cairo_xlib_surface_create(display, xctx->save_pixmap, visual, xctx->xrect[0].width, xctx->xrect[0].height); @@ -4834,12 +4839,9 @@ void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, cairo_set_source_surface(ct, xctx->cairo_save_sfc, 0, 0); cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); cairo_paint(ct); - for(int i = 0; i < xctx->rects[GRIDLAYER]; ++i) { - xRect *r = &xctx->rect[GRIDLAYER][i]; - if(r->flags & 1) { - setup_graph_data(i, 0, &xctx->graph_struct); - draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, (void *)ct); - } + if(r->flags & 1) { + setup_graph_data(i, 0, &xctx->graph_struct); + draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, (void *)ct); } #endif closure.buffer = NULL; diff --git a/src/psprint.c b/src/psprint.c index 3332aeb8..def868ff 100644 --- a/src/psprint.c +++ b/src/psprint.c @@ -250,8 +250,9 @@ static int ps_embedded_image(xRect* r, double x1, double y1, double x2, double y return 1; } -static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, double ry2) +static int ps_embedded_graph(int i, double rx1, double ry1, double rx2, double ry2) { + xRect *r = &xctx->rect[GRIDLAYER][i]; #if defined(HAS_LIBJPEG) && HAS_CAIRO==1 Zoom_info zi; double rw, rh, scale; @@ -269,7 +270,7 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl int quality=40; const char *quality_attr; size_t oLength; - int i; + int j; double sx1, sy1, sx2, sy2; /* screen position */ @@ -323,7 +324,9 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl d_c = tclgetboolvar("dark_colorscheme"); tclsetboolvar("dark_colorscheme", 0); build_colors(0, 0); - draw(); + setup_graph_data(i, 0, &xctx->graph_struct); + draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, NULL); + dbg(1, "width=%d, rwi=%d height=%d rhi=%d\n", xctx->xrect[0].width, rwi, xctx->xrect[0].height, rhi); #ifdef __unix__ png_sfc = cairo_xlib_surface_create(display, xctx->save_pixmap, visual, @@ -337,13 +340,8 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl cairo_set_source_surface(ct, xctx->cairo_save_sfc, 0, 0); cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); cairo_paint(ct); - for (i = 0; i < xctx->rects[GRIDLAYER]; ++i) { - xRect* r2 = &xctx->rect[GRIDLAYER][i]; - if (r2->flags & 1) { - setup_graph_data(i, 0, &xctx->graph_struct); - draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, (void *)ct); - } - } + setup_graph_data(i, 0, &xctx->graph_struct); + draw_graph(i, 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), &xctx->graph_struct, (void *)ct); #endif cairo_image_surface_write_to_jpeg_mem(png_sfc, &jpgData, &fileSize, quality); @@ -382,10 +380,10 @@ static int ps_embedded_graph(xRect* r, double rx1, double ry1, double rx2, doubl fprintf(fd, "} exec\n"); #if 1 /* break lines */ - for (i = 0; i < oLength; ++i) + for (j = 0; j < oLength; ++j) { - fputc(ascii85EncodedJpeg[i],fd); - if(i > 0 && (i % 64) == 0) + fputc(ascii85EncodedJpeg[j],fd); + if(j > 0 && (j % 64) == 0) { fputc('\n',fd); /* if (ascii85Encode[i+1]=='%') idx=63; imageMagic does this for some reason?! @@ -1449,8 +1447,8 @@ void create_ps(char **psfile, int what, int fullzoom, int eps) continue; } if (c == GRIDLAYER && (xctx->rect[c][i].flags & 1)) { /* graph */ - xRect* r = &xctx->rect[c][i]; - ps_embedded_graph(r, r->x1, r->y1, r->x2, r->y2); + xRect *r = &xctx->rect[c][i]; + ps_embedded_graph(i, r->x1, r->y1, r->x2, r->y2); } if(c != GRIDLAYER || !(xctx->rect[c][i].flags & 1) ) { ps_filledrect(c, xctx->rect[c][i].x1, xctx->rect[c][i].y1, diff --git a/src/svgdraw.c b/src/svgdraw.c index 601de7da..e277e7c9 100644 --- a/src/svgdraw.c +++ b/src/svgdraw.c @@ -1035,7 +1035,7 @@ void svg_draw(void) { if(xctx->rect[c][i].flags & 1) { /* graph */ xRect *r = &xctx->rect[c][i]; - svg_embedded_graph(fd, r, r->x1, r->y1, r->x2, r->y2); + svg_embedded_graph(fd, i, r->x1, r->y1, r->x2, r->y2); } } } diff --git a/src/xschem.h b/src/xschem.h index 5dae2b9f..84e30e0e 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1329,7 +1329,7 @@ extern int process_options(int argc, char **argv); extern void calc_drawing_bbox(xRect *boundbox, int selected); extern int ps_draw(int what, int fullzoom, int eps); extern void svg_draw(void); -extern void svg_embedded_graph(FILE *fd, xRect *r, double rx1, double ry1, double rx2, double ry2); +extern void svg_embedded_graph(FILE *fd, int i, double rx1, double ry1, double rx2, double ry2); extern void set_viewport_size(int w, int h, double lw); extern void print_image(); extern const char *get_trailing_path(const char *str, int no_of_dir, int skip_ext); diff --git a/src/xschem.tcl b/src/xschem.tcl index a3ed1fd3..dd5d47cb 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -8412,9 +8412,20 @@ proc build_widgets { {topwin {} } } { -variable intuitive_interface -selectcolor $selectcolor \ -command {xschem set intuitive_interface $intuitive_interface} - $topwin.menubar.option add checkbutton -label "Draw crosshair" \ - -variable draw_crosshair -selectcolor $selectcolor -accelerator {Alt-X} + $topwin.menubar.option add cascade -label "Crosshair" \ + -menu $topwin.menubar.option.crosshair + menu $topwin.menubar.option.crosshair -tearoff 0 + + $topwin.menubar.option.crosshair add checkbutton -label "Draw snap cursor" \ + -variable snap_cursor -selectcolor $selectcolor + $topwin.menubar.option.crosshair add checkbutton -label "Draw crosshair" \ + -variable draw_crosshair -selectcolor $selectcolor -accelerator {Alt-X} + $topwin.menubar.option.crosshair add command -label "Crosshair size" \ + -command { + input_line "Enter crosshair size (int, 0 = full screen width):" \ + "set crosshair_size" $crosshair_size + } $topwin.menubar.option add command -label "Replace \[ and \] for buses in SPICE netlist" \ -command { input_line "Enter two characters to replace default bus \[\] delimiters:" "set bus_replacement_char" From dc270aa0296222939bc68d67273c4be17b8a0c4c Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 21 Feb 2025 12:53:04 +0100 Subject: [PATCH 07/67] resize a bit graph wave label / axis label sizes --- src/draw.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/draw.c b/src/draw.c index 457d7ed7..e24ed104 100644 --- a/src/draw.c +++ b/src/draw.c @@ -3055,10 +3055,9 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr) gr->gh = gr->gy2 - gr->gy1; /* set margins */ tmp = gr->rw * 0.14; - gr->marginx = tmp < 50 ? 50 : tmp; + gr->marginx = tmp; tmp = gr->rh * 0.14; - gr->marginy = tmp < 40 ? 40 : tmp; - + gr->marginy = tmp; /* calculate graph bounding box (container - margin) * This is the box where plot is done */ gr->x1 = gr->rx1 + gr->marginx; @@ -3089,8 +3088,8 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr) if(tmp < gr->txtsizey) gr->txtsizey = tmp; gr->txtsizey *= gr->magy; - gr->txtsizex = gr->w / gr->divx * 0.0033; - tmp = gr->marginy * 0.0063; + gr->txtsizex = gr->w / gr->divx * 0.0040; + tmp = gr->marginy * 0.0075; if(tmp < gr->txtsizex) gr->txtsizex = tmp; gr->txtsizex *= gr->magx; @@ -3258,7 +3257,7 @@ static void draw_graph_variables(int wcnt, int wave_color, int n_nodes, int swee if(gr->unitx != 1.0) my_snprintf(tmpstr, S(tmpstr), "%s[%c]", stok ? stok : "" , gr->unitx_suffix); else my_snprintf(tmpstr, S(tmpstr), "%s", stok ? stok : ""); draw_string(wave_color, NOW, tmpstr, 2, 1, 0, 0, - gr->rx1 + 2 + gr->rw / n_nodes * wcnt, gr->ry2-5, gr->txtsizelab, gr->txtsizelab); + gr->rx1 + 2 + gr->rw / n_nodes * wcnt, gr->ry2-2, gr->txtsizelab, gr->txtsizelab); } if(gr->legend || gr->digital) { From 3eab6bf2dcc0c3ef504d642bdcf08c6fed14d67d Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 21 Feb 2025 14:31:30 +0100 Subject: [PATCH 08/67] added attribute `attach="name1 name2 ..."` for components to designate other objects to be selected with it. Designated objects must have a matching `name=name1` attribute. This allows to create object groups --- doc/xschem_man/component_property_syntax.html | 5 ++ src/select.c | 65 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/doc/xschem_man/component_property_syntax.html b/doc/xschem_man/component_property_syntax.html index bf4148f1..75acf5a2 100644 --- a/doc/xschem_man/component_property_syntax.html +++ b/doc/xschem_man/component_property_syntax.html @@ -138,6 +138,11 @@ name="mchanged_name" model=\"nmos\" w="20u" l="3u" m="10"
  • text_layer_<n>
  • This attribute sets the layer of symbol text item number n. This allows instance based symbol text color customization.

    +
  • attach
  • +

    An attribute attach="x1 g3 p4" will "attach" specified objects that have a matching name=... + attribute. These objects can be any xschem objects, like other elements, wires, rectangles, polygons, texts etc.
    + Attached objects will be selected when selecting the component with this attribute set. + This allows to create "object groups"

  • highlight
  • If set to true the symbol will be highlighted when one of the nets attached to its pins are highlighted.

  • net_name
  • diff --git a/src/select.c b/src/select.c index c3ae79c0..87bc1b68 100644 --- a/src/select.c +++ b/src/select.c @@ -937,6 +937,68 @@ void select_wire(int i,unsigned short select_mode, int fast) xctx->need_reb_sel_arr=1; } + +static int select_attached_items(int inst, const char *name) +{ + int i, c; + int found = 0; + char *attach = NULL; + char *att_save, *att_ptr; + if(!name || !name[0]) return found; + my_strdup2(_ALLOC_ID_, &attach, name); + att_ptr = attach; + while( (name = my_strtok_r(att_ptr, " \n", "\"", 0, &att_save)) ) { + att_ptr = NULL; + for(c = 0; c < cadlayers; c++) { + for(i = 0; i < xctx->rects[c]; i++) { + if(!strcmp(name, get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0))) { + found = 1; + select_box(c, i, SELECTED, 1, 0); + } + } + for(i = 0; i < xctx->lines[c]; i++) { + if(!strcmp(name, get_tok_value(xctx->line[c][i].prop_ptr, "name", 0))) { + found = 1; + select_line(c, i, SELECTED, 1); + } + } + + for(i = 0; i < xctx->polygons[c]; i++) { + if(!strcmp(name, get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0))) { + found = 1; + select_polygon(c, i, SELECTED, 1); + } + } + for(i = 0; i < xctx->arcs[c]; i++) { + if(!strcmp(name, get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0))) { + found = 1; + select_arc(c, i, SELECTED, 1); + } + } + } + for(i = 0; i < xctx->wires; i++) { + if(!strcmp(name, get_tok_value(xctx->wire[i].prop_ptr, "name", 0))) { + found = 1; + select_wire(i, SELECTED, 1); + } + } + for(i = 0; i < xctx->texts; i++) { + if(!strcmp(name, get_tok_value(xctx->text[i].prop_ptr, "name", 0))) { + found = 1; + select_text(i, SELECTED, 1); + } + } + for(i = 0; i < xctx->instances; i++) { + if(i != inst && !strcmp(name, xctx->inst[i].instname)) { + found = 1; + select_element(i, SELECTED, 1, 0); + } + } + } + my_free(_ALLOC_ID_, &attach); + return found; +} + /* fast == 1: do not update status line * fast == 2: do not draw / undraw selected elements * fast == 3: 1 + 2 @@ -991,6 +1053,7 @@ void select_element(int i,unsigned short select_mode, int fast, int override_loc } } } + select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0)); xctx->need_reb_sel_arr=1; } @@ -1688,6 +1751,8 @@ int floaters_from_selected_inst() } my_strdup2(_ALLOC_ID_, &xctx->inst[i].prop_ptr, subst_token(xctx->inst[i].prop_ptr, "hide_texts", "true")); + my_strdup2(_ALLOC_ID_, &xctx->inst[i].prop_ptr, + subst_token(xctx->inst[i].prop_ptr, "attach", xctx->inst[i].instname)); set_inst_flags(&xctx->inst[i]); for(t = 0; t < sym->texts; t++) { double txtx0, txty0; From 4e2af7ddb914ad9d154c7f685b23b699907762f2 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 21 Feb 2025 17:49:38 +0100 Subject: [PATCH 09/67] attach attribute: update copied objects --- src/move.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/select.c | 4 ++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/move.c b/src/move.c index c6fc2b55..f03b7871 100644 --- a/src/move.c +++ b/src/move.c @@ -513,6 +513,81 @@ void draw_selection(GC g, int interruptable) xctx->movelastsel = i; } +static void update_attached_object_refs(int from, int to) +{ + int i, c; + char *to_name = xctx->inst[to].instname; + char *from_name = xctx->inst[from].instname; + const char *attach = get_tok_value(xctx->inst[to].prop_ptr, "attach", 0); + char *new_attach; + + if(!from_name || !from_name[0]) return; + if(!to_name || !to_name[0]) return; + if(!attach[0]) return; + + new_attach = str_replace(attach, from_name, to_name, 1, 1); + my_strdup(_ALLOC_ID_, &xctx->inst[to].prop_ptr, + subst_token(xctx->inst[to].prop_ptr, "attach", new_attach) ); + + for(c = 0; c < cadlayers; c++) { + for(i = 0; i < xctx->rects[c]; i++) { + if(xctx->rect[c][i].sel == SELECTED) { + if( !strcmp(from_name, get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->rect[c][i].prop_ptr, + subst_token(xctx->rect[c][i].prop_ptr, "name", to_name) ); + } + if(c == GRIDLAYER) { + const char *node = get_tok_value(xctx->rect[c][i].prop_ptr, "node", 2); + if(node && node[0]) { + const char *new_node = str_replace(node, from_name, to_name, 1, 1); + my_strdup(_ALLOC_ID_, &xctx->rect[c][i].prop_ptr, + subst_token(xctx->rect[c][i].prop_ptr, "node", new_node)); + } + } + } + } + for(i = 0; i < xctx->lines[c]; i++) { + if(xctx->line[c][i].sel == SELECTED && + !strcmp(from_name, get_tok_value(xctx->line[c][i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->line[c][i].prop_ptr, + subst_token(xctx->line[c][i].prop_ptr, "name", to_name) ); + } + } + + for(i = 0; i < xctx->polygons[c]; i++) { + if(xctx->poly[c][i].sel == SELECTED && + !strcmp(from_name, get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->poly[c][i].prop_ptr, + subst_token(xctx->poly[c][i].prop_ptr, "name", to_name) ); + + } + } + for(i = 0; i < xctx->arcs[c]; i++) { + if(xctx->arc[c][i].sel == SELECTED && + !strcmp(from_name, get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->arc[c][i].prop_ptr, + subst_token(xctx->arc[c][i].prop_ptr, "name", to_name) ); + } + } + } + for(i = 0; i < xctx->wires; i++) { + if(xctx->wire[i].sel == SELECTED && + !strcmp(from_name, get_tok_value(xctx->wire[i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->wire[i].prop_ptr, + subst_token(xctx->wire[i].prop_ptr, "name", to_name) ); + } + } + for(i = 0; i < xctx->texts; i++) { + if(xctx->text[i].sel == SELECTED && + !strcmp(from_name, get_tok_value(xctx->text[i].prop_ptr, "name", 0))) { + my_strdup(_ALLOC_ID_, &xctx->text[i].prop_ptr, + subst_token(xctx->text[i].prop_ptr, "name", to_name) ); + set_text_flags(&xctx->text[i]); + } + } +} + + void copy_objects(int what) { int tmpi, c, i, n, k /*, tmp */ ; @@ -881,6 +956,9 @@ void copy_objects(int what) newpropcnt++; new_prop_string(xctx->instances, xctx->inst[n].prop_ptr, /* sets also inst[].instname */ tclgetboolvar("disable_unique_names")); + + update_attached_object_refs(n, xctx->instances); + hash_names(xctx->instances, XINSERT); xctx->instances++; /* symbol_bbox calls translate and translate must have updated xctx->instances */ symbol_bbox(xctx->instances-1, diff --git a/src/select.c b/src/select.c index 87bc1b68..c90d85f5 100644 --- a/src/select.c +++ b/src/select.c @@ -1053,7 +1053,9 @@ void select_element(int i,unsigned short select_mode, int fast, int override_loc } } } - select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0)); + if(!fast && select_mode == SELECTED) { + select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0)); + } xctx->need_reb_sel_arr=1; } From 935ec2777202992e4dea70aeaae2425271201151 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sat, 22 Feb 2025 01:30:38 +0100 Subject: [PATCH 10/67] floater objects: add command ctrl-u to unselect attached (so some instance with the attach=.. attribute) objects, that is objects (not element instances) with a non empty name=... atttribute. Extend upcate references of attached floaters to change instance name property and `xschem setprop instance name` command --- doc/xschem_man/commands.html | 1 + src/callback.c | 69 +++++++++++++++++++++++++++--------- src/editprop.c | 8 +++-- src/keys.help | 1 + src/move.c | 27 +++++++------- src/scheduler.c | 23 ++++++++++-- src/select.c | 2 +- src/xschem.h | 3 ++ src/xschem.tcl | 2 ++ 9 files changed, 101 insertions(+), 35 deletions(-) diff --git a/doc/xschem_man/commands.html b/doc/xschem_man/commands.html index 0a4f589e..02f5777a 100644 --- a/doc/xschem_man/commands.html +++ b/doc/xschem_man/commands.html @@ -229,6 +229,7 @@ ctrl+alt 's' Save-as symbol - 't' Place text shift 'T' Toggle *_ignore flag on selected instances alt 'u' Align to current grid selected objects +ctrl 'u' Unselect attached floater objects shift 'U' Redo - 'u' Undo - 'v' Constrained vertical move/copy of objects diff --git a/src/callback.c b/src/callback.c index d2ca603d..25e807b5 100644 --- a/src/callback.c +++ b/src/callback.c @@ -2383,6 +2383,54 @@ static int grabscreen(const char *win_path, int event, int mx, int my, KeySym ke } #endif +void unselect_attached_floaters(void) +{ + int c, i, found = 0; + for(c = 0; c < cadlayers; c++) { + for(i = 0; i < xctx->rects[c]; i++) { + if(get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_box(c, i, 0, 1, 0); + } + } + for(i = 0; i < xctx->lines[c]; i++) { + if(get_tok_value(xctx->line[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_line(c, i, 0, 1); + } + } + + for(i = 0; i < xctx->polygons[c]; i++) { + if(get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_polygon(c, i, 0, 1); + } + } + for(i = 0; i < xctx->arcs[c]; i++) { + if(get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_arc(c, i, 0, 1); + } + } + } + for(i = 0; i < xctx->wires; i++) { + if(get_tok_value(xctx->wire[i].prop_ptr, "name", 0)[0]) { + found = 1; + select_wire(i, 0, 1); + } + } + for(i = 0; i < xctx->texts; i++) { + if(get_tok_value(xctx->text[i].prop_ptr, "name", 0)[0]) { + found = 1; + select_text(i, 0, 1); + } + } + if(found) { + rebuild_selected_array(); + draw_selection(xctx->gc[SELLAYER],0); + } +} + static void handle_enter_notify(int draw_xhair, int crosshair_size) { struct stat buf; @@ -3477,7 +3525,11 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m draw(); return; } - if(0 && (key=='u') && rstate==ControlMask) /* testmode */ + if(key=='u' && rstate==ControlMask) /* unselect attached floater elements */ + { + unselect_attached_floaters(); + } + if(0 && (key=='|') && rstate==ControlMask) /* testmode */ { static int x = 0; @@ -3973,21 +4025,6 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m check_unique_names(1); return; } - if( 0 && (key==';') && (state & ControlMask) ) /* testmode */ - { - return; - } - if(0 && key=='~' && (state & ControlMask)) { /* testmode */ - return; - } - if(0 && key=='|' && !(state & ControlMask)) { /* testmode */ - return; - } - if(0 && key=='|' && (state & ControlMask)) /* testmode */ - { - return; - } - if(key=='f' && rstate == ControlMask) /* search */ { if(xctx->semaphore >= 2) return; diff --git a/src/editprop.c b/src/editprop.c index 3a7f9323..ee8f1ac3 100644 --- a/src/editprop.c +++ b/src/editprop.c @@ -1637,6 +1637,7 @@ static int update_symbol(const char *result, int x, int selected_inst) /* preserve backslashes in name ---------0---------------------------------->. */ my_strdup(_ALLOC_ID_, &name, get_tok_value(xctx->inst[*ii].prop_ptr, "name", 1)); if(name && name[0] ) { + char *old_name = NULL; dbg(1, "update_symbol(): prefix!='\\0', name=%s\n", name); /* change prefix if changing symbol type; */ if(prefix && old_prefix && old_prefix != prefix) { @@ -1649,11 +1650,14 @@ static int update_symbol(const char *result, int x, int selected_inst) if(!pushed) { xctx->push_undo(); pushed=1;} if(!k) hash_names(-1, XINSERT); hash_names(*ii, XDELETE); - dbg(1, "update_symbol(): delete %s\n", xctx->inst[*ii].instname); + dbg(0, "update_symbol(): delete %s\n", xctx->inst[*ii].instname); + my_strdup2(_ALLOC_ID_, &old_name, xctx->inst[*ii].instname); new_prop_string(*ii, ptr, /* sets also inst[].instname */ tclgetboolvar("disable_unique_names")); /* set new prop_ptr */ hash_names(*ii, XINSERT); - dbg(1, "update_symbol(): insert %s\n", xctx->inst[*ii].instname); + update_attached_object_refs(old_name, *ii, 1); + dbg(0, "update_symbol(): insert %s\n", xctx->inst[*ii].instname); + my_free(_ALLOC_ID_, &old_name); } set_inst_flags(&xctx->inst[*ii]); } /* end for(k=0;klastsel; ++k) */ diff --git a/src/keys.help b/src/keys.help index a73197bf..a473aa45 100644 --- a/src/keys.help +++ b/src/keys.help @@ -169,6 +169,7 @@ ctrl+alt 's' Save-as symbol - 't' Place text shift 'T' Toggle *_ignore flag on selected instances alt 'u' Align to current grid selected objects +ctrl 'u' Unselect attached floater objects shift 'U' Redo - 'u' Undo - 'v' Constrained vertical move/copy of objects diff --git a/src/move.c b/src/move.c index f03b7871..20ee6f99 100644 --- a/src/move.c +++ b/src/move.c @@ -513,12 +513,13 @@ void draw_selection(GC g, int interruptable) xctx->movelastsel = i; } -static void update_attached_object_refs(int from, int to) +/* sel: if set to 1 change references only on selected items, like in a copy operation. + * If set to 0 operate on all objects with matching name=... attribute */ +void update_attached_object_refs(const char *from_name, int inst, int sel) { int i, c; - char *to_name = xctx->inst[to].instname; - char *from_name = xctx->inst[from].instname; - const char *attach = get_tok_value(xctx->inst[to].prop_ptr, "attach", 0); + char *to_name = xctx->inst[inst].instname; + const char *attach = get_tok_value(xctx->inst[inst].prop_ptr, "attach", 0); char *new_attach; if(!from_name || !from_name[0]) return; @@ -526,12 +527,12 @@ static void update_attached_object_refs(int from, int to) if(!attach[0]) return; new_attach = str_replace(attach, from_name, to_name, 1, 1); - my_strdup(_ALLOC_ID_, &xctx->inst[to].prop_ptr, - subst_token(xctx->inst[to].prop_ptr, "attach", new_attach) ); + my_strdup(_ALLOC_ID_, &xctx->inst[inst].prop_ptr, + subst_token(xctx->inst[inst].prop_ptr, "attach", new_attach) ); for(c = 0; c < cadlayers; c++) { for(i = 0; i < xctx->rects[c]; i++) { - if(xctx->rect[c][i].sel == SELECTED) { + if(!sel || xctx->rect[c][i].sel == SELECTED) { if( !strcmp(from_name, get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->rect[c][i].prop_ptr, subst_token(xctx->rect[c][i].prop_ptr, "name", to_name) ); @@ -547,7 +548,7 @@ static void update_attached_object_refs(int from, int to) } } for(i = 0; i < xctx->lines[c]; i++) { - if(xctx->line[c][i].sel == SELECTED && + if((!sel || xctx->line[c][i].sel == SELECTED) && !strcmp(from_name, get_tok_value(xctx->line[c][i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->line[c][i].prop_ptr, subst_token(xctx->line[c][i].prop_ptr, "name", to_name) ); @@ -555,7 +556,7 @@ static void update_attached_object_refs(int from, int to) } for(i = 0; i < xctx->polygons[c]; i++) { - if(xctx->poly[c][i].sel == SELECTED && + if((!sel || xctx->poly[c][i].sel == SELECTED) && !strcmp(from_name, get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->poly[c][i].prop_ptr, subst_token(xctx->poly[c][i].prop_ptr, "name", to_name) ); @@ -563,7 +564,7 @@ static void update_attached_object_refs(int from, int to) } } for(i = 0; i < xctx->arcs[c]; i++) { - if(xctx->arc[c][i].sel == SELECTED && + if((!sel || xctx->arc[c][i].sel == SELECTED) && !strcmp(from_name, get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->arc[c][i].prop_ptr, subst_token(xctx->arc[c][i].prop_ptr, "name", to_name) ); @@ -571,14 +572,14 @@ static void update_attached_object_refs(int from, int to) } } for(i = 0; i < xctx->wires; i++) { - if(xctx->wire[i].sel == SELECTED && + if((!sel || xctx->wire[i].sel == SELECTED) && !strcmp(from_name, get_tok_value(xctx->wire[i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->wire[i].prop_ptr, subst_token(xctx->wire[i].prop_ptr, "name", to_name) ); } } for(i = 0; i < xctx->texts; i++) { - if(xctx->text[i].sel == SELECTED && + if((!sel || xctx->text[i].sel == SELECTED) && !strcmp(from_name, get_tok_value(xctx->text[i].prop_ptr, "name", 0))) { my_strdup(_ALLOC_ID_, &xctx->text[i].prop_ptr, subst_token(xctx->text[i].prop_ptr, "name", to_name) ); @@ -957,7 +958,7 @@ void copy_objects(int what) new_prop_string(xctx->instances, xctx->inst[n].prop_ptr, /* sets also inst[].instname */ tclgetboolvar("disable_unique_names")); - update_attached_object_refs(n, xctx->instances); + update_attached_object_refs(xctx->inst[n].instname, xctx->instances, 1); hash_names(xctx->instances, XINSERT); xctx->instances++; /* symbol_bbox calls translate and translate must have updated xctx->instances */ diff --git a/src/scheduler.c b/src/scheduler.c index 38e6866e..538e1bbf 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -5461,7 +5461,8 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } else { char *translated_sym = NULL; int sym_number = -1; - char *subst = NULL; + char *subst = NULL, *old_name = NULL;; + if(!fast) { symbol_bbox(inst, &xctx->inst[inst].x1, &xctx->inst[inst].y1, &xctx->inst[inst].x2, &xctx->inst[inst].y2); xctx->push_undo(); @@ -5469,7 +5470,12 @@ 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(argc > 4 && !strcmp(argv[4], "name") && fast == 0) hash_names(-1, XINSERT); + if(argc > 4 && !strcmp(argv[4], "name")) { + if(fast == 0) { + hash_names(-1, XINSERT); + } + my_strdup2(_ALLOC_ID_, &old_name, xctx->inst[inst].instname); + } if(argc > 5) { if(!strcmp(argv[4], "allprops")) { hash_names(-1, XINSERT); @@ -5486,7 +5492,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } hash_names(inst, XDELETE); new_prop_string(inst, subst, tclgetboolvar("disable_unique_names")); - + if(old_name) { + update_attached_object_refs(old_name, inst, 0); + } my_strdup2(_ALLOC_ID_, &translated_sym, translate(inst, xctx->inst[inst].name)); sym_number=match_symbol(translated_sym); @@ -5495,6 +5503,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg xctx->inst[inst].ptr=sym_number; } if(subst) my_free(_ALLOC_ID_, &subst); + if(old_name) my_free(_ALLOC_ID_, &old_name); set_inst_flags(&xctx->inst[inst]); hash_names(inst, XINSERT); if(!fast) { @@ -6215,6 +6224,14 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } + /* unselect_attached_floaters + * Unselect objects (not symbol instances) attached to some instance with a + * non empty name=... attribute */ + else if(!strcmp(argv[1], "unselect_attached_floaters")) + { + if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} + unselect_attached_floaters(); + } /* update_all_sym_bboxes * Update all symbol bounding boxes */ else if(!strcmp(argv[1], "update_all_sym_bboxes")) diff --git a/src/select.c b/src/select.c index c90d85f5..8386da34 100644 --- a/src/select.c +++ b/src/select.c @@ -1282,7 +1282,7 @@ Selected select_object(double mx,double my, unsigned short select_mode, break; case ELEMENT: if(xctx->inst[sel.n].sel) xctx->already_selected = 1; - select_element(sel.n,select_mode,0, override_lock); + select_element(sel.n, select_mode,0, override_lock); break; default: break; diff --git a/src/xschem.h b/src/xschem.h index 84e30e0e..d4e74449 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1402,6 +1402,7 @@ extern void abort_operation(void); extern void draw_crosshair(int what, int state); extern void backannotate_at_cursor_b_pos(xRect *r, Graph_ctx *gr); /* extern void snapped_wire(double c_snap); */ +extern void unselect_attached_floaters(void); extern int callback(const char *win_path, int event, int mx, int my, KeySym key, int button, int aux, int state); extern void resetwin(int create_pixmap, int clear_pixmap, int force, int w, int h); @@ -1546,6 +1547,8 @@ extern void new_line(int what, double mx_snap, double my_snap); extern void new_arc(int what, double sweep, double mousex_snap, double mousey_snap); extern void arc_3_points(double x1, double y1, double x2, double y2, double x3, double y3, double *x, double *y, double *r, double *a, double *b); +/* sel: if set to 1 change references only on selected items, like in a copy operation */ +extern void update_attached_object_refs(const char *from_name, int inst, int sel); extern void move_objects(int what,int merge, double dx, double dy); extern void check_collapsing_objects(); extern void redraw_w_a_l_r_p_z_rubbers(int force); /* redraw wire, arcs, line, polygon rubbers */ diff --git a/src/xschem.tcl b/src/xschem.tcl index dd5d47cb..e35d90e6 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -8680,6 +8680,8 @@ proc build_widgets { {topwin {} } } { -command "xschem net_label 0" -accelerator Alt-Shift-L $topwin.menubar.sym add command -label "Change selected inst. texts to floaters" \ -command "xschem floaters_from_selected_inst" + $topwin.menubar.sym add command -label "Unselect attached floaters" \ + -command "xschem unselect_attached_floaters" $topwin.menubar.sym add command -label "Print list of highlight nets" \ -command "xschem print_hilight_net 1" -accelerator J $topwin.menubar.sym add command -label "Print list of highlight nets, with buses expanded" \ From 64e29f6a8b8f0d8d1a88bc86bcbaf0c18a945807 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sat, 22 Feb 2025 01:38:38 +0100 Subject: [PATCH 11/67] doc updates --- doc/xschem_man/developer_info.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html index 0cc1b8b6..44a2b51b 100644 --- a/doc/xschem_man/developer_info.html +++ b/doc/xschem_man/developer_info.html @@ -1634,6 +1634,9 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns" Unhighlight selected nets/pins
  • unselect_all [draw]
  •     Unselect everything. If draw is given and set to '0' no drawing is done 
    +
  • unselect_attached_floaters
  • +   Unselect objects (not symbol instances) attached to some instance with a 
    +   non empty name=... attribute 
  • update_all_sym_bboxes
  •     Update all symbol bounding boxes 
  • update_op
  • 
    From a592216c19a9ccfaa29af2be631bb0508b922b4f Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 02:18:27 +0100
    Subject: [PATCH 12/67] update solar_panel.sch (grouped aka attached floaters,
     scope meter object), better selection of left-y axis in small graphs
    
    ---
     src/callback.c                         |  6 +++-
     xschem_library/ngspice/solar_panel.sch | 40 ++++++++++++++++++++++----
     2 files changed, 40 insertions(+), 6 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index 25e807b5..bd11a052 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -50,8 +50,12 @@ static int waves_selected(int event, KeySym key, int state, int button)
       else if(event == ButtonRelease && button == Button2) skip = 1;
       /* else if(event == KeyPress && (state & ShiftMask)) skip = 1; */
       else if(!skip) for(i=0; i< xctx->rects[GRIDLAYER]; ++i) {
    +    double lmargin;
         xRect *r;
         r = &xctx->rect[GRIDLAYER][i];
    +    lmargin = (r->x2 - r->x1) / 20.;
    +    lmargin = lmargin < 3. ? 3. : lmargin;
    +    lmargin = lmargin > 20. ? 20. : lmargin;
         if(!(r->flags & 1) ) continue;
         if(!strboolcmp(get_tok_value(xctx->rect[GRIDLAYER][i].prop_ptr, "lock", 0), "true")) continue;
     
    @@ -59,7 +63,7 @@ static int waves_selected(int event, KeySym key, int state, int button)
           (xctx->ui_state & GRAPHPAN) ||
           (event != -3 &&
              (
    -           POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + 20,  r->y1 + 8,  r->x2 - 20,  r->y2 - 8) ||
    +           POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + lmargin,  r->y1 + 8,  r->x2 - 20,  r->y2 - 8) ||
                POINTINSIDE(xctx->mousex, xctx->mousey, r->x1,  r->y1,  r->x1 + 20,  r->y1 + 8) ||
                POINTINSIDE(xctx->mousex, xctx->mousey, r->x2 - 20,  r->y2 - 8,  r->x2,  r->y2)
              )
    diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch
    index 8addbfa0..8a00438b 100644
    --- a/xschem_library/ngspice/solar_panel.sch
    +++ b/xschem_library/ngspice/solar_panel.sch
    @@ -193,6 +193,31 @@ autoload=0
     
     sim_type=tran
     xrawfile=$netlist_dir/solar_panel.raw}
    +B 2 390 -720 500 -640 {name=l21
    +flags=graph 
    +y1 = 0.00033
    +y2 = 21
    +divy = 5
    +subdivy=1
    +x1=5e-10
    +x2=0.001
    +divx=9
    +
    + unitx=u subdivx=4
    +
    +
    +hilight_wave=-1
    +digital=0
    +ypos1=0.00261891
    +ypos2=0.51596
    +color=8
    +node="tcleval([xschem translate l21 @#0:net_name])"
    +jpeg_quality=30
    +autoload=0
    +
    +sim_type=tran
    +xrawfile=$netlist_dir/solar_panel.raw
    +linewidth_mult=0.4}
     B 18 65 -960 320 -775 {}
     A 5 320 -960 5.590169943749475 243.434948822922 360 {fill=true}
     P 7 6 395 -775 340 -931.25 335 -945 322.5 -960 310 -965 65 -975 {}
    @@ -232,13 +257,14 @@ T {Floater text
     example} 870 -440 0 0 0.4 0.4 {}
     T {@spice_get_current} 875 -598.75 0 0 0.3 0.3 {layer=7 name=L2}
     T {@spice_get_current} 1015 -268.75 0 0 0.3 0.3 {layer=7 name=C1}
    +T {@spice_get_voltage} 427.5 -750 0 0 0.4 0.4 {name=l21}
     N 1010 -210 1100 -210 {lab=0}
     N 1100 -300 1100 -210 {lab=0}
     N 640 -610 730 -610 {lab=#net1}
     N 1010 -440 1040 -440 {lab=VO}
     N 1010 -440 1010 -310 {lab=VO}
     N 1010 -250 1010 -210 {lab=0}
    -N 530 -610 580 -610 {lab=PANEL}
    +N 360 -610 580 -610 {lab=PANEL}
     N 1010 -610 1010 -440 {lab=VO}
     N 820 -610 860 -610 {lab=SW}
     N 820 -610 820 -490 {lab=SW}
    @@ -300,7 +326,7 @@ lab=0}
     C {title.sym} 160 -40 0 0 {name=l1 author="Stefan Schippers"}
     C {code_shown.sym} 170 -310 0 0 {name=CONTROL
     value="tcleval(
    -.probe alli
    +.option savecurrents
     .control
       * example of tcl evaluation of code blocks:
       *   current path: $path 
    @@ -325,7 +351,8 @@ m=1
     value=40u
     footprint=1206
     device=inductor
    -hide_texts=true}
    +hide_texts=true
    +attach=L2}
     C {lab_pin.sym} 1140 -440 0 1 {name=l7  lab=LED }
     C {lab_pin.sym} 820 -550 0 1 {name=l9  lab=SW }
     C {capa.sym} 1010 -280 0 0 {name=C1
    @@ -333,9 +360,10 @@ m=1
     value=500n
     footprint=1206
     device="ceramic capacitor"
    -hide_texts=true}
    +hide_texts=true
    +attach=C1}
     C {lab_pin.sym} 1010 -400 0 1 {name=l10  lab=VO }
    -C {lab_pin.sym} 530 -610 0 0 {name=l3  lab=PANEL }
    +C {lab_pin.sym} 360 -610 0 0 {name=l3  lab=PANEL }
     C {ammeter.sym} 970 -610 3 0 {name=Vind}
     C {isource_table.sym} 1100 -330 0 0 {name=G2[9..0] CTRL="V(LED)" TABLE="
     + (0, 0)
    @@ -403,3 +431,5 @@ C {spice_probe.sym} 850 -1030 0 1 {name=p4 analysis=tran}
     C {spice_probe.sym} 810 -890 0 1 {name=p5 analysis=tran}
     C {spice_probe.sym} 760 -670 0 0 {name=p6 analysis=tran}
     C {spice_probe.sym} 160 -450 0 0 {name=p7 analysis=tran}
    +C {lab_show.sym} 520 -610 0 0 {name=l21
    +attach=l21}
    
    From 0e438829c641ff9436fd2d3d6073b9d933922a86 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 10:33:13 +0100
    Subject: [PATCH 13/67] allow locked attached rectangles, so they can not be
     selected accidentally. Small resize of x/y graph axis labels
    
    ---
     src/callback.c                         |  5 +++--
     src/draw.c                             | 11 ++++++-----
     src/select.c                           |  2 +-
     xschem_library/ngspice/solar_panel.sch |  3 ++-
     4 files changed, 12 insertions(+), 9 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index bd11a052..03ee028b 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -57,7 +57,8 @@ static int waves_selected(int event, KeySym key, int state, int button)
         lmargin = lmargin < 3. ? 3. : lmargin;
         lmargin = lmargin > 20. ? 20. : lmargin;
         if(!(r->flags & 1) ) continue;
    -    if(!strboolcmp(get_tok_value(xctx->rect[GRIDLAYER][i].prop_ptr, "lock", 0), "true")) continue;
    +    if( !graph_use_ctrl_key && !(state & ControlMask) && 
    +       !strboolcmp(get_tok_value(xctx->rect[GRIDLAYER][i].prop_ptr, "lock", 0), "true")) continue;
     
         check =
           (xctx->ui_state & GRAPHPAN) ||
    @@ -2394,7 +2395,7 @@ void unselect_attached_floaters(void)
         for(i = 0; i < xctx->rects[c]; i++) {
           if(get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0)[0]) {
             found = 1;
    -        select_box(c, i, 0, 1,  0);
    +        select_box(c, i, 0, 1,  1);
           }
         }
         for(i = 0; i < xctx->lines[c]; i++) {
    diff --git a/src/draw.c b/src/draw.c
    index e24ed104..25ff72ee 100644
    --- a/src/draw.c
    +++ b/src/draw.c
    @@ -3082,14 +3082,15 @@ void setup_graph_data(int i, int skip, Graph_ctx *gr)
     
       /* x axis, y axis text sizes */
       gr->txtsizey = gr->h / gr->divy * 0.0095;
    -  tmp = gr->marginx * 0.003;
    -  if(tmp < gr->txtsizey) gr->txtsizey = tmp;
    -  tmp = gr->marginy * 0.02;
    +  tmp = gr->marginx * 0.004;
       if(tmp < gr->txtsizey) gr->txtsizey = tmp;
    +  /* tmp = gr->marginy * 0.02;
    +   * if(tmp < gr->txtsizey) gr->txtsizey = tmp;
    +   */
       gr->txtsizey *= gr->magy;
     
    -  gr->txtsizex = gr->w / gr->divx * 0.0040;
    -  tmp = gr->marginy * 0.0075;
    +  gr->txtsizex = gr->w / gr->divx * 0.0070;
    +  tmp = gr->marginy * 0.0065;
       if(tmp < gr->txtsizex) gr->txtsizex = tmp;
       gr->txtsizex *= gr->magx;
     
    diff --git a/src/select.c b/src/select.c
    index 8386da34..cca8885d 100644
    --- a/src/select.c
    +++ b/src/select.c
    @@ -953,7 +953,7 @@ static int select_attached_items(int inst, const char *name)
           for(i = 0; i < xctx->rects[c]; i++) {
             if(!strcmp(name, get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0))) {
               found = 1;
    -          select_box(c, i, SELECTED, 1,  0);
    +          select_box(c, i, SELECTED, 1,  1);
             }
           }
           for(i = 0; i < xctx->lines[c]; i++) {
    diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch
    index 8a00438b..f7aa2f21 100644
    --- a/xschem_library/ngspice/solar_panel.sch
    +++ b/xschem_library/ngspice/solar_panel.sch
    @@ -217,7 +217,8 @@ autoload=0
     
     sim_type=tran
     xrawfile=$netlist_dir/solar_panel.raw
    -linewidth_mult=0.4}
    +linewidth_mult=0.4
    +lock=1}
     B 18 65 -960 320 -775 {}
     A 5 320 -960 5.590169943749475 243.434948822922 360 {fill=true}
     P 7 6 395 -775 340 -931.25 335 -945 322.5 -960 310 -965 65 -975 {}
    
    From a93fb64e125c06ee8f439711c29ba8adfbf691cb Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 11:37:19 +0100
    Subject: [PATCH 14/67] better threshold detection for bezier selection
    
    ---
     src/findnet.c | 21 +++++++--------------
     1 file changed, 7 insertions(+), 14 deletions(-)
    
    diff --git a/src/findnet.c b/src/findnet.c
    index ba36fc71..98965b8d 100644
    --- a/src/findnet.c
    +++ b/src/findnet.c
    @@ -49,7 +49,7 @@ static void find_closest_wire(double mx, double my)
      }
     }
     
    -static void find_closest_bezier(double mx, double my, int c, int i, int *l, int *col)
    +static double find_closest_bezier(double mx, double my, double d, int c, int i, int *l, int *col)
     {
       const double bez_steps = 1.0/8.0; /* divide the t = [0,1] interval into 8 steps */
       int b;
    @@ -97,13 +97,14 @@ static void find_closest_bezier(double mx, double my, int c, int i, int *l, int
           xp1 = (1 - t1) * (1 - t1) * x0 + 2 * (1 - t1) * t1 * x1 + t1 * t1 * x2;
           yp1 = (1 - t1) * (1 - t1) * y0 + 2 * (1 - t1) * t1 * y1 + t1 * t1 * y2;
           ORDER(xp, yp, xp1, yp1);
    -      if( (tmp = dist(xp, yp, xp1, yp1, mx, my)) < distance )
    +      if( (tmp = dist(xp, yp, xp1, yp1, mx, my)) < d )
           {
    -       *l = i; distance = tmp; *col = c;
    -       dbg(1, "find_closest_bezier(): distance=%.16g  n=%d\n", distance, i);
    +       *l = i; d = tmp; *col = c;
           }
         }
       }
    +  dbg(1, "find_closest_bezier(): d=%.16g  n=%d\n", d, i);
    +  return d;
     }
     
     static void find_closest_polygon(double mx, double my)
    @@ -126,15 +127,7 @@ static void find_closest_polygon(double mx, double my)
         bezier = bezier && (p->points > 2);
         
         if(bezier) {
    -      double ds = xctx->cadhalfdotsize;
    -
    -      find_closest_bezier(mx, my, c, i, &l, &col);
    -      for(j = 0; j < p->points; j++) {
    -        if( POINTINSIDE(mx, my, p->x[j] - ds, p->y[j] - ds, p->x[j] + ds, p->y[j] + ds)) {
    -           l = i; d = 0.0;col = c;
    -           break;
    -        }
    -      }
    +      d = find_closest_bezier(mx, my, d, c, i, &l, &col);
         } else {
           for(j=0; jpoly[c][i].points-1; ++j) {
             x1 = p->x[j];
    @@ -145,9 +138,9 @@ static void find_closest_polygon(double mx, double my)
             if( (tmp = dist(x1, y1, x2, y2, mx, my)) < d )
             {
              l = i; d = tmp;col = c;
    -         dbg(1, "find_closest_polygon(): d=%.16g  n=%d\n", d, i);
             }
           }
    +      dbg(1, "find_closest_polygon(): d=%.16g  n=%d\n", d, i);
         }
       } /* end for i */
      } /* end for c */
    
    From e266642c2c80021efd5437349ae7ca12b875d00c Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 12:47:54 +0100
    Subject: [PATCH 15/67] lock attribute extended to all objects
    
    ---
     src/actions.c   |   8 ++--
     src/callback.c  |  10 ++--
     src/findnet.c   |  36 ++++++++------
     src/netlist.c   |   2 +-
     src/paste.c     |  10 ++--
     src/scheduler.c |  22 ++++-----
     src/select.c    | 122 +++++++++++++++++++++++++-----------------------
     src/xschem.h    |  10 ++--
     8 files changed, 115 insertions(+), 105 deletions(-)
    
    diff --git a/src/actions.c b/src/actions.c
    index ba5bac99..a8b92558 100644
    --- a/src/actions.c
    +++ b/src/actions.c
    @@ -1066,7 +1066,7 @@ void clear_partial_selected_wires(void)
       rebuild_selected_array();
       for(j=0; j < xctx->lastsel; ++j) if(xctx->sel_array[j].type == WIRE) {
         int wire = xctx->sel_array[j].n;
    -    select_wire(wire, 0, 1);
    +    select_wire(wire, 0, 1, 1);
       }
       xctx->need_reb_sel_arr = 1;
       rebuild_selected_array();
    @@ -1229,7 +1229,7 @@ int unselect_partial_sel_wires(void)
           while(wptr) {
             xWire *w = &xctx->wire[wptr->n];
             if(touch(w->x1, w->y1, w->x2, w->y2, pinx0, piny0) && w->sel && w->sel != SELECTED) {
    -          select_wire(wptr->n, 0, 1);
    +          select_wire(wptr->n, 0, 1, 1);
               changed = 1;
             }
             wptr = wptr->next;
    @@ -1258,7 +1258,7 @@ int unselect_partial_sel_wires(void)
             }
             if(touch(w->x1, w->y1, w->x2, w->y2, x0, y0) && w->sel && w->sel != SELECTED) {
               xctx->wire[wptr->n].sel = 0;
    -          select_wire(wptr->n, 0, 1);
    +          select_wire(wptr->n, 0, 1, 1);
               changed = 1;
             }
             wptr = wptr->next;
    @@ -3830,7 +3830,7 @@ int place_text(int draw_text, double mx, double my)
       dbg(1,"props=%s, txt=%s\n", props, txt);
     
       create_text(draw_text, mx, my, 0, 0, txt, props, atof(hsize), atof(vsize));
    -  select_text(xctx->texts - 1, SELECTED, 0);
    +  select_text(xctx->texts - 1, SELECTED, 0, 1);
       rebuild_selected_array(); /* sets xctx->ui_state |= SELECTION */
       drawtemprect(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0);
       drawtempline(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0);
    diff --git a/src/callback.c b/src/callback.c
    index 03ee028b..2866420c 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -2401,33 +2401,33 @@ void unselect_attached_floaters(void)
         for(i = 0; i < xctx->lines[c]; i++) {
           if(get_tok_value(xctx->line[c][i].prop_ptr, "name", 0)[0]) {
             found = 1;
    -        select_line(c, i, 0, 1);
    +        select_line(c, i, 0, 1, 1);
           }
         }
     
         for(i = 0; i < xctx->polygons[c]; i++) {
           if(get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0)[0]) {
             found = 1;
    -        select_polygon(c, i, 0, 1);
    +        select_polygon(c, i, 0, 1, 1);
           }
         }
         for(i = 0; i < xctx->arcs[c]; i++) {
           if(get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0)[0]) {
             found = 1;
    -        select_arc(c, i, 0, 1);
    +        select_arc(c, i, 0, 1, 1);
           }
         }
       }
       for(i = 0; i < xctx->wires; i++) {
         if(get_tok_value(xctx->wire[i].prop_ptr, "name", 0)[0]) {
          found = 1;
    -     select_wire(i, 0, 1);
    +     select_wire(i, 0, 1, 1);
         }
       }
       for(i = 0; i < xctx->texts; i++) {
         if(get_tok_value(xctx->text[i].prop_ptr, "name", 0)[0]) {
          found = 1;
    -     select_text(i, 0, 1);
    +     select_text(i, 0, 1, 1);
         }
       }
       if(found) {
    diff --git a/src/findnet.c b/src/findnet.c
    index 98965b8d..41d2d489 100644
    --- a/src/findnet.c
    +++ b/src/findnet.c
    @@ -25,7 +25,7 @@
     static double distance; /* safe to keep even with multiple schematics */
     static Selected sel; /* safe to keep even with multiple schematics */
     
    -static void find_closest_wire(double mx, double my)
    +static void find_closest_wire(double mx, double my, int override_lock)
     /* returns the net that is closest to the mouse pointer */
     /* if there are nets and distance < CADWIREMINDIST */
     {
    @@ -42,7 +42,8 @@ static void find_closest_wire(double mx, double my)
        w = i; d = tmp;
       }
      }
    - if( d <= threshold && w!=-1)
    + if( w != -1 && d <= threshold &&
    +   (override_lock || strboolcmp(get_tok_value(xctx->wire[w].prop_ptr, "lock", 0), "true")) )
      {
       sel.n = w; sel.type = WIRE; 
       distance = d;
    @@ -107,7 +108,7 @@ static double find_closest_bezier(double mx, double my, double d, int c, int i,
       return d;
     }
     
    -static void find_closest_polygon(double mx, double my)
    +static void find_closest_polygon(double mx, double my, int override_lock)
     /* returns the polygon that is closest to the mouse pointer */
     /* if there are lines and distance < CADWIREMINDIST */
     {
    @@ -144,7 +145,8 @@ static void find_closest_polygon(double mx, double my)
         }
       } /* end for i */
      } /* end for c */
    - if( d <= threshold && l!=-1)
    + if( d <= threshold && l!=-1 &&
    +   (override_lock || strboolcmp(get_tok_value(xctx->poly[col][l].prop_ptr, "lock", 0), "true")))
      {
       sel.n = l; sel.type = POLYGON; sel.col = col;
       distance = d;
    @@ -152,7 +154,7 @@ static void find_closest_polygon(double mx, double my)
     }
     
     
    -static void find_closest_line(double mx, double my)
    +static void find_closest_line(double mx, double my, int override_lock)
     /* returns the line that is closest to the mouse pointer */
     /* if there are lines and distance < CADWIREMINDIST */
     {
    @@ -174,7 +176,8 @@ static void find_closest_line(double mx, double my)
        }
       } /* end for i */
      } /* end for c */
    - if( d <= threshold && l!=-1)
    + if( d <= threshold && l!=-1 &&
    +   (override_lock || strboolcmp(get_tok_value(xctx->line[col][l].prop_ptr, "lock", 0), "true")))
      {
       sel.n = l; sel.type = LINE; sel.col = col;
       distance = d;
    @@ -319,7 +322,7 @@ void xfind_closest_net_or_symbol_pin(double mx, double my, double *x, double *y)
     }
     #endif
     
    -static void find_closest_arc(double mx, double my)
    +static void find_closest_arc(double mx, double my, int override_lock)
     {
      double dist, angle, angle1, angle2;
      int i, c, r=-1, col;
    @@ -364,7 +367,8 @@ static void find_closest_arc(double mx, double my)
         }
       } /* end for i */
      } /* end for c */
    - if( r!=-1 && d <= threshold ) /*  * pow(xctx->arc[col][r].r, 2)) */
    + if(r!=-1 && d <= threshold &&
    +    strboolcmp(get_tok_value(xctx->arc[col][r].prop_ptr, "lock", 0), "true"))
      {
       sel.n = r; sel.type = ARC; sel.col = col;
       distance = d;
    @@ -432,7 +436,7 @@ static void find_closest_element(double mx, double my, int override_lock)
       }
     }
     
    -static void find_closest_text(double mx, double my)
    +static void find_closest_text(double mx, double my, int override_lock)
     {
      short rot, flip;
      double xx1, xx2, yy1, yy2;
    @@ -469,7 +473,9 @@ static void find_closest_text(double mx, double my)
          dbg(2, "find_closest_text(): finding closest text, texts=%d, dist=%.16g\n", i, d);
        }
       } /* end for i */
    - if( d <= threshold && r!=-1)
    +
    + if( r != -1 && d <= threshold &&
    +   (override_lock || strboolcmp(get_tok_value(xctx->text[r].prop_ptr, "lock", 0), "true")) )
      {
       sel.n = r; sel.type = xTEXT;
       distance = d;
    @@ -480,14 +486,14 @@ Selected find_closest_obj(double mx, double my, int override_lock)
     {
      sel.n = 0L; sel.col = 0; sel.type = 0;
      distance = DBL_MAX;
    - find_closest_line(mx, my);
    - find_closest_polygon(mx, my);
    + find_closest_line(mx, my, override_lock);
    + find_closest_polygon(mx, my, override_lock);
      /* dbg(1, "1 find_closest_obj(): sel.n=%d, sel.col=%d, sel.type=%d\n", sel.n, sel.col, sel.type); */
      find_closest_box(mx, my, override_lock);
    - find_closest_arc(mx, my);
    + find_closest_arc(mx, my, override_lock);
      /* dbg(1, "2 find_closest_obj(): sel.n=%d, sel.col=%d, sel.type=%d\n", sel.n, sel.col, sel.type); */
    - find_closest_text(mx, my);
    - find_closest_wire(mx, my);
    + find_closest_text(mx, my, override_lock);
    + find_closest_wire(mx, my, override_lock);
      find_closest_element(mx, my, override_lock);
      return sel;    /*sel.type = 0 if nothing found */
     }
    diff --git a/src/netlist.c b/src/netlist.c
    index afa7f5b0..407049a2 100644
    --- a/src/netlist.c
    +++ b/src/netlist.c
    @@ -678,7 +678,7 @@ static void print_wires(void)
      ptr=xctx->wire_spatial_table[0][1];
      while(ptr)
      {
    -  select_wire(ptr->n,SELECTED, 1);
    +  select_wire(ptr->n,SELECTED, 1, 1);
       rebuild_selected_array();
       ptr=ptr->next;
      }
    diff --git a/src/paste.c b/src/paste.c
    index 7ed3c536..4b784524 100644
    --- a/src/paste.c
    +++ b/src/paste.c
    @@ -48,7 +48,7 @@ static void merge_text(FILE *fd)
          xctx->text[i].sel=0;
          load_ascii_string(&xctx->text[i].prop_ptr,fd);
          set_text_flags(&xctx->text[i]);
    -     select_text(i,SELECTED, 1);
    +     select_text(i,SELECTED, 1, 1);
          xctx->texts++;
     }
     
    @@ -66,7 +66,7 @@ static void merge_wire(FILE *fd)
         load_ascii_string( &ptr, fd);
         storeobject(-1, x1,y1,x2,y2,WIRE,0,SELECTED,ptr);
         my_free(_ALLOC_ID_, &ptr);
    -    select_wire(i, SELECTED, 1);
    +    select_wire(i, SELECTED, 1, 1);
     }
     
     static void merge_box(FILE *fd)
    @@ -172,7 +172,7 @@ static void merge_arc(FILE *fd)
           ptr[i].dash = 0;
         }
     
    -    select_arc(c,i, SELECTED, 1);
    +    select_arc(c,i, SELECTED, 1, 1);
         xctx->arcs[c]++;
     }
     
    @@ -232,7 +232,7 @@ static void merge_polygon(FILE *fd)
           ptr[i].dash = 0;
         }
     
    -    select_polygon(c,i, SELECTED, 1);
    +    select_polygon(c,i, SELECTED, 1, 1);
         xctx->polygons[c]++;
     }
     
    @@ -271,7 +271,7 @@ static void merge_line(FILE *fd)
           ptr[i].bus = 1;
         else
           ptr[i].bus = 0;
    -    select_line(c,i, SELECTED, 1);
    +    select_line(c,i, SELECTED, 1, 1);
         xctx->lines[c]++;
     }
     
    diff --git a/src/scheduler.c b/src/scheduler.c
    index 538e1bbf..f5e9835c 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -4960,7 +4960,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             int n=atoi(argv[3]);
             int valid = n < xctx->wires && n >= 0;
             if(valid) {
    -          select_wire(n, sel, fast);
    +          select_wire(n, sel, fast, 1);
               xctx->ui_state |= SELECTION;
             }
             Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC);
    @@ -4970,7 +4970,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             int n=atoi(argv[4]);
             int valid = n < xctx->lines[c] && n >= 0 && c < cadlayers && c >= 0;
             if(valid) {
    -          select_line(c, n, sel, fast);
    +          select_line(c, n, sel, fast, 0);
               xctx->ui_state |= SELECTION;
             }
             Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC);
    @@ -4990,7 +4990,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             int n=atoi(argv[4]);
             int valid = n < xctx->arcs[c] && n >= 0 && c < cadlayers && c >= 0;
             if(valid) {
    -          select_arc(c, n, sel, fast);
    +          select_arc(c, n, sel, fast, 0);
               xctx->ui_state |= SELECTION;
             }
             Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC);
    @@ -5000,7 +5000,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             int n=atoi(argv[4]);
             int valid = n < xctx->polygons[c] && n >= 0 && c < cadlayers && c >= 0;
             if(valid) {
    -          select_polygon(c, n, sel, fast);
    +          select_polygon(c, n, sel, fast, 0);
               xctx->ui_state |= SELECTION;
             }
             Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC);
    @@ -5009,7 +5009,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             int n=atoi(argv[3]);
             int valid = n < xctx->texts && n >= 0;
             if(valid) {
    -          select_text(n, sel, fast);
    +          select_text(n, sel, fast, 0);
               xctx->ui_state |= SELECTION;
             }
             Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC);
    @@ -5936,22 +5936,22 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
                   select_element(n, SELECTED, 1, 1);
                   break;
                 case WIRE:
    -              select_wire(n, SELECTED, 1);
    +              select_wire(n, SELECTED, 1, 1);
                   break;
                 case xTEXT:
    -              select_text(n, SELECTED, 1);
    +              select_text(n, SELECTED, 1, 1);
                   break;
                 case xRECT:
    -              select_box(c, n, SELECTED, 1, 0);
    +              select_box(c, n, SELECTED, 1, 1);
                   break;
                 case LINE:
    -              select_line(c, n, SELECTED, 1);
    +              select_line(c, n, SELECTED, 1, 1);
                   break;
                 case POLYGON:
    -              select_polygon(c, n, SELECTED, 1);
    +              select_polygon(c, n, SELECTED, 1, 1);
                   break;
                 case ARC:
    -              select_arc(c, n, SELECTED, 1);
    +              select_arc(c, n, SELECTED, 1, 1);
                   break;
               }
             }
    diff --git a/src/select.c b/src/select.c
    index cca8885d..524e5f1c 100644
    --- a/src/select.c
    +++ b/src/select.c
    @@ -896,10 +896,12 @@ void unselect_all(int dr)
       }
     }
     
    -void select_wire(int i,unsigned short select_mode, int fast)
    +void select_wire(int i,unsigned short select_mode, int fast, int override_lock)
     {
       char str[1024];       /* overflow safe */
       /*my_strncpy(s,xctx->wire[i].prop_ptr!=NULL?xctx->wire[i].prop_ptr:"",256); */
    +  if(!strboolcmp(get_tok_value(xctx->wire[i].prop_ptr, "lock", 0), "true") &&
    +      select_mode == SELECTED && !override_lock) return;
       if( !fast )
       {
         my_snprintf(str, S(str), "Info: selected wire: n=%d end1=%d end2=%d\nnode=%s",i,
    @@ -959,33 +961,33 @@ static int select_attached_items(int inst, const char *name)
           for(i = 0; i < xctx->lines[c]; i++) {
             if(!strcmp(name, get_tok_value(xctx->line[c][i].prop_ptr, "name", 0))) {
               found = 1;
    -          select_line(c, i, SELECTED, 1);
    +          select_line(c, i, SELECTED, 1, 1);
             }
           }
       
           for(i = 0; i < xctx->polygons[c]; i++) {
             if(!strcmp(name, get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0))) {
               found = 1;
    -          select_polygon(c, i, SELECTED, 1);
    +          select_polygon(c, i, SELECTED, 1, 1);
             }
           }
           for(i = 0; i < xctx->arcs[c]; i++) {
             if(!strcmp(name, get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0))) {
               found = 1;
    -          select_arc(c, i, SELECTED, 1);
    +          select_arc(c, i, SELECTED, 1, 1);
             }
           } 
         }
         for(i = 0; i < xctx->wires; i++) {
           if(!strcmp(name, get_tok_value(xctx->wire[i].prop_ptr, "name", 0))) {
            found = 1;
    -       select_wire(i, SELECTED, 1);
    +       select_wire(i, SELECTED, 1, 1);
           }
         } 
         for(i = 0; i < xctx->texts; i++) {
           if(!strcmp(name, get_tok_value(xctx->text[i].prop_ptr, "name", 0))) {
            found = 1;
    -       select_text(i, SELECTED, 1);
    +       select_text(i, SELECTED, 1, 1);
           }
         }
         for(i = 0; i < xctx->instances; i++) {
    @@ -1059,13 +1061,15 @@ void select_element(int i,unsigned short select_mode, int fast, int override_loc
       xctx->need_reb_sel_arr=1;
     }
     
    -void select_text(int i,unsigned short select_mode, int fast)
    +void select_text(int i, unsigned short select_mode, int fast, int override_lock)
     {
       char str[1024];       /* overflow safe */
       char s[256];          /* overflow safe */
       #if HAS_CAIRO==1
       int customfont;
       #endif
    +  if(!strboolcmp(get_tok_value(xctx->text[i].prop_ptr, "lock", 0), "true") &&
    +      select_mode == SELECTED && !override_lock) return;
       if(!fast) {
         my_strncpy(s,xctx->text[i].prop_ptr!=NULL?xctx->text[i].prop_ptr:"",S(s));
         my_snprintf(str, S(str), "Info: selected text %d: properties: %s", i,s);
    @@ -1140,7 +1144,7 @@ void select_box(int c, int i, unsigned short select_mode, int fast, int override
     
     
     
    -void select_arc(int c, int i, unsigned short select_mode, int fast)
    +void select_arc(int c, int i, unsigned short select_mode, int fast, int override_lock)
     {
       char str[1024];   /* overflow safe */
       char s[256];    /* overflow safe */
    @@ -1170,11 +1174,14 @@ void select_arc(int c, int i, unsigned short select_mode, int fast)
       xctx->need_reb_sel_arr=1;
     }
     
    -void select_polygon(int c, int i, unsigned short select_mode, int fast )
    +void select_polygon(int c, int i, unsigned short select_mode, int fast, int override_lock )
     {
       char str[1024];       /* overflow safe */
       char s[256];          /* overflow safe */
       int bezier;
    +
    +  if(!strboolcmp(get_tok_value(xctx->poly[c][i].prop_ptr, "lock", 0), "true") &&
    +      select_mode == SELECTED && !override_lock) return;
       if(!fast)
       {
        my_strncpy(s,xctx->poly[c][i].prop_ptr!=NULL?xctx->poly[c][i].prop_ptr:"",S(s));
    @@ -1199,10 +1206,13 @@ void select_polygon(int c, int i, unsigned short select_mode, int fast )
       xctx->need_reb_sel_arr=1;
     }
     
    -void select_line(int c, int i, unsigned short select_mode, int fast )
    +void select_line(int c, int i, unsigned short select_mode, int fast, int override_lock )
     {
       char str[1024];       /* overflow safe */
       char s[256];          /* overflow safe */
    +
    +  if(!strboolcmp(get_tok_value(xctx->line[c][i].prop_ptr, "lock", 0), "true") &&
    +      select_mode == SELECTED && !override_lock) return;
       if(!fast)
       {
        my_strncpy(s,xctx->line[c][i].prop_ptr!=NULL?xctx->line[c][i].prop_ptr:"",S(s));
    @@ -1258,19 +1268,19 @@ Selected select_object(double mx,double my, unsigned short select_mode,
        {
         case WIRE:
          if(xctx->wire[sel.n].sel) xctx->already_selected = 1;
    -     select_wire(sel.n, select_mode, 0);
    +     select_wire(sel.n, select_mode, 0, override_lock);
          break;
         case xTEXT:
          if(xctx->text[sel.n].sel) xctx->already_selected = 1;
    -     select_text(sel.n, select_mode, 0);
    +     select_text(sel.n, select_mode, 0, override_lock);
          break;
         case LINE:
          if(xctx->line[sel.col][sel.n].sel) xctx->already_selected = 1;
    -     select_line(sel.col, sel.n, select_mode,0);
    +     select_line(sel.col, sel.n, select_mode,0, override_lock);
          break;
         case POLYGON:
          if(xctx->poly[sel.col][sel.n].sel) xctx->already_selected = 1;
    -     select_polygon(sel.col, sel.n, select_mode,0);
    +     select_polygon(sel.col, sel.n, select_mode,0, override_lock);
          break;
         case xRECT:
          if(xctx->rect[sel.col][sel.n].sel) xctx->already_selected = 1;
    @@ -1278,7 +1288,7 @@ Selected select_object(double mx,double my, unsigned short select_mode,
          break;
         case ARC:
          if(xctx->arc[sel.col][sel.n].sel) xctx->already_selected = 1;
    -     select_arc(sel.col,sel.n, select_mode,0);
    +     select_arc(sel.col,sel.n, select_mode,0, override_lock);
          break;
         case ELEMENT:
          if(xctx->inst[sel.n].sel) xctx->already_selected = 1;
    @@ -1325,10 +1335,10 @@ void select_attached_nets(void)
               for(wptr=xctx->wire_spatial_table[sqx][sqy]; wptr; wptr=wptr->next) {
                 i = wptr->n;
                 if(xctx->wire[i].x1 == x0 &&  xctx->wire[i].y1 == y0) {
    -               select_wire(i,SELECTED1, 1);
    +               select_wire(i,SELECTED1, 1, 0);
                 }
                 if(xctx->wire[i].x2 == x0 &&  xctx->wire[i].y2 == y0) {
    -               select_wire(i,SELECTED2, 1);
    +               select_wire(i,SELECTED2, 1, 0);
                 }
               }
             }
    @@ -1351,10 +1361,10 @@ void select_attached_nets(void)
               i = wptr->n;
               if(i == wire) continue;
               if(xctx->wire[i].x1 == x0 &&  xctx->wire[i].y1 == y0) {
    -             select_wire(i,SELECTED1, 1);
    +             select_wire(i,SELECTED1, 1, 0);
               }
               if(xctx->wire[i].x2 == x0 &&  xctx->wire[i].y2 == y0) {
    -             select_wire(i,SELECTED2, 1);
    +             select_wire(i,SELECTED2, 1, 0);
               }
             }
           }
    @@ -1381,23 +1391,23 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
          if(RECT_INSIDE(xctx->wire[i].x1,xctx->wire[i].y1,xctx->wire[i].x2,xctx->wire[i].y2, x1,y1,x2,y2))
          {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       select_wire(i, SELECTED, 1);
    +       select_wire(i, SELECTED, 1, 0);
          }
          else if(stretch && POINTINSIDE(xctx->wire[i].x1,xctx->wire[i].y1, x1,y1,x2,y2) )
          {
            xctx->ui_state |= SELECTION;
    -       select_wire(i, SELECTED1, 1);
    +       select_wire(i, SELECTED1, 1, 0);
          }
          else if(stretch && POINTINSIDE(xctx->wire[i].x2,xctx->wire[i].y2, x1,y1,x2,y2) )
          {
            xctx->ui_state |= SELECTION;
    -       select_wire(i, SELECTED2, 1);
    +       select_wire(i, SELECTED2, 1, 0);
          }
        } else {
          if(RECT_INSIDE(xctx->wire[i].x1,xctx->wire[i].y1,xctx->wire[i].x2,xctx->wire[i].y2, x1,y1,x2,y2))
          {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       select_wire(i, 0, 1);
    +       select_wire(i, 0, 1, 0);
          }
        }
      }
    @@ -1423,7 +1433,7 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
       if(RECT_INSIDE(xx1,yy1, xx2, yy2,x1,y1,x2,y2))
       {
        xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -   sel ? select_text(i, SELECTED, 1): select_text(i, 0, 1);
    +   sel ? select_text(i, SELECTED, 1, 0): select_text(i, 0, 1, 0);
       }
      }
      for(i=0;iinstances; ++i)
    @@ -1432,9 +1442,7 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
        {
          xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
          if(sel) {
    -       if(strboolcmp(get_tok_value(xctx->inst[i].prop_ptr, "lock", 0), "true")) {
    -         select_element(i, SELECTED, 1, 1);
    -       }
    +       select_element(i, SELECTED, 1, 0);
          } else {
            select_element(i, 0, 1, 0);
          }
    @@ -1468,14 +1476,14 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
          }
          if(flag) {
            if(selected_points==0) {
    -         select_polygon(c, i, 0, 1);
    +         select_polygon(c, i, 0, 1, 0);
            }
            if(selected_points==xctx->poly[c][i].points) {
              xctx->ui_state |= SELECTION;
    -         select_polygon(c, i, SELECTED, 1);
    +         select_polygon(c, i, SELECTED, 1, 0);
            } else if(selected_points) {
              /* for polygon, SELECTED1 means partial sel */
    -         if(sel && stretch) select_polygon(c, i, SELECTED1,1);
    +         if(sel && stretch) select_polygon(c, i, SELECTED1, 1, 0);
            }
          }
      
    @@ -1486,23 +1494,23 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
            if(RECT_INSIDE(xctx->line[c][i].x1,xctx->line[c][i].y1,xctx->line[c][i].x2,xctx->line[c][i].y2, x1,y1,x2,y2))
            {
              xctx->ui_state |= SELECTION;
    -         select_line(c,i,SELECTED,1);
    +         select_line(c, i, SELECTED, 1, 0);
            }
            else if(stretch && POINTINSIDE(xctx->line[c][i].x1,xctx->line[c][i].y1, x1,y1,x2,y2) )
            {
              xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -         select_line(c, i,SELECTED1,1);
    +         select_line(c, i, SELECTED1, 1, 0);
            }
            else if(stretch && POINTINSIDE(xctx->line[c][i].x2,xctx->line[c][i].y2, x1,y1,x2,y2) )
            {
              xctx->ui_state |= SELECTION;
    -         select_line(c, i,SELECTED2,1);
    +         select_line(c, i, SELECTED2, 1, 0);
            }
          } else {
            if(RECT_INSIDE(xctx->line[c][i].x1,xctx->line[c][i].y1,xctx->line[c][i].x2,xctx->line[c][i].y2, x1,y1,x2,y2))
            {
              xctx->ui_state |= SELECTION;
    -         select_line(c,i,0,1);
    +         select_line(c, i, 0, 1, 0);
            }
          }
        }
    @@ -1519,22 +1527,22 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
          arc_bbox(x, y, r, a, b, &tmp.x1, &tmp.y1, &tmp.x2, &tmp.y2);
          if(RECT_INSIDE(tmp.x1, tmp.y1, tmp.x2, tmp.y2, x1,y1,x2,y2)) {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       sel? select_arc(c, i, SELECTED,1): select_arc(c, i, 0,1);
    +       sel? select_arc(c, i, SELECTED,1, 0): select_arc(c, i, 0,1, 0);
          }
          else if( sel && stretch && POINTINSIDE(x, y, x1, y1, x2, y2) )
          {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       select_arc(c, i,SELECTED1,1);
    +       select_arc(c, i,SELECTED1,1, 0);
          }
          else if( sel && stretch && POINTINSIDE(xb, yb, x1, y1, x2, y2) )
          {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       select_arc(c, i,SELECTED3,1);
    +       select_arc(c, i,SELECTED3,1, 0);
          }
          else if( sel && stretch && POINTINSIDE(xa, ya, x1, y1, x2, y2) )
          {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       select_arc(c, i,SELECTED2,1);
    +       select_arc(c, i,SELECTED2,1, 0);
          }
        }
        for(i=0;irects[c]; ++i)
    @@ -1545,9 +1553,7 @@ void select_inside(int stretch, double x1,double y1, double x2, double y2, int s
     
     
           if(sel) {
    -        if(strboolcmp(get_tok_value(xctx->rect[c][i].prop_ptr, "lock", 0), "true")) {
    -          select_box(c,i, SELECTED, 1, 1);
    -        }
    +        select_box(c,i, SELECTED, 1, 0);
           } else {
             select_box(c,i, 0, 1, 0);
           }
    @@ -1609,7 +1615,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
       ly2 = xctx->wire[i].y2;
       if(lineclip(&lx1, &ly1, &lx2, &ly2, x1,y1,x2,y2)) {
        xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -   sel ? select_wire(i,SELECTED, 1): select_wire(i,0, 1);
    +   sel ? select_wire(i,SELECTED, 1, 0): select_wire(i,0, 1, 0);
       }
      }
      for(i=0;itexts; ++i)
    @@ -1635,7 +1641,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
       if(RECT_TOUCH(xx1, yy1, xx2, yy2,x1,y1,x2,y2))
       {
        xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -   sel ? select_text(i, SELECTED, 1): select_text(i, 0, 1);
    +   sel ? select_text(i, SELECTED, 1, 0): select_text(i, 0, 1, 0);
       }
      }
      for(i=0;iinstances; ++i)
    @@ -1644,9 +1650,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
       {
        xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
        if(sel) {
    -     if(strboolcmp(get_tok_value(xctx->inst[i].prop_ptr, "lock", 0), "true")) {
    -       select_element(i, SELECTED, 1, 1);
    -     }
    +     select_element(i, SELECTED, 1, 0);
        } else {
          select_element(i, 0, 1, 0);
        }
    @@ -1670,7 +1674,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
            }
          }
          if(flag) {
    -       sel ? select_polygon(c, i, SELECTED, 1):  select_polygon(c, i, 0, 1);
    +       sel ? select_polygon(c, i, SELECTED, 1, 0):  select_polygon(c, i, 0, 1, 0);
          }
        }
        for(i=0;ilines[c]; ++i)
    @@ -1682,7 +1686,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
         ly2 = xctx->line[c][i].y2;
         if(lineclip(&lx1, &ly1, &lx2, &ly2, x1,y1,x2,y2)) {
          xctx->ui_state |= SELECTION;
    -     sel? select_line(c,i,SELECTED,1): select_line(c,i,0,1);
    +     sel? select_line(c, i, SELECTED, 1, 0): select_line(c, i, 0, 1, 0);
         }
        }
        for(i=0;iarcs[c]; ++i) {
    @@ -1698,7 +1702,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
          arc_bbox(x, y, r, a, b, &tmp.x1, &tmp.y1, &tmp.x2, &tmp.y2);
          if(RECT_TOUCH(tmp.x1, tmp.y1, tmp.x2, tmp.y2, x1,y1,x2,y2)) {
            xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
    -       sel? select_arc(c, i, SELECTED,1): select_arc(c, i, 0,1);
    +       sel? select_arc(c, i, SELECTED,1, 0): select_arc(c, i, 0,1, 0);
          }
        }
        for(i=0;irects[c]; ++i)
    @@ -1707,9 +1711,7 @@ void select_touch(double x1,double y1, double x2, double y2, int sel) /*added un
         {
           xctx->ui_state |= SELECTION; /* set xctx->ui_state to SELECTION also if unselecting by area ???? */
           if(sel) {
    -        if(strboolcmp(get_tok_value(xctx->rect[c][i].prop_ptr, "lock", 0), "true")) {
    -          select_box(c,i, SELECTED, 1, 1);
    -        }
    +        select_box(c,i, SELECTED, 1, 0);
           } else {
             select_box(c,i, 0, 1, 0);
           }
    @@ -1797,33 +1799,35 @@ void select_all(void)
     
      for(i=0;iwires; ++i)
      {
    -   select_wire(i,SELECTED, 1);
    +   select_wire(i,SELECTED, 1, 0);
      }
      for(i=0;itexts; ++i)
      {
    -   select_text(i, SELECTED, 1);
    +   select_text(i, SELECTED, 1, 0);
      }
      for(i=0;iinstances; ++i)
      {
    -   select_element(i,SELECTED,1, 0);
    +   select_element(i, SELECTED, 1, 0);
    +   /* following not done in select_element() due to fast=1 argument */
    +   select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0));
      }
      for(c=0;cpolygons[c]; ++i)
       {
    -    select_polygon(c,i,SELECTED,1);
    +    select_polygon(c, i, SELECTED, 1, 0);
       }
       for(i=0;ilines[c]; ++i)
       {
    -    select_line(c,i,SELECTED,1);
    +    select_line(c, i, SELECTED, 1, 0);
       }
       for(i=0;iarcs[c]; ++i)
       {
    -    select_arc(c,i, SELECTED, 1);
    +    select_arc(c, i, SELECTED, 1, 0);
       }
       for(i=0;irects[c]; ++i)
       {
    -    select_box(c,i, SELECTED, 1, 0);
    +    select_box(c, i, SELECTED, 1, 0);
       }
      } /* end for c */
      drawtemparc(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0, 0.0);
    diff --git a/src/xschem.h b/src/xschem.h
    index d4e74449..367cb9a6 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -1690,13 +1690,13 @@ extern const char *expandlabel(const char *s, int *m);
     extern void parse(const char *s);
     extern void clear_expandlabel_data(void);
     extern void merge_file(int selection_load, const char ext[]);
    -extern void select_wire(int i, unsigned short select_mode, int fast);
    +extern void select_wire(int i, unsigned short select_mode, int fast, int override_lock);
     extern void select_element(int i, unsigned short select_mode, int fast, int override_lock);
    -extern void select_text(int i, unsigned short select_mode, int fast);
    +extern void select_text(int i, unsigned short select_mode, int fast, int override_lock);
     extern void select_box(int c, int i, unsigned short select_mode, int fast, int override_lock);
    -extern void select_arc(int c, int i, unsigned short select_mode, int fast);
    -extern void select_line(int c, int i, unsigned short select_mode, int fast);
    -extern void select_polygon(int c, int i, unsigned short select_mode, int fast );
    +extern void select_arc(int c, int i, unsigned short select_mode, int fast, int override_lock);
    +extern void select_line(int c, int i, unsigned short select_mode, int fast, int override_lock);
    +extern void select_polygon(int c, int i, unsigned short select_mode, int fast, int override_lock );
     extern const char *net_name(int i, int j, int *mult, int hash_prefix_unnamed_net, int erc);
     extern int record_global_node(int what, FILE *fp, const char *node);
     extern int count_items(const char *s, const char *sep, const char *quote);
    
    From 6f0133a1b2df5edd47c4132a1b216712f256af67 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 19:38:05 +0100
    Subject: [PATCH 16/67] changed some *attached*() function names for
     sonsistency
    
    ---
     src/editprop.c  | 2 +-
     src/move.c      | 4 ++--
     src/scheduler.c | 2 +-
     src/select.c    | 6 +++---
     src/xschem.h    | 2 +-
     5 files changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/src/editprop.c b/src/editprop.c
    index ee8f1ac3..5a4aafa6 100644
    --- a/src/editprop.c
    +++ b/src/editprop.c
    @@ -1655,7 +1655,7 @@ static int update_symbol(const char *result, int x, int selected_inst)
           new_prop_string(*ii, ptr,               /* sets also inst[].instname */
              tclgetboolvar("disable_unique_names")); /* set new prop_ptr */
           hash_names(*ii, XINSERT);
    -      update_attached_object_refs(old_name, *ii, 1);
    +      update_attached_floater_refs(old_name, *ii, 1);
           dbg(0, "update_symbol(): insert %s\n", xctx->inst[*ii].instname);
           my_free(_ALLOC_ID_, &old_name);
         }
    diff --git a/src/move.c b/src/move.c
    index 20ee6f99..30af9a1d 100644
    --- a/src/move.c
    +++ b/src/move.c
    @@ -515,7 +515,7 @@ void draw_selection(GC g, int interruptable)
     
     /* sel: if set to 1 change references only on selected items, like in a copy operation.
      * If set to 0 operate on all objects with matching name=... attribute */
    -void update_attached_object_refs(const char *from_name, int inst, int sel)
    +void update_attached_floater_refs(const char *from_name, int inst, int sel)
     {
       int i, c;
       char *to_name = xctx->inst[inst].instname;
    @@ -958,7 +958,7 @@ void copy_objects(int what)
             new_prop_string(xctx->instances, xctx->inst[n].prop_ptr, /* sets also inst[].instname */
               tclgetboolvar("disable_unique_names"));
     
    -        update_attached_object_refs(xctx->inst[n].instname, xctx->instances, 1);
    +        update_attached_floater_refs(xctx->inst[n].instname, xctx->instances, 1);
     
             hash_names(xctx->instances, XINSERT);
             xctx->instances++; /* symbol_bbox calls translate and translate must have updated xctx->instances */
    diff --git a/src/scheduler.c b/src/scheduler.c
    index f5e9835c..43bf458a 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -5493,7 +5493,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
               hash_names(inst, XDELETE);
               new_prop_string(inst, subst, tclgetboolvar("disable_unique_names"));
               if(old_name) {
    -            update_attached_object_refs(old_name, inst, 0);
    +            update_attached_floater_refs(old_name, inst, 0);
               }
               my_strdup2(_ALLOC_ID_, &translated_sym, translate(inst, xctx->inst[inst].name));
               sym_number=match_symbol(translated_sym);
    diff --git a/src/select.c b/src/select.c
    index 524e5f1c..f815d0d5 100644
    --- a/src/select.c
    +++ b/src/select.c
    @@ -940,7 +940,7 @@ void select_wire(int i,unsigned short select_mode, int fast, int override_lock)
     }
     
     
    -static int select_attached_items(int inst, const char *name)
    +static int select_attached_floaters(int inst, const char *name)
     { 
       int i, c;
       int found = 0;
    @@ -1056,7 +1056,7 @@ void select_element(int i,unsigned short select_mode, int fast, int override_loc
         }
       }
       if(!fast && select_mode == SELECTED) {
    -    select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0));
    +    select_attached_floaters(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0));
       }
       xctx->need_reb_sel_arr=1;
     }
    @@ -1809,7 +1809,7 @@ void select_all(void)
      {
        select_element(i, SELECTED, 1, 0);
        /* following not done in select_element() due to fast=1 argument */
    -   select_attached_items(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0));
    +   select_attached_floaters(i, get_tok_value(xctx->inst[i].prop_ptr, "attach", 0));
      }
      for(c=0;c
    Date: Sat, 22 Feb 2025 19:40:04 +0100
    Subject: [PATCH 17/67] (2) changed some *attached*() function names for
     sonsistency
    
    ---
     src/editprop.c  | 2 +-
     src/move.c      | 4 ++--
     src/scheduler.c | 2 +-
     src/xschem.h    | 2 +-
     4 files changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/src/editprop.c b/src/editprop.c
    index 5a4aafa6..77adafd0 100644
    --- a/src/editprop.c
    +++ b/src/editprop.c
    @@ -1655,7 +1655,7 @@ static int update_symbol(const char *result, int x, int selected_inst)
           new_prop_string(*ii, ptr,               /* sets also inst[].instname */
              tclgetboolvar("disable_unique_names")); /* set new prop_ptr */
           hash_names(*ii, XINSERT);
    -      update_attached_floater_refs(old_name, *ii, 1);
    +      update_attached_floaters(old_name, *ii, 1);
           dbg(0, "update_symbol(): insert %s\n", xctx->inst[*ii].instname);
           my_free(_ALLOC_ID_, &old_name);
         }
    diff --git a/src/move.c b/src/move.c
    index 30af9a1d..03b5a2a9 100644
    --- a/src/move.c
    +++ b/src/move.c
    @@ -515,7 +515,7 @@ void draw_selection(GC g, int interruptable)
     
     /* sel: if set to 1 change references only on selected items, like in a copy operation.
      * If set to 0 operate on all objects with matching name=... attribute */
    -void update_attached_floater_refs(const char *from_name, int inst, int sel)
    +void update_attached_floaters(const char *from_name, int inst, int sel)
     {
       int i, c;
       char *to_name = xctx->inst[inst].instname;
    @@ -958,7 +958,7 @@ void copy_objects(int what)
             new_prop_string(xctx->instances, xctx->inst[n].prop_ptr, /* sets also inst[].instname */
               tclgetboolvar("disable_unique_names"));
     
    -        update_attached_floater_refs(xctx->inst[n].instname, xctx->instances, 1);
    +        update_attached_floaters(xctx->inst[n].instname, xctx->instances, 1);
     
             hash_names(xctx->instances, XINSERT);
             xctx->instances++; /* symbol_bbox calls translate and translate must have updated xctx->instances */
    diff --git a/src/scheduler.c b/src/scheduler.c
    index 43bf458a..4d464966 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -5493,7 +5493,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
               hash_names(inst, XDELETE);
               new_prop_string(inst, subst, tclgetboolvar("disable_unique_names"));
               if(old_name) {
    -            update_attached_floater_refs(old_name, inst, 0);
    +            update_attached_floaters(old_name, inst, 0);
               }
               my_strdup2(_ALLOC_ID_, &translated_sym, translate(inst, xctx->inst[inst].name));
               sym_number=match_symbol(translated_sym);
    diff --git a/src/xschem.h b/src/xschem.h
    index da175669..1384874c 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -1548,7 +1548,7 @@ extern void new_arc(int what, double sweep, double mousex_snap, double mousey_sn
     extern void arc_3_points(double x1, double y1, double x2, double y2, double x3, double y3,
              double *x, double *y, double *r, double *a, double *b);
     /* sel: if set to 1 change references only on selected items, like in a copy operation */
    -extern void update_attached_floater_refs(const char *from_name, int inst, int sel);
    +extern void update_attached_floaters(const char *from_name, int inst, int sel);
     extern void move_objects(int what,int merge, double dx, double dy);
     extern void check_collapsing_objects();
     extern void redraw_w_a_l_r_p_z_rubbers(int force); /* redraw wire, arcs, line, polygon rubbers */
    
    From 7144ead895f426a4ae756bbddc3a0b8c0152e5a5 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sat, 22 Feb 2025 19:49:43 +0100
    Subject: [PATCH 18/67] remove dbg messages
    
    ---
     src/editprop.c | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/src/editprop.c b/src/editprop.c
    index 77adafd0..2ed70f4d 100644
    --- a/src/editprop.c
    +++ b/src/editprop.c
    @@ -1650,13 +1650,13 @@ static int update_symbol(const char *result, int x, int selected_inst)
           if(!pushed) { xctx->push_undo(); pushed=1;}
           if(!k) hash_names(-1, XINSERT);
           hash_names(*ii, XDELETE);
    -      dbg(0, "update_symbol(): delete %s\n", xctx->inst[*ii].instname);
    +      dbg(1, "update_symbol(): delete %s\n", xctx->inst[*ii].instname);
           my_strdup2(_ALLOC_ID_, &old_name, xctx->inst[*ii].instname);
           new_prop_string(*ii, ptr,               /* sets also inst[].instname */
              tclgetboolvar("disable_unique_names")); /* set new prop_ptr */
           hash_names(*ii, XINSERT);
           update_attached_floaters(old_name, *ii, 1);
    -      dbg(0, "update_symbol(): insert %s\n", xctx->inst[*ii].instname);
    +      dbg(1, "update_symbol(): insert %s\n", xctx->inst[*ii].instname);
           my_free(_ALLOC_ID_, &old_name);
         }
         set_inst_flags(&xctx->inst[*ii]);
    
    From a306d97725ebf56a12d29f656b5a723e070837a4 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sun, 23 Feb 2025 12:12:59 +0100
    Subject: [PATCH 19/67] update docs (#pattern#@name tag)
    
    ---
     doc/xschem_man/symbol_property_syntax.html | 13 +++++++++++++
     src/editprop.c                             |  4 ++++
     src/save.c                                 |  3 ++-
     src/xschem.tcl                             | 10 ++++++++--
     4 files changed, 27 insertions(+), 3 deletions(-)
    
    diff --git a/doc/xschem_man/symbol_property_syntax.html b/doc/xschem_man/symbol_property_syntax.html
    index 9aa02448..5ff5abce 100644
    --- a/doc/xschem_man/symbol_property_syntax.html
    +++ b/doc/xschem_man/symbol_property_syntax.html
    @@ -551,6 +551,19 @@ verilog_format="xnor #(@risedel , @falldel ) @name ( @@Z , @@A , @@B );"
      
      

    PREDEFINED SYMBOL VALUES

      +
    • @name
    • +

      This expands to the instance name of the symbol (like C1, R2, X3, ...) + It is replaced with the value of the name=... attribute given when placing an instance of the symbol + in a schematic.

      +
    • #pattern#@name
    • +

      This is a variation of the above where pattern is prefixed to all substituted @name + patterns. The difference with pattern@name is that in case of vector instances (this means + a placement of an instance with name=R2[3:0], for example) the pattern string + is added to all single instances when expanding the name. see below examples.
      + pattern@name where name=R2[2:0] is given in the instance placement, + expands to patternR2[2],R2[1],R2[0]
      + #pattern#@name where name=R2[2:0] is given in the instance placement, + expands to patternR2[2],patternR2[1],patternR2[0]

    • @symname
    • This expands to the name of the symbol

    • @symref
    • diff --git a/src/editprop.c b/src/editprop.c index 2ed70f4d..dbe65604 100644 --- a/src/editprop.c +++ b/src/editprop.c @@ -594,7 +594,11 @@ FILE *my_fopen(const char *f, const char *m) st = stat(f, &buf); if(st) return NULL; /* not existing or error */ +#ifdef __unix__ if(!S_ISREG(buf.st_mode)) return NULL; /* not a regular file/symlink to a regular file */ +#else + /* TBD */ +#endif fd = fopen(f, m); return fd; } diff --git a/src/save.c b/src/save.c index cefd1a04..3cd03bb0 100644 --- a/src/save.c +++ b/src/save.c @@ -2783,7 +2783,8 @@ static void load_inst(int k, FILE *fd) if(name[0] == '/') my_strdup2(_ALLOC_ID_, &xctx->inst[i].name, rel_sym_path(name)); else my_strdup2(_ALLOC_ID_, &xctx->inst[i].name, name); #else - my_strdup2(_ALLOC_ID_, &xctx->inst[i].name, rel_sym_path(name)); + if(isupper(name[0]) && name[1] == ':' && name[1] == '/') my_strdup2(_ALLOC_ID_, &xctx->inst[i].name, rel_sym_path(name)); + else my_strdup2(_ALLOC_ID_, &xctx->inst[i].name, name); #endif my_free(_ALLOC_ID_, &tmp); if(fscanf(fd, "%lf %lf %hd %hd", &xctx->inst[i].x0, &xctx->inst[i].y0, diff --git a/src/xschem.tcl b/src/xschem.tcl index e35d90e6..f2688bd2 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -6744,8 +6744,14 @@ proc rel_sym_path {symbol} { global OS pathlist env regsub {^~/} $symbol ${env(HOME)}/ symbol - if {![regexp {^/} $symbol]} { - set symbol [pwd]/$symbol + if {$OS eq "Windows"} { + if {![regexp {^[A-Za-z]\:/} $symbol]} { + set symbol [pwd]/$symbol + } + } else { + if {![regexp {^/} $symbol]} { + set symbol [pwd]/$symbol + } } set curr_dirname [pwd] set name {} From c604a1f0331ff7e6fa082310ad300ab23731292b Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 23 Feb 2025 12:39:59 +0100 Subject: [PATCH 20/67] higher priority of `unlocked` sub-attribute in rect `graph` attribute. Do not propagate zoom / pan to any other graphs even if they are locked. --- src/callback.c | 4 +- xschem_library/ngspice/solar_panel.sch | 152 ++++++++++++------------- 2 files changed, 79 insertions(+), 77 deletions(-) diff --git a/src/callback.c b/src/callback.c index 2866420c..346f6f63 100644 --- a/src/callback.c +++ b/src/callback.c @@ -926,7 +926,9 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int if(gr->dataset >= 0 /* && gr->dataset < xctx->raw->datasets */) dataset =gr->dataset; else dataset = -1; - if(!strcmp(curr_sim_type, + /* if master graph has unlocked X axis do not zoom/pan any other graphs: same_sim_type = 0 */ + if(!(xctx->rect[GRIDLAYER][xctx->graph_master].flags & 2) && + !strcmp(curr_sim_type, get_tok_value(xctx->rect[GRIDLAYER][xctx->graph_master].prop_ptr, "sim_type", 0))) { same_sim_type = 1; } diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch index f7aa2f21..f03b1f28 100644 --- a/xschem_library/ngspice/solar_panel.sch +++ b/xschem_library/ngspice/solar_panel.sch @@ -26,34 +26,34 @@ K {} V {} S {} E {} -L 2 105 -1005 105 -775 {} -L 2 145 -1005 145 -775 {} -L 2 185 -1005 185 -775 {} -L 2 225 -1005 225 -775 {} -L 2 265 -1005 265 -775 {} -L 2 305 -1005 305 -775 {} -L 2 345 -1005 345 -775 {} -L 2 385 -1005 385 -775 {} -L 2 65 -975 395 -975 {} -L 2 65 -935 395 -935 {} -L 2 65 -895 395 -895 {} -L 2 65 -855 395 -855 {} -L 2 65 -815 395 -815 {} -L 4 65 -1015 65 -755 {} -L 4 55 -775 405 -775 {} -L 4 105 -775 105 -765 {} -L 4 145 -775 145 -765 {} -L 4 185 -775 185 -765 {} -L 4 225 -775 225 -765 {} -L 4 265 -775 265 -765 {} -L 4 305 -775 305 -765 {} -L 4 345 -775 345 -765 {} -L 4 385 -775 385 -765 {} -L 4 55 -815 65 -815 {} -L 4 55 -855 65 -855 {} -L 4 55 -895 65 -895 {} -L 4 55 -935 65 -935 {} -L 4 55 -975 65 -975 {} +L 2 95 -1105 95 -875 {} +L 2 135 -1105 135 -875 {} +L 2 175 -1105 175 -875 {} +L 2 215 -1105 215 -875 {} +L 2 255 -1105 255 -875 {} +L 2 295 -1105 295 -875 {} +L 2 335 -1105 335 -875 {} +L 2 375 -1105 375 -875 {} +L 2 55 -1075 385 -1075 {} +L 2 55 -1035 385 -1035 {} +L 2 55 -995 385 -995 {} +L 2 55 -955 385 -955 {} +L 2 55 -915 385 -915 {} +L 4 55 -1115 55 -855 {} +L 4 45 -875 395 -875 {} +L 4 95 -875 95 -865 {} +L 4 135 -875 135 -865 {} +L 4 175 -875 175 -865 {} +L 4 215 -875 215 -865 {} +L 4 255 -875 255 -865 {} +L 4 295 -875 295 -865 {} +L 4 335 -875 335 -865 {} +L 4 375 -875 375 -865 {} +L 4 45 -915 55 -915 {} +L 4 45 -955 55 -955 {} +L 4 45 -995 55 -995 {} +L 4 45 -1035 55 -1035 {} +L 4 45 -1075 55 -1075 {} L 4 610 -1100 630 -1140 {} L 4 630 -1140 650 -1100 {} L 4 650 -1100 670 -1140 {} @@ -83,13 +83,13 @@ L 4 960 -340 970 -340 {} L 4 960 -340 970 -330 {} L 4 970 -330 980 -340 {} L 4 970 -340 980 -340 {} -B 2 1260 -560 1680 -390 {flags=graph +B 2 1370 -555 1790 -385 {flags=graph y1 = -0.00068 y2 = 22 divy = 6 subdivy=1 -x1=2.9358714e-05 -x2=0.0010293582 +x1=0.00043584075 +x2=0.00062432018 divx=8 node="PANEL LED" unitx=m @@ -104,13 +104,13 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 1260 -390 1680 -220 {flags=graph +B 2 1370 -385 1790 -215 {flags=graph y1 = -0.0012 y2 = 6.8 divy = 4 subdivy=1 -x1=2.9358714e-05 -x2=0.0010293582 +x1=0.00043584075 +x2=0.00062432018 divx=8 unitx=m color="7 4 6" @@ -123,13 +123,13 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 1260 -750 1680 -560 {flags=graph +B 2 1370 -745 1790 -555 {flags=graph y1 = -2.7e-05 y2 = 100 divy = 5 subdivy=1 -x1=2.9358714e-05 -x2=0.0010293582 +x1=0.00043584075 +x2=0.00062432018 divx=9 unitx=m subdivx=4 @@ -146,13 +146,13 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 1260 -940 1680 -750 {flags=graph +B 2 1370 -935 1790 -745 {flags=graph y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=2.9358714e-05 -x2=0.0010293582 +x1=0.00043584075 +x2=0.00062432018 divx=9 unitx=u subdivx=4 @@ -169,13 +169,13 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 1260 -1140 1680 -950 {flags=graph +B 2 1370 -1135 1790 -945 {flags=graph y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=2.9358714e-05 -x2=0.0010293582 +x1=0.00043584075 +x2=0.00062432018 divx=9 unitx=u subdivx=4 @@ -193,14 +193,14 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 390 -720 500 -640 {name=l21 -flags=graph -y1 = 0.00033 +B 2 360 -730 470 -650 {name=l21 +flags=graph,unlocked,private_cursor +y1 = 19 y2 = 21 divy = 5 subdivy=1 -x1=5e-10 -x2=0.001 +x1=0.00045143494 +x2=0.00063991437 divx=9 unitx=u subdivx=4 @@ -218,30 +218,31 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw linewidth_mult=0.4 -lock=1} -B 18 65 -960 320 -775 {} -A 5 320 -960 5.590169943749475 243.434948822922 360 {fill=true} -P 7 6 395 -775 340 -931.25 335 -945 322.5 -960 310 -965 65 -975 {} -T {2x10 1W white LED} 1240 -200 0 0 0.4 0.4 {layer=8} -T {2xseries 1W white LEDs} 1220 -140 0 0 0.4 0.4 {} -T {2.5} 95 -755 0 0 0.2 0.2 {} -T {5.0} 135 -755 0 0 0.2 0.2 {} -T {7.5} 175 -755 0 0 0.2 0.2 {} -T {10.0} 215 -755 0 0 0.2 0.2 {} -T {12.5} 255 -755 0 0 0.2 0.2 {} -T {15.0} 295 -755 0 0 0.2 0.2 {} -T {17.5} 335 -755 0 0 0.2 0.2 {} -T {20.0} 375 -755 0 0 0.2 0.2 {} -T {2.5} 35 -980 0 0 0.2 0.2 {} -T {2.0} 35 -940 0 0 0.2 0.2 {} -T {1.5} 35 -895 0 0 0.2 0.2 {} -T {1.0} 35 -860 0 0 0.2 0.2 {} -T {0.5} 35 -820 0 0 0.2 0.2 {} -T {25C, 1000W/m2} 190 -1030 0 0 0.2 0.2 {} -T {V} 410 -765 0 0 0.4 0.4 {} -T {I} 40 -1020 0 0 0.4 0.4 {} -T {SOLAR PANEL} 145 -1055 0 0 0.4 0.4 {} -T {Maximum Power} 307.5 -980 0 0 0.2 0.2 {layer=8} +lock=0 +cursor2_x=0.00052215} +B 18 55 -1060 310 -875 {} +A 5 310 -1060 5.590169943749475 243.434948822922 360 {fill=true} +P 7 6 385 -875 330 -1031.25 325 -1045 312.5 -1060 300 -1065 55 -1075 {} +T {2x10 1W white LED} 1350 -195 0 0 0.4 0.4 {layer=8} +T {2xseries 1W white LEDs} 1330 -135 0 0 0.4 0.4 {} +T {2.5} 85 -855 0 0 0.2 0.2 {} +T {5.0} 125 -855 0 0 0.2 0.2 {} +T {7.5} 165 -855 0 0 0.2 0.2 {} +T {10.0} 205 -855 0 0 0.2 0.2 {} +T {12.5} 245 -855 0 0 0.2 0.2 {} +T {15.0} 285 -855 0 0 0.2 0.2 {} +T {17.5} 325 -855 0 0 0.2 0.2 {} +T {20.0} 365 -855 0 0 0.2 0.2 {} +T {2.5} 25 -1080 0 0 0.2 0.2 {} +T {2.0} 25 -1040 0 0 0.2 0.2 {} +T {1.5} 25 -995 0 0 0.2 0.2 {} +T {1.0} 25 -960 0 0 0.2 0.2 {} +T {0.5} 25 -920 0 0 0.2 0.2 {} +T {25C, 1000W/m2} 180 -1130 0 0 0.2 0.2 {} +T {V} 400 -865 0 0 0.4 0.4 {} +T {I} 30 -1120 0 0 0.4 0.4 {} +T {SOLAR PANEL} 135 -1155 0 0 0.4 0.4 {} +T {Maximum Power} 297.5 -1080 0 0 0.2 0.2 {layer=8} T {set between 0 and 1 to simulate sun radiation @@ -258,7 +259,6 @@ T {Floater text example} 870 -440 0 0 0.4 0.4 {} T {@spice_get_current} 875 -598.75 0 0 0.3 0.3 {layer=7 name=L2} T {@spice_get_current} 1015 -268.75 0 0 0.3 0.3 {layer=7 name=C1} -T {@spice_get_voltage} 427.5 -750 0 0 0.4 0.4 {name=l21} N 1010 -210 1100 -210 {lab=0} N 1100 -300 1100 -210 {lab=0} N 640 -610 730 -610 {lab=#net1} @@ -273,7 +273,7 @@ N 790 -610 820 -610 {lab=SW} N 820 -210 1010 -210 {lab=0} N 800 -210 820 -210 {lab=0} N 1000 -610 1010 -610 {lab=VO} -N 1100 -440 1140 -440 {lab=LED} +N 1100 -440 1280 -440 {lab=LED} N 1100 -440 1100 -360 {lab=LED} N 820 -430 820 -390 { lab=#net2} N 920 -610 940 -610 { lab=#net3} @@ -354,7 +354,7 @@ footprint=1206 device=inductor hide_texts=true attach=L2} -C {lab_pin.sym} 1140 -440 0 1 {name=l7 lab=LED } +C {lab_pin.sym} 1280 -440 0 1 {name=l7 lab=LED } C {lab_pin.sym} 820 -550 0 1 {name=l9 lab=SW } C {capa.sym} 1010 -280 0 0 {name=C1 m=1 @@ -432,5 +432,5 @@ C {spice_probe.sym} 850 -1030 0 1 {name=p4 analysis=tran} C {spice_probe.sym} 810 -890 0 1 {name=p5 analysis=tran} C {spice_probe.sym} 760 -670 0 0 {name=p6 analysis=tran} C {spice_probe.sym} 160 -450 0 0 {name=p7 analysis=tran} -C {lab_show.sym} 520 -610 0 0 {name=l21 +C {scope.sym} 490 -610 0 0 {name=l21 attach=l21} From 6a75c8a6dd322ce7f307fb3fd8a2c5c1d98d66ca Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 23 Feb 2025 13:50:39 +0100 Subject: [PATCH 21/67] initial commit to add scope.sym symbol --- src/xschem.h | 2 +- xschem_library/devices/scope.sym | 33 ++++++++++ xschem_library/ngspice/solar_panel.sch | 87 ++++++++++++++++++++------ 3 files changed, 101 insertions(+), 21 deletions(-) create mode 100644 xschem_library/devices/scope.sym diff --git a/src/xschem.h b/src/xschem.h index 1384874c..b3a94527 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -375,7 +375,7 @@ extern char win_temp_dir[PATH_MAX]; * show_label also used on metal option type symbols (pass-through symbols) * to optionally short two nets (using *_ignore=[true|false] attribute) */ #define IS_LABEL_SH_OR_PIN(type) (!(strcmp(type,"label") && strcmp(type,"ipin") && strcmp(type,"opin") && \ - strcmp(type,"show_label") && strcmp(type,"iopin") && strcmp(type,"bus_tap"))) + strcmp(type,"scope") && strcmp(type,"show_label") && strcmp(type,"iopin") && strcmp(type,"bus_tap"))) #define IS_LABEL_OR_PIN(type) (!(strcmp(type,"label") && strcmp(type,"ipin") && \ strcmp(type,"opin") && strcmp(type,"iopin"))) #define IS_PIN(type) (!(strcmp(type,"ipin") && strcmp(type,"opin") && strcmp(type,"iopin"))) diff --git a/xschem_library/devices/scope.sym b/xschem_library/devices/scope.sym new file mode 100644 index 00000000..780f73ba --- /dev/null +++ b/xschem_library/devices/scope.sym @@ -0,0 +1,33 @@ +v {xschem version=3.4.6 file_version=1.2 +* +* This file is part of XSCHEM, +* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit +* simulation. +* Copyright (C) 1998-2024 Stefan Frederik Schippers +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +} +G {} +K {type=scope +template="name=l1" +highlight=true} +V {} +S {} +E {} +L 4 -10 -20 0 0 {} +B 5 -1.25 -1.25 1.25 1.25 {name=p dir=in} +P 4 5 -140 -20 -140 -140 -10 -140 -10 -20 -140 -20 {} +T {@#0:net_name} -12.5 -35.625 0 1 0.2 0.2 {layer=4} +T {@spice_get_voltage} -137.5 -137.5 0 0 0.2 0.2 {layer=15} diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch index f03b1f28..051ac194 100644 --- a/xschem_library/ngspice/solar_panel.sch +++ b/xschem_library/ngspice/solar_panel.sch @@ -88,8 +88,8 @@ y1 = -0.00068 y2 = 22 divy = 6 subdivy=1 -x1=0.00043584075 -x2=0.00062432018 +x1=0.00044137417 +x2=0.0006298536 divx=8 node="PANEL LED" unitx=m @@ -109,8 +109,8 @@ y1 = -0.0012 y2 = 6.8 divy = 4 subdivy=1 -x1=0.00043584075 -x2=0.00062432018 +x1=0.00044137417 +x2=0.0006298536 divx=8 unitx=m color="7 4 6" @@ -128,8 +128,8 @@ y1 = -2.7e-05 y2 = 100 divy = 5 subdivy=1 -x1=0.00043584075 -x2=0.00062432018 +x1=0.00044137417 +x2=0.0006298536 divx=9 unitx=m subdivx=4 @@ -151,8 +151,8 @@ y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=0.00043584075 -x2=0.00062432018 +x1=0.00044137417 +x2=0.0006298536 divx=9 unitx=u subdivx=4 @@ -174,8 +174,8 @@ y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=0.00043584075 -x2=0.00062432018 +x1=0.00044137417 +x2=0.0006298536 divx=9 unitx=u subdivx=4 @@ -194,18 +194,15 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} B 2 360 -730 470 -650 {name=l21 -flags=graph,unlocked,private_cursor -y1 = 19 +flags=graph,unlocked +y1 = 0.00033 y2 = 21 divy = 5 subdivy=1 -x1=0.00045143494 -x2=0.00063991437 +x1=5e-10 +x2=0.001 divx=9 - - unitx=u subdivx=4 - - +unitx=u subdivx=4 hilight_wave=-1 digital=0 ypos1=0.00261891 @@ -214,11 +211,56 @@ color=8 node="tcleval([xschem translate l21 @#0:net_name])" jpeg_quality=30 autoload=0 - sim_type=tran xrawfile=$netlist_dir/solar_panel.raw linewidth_mult=0.4 -lock=0 +lock=1 +cursor2_x=0.00052215} +B 2 1210 -560 1320 -480 {name=l22 +flags=graph,unlocked +y1 = -0.59 +y2 = 7.3 +divy = 5 +subdivy=1 +x1=5e-10 +x2=0.001 +divx=9 +unitx=u subdivx=4 +hilight_wave=-1 +digital=0 +ypos1=0.00261891 +ypos2=0.51596 +color=8 +node="tcleval([xschem translate l22 @#0:net_name])" +jpeg_quality=30 +autoload=0 +sim_type=tran +xrawfile=$netlist_dir/solar_panel.raw +linewidth_mult=0.4 +lock=1 +cursor2_x=0.00052215} +B 2 760 -1200 870 -1120 {name=l23 +flags=graph,unlocked +y1 = 4.9e-10 +y2 = 1 +divy = 5 +subdivy=1 +x1=0.00051506091 +x2=0.00053874392 +divx=9 +unitx=u subdivx=4 +hilight_wave=-1 +digital=0 +ypos1=0.00261891 +ypos2=0.51596 +color=8 +node="tcleval([xschem translate l23 @#0:net_name])" +jpeg_quality=30 +autoload=0 +sim_type=tran +xrawfile=$netlist_dir/solar_panel.raw +linewidth_mult=0.4 +lock=1 cursor2_x=0.00052215} B 18 55 -1060 310 -875 {} A 5 310 -1060 5.590169943749475 243.434948822922 360 {fill=true} @@ -434,3 +476,8 @@ C {spice_probe.sym} 760 -670 0 0 {name=p6 analysis=tran} C {spice_probe.sym} 160 -450 0 0 {name=p7 analysis=tran} C {scope.sym} 490 -610 0 0 {name=l21 attach=l21} +C {scope.sym} 1190 -440 0 1 {name=l22 +attach=l22} +C {scope.sym} 740 -1080 0 1 {name=l23 +attach=l23} +C {lab_show.sym} 440 -610 2 0 {name=l24} From 1d3c2dde5990343daf6f9f07037a65446d71bc6f Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 23 Feb 2025 14:03:53 +0100 Subject: [PATCH 22/67] fix regression: Cancel pressed when inserting new symbol --- src/actions.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/actions.c b/src/actions.c index a8b92558..c377e0d6 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1484,7 +1484,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot } else { my_strncpy(name1, trim_chars(symbol_name, " \t\n"), S(name1)); } - + if(!name1[0]) return 0; dbg(1, "place_symbol(): 1: name1=%s first_call=%d\n",name1, first_call); /* remove tcleval( given in file selector, if any ... */ if(strstr(name1, "tcleval(")) { @@ -1576,6 +1576,47 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot if(xctx->prep_hash_inst) hash_inst(XINSERT, n); /* no need to rehash, add item */ /* xctx->prep_hash_inst=0; */ + + + if(xctx->sym[i].type && !strcmp(xctx->sym[i].type, "scope")) { + char *prop = NULL; + /* +name=l21 +flags=graph,unlocked +y1 = 0.00033 +y2 = 21 +divy = 5 +subdivy=1 +x1=5e-10 +x2=0.001 +divx=9 +unitx=u subdivx=4 +hilight_wave=-1 +digital=0 +ypos1=0.00261891 +ypos2=0.51596 +color=8 +node="tcleval([xschem translate l21 @#0:net_name])" +jpeg_quality=30 +autoload=0 +sim_type=tran +xrawfile=$netlist_dir/solar_panel.raw +linewidth_mult=0.4 +lock=1 +cursor2_x=0.00052215 +*/ + + + my_mstrcat(_ALLOC_ID_, &prop, "name=", xctx->inst[n].instname, NULL); + + dbg(0, "name=%s\n", xctx->inst[n].instname); + storeobject(-1, x-130, y-120, x - 20, y - 40, xRECT, 2, SELECTED, prop); + my_free(_ALLOC_ID_, &prop); + } + + + + if(draw_sym & 3) { bbox(ADD, xctx->inst[n].x1, xctx->inst[n].y1, xctx->inst[n].x2, xctx->inst[n].y2); } From bd2c65bc1ad02102fa6336ddaa32853aa70bbe54 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 23 Feb 2025 14:58:44 +0100 Subject: [PATCH 23/67] embed graph inside scope symbol --- src/actions.c | 44 ++-------- xschem_library/ngspice/solar_panel.sch | 112 ++++++++----------------- 2 files changed, 44 insertions(+), 112 deletions(-) diff --git a/src/actions.c b/src/actions.c index c377e0d6..a31c5063 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1576,47 +1576,21 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot if(xctx->prep_hash_inst) hash_inst(XINSERT, n); /* no need to rehash, add item */ /* xctx->prep_hash_inst=0; */ - - + /* embed a (locked) graph object floater inside the symbol */ if(xctx->sym[i].type && !strcmp(xctx->sym[i].type, "scope")) { char *prop = NULL; - /* -name=l21 -flags=graph,unlocked -y1 = 0.00033 -y2 = 21 -divy = 5 -subdivy=1 -x1=5e-10 -x2=0.001 -divx=9 -unitx=u subdivx=4 -hilight_wave=-1 -digital=0 -ypos1=0.00261891 -ypos2=0.51596 -color=8 -node="tcleval([xschem translate l21 @#0:net_name])" -jpeg_quality=30 -autoload=0 -sim_type=tran -xrawfile=$netlist_dir/solar_panel.raw -linewidth_mult=0.4 -lock=1 -cursor2_x=0.00052215 -*/ - - - my_mstrcat(_ALLOC_ID_, &prop, "name=", xctx->inst[n].instname, NULL); - - dbg(0, "name=%s\n", xctx->inst[n].instname); + my_strdup(_ALLOC_ID_, &xctx->inst[n].prop_ptr, + subst_token(xctx->inst[n].prop_ptr, "attach", xctx->inst[n].instname)); + my_mstrcat(_ALLOC_ID_, &prop, "name=", xctx->inst[n].instname, "\n", NULL); + my_mstrcat(_ALLOC_ID_, &prop, "flags=graph,unlocked\n", NULL); + my_mstrcat(_ALLOC_ID_, &prop, "lock=1\n", NULL); + my_mstrcat(_ALLOC_ID_, &prop, "color=8\n", NULL); + my_mstrcat(_ALLOC_ID_, &prop, "node=\"tcleval([xschem translate ", + xctx->inst[n].instname, " @#0:net_name])\"", NULL); storeobject(-1, x-130, y-120, x - 20, y - 40, xRECT, 2, SELECTED, prop); my_free(_ALLOC_ID_, &prop); } - - - if(draw_sym & 3) { bbox(ADD, xctx->inst[n].x1, xctx->inst[n].y1, xctx->inst[n].x2, xctx->inst[n].y2); } diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch index 051ac194..e73819bd 100644 --- a/xschem_library/ngspice/solar_panel.sch +++ b/xschem_library/ngspice/solar_panel.sch @@ -88,8 +88,8 @@ y1 = -0.00068 y2 = 22 divy = 6 subdivy=1 -x1=0.00044137417 -x2=0.0006298536 +x1=5e-10 +x2=0.001 divx=8 node="PANEL LED" unitx=m @@ -109,8 +109,8 @@ y1 = -0.0012 y2 = 6.8 divy = 4 subdivy=1 -x1=0.00044137417 -x2=0.0006298536 +x1=5e-10 +x2=0.001 divx=8 unitx=m color="7 4 6" @@ -128,8 +128,8 @@ y1 = -2.7e-05 y2 = 100 divy = 5 subdivy=1 -x1=0.00044137417 -x2=0.0006298536 +x1=5e-10 +x2=0.001 divx=9 unitx=m subdivx=4 @@ -151,8 +151,8 @@ y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=0.00044137417 -x2=0.0006298536 +x1=5e-10 +x2=0.001 divx=9 unitx=u subdivx=4 @@ -174,8 +174,8 @@ y1 = 0 y2 = 1 divy = 5 subdivy=1 -x1=0.00044137417 -x2=0.0006298536 +x1=5e-10 +x2=0.001 divx=9 unitx=u subdivx=4 @@ -193,75 +193,33 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 360 -730 470 -650 {name=l21 -flags=graph,unlocked -y1 = 0.00033 -y2 = 21 -divy = 5 -subdivy=1 -x1=5e-10 -x2=0.001 -divx=9 -unitx=u subdivx=4 -hilight_wave=-1 -digital=0 -ypos1=0.00261891 -ypos2=0.51596 +B 2 270 -730 380 -650 {name=l21 +flags=graph,unlocked +lock=1 color=8 node="tcleval([xschem translate l21 @#0:net_name])" -jpeg_quality=30 -autoload=0 -sim_type=tran -xrawfile=$netlist_dir/solar_panel.raw -linewidth_mult=0.4 -lock=1 -cursor2_x=0.00052215} -B 2 1210 -560 1320 -480 {name=l22 -flags=graph,unlocked -y1 = -0.59 -y2 = 7.3 -divy = 5 -subdivy=1 +y1=0.00033 +y2=21 x1=5e-10 -x2=0.001 -divx=9 -unitx=u subdivx=4 -hilight_wave=-1 -digital=0 -ypos1=0.00261891 -ypos2=0.51596 +x2=0.001} +B 2 1230 -560 1340 -480 {name=l22 +flags=graph,unlocked +lock=1 color=8 node="tcleval([xschem translate l22 @#0:net_name])" -jpeg_quality=30 -autoload=0 -sim_type=tran -xrawfile=$netlist_dir/solar_panel.raw -linewidth_mult=0.4 +y1=0.00033 +y2=21 +x1=5e-10 +x2=0.001} +B 2 780 -1200 890 -1120 {name=l23 +flags=graph,unlocked lock=1 -cursor2_x=0.00052215} -B 2 760 -1200 870 -1120 {name=l23 -flags=graph,unlocked -y1 = 4.9e-10 -y2 = 1 -divy = 5 -subdivy=1 -x1=0.00051506091 -x2=0.00053874392 -divx=9 -unitx=u subdivx=4 -hilight_wave=-1 -digital=0 -ypos1=0.00261891 -ypos2=0.51596 color=8 node="tcleval([xschem translate l23 @#0:net_name])" -jpeg_quality=30 -autoload=0 -sim_type=tran -xrawfile=$netlist_dir/solar_panel.raw -linewidth_mult=0.4 -lock=1 -cursor2_x=0.00052215} +y1=-0.55990715 +y2=1.5136929 +x1=0.00048981872 +x2=0.0005150806} B 18 55 -1060 310 -875 {} A 5 310 -1060 5.590169943749475 243.434948822922 360 {fill=true} P 7 6 385 -875 330 -1031.25 325 -1045 312.5 -1060 300 -1065 55 -1075 {} @@ -474,10 +432,10 @@ C {spice_probe.sym} 850 -1030 0 1 {name=p4 analysis=tran} C {spice_probe.sym} 810 -890 0 1 {name=p5 analysis=tran} C {spice_probe.sym} 760 -670 0 0 {name=p6 analysis=tran} C {spice_probe.sym} 160 -450 0 0 {name=p7 analysis=tran} -C {scope.sym} 490 -610 0 0 {name=l21 -attach=l21} -C {scope.sym} 1190 -440 0 1 {name=l22 -attach=l22} -C {scope.sym} 740 -1080 0 1 {name=l23 -attach=l23} C {lab_show.sym} 440 -610 2 0 {name=l24} +C {scope.sym} 400 -610 0 0 {name=l21 +attach=l21} +C {scope.sym} 1210 -440 0 1 {name=l22 +attach=l22} +C {scope.sym} 760 -1080 0 1 {name=l23 +attach=l23} From b205b4a4964f32ef6e29d665af56ec4c640d5536 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sun, 23 Feb 2025 23:49:31 +0100 Subject: [PATCH 24/67] fix ui problem when ctrl-clicking a launcher (drag_elements remained set) --- src/callback.c | 1 + src/scheduler.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/callback.c b/src/callback.c index 346f6f63..8b702791 100644 --- a/src/callback.c +++ b/src/callback.c @@ -4359,6 +4359,7 @@ static void handle_button_release(int event, KeySym key, int state, int button, int savesem = xctx->semaphore; move_objects(ABORT, 0, 0.0, 0.0); unselect_all(1); + xctx->drag_elements = 0; /* after ctrl-Button1Press on a launcher need to clear this */ select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); rebuild_selected_array(); xctx->semaphore = 0; diff --git a/src/scheduler.c b/src/scheduler.c index 4d464966..bcd97afe 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -2103,6 +2103,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg my_snprintf(res, S(res), "semaphore=%d\n", xctx->semaphore); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "ui_state=%d\n", xctx->ui_state); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "ui_state2=%d\n", xctx->ui_state2); Tcl_AppendResult(interp, res, NULL); + my_snprintf(res, S(res), "drag_elements=%d\n", xctx->drag_elements); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "last_command=%d\n", xctx->last_command); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_net_structs=%d\n", xctx->prep_net_structs); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_hi_structs=%d\n", xctx->prep_hi_structs); Tcl_AppendResult(interp, res, NULL); From f9ccf878c2fa94babe30bcb5224e3af8fd51f92c Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Mon, 24 Feb 2025 15:23:35 +0100 Subject: [PATCH 25/67] differential scope2.sym, current mwasure scope_ammeter.sym --- doc/xschem_man/developer_info.html | 11 ++- src/actions.c | 44 +++++++++-- src/callback.c | 1 - src/move.c | 2 +- src/scheduler.c | 29 +++++++- src/token.c | 94 ++++++++++++++++++++++++ src/xschem.h | 1 + xschem_library/devices/scope.sym | 8 +- xschem_library/devices/scope2.sym | 36 +++++++++ xschem_library/devices/scope_ammeter.sym | 31 ++++++++ xschem_library/ngspice/comp_ngspice.sch | 14 +++- xschem_library/ngspice/solar_panel.sch | 64 ++++++++++------ 12 files changed, 296 insertions(+), 39 deletions(-) create mode 100644 xschem_library/devices/scope2.sym create mode 100644 xschem_library/devices/scope_ammeter.sym diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html index 44a2b51b..844379c7 100644 --- a/doc/xschem_man/developer_info.html +++ b/doc/xschem_man/developer_info.html @@ -551,7 +551,6 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns" -
    • abort_operation
    • @@ -828,6 +827,15 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
          return result of get_cell function 
    • get_cell_w_ext cell n_dirs
    •     return result of get_cell_w_ext function 
      +
    • get_fqdevice instname param modelparam
    • +   get the full pathname of "instname" device
      +   modelparam: 
      +     0: current, 1: modelparam, 2: modelvoltage
      +   param: device parameter, like ib, gm, vth
      +   set param to {} (empty str) for just branch current of 2 terminal device
      +   for parameters like "vth" modelparam must be 2
      +   for parameters like "ib" modelparam must be 0
      +   for parameters like "gm" modelparam must be 1
    • getprop instance|instance_pin|symbol|text ref
    •        
              getprop instance inst
      @@ -1735,6 +1743,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
       
       
       
      +
       
       
        
    diff --git a/src/actions.c b/src/actions.c index a31c5063..e6808c67 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1450,7 +1450,6 @@ void delete_files(void) void place_net_label(int type) { - unselect_all(1); if(type == 1) { const char *lab = tcleval("rel_sym_path [find_file_first lab_pin.sym]"); place_symbol(-1, lab, xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1/*to_push_undo*/); @@ -1478,6 +1477,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot char name[PATH_MAX]; char name1[PATH_MAX]; char tclev = 0; + if(symbol_name==NULL) { tcleval("load_file_dialog {Choose symbol} *.\\{sym,tcl\\} INITIALINSTDIR"); my_strncpy(name1, tclresult(), S(name1)); @@ -1562,7 +1562,11 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot /* After having assigned prop_ptr to new instance translate symbol reference * to resolve @params --> res.tcl(@value\) --> res.tcl(100) */ my_strncpy(name, translate(n, name), S(name)); - i = match_symbol(name); + /* parameters like res.tcl(@value\) have been resolved, so reload symbol removing previous */ + if(strcmp(name, name1)) { + remove_symbol(i); + i = match_symbol(name); + } xctx->inst[n].ptr = i; set_inst_flags(&xctx->inst[n]); hash_names(n, XINSERT); @@ -1579,15 +1583,44 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot /* embed a (locked) graph object floater inside the symbol */ if(xctx->sym[i].type && !strcmp(xctx->sym[i].type, "scope")) { char *prop = NULL; + my_strdup(_ALLOC_ID_, &xctx->inst[n].prop_ptr, subst_token(xctx->inst[n].prop_ptr, "attach", xctx->inst[n].instname)); my_mstrcat(_ALLOC_ID_, &prop, "name=", xctx->inst[n].instname, "\n", NULL); my_mstrcat(_ALLOC_ID_, &prop, "flags=graph,unlocked\n", NULL); my_mstrcat(_ALLOC_ID_, &prop, "lock=1\n", NULL); my_mstrcat(_ALLOC_ID_, &prop, "color=8\n", NULL); - my_mstrcat(_ALLOC_ID_, &prop, "node=\"tcleval([xschem translate ", - xctx->inst[n].instname, " @#0:net_name])\"", NULL); - storeobject(-1, x-130, y-120, x - 20, y - 40, xRECT, 2, SELECTED, prop); + if(xctx->sym[i].rects[PINLAYER] == 0) { + if(xctx->lastsel == 1 && xctx->sel_array[0].type==ELEMENT) { + my_mstrcat(_ALLOC_ID_, &prop, "node=\"tcleval([xschem get_fqdevice [xschem translate ", + xctx->inst[n].instname, " @device]])\"\n", NULL); + my_strdup(_ALLOC_ID_, &xctx->inst[n].prop_ptr, + subst_token(xctx->inst[n].prop_ptr, "device", xctx->inst[xctx->sel_array[0].n].instname)); + } else { + const char msg[]="scope_ammeter is being inserted but no selected ammeter device/vsource to link to\n"; + dbg(0, "%s", msg); + if(has_x) tclvareval("alert_ {", msg, "} {}", NULL); + if(xctx->inst[n].instname) my_free(_ALLOC_ID_, &xctx->inst[n].instname); + if(xctx->inst[n].name) my_free(_ALLOC_ID_, &xctx->inst[n].name); + if(xctx->inst[n].prop_ptr) my_free(_ALLOC_ID_, &xctx->inst[n].prop_ptr); + if(xctx->inst[n].lab) my_free(_ALLOC_ID_, &xctx->inst[n].lab); + if(prop) my_free(_ALLOC_ID_, &prop); + xctx->instances--; + return 0; + } + } else if(xctx->sym[i].rects[PINLAYER] == 1) { + my_mstrcat(_ALLOC_ID_, &prop, + "node=\"tcleval(", + "[xschem translate ", xctx->inst[n].instname, " @#0:net_name]", + ")\"\n", NULL); + } else { + my_mstrcat(_ALLOC_ID_, &prop, + "node=\"tcleval(", + "[xschem translate ", xctx->inst[n].instname, " @#0:net_name] ", + "[xschem translate ", xctx->inst[n].instname, " @#1:net_name] -", + ")\"\n", NULL); + } + storeobject(-1, x + 20, y-125, x + 130 , y - 25, xRECT, 2, SELECTED, prop); my_free(_ALLOC_ID_, &prop); } @@ -1602,6 +1635,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot /* hilight new element 24122002 */ if(draw_sym & 4 ) { + unselect_all(1); select_element(n, SELECTED,0, 1); drawtemparc(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0, 0.0); drawtemprect(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0); diff --git a/src/callback.c b/src/callback.c index 8b702791..1bbd260e 100644 --- a/src/callback.c +++ b/src/callback.c @@ -202,7 +202,6 @@ static void start_place_symbol(void) tclvareval("set INITIALINSTDIR [file dirname {", abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""), "}]", NULL); } - unselect_all(1); xctx->mx_double_save = xctx->mousex_snap; xctx->my_double_save = xctx->mousey_snap; if(place_symbol(-1,NULL,xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1/* to_push_undo */) ) { diff --git a/src/move.c b/src/move.c index 03b5a2a9..6ec3ba09 100644 --- a/src/move.c +++ b/src/move.c @@ -540,7 +540,7 @@ void update_attached_floaters(const char *from_name, int inst, int sel) if(c == GRIDLAYER) { const char *node = get_tok_value(xctx->rect[c][i].prop_ptr, "node", 2); if(node && node[0]) { - const char *new_node = str_replace(node, from_name, to_name, 1, 1); + const char *new_node = str_replace(node, from_name, to_name, 1, -1); my_strdup(_ALLOC_ID_, &xctx->rect[c][i].prop_ptr, subst_token(xctx->rect[c][i].prop_ptr, "node", new_node)); } diff --git a/src/scheduler.c b/src/scheduler.c index bcd97afe..bcbd4fd3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1798,8 +1798,34 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_SetResult(interp, (char *)get_cell_w_ext(argv[2], atoi(argv[3])), TCL_VOLATILE); } } - /************ end xschem get subcommands *************/ + + + /* get_fqdevice instname param modelparam + * get the full pathname of "instname" device + * modelparam: + * 0: current, 1: modelparam, 2: modelvoltage + * param: device parameter, like ib, gm, vth + * set param to {} (empty str) for just branch current of 2 terminal device + * for parameters like "vth" modelparam must be 2 + * for parameters like "ib" modelparam must be 0 + * for parameters like "gm" modelparam must be 1 + */ + else if(!strcmp(argv[1], "get_fqdevice")) + { + char *fqdev; + if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} + if(argc > 4) { + fqdev = get_fqdevice(argv[3], atoi(argv[4]), argv[2]); + Tcl_SetResult(interp, fqdev, TCL_VOLATILE); + my_free(_ALLOC_ID_, &fqdev); + } else if(argc > 2) { + fqdev = get_fqdevice("", 0, argv[2]); + Tcl_SetResult(interp, fqdev, TCL_VOLATILE); + my_free(_ALLOC_ID_, &fqdev); + } + } + /* getprop instance|instance_pin|symbol|text ref * * getprop instance inst @@ -3605,7 +3631,6 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg tclvareval("set INITIALINSTDIR [file dirname {", abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""), "}]", NULL); } - unselect_all(1); xctx->mx_double_save = xctx->mousex_snap; xctx->my_double_save = xctx->mousey_snap; if(argc > 3) { diff --git a/src/token.c b/src/token.c index ee6a1a55..b4a80860 100644 --- a/src/token.c +++ b/src/token.c @@ -184,6 +184,7 @@ int match_symbol(const char *name) /* never returns -1, if symbol not found loa int i,found; found=0; + dbg(1, "match_symbol(): name=%s\n", name); for(i=0;isymbols; ++i) { /* dbg(1, "match_symbol(): name=%s, sym[i].name=%s\n",name, xctx->sym[i].name);*/ if(xctx->x_strcmp(name, xctx->sym[i].name) == 0) @@ -3779,6 +3780,99 @@ const char *spice_get_node(const char *token) } + + +/* caller must free returned value + * get the full pathname of "instname" device + * modelparam: + * 0: current, 1: modelparam, 2: modelvoltage + * param: device parameter, like "ib", "gm", "vth" + * set param to {} (empty str) for just branch current of 2 terminal device + * for parameters like "vth" modelparam must be 2 + * for parameters like "ib" modelparam must be 0 + * for parameters like "gm" modelparam must be 1 + */ +char *get_fqdevice(const char *param, int modelparam, const char *instname) +{ + int start_level; /* hierarchy level where waves were loaded */ + char *fqdev = NULL; + const char *path = xctx->sch_path[xctx->currsch] + 1; + char *dev = NULL; + size_t len; + int idx; + int sim_is_xyce = tcleval("sim_is_xyce")[0] == '1' ? 1 : 0; + int skip = 0; + char *iprefix = modelparam == 0 ? "i(" : modelparam == 1 ? "" : "v("; + char *ipostfix = modelparam == 1 ? "" : ")"; + int prefix; + + start_level = sch_waves_loaded(); + /* skip path components that are above the level where raw file was loaded */ + while(*path && skip < start_level) { + if(*path == '.') skip++; + ++path; + } + my_strdup2(_ALLOC_ID_, &dev, instname); + strtolower(dev); + prefix=dev[0]; + len = strlen(path) + strlen(dev) + 40; /* some extra chars for i(..) wrapper */ + fqdev = my_malloc(_ALLOC_ID_, len); + if(!sim_is_xyce) { + int vsource = (prefix == 'v') || (prefix == 'e'); + if(path[0]) { + if(vsource) { + my_snprintf(fqdev, len, "i(%c.%s%s)", prefix, path, dev); + } else if(prefix=='q') { + my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", + iprefix, prefix, path, dev, param ? param : "ic", ipostfix); + } else if(prefix=='d' || prefix == 'm') { + my_snprintf(fqdev, len, "%s@%c.%s%s[%s]%s", + iprefix, prefix, path, dev, param ? param : "id", ipostfix); + } else if(prefix=='i') { + my_snprintf(fqdev, len, "i(@%c.%s%s[current])", prefix, path, dev); + } else { + my_snprintf(fqdev, len, "i(@%c.%s%s[i])", prefix, path, dev); + } + } else { + if(vsource) { + my_snprintf(fqdev, len, "i(%s)", dev); + } else if(prefix == 'q') { + my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "ic", ipostfix); + } else if(prefix == 'd' || prefix == 'm') { + my_snprintf(fqdev, len, "%s@%s[%s]%s", iprefix, dev, param ? param : "id", ipostfix); + } else if(prefix == 'i') { + my_snprintf(fqdev, len, "i(@%s[current])", dev); + } else { + my_snprintf(fqdev, len, "i(@%s[i])", dev); + } + } + } else { + my_snprintf(fqdev, len, "i(%s%s)", path, dev); + } + dbg(1, "fqdev=%s\n", fqdev); + strtolower(fqdev); + idx = get_raw_index(fqdev, NULL); + /* special handling for resistors that are converted to b sources: + * i(@r.x4.r1[i]) --> i(@b.x4.br1[i]) + */ + if(idx < 0 && !strncmp(fqdev, "i(@r", 4)) { + if(path[0]) { + my_snprintf(fqdev, len, "i(@b.%sb%s[i])", path, dev); + } else { + my_snprintf(fqdev, len, "i(@b%s[i])", dev); + } + dbg(1, "fqdev=%s\n", fqdev); + } + + + my_free(_ALLOC_ID_, &dev); + return fqdev; + +} + + + + /* 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 */ diff --git a/src/xschem.h b/src/xschem.h index b3a94527..60a192ef 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1613,6 +1613,7 @@ 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 char *get_fqdevice(const char *param, int modelparam, const char *instname); 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, diff --git a/xschem_library/devices/scope.sym b/xschem_library/devices/scope.sym index 780f73ba..7e36383f 100644 --- a/xschem_library/devices/scope.sym +++ b/xschem_library/devices/scope.sym @@ -26,8 +26,8 @@ highlight=true} V {} S {} E {} -L 4 -10 -20 0 0 {} +L 4 0 0 10 -20 {} B 5 -1.25 -1.25 1.25 1.25 {name=p dir=in} -P 4 5 -140 -20 -140 -140 -10 -140 -10 -20 -140 -20 {} -T {@#0:net_name} -12.5 -35.625 0 1 0.2 0.2 {layer=4} -T {@spice_get_voltage} -137.5 -137.5 0 0 0.2 0.2 {layer=15} +P 4 5 10 -20 10 -140 140 -140 140 -20 10 -20 {} +T {@#0:net_name} 12.5 -18.125 0 0 0.2 0.2 {layer=4} +T {@#0:spice_get_voltage} 12.5 -139.375 0 0 0.15 0.15 {layer=15} diff --git a/xschem_library/devices/scope2.sym b/xschem_library/devices/scope2.sym new file mode 100644 index 00000000..ec7ec36f --- /dev/null +++ b/xschem_library/devices/scope2.sym @@ -0,0 +1,36 @@ +v {xschem version=3.4.6 file_version=1.2 +* +* This file is part of XSCHEM, +* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit +* simulation. +* Copyright (C) 1998-2024 Stefan Frederik Schippers +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +} +G {} +K {type=scope +template="name=l1" +highlight=true} +V {} +S {} +E {} +L 4 0 0 10 -20 {} +L 4 0 -160 10 -140 {} +B 5 -1.25 -161.25 1.25 -158.75 {name=p dir=in} +B 5 -1.25 -1.25 1.25 1.25 {name=m dir=in} +P 4 5 10 -20 10 -140 140 -140 140 -20 10 -20 {} +T {@#1:net_name} 12.5 -18.125 0 0 0.2 0.2 {layer=4} +T {@#0:net_name} 12.5 -153.125 0 0 0.2 0.2 {layer=4} +T {@spice_get_diff_voltage} 12.5 -139.375 0 0 0.15 0.15 {layer=15} diff --git a/xschem_library/devices/scope_ammeter.sym b/xschem_library/devices/scope_ammeter.sym new file mode 100644 index 00000000..c38fc591 --- /dev/null +++ b/xschem_library/devices/scope_ammeter.sym @@ -0,0 +1,31 @@ +v {xschem version=3.4.6 file_version=1.2 +* +* This file is part of XSCHEM, +* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit +* simulation. +* Copyright (C) 1998-2024 Stefan Frederik Schippers +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +} +G {} +K {type=scope +template="name=l1" +highlight=true} +V {} +S {} +E {} +P 4 5 10 -20 10 -140 140 -140 140 -20 10 -20 {} +T {@device} 12.5 -18.125 0 0 0.2 0.2 {layer=4} +T {tcleval(@spice_get_node [xschem get_fqdevice @device ] )} 12.5 -139.375 0 0 0.15 0.15 {layer=15} diff --git a/xschem_library/ngspice/comp_ngspice.sch b/xschem_library/ngspice/comp_ngspice.sch index 36809ee7..c0c08778 100644 --- a/xschem_library/ngspice/comp_ngspice.sch +++ b/xschem_library/ngspice/comp_ngspice.sch @@ -1,4 +1,4 @@ -v {xschem version=3.4.6RC file_version=1.2 +v {xschem version=3.4.6 file_version=1.2 * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit @@ -24,6 +24,15 @@ K {} V {} S {} E {} +B 2 520 -415 630 -315 {name=l4 +flags=graph,unlocked +lock=1 +color=8 +node="tcleval([xschem get_fqdevice [xschem translate l4 @device]])" +y1=-0.00014 +y2=0.00013 +x1=0.00044464632 +x2=0.00046163671} T {( @#0:resolved_net )} 440 -265 0 1 0.2 0.2 {name=l2 layer=15} T {( @#0:resolved_net )} 100 -285 0 1 0.2 0.2 {name=p1 layer=15} T {( @#0:resolved_net )} 100 -235 0 1 0.2 0.2 {name=p2 layer=15} @@ -49,3 +58,6 @@ m=1} C {parax_cap.sym} 630 -230 0 0 {name=C3 gnd=0 value=\{COUT\} m=1} C {vsource.sym} 150 -260 1 0 {name=V1 value=0} C {vsource.sym} 150 -210 1 0 {name=V2 value=0} +C {scope_ammeter.sym} 500 -290 0 0 {name=l4 +attach=l4 +device=R1} diff --git a/xschem_library/ngspice/solar_panel.sch b/xschem_library/ngspice/solar_panel.sch index e73819bd..6a383ee2 100644 --- a/xschem_library/ngspice/solar_panel.sch +++ b/xschem_library/ngspice/solar_panel.sch @@ -193,16 +193,16 @@ autoload=0 sim_type=tran xrawfile=$netlist_dir/solar_panel.raw} -B 2 270 -730 380 -650 {name=l21 +B 2 1210 -605 1320 -505 {name=l21 flags=graph,unlocked lock=1 color=8 -node="tcleval([xschem translate l21 @#0:net_name])" -y1=0.00033 -y2=21 +node="tcleval([xschem translate l21 @#0:net_name] [xschem translate l21 @#1:net_name] -)" x1=5e-10 -x2=0.001} -B 2 1230 -560 1340 -480 {name=l22 +x2=0.001 +y1=-4.8 +y2=15} +B 2 400 -735 510 -635 {name=l22 flags=graph,unlocked lock=1 color=8 @@ -211,15 +211,24 @@ y1=0.00033 y2=21 x1=5e-10 x2=0.001} -B 2 780 -1200 890 -1120 {name=l23 +B 2 600 -485 710 -385 {name=l23 flags=graph,unlocked lock=1 color=8 -node="tcleval([xschem translate l23 @#0:net_name])" -y1=-0.55990715 -y2=1.5136929 -x1=0.00048981872 -x2=0.0005150806} +node="tcleval([xschem get_fqdevice [xschem translate l23 @device]])" +x1=5e-10 +x2=0.001 +y1=-2.9 +y2=6.8} +B 2 640 -295 750 -195 {name=l26 +flags=graph,unlocked +lock=1 +color=8 +node="tcleval([xschem get_fqdevice [xschem translate l26 @device]])" +x1=5e-10 +x2=0.001 +y1=-2.9 +y2=6.8} B 18 55 -1060 310 -875 {} A 5 310 -1060 5.590169943749475 243.434948822922 360 {fill=true} P 7 6 385 -875 330 -1031.25 325 -1045 312.5 -1060 300 -1065 55 -1075 {} @@ -259,19 +268,19 @@ T {Floater text example} 870 -440 0 0 0.4 0.4 {} T {@spice_get_current} 875 -598.75 0 0 0.3 0.3 {layer=7 name=L2} T {@spice_get_current} 1015 -268.75 0 0 0.3 0.3 {layer=7 name=C1} -N 1010 -210 1100 -210 {lab=0} -N 1100 -300 1100 -210 {lab=0} +N 1010 -180 1100 -180 {lab=0} +N 1100 -300 1100 -180 {lab=0} N 640 -610 730 -610 {lab=#net1} N 1010 -440 1040 -440 {lab=VO} N 1010 -440 1010 -310 {lab=VO} -N 1010 -250 1010 -210 {lab=0} +N 1010 -250 1010 -180 {lab=0} N 360 -610 580 -610 {lab=PANEL} N 1010 -610 1010 -440 {lab=VO} N 820 -610 860 -610 {lab=SW} N 820 -610 820 -490 {lab=SW} N 790 -610 820 -610 {lab=SW} -N 820 -210 1010 -210 {lab=0} -N 800 -210 820 -210 {lab=0} +N 820 -180 1010 -180 {lab=0} +N 800 -180 820 -180 {lab=0} N 1000 -610 1010 -610 {lab=VO} N 1100 -440 1280 -440 {lab=LED} N 1100 -440 1100 -360 {lab=LED} @@ -322,10 +331,12 @@ N 770 -1080 770 -1030 { lab=TRIANG} N 770 -1030 890 -1030 { lab=TRIANG} -N 820 -330 820 -210 { +N 820 -330 820 -180 { lab=0} +N 1190 -480 1190 -440 {lab=LED} +N 1190 -670 1190 -640 {lab=PANEL} C {title.sym} 160 -40 0 0 {name=l1 author="Stefan Schippers"} -C {code_shown.sym} 170 -310 0 0 {name=CONTROL +C {code_shown.sym} 180 -310 0 0 {name=CONTROL value="tcleval( .option savecurrents .control @@ -345,7 +356,7 @@ C {code.sym} 20 -240 0 0 {name=MODELS value=".MODEL DIODE D(IS=1.139e-08 RS=0.99 .MODEL swmod SW(VT=0.5 VH=0.01 RON=0.01 ROFF=10000000) "} C {lab_pin.sym} 650 -530 0 1 {name=l4 lab=PANEL } -C {lab_pin.sym} 800 -210 0 0 {name=l6 lab=0 } +C {lab_pin.sym} 800 -180 0 0 {name=l6 lab=0 } C {ammeter.sym} 1070 -440 3 0 {name=Vled} C {ind.sym} 890 -610 3 1 {name=L2 m=1 @@ -433,9 +444,14 @@ C {spice_probe.sym} 810 -890 0 1 {name=p5 analysis=tran} C {spice_probe.sym} 760 -670 0 0 {name=p6 analysis=tran} C {spice_probe.sym} 160 -450 0 0 {name=p7 analysis=tran} C {lab_show.sym} 440 -610 2 0 {name=l24} -C {scope.sym} 400 -610 0 0 {name=l21 +C {lab_pin.sym} 1190 -670 0 0 {name=l25 lab=PANEL } +C {scope2.sym} 1190 -480 0 0 {name=l21 attach=l21} -C {scope.sym} 1210 -440 0 1 {name=l22 +C {scope.sym} 380 -610 0 0 {name=l22 attach=l22} -C {scope.sym} 760 -1080 0 1 {name=l23 -attach=l23} +C {scope_ammeter.sym} 580 -360 0 0 {name=l23 +attach=l23 +device=Vcap} +C {scope_ammeter.sym} 620 -170 0 0 {name=l26 +attach=l26 +device=Vdiode} From d3e0ca82b4498e6a44ceae88597c5998a17752d4 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 25 Feb 2025 02:00:55 +0100 Subject: [PATCH 26/67] eval_expr.y: references to unknown identifiers cause the lexer to return an error so the expression is not evaluated and returned as is, instead of erroneously interpreting the unknown identifier as 0 --- src/eval_expr.y | 5 +++-- xschem_library/devices/scope_ammeter.sym | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eval_expr.y b/src/eval_expr.y index 5198718f..80eb696f 100644 --- a/src/eval_expr.y +++ b/src/eval_expr.y @@ -274,10 +274,11 @@ static int kklex() while (c != 0 && isalnum(c) && i < length); str--; symbuf[i] = '\0'; - s = getsym (symbuf); + s = getsym(symbuf); kklval.tptr = s; dbg(dbglev, "ylex: FNCT=%s\n", symbuf); - return FNCT; + if(s) return FNCT; + return 0; /* error : undefined identifier */ } /* Any other character is a token by itself. */ return c; diff --git a/xschem_library/devices/scope_ammeter.sym b/xschem_library/devices/scope_ammeter.sym index c38fc591..804b5366 100644 --- a/xschem_library/devices/scope_ammeter.sym +++ b/xschem_library/devices/scope_ammeter.sym @@ -28,4 +28,4 @@ S {} E {} P 4 5 10 -20 10 -140 140 -140 140 -20 10 -20 {} T {@device} 12.5 -18.125 0 0 0.2 0.2 {layer=4} -T {tcleval(@spice_get_node [xschem get_fqdevice @device ] )} 12.5 -139.375 0 0 0.15 0.15 {layer=15} +T {tcleval(@spice_get_node [xschem get_fqdevice @device ] )} 12.5 -139.375 0 0 0.15 0.15 {layer=17} From 43a8b62a241046cb533e9ff07c56949ef71ffb59 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 26 Feb 2025 16:01:32 +0100 Subject: [PATCH 27/67] proc to_eng: return arg as is if not valid expression --- src/xschem.tcl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xschem.tcl b/src/xschem.tcl index f2688bd2..4a908d8c 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -628,6 +628,9 @@ proc from_eng {i} { ## convert number to engineering form proc to_eng {args} { set suffix {} + if { [catch {[uplevel #0 expr [join $args]]} i] } { + return $args + } set i [uplevel #0 expr [join $args]] set absi [expr {abs($i)}] From 2d6b7c4fe1fee26d9121889742cd6309594f12fd Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 26 Feb 2025 16:03:36 +0100 Subject: [PATCH 28/67] revert previous --- src/xschem.tcl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 4a908d8c..f2688bd2 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -628,9 +628,6 @@ proc from_eng {i} { ## convert number to engineering form proc to_eng {args} { set suffix {} - if { [catch {[uplevel #0 expr [join $args]]} i] } { - return $args - } set i [uplevel #0 expr [join $args]] set absi [expr {abs($i)}] From c19c25873b2c754dcca17b28702526ace81d6793 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 26 Feb 2025 19:33:14 +0100 Subject: [PATCH 29/67] (2)proc to_eng: return arg as is if not valid expression --- src/xschem.tcl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xschem.tcl b/src/xschem.tcl index f2688bd2..0c5da912 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -628,6 +628,10 @@ proc from_eng {i} { ## convert number to engineering form proc to_eng {args} { set suffix {} + + if {[ catch {uplevel #0 expr [join $args]} i]} { + return [join $args] + } set i [uplevel #0 expr [join $args]] set absi [expr {abs($i)}] From 028ca8cb5a4d8b4aa3b192ddf16e70686448cb4b Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 27 Feb 2025 01:45:12 +0100 Subject: [PATCH 30/67] proc editdata: added Copy (to Clipboard) button --- src/xschem.tcl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/xschem.tcl b/src/xschem.tcl index 0c5da912..d0803710 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -6451,12 +6451,17 @@ proc editdata {{data {}} {title {Edit data}} } { # wm transient $window [xschem get topwindow] frame $window.buttons pack $window.buttons -side bottom -fill x -pady 2m + button $window.buttons.copy -text Copy -command " + clipboard clear + clipboard append \[$window.text get 1.0 {end - 1 chars}\] + " button $window.buttons.ok -text OK -command " set retval \[$window.text get 1.0 {end - 1 chars}\]; destroy $window " button $window.buttons.cancel -text Cancel -command "destroy $window" pack $window.buttons.ok -side left -expand 1 pack $window.buttons.cancel -side left -expand 1 + pack $window.buttons.copy -side left -expand 1 eval text $window.text -undo 1 -relief sunken -bd 2 -yscrollcommand \"$window.yscroll set\" -setgrid 1 \ -xscrollcommand \"$window.xscroll set\" -wrap none -height 30 $text_tabs_setting From bba3f434d519cd3b5469f2858b5cb430a7993836 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 27 Feb 2025 13:04:57 +0100 Subject: [PATCH 31/67] fix an error in change_sch_path() (level check) and go_back() (no set title) --- src/actions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actions.c b/src/actions.c index e6808c67..159ddcfe 100644 --- a/src/actions.c +++ b/src/actions.c @@ -2263,7 +2263,7 @@ int change_sch_path(int instnumber, int dr) char *ptr; size_t pathlen; int res = 0; - if(level <= 0 ) return 0; + if(level < 0 ) return 0; my_strdup2(_ALLOC_ID_, &instname, get_tok_value(xctx->hier_attr[level].prop_ptr, "name", 0)); my_strdup2(_ALLOC_ID_, &expanded_instname, expandlabel(instname, &inst_mult)); my_strdup2(_ALLOC_ID_, &path, xctx->sch_path[xctx->currsch]); @@ -2498,7 +2498,7 @@ void go_back(int what) char filename[PATH_MAX]; int prev_sch_type; int confirm = what & 1; - int set_title = !(confirm & 2); + int set_title = !(what & 2); save_ok=1; dbg(1,"go_back(): sch[xctx->currsch]=%s\n", xctx->sch[xctx->currsch]); From becbf7cf9cac74daf75dcf2dd490fdb723e0a629 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 28 Feb 2025 16:08:35 +0100 Subject: [PATCH 32/67] descend_schematic() return value indicates if descended into a valid schematic (1) or empty one (0) --- src/actions.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/actions.c b/src/actions.c index 159ddcfe..d56798ac 100644 --- a/src/actions.c +++ b/src/actions.c @@ -2310,6 +2310,7 @@ int descend_schematic(int instnumber, int fallback, int alert, int set_title) int inst_mult, inst_number; int save_ok = 0; int i, n = 0; + int descend_ok = 1; if(xctx->currsch + 1 >= CADMAXHIER) { dbg(0, "descend_schematic(): max hierarchy depth reached: %d", CADMAXHIER); @@ -2465,24 +2466,25 @@ int descend_schematic(int instnumber, int fallback, int alert, int set_title) dbg(1, "descend_schematic(): filename=%s\n", filename); /* we are descending from a parent schematic downloaded from the web */ if(!tclgetboolvar("keep_symbols")) remove_symbols(); - load_schematic(1, filename, (set_title & 1), alert); - if(xctx->hilight_nets) { - prepare_netlist_structs(0); - propagate_hilights(1, 0, XINSERT_NOREPLACE); - } - dbg(1, "descend_schematic(): before zoom(): prep_hash_inst=%d\n", xctx->prep_hash_inst); - - if(xctx->rects[GRIDLAYER] > 0 && tcleval("info exists ngspice::ngspice_data")[0] == '0') { - Graph_ctx *gr = &xctx->graph_struct; - xRect *r = &xctx->rect[GRIDLAYER][0]; - if(r->flags & 1) { - backannotate_at_cursor_b_pos(r, gr); + descend_ok = load_schematic(1, filename, (set_title & 1), alert); + if(descend_ok) { + if(xctx->hilight_nets) { + prepare_netlist_structs(0); + propagate_hilights(1, 0, XINSERT_NOREPLACE); + } + dbg(1, "descend_schematic(): before zoom(): prep_hash_inst=%d\n", xctx->prep_hash_inst); + + if(xctx->rects[GRIDLAYER] > 0 && tcleval("info exists ngspice::ngspice_data")[0] == '0') { + Graph_ctx *gr = &xctx->graph_struct; + xRect *r = &xctx->rect[GRIDLAYER][0]; + if(r->flags & 1) { + backannotate_at_cursor_b_pos(r, gr); + } } } - zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); } - return 1; + return descend_ok; } /* From be1a0deb89fe88d61094780238c1648939a51feb Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 28 Feb 2025 18:04:56 +0100 Subject: [PATCH 33/67] fix typo in draw_graph() --- src/callback.c | 2 +- src/draw.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/callback.c b/src/callback.c index 1bbd260e..7b9dea45 100644 --- a/src/callback.c +++ b/src/callback.c @@ -431,7 +431,7 @@ static int waves_callback(int event, int mx, int my, KeySym key, int button, int int access_cond = !graph_use_ctrl_key || (state & ControlMask); dbg(1, "uistate=%d, graph_flags=%d\n", xctx->ui_state, xctx->graph_flags); - if(event != -3 && !xctx->raw) return 0; + /* if(event != -3 && !xctx->raw) return 0; */ rstate = state; /* rstate does not have ShiftMask bit, so easier to test for KeyPress events */ rstate &= ~ShiftMask; /* don't use ShiftMask, identifying characters is sufficient */ #if HAS_CAIRO==1 diff --git a/src/draw.c b/src/draw.c index 25ff72ee..71588a88 100644 --- a/src/draw.c +++ b/src/draw.c @@ -3782,7 +3782,7 @@ int find_closest_wave(int i, Graph_ctx *gr) * 8: all drawing, if not set do only XCopyArea / x-cursor if specified * ct is a pointer used in windows for cairo */ -void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct) +void draw_graph(int i, int flags, Graph_ctx *gr, void *ct) { int wc = 4, wave_color = 4; char *node = NULL, *color = NULL, *sweep = NULL; @@ -3804,12 +3804,11 @@ void draw_graph(int i, const int flags, Graph_ctx *gr, void *ct) int save_extra_idx = -1; double cursor1, cursor2; - if(xctx->only_probes) return; if(RECT_OUTSIDE( gr->sx1, gr->sy1, gr->sx2, gr->sy2, xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2)) return; - if(r->flags & 4) { /* private_cursor */ + if(r->flags & 2) { /* private_cursor */ const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0); if(s[0]) { cursor1 = atof_eng(s); From aa68a3a0d2472b01d093394775e0b4cf2c437dab Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 28 Feb 2025 18:51:35 +0100 Subject: [PATCH 34/67] fix spurious overwrite of xctx->graph_struct done in raw_read() called from draw_graph() which uses xctx->graph_struct --- src/save.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/save.c b/src/save.c index 3cd03bb0..d36cd80f 100644 --- a/src/save.c +++ b/src/save.c @@ -1044,8 +1044,10 @@ int raw_read(const char *f, Raw **rawptr, const char *type, int no_warning, doub xRect *r; r = &xctx->rect[GRIDLAYER][0]; if(r->flags & 1) { - setup_graph_data(0, 0, &xctx->graph_struct); - backannotate_at_cursor_b_pos(r, &xctx->graph_struct); + /* don't overwrite xctx->graph_struct, being used in draw_graph() which calls raw_read() */ + Graph_ctx gr_ctx; + setup_graph_data(0, 0, &gr_ctx); + backannotate_at_cursor_b_pos(r, &gr_ctx); } } } From efdd45f41438d20fed88d386792a21e4c55b52f8 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 28 Feb 2025 19:19:06 +0100 Subject: [PATCH 35/67] revert wrong fix (fix typo in draw_graph()) not a typo... --- src/draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.c b/src/draw.c index 71588a88..c6af613f 100644 --- a/src/draw.c +++ b/src/draw.c @@ -3808,7 +3808,7 @@ void draw_graph(int i, int flags, Graph_ctx *gr, void *ct) if(RECT_OUTSIDE( gr->sx1, gr->sy1, gr->sx2, gr->sy2, xctx->areax1, xctx->areay1, xctx->areax2, xctx->areay2)) return; - if(r->flags & 2) { /* private_cursor */ + if(r->flags & 4) { /* private_cursor */ const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0); if(s[0]) { cursor1 = atof_eng(s); From 0ea6af2c9960a84cad31b383fa597a09c2e81367 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 4 Mar 2025 00:33:27 +0100 Subject: [PATCH 36/67] better detection of expr() in netlister functions (print_spice_element(), ...) --- src/token.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/token.c b/src/token.c index b4a80860..469e06c1 100644 --- a/src/token.c +++ b/src/token.c @@ -1199,7 +1199,7 @@ static void print_vhdl_primitive(FILE *fd, int inst) /* netlist primitives, 200 } } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ - if(strstr(result, "expr(") ) { + if(strstr(result, "expr(") == result) { result = eval_expr(result); } dbg(1, "print_vhdl_primitive(): after translate3() result=%s\n", result); @@ -2463,7 +2463,7 @@ int print_spice_element(FILE *fd, int inst) value = spiceprefixtag; } - if(strstr(value, "expr(") ) { + if(strstr(value, "expr(") == value) { value = eval_expr(value); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ @@ -2518,7 +2518,7 @@ int print_spice_element(FILE *fd, int inst) if(result) { my_strdup(_ALLOC_ID_, &result, tcl_hook2(result)); } - if(strstr(result, "expr(") ) { + if(strstr(result, "expr(") == result) { result = eval_expr(result); } if(result) fprintf(fd, "%s", result); @@ -3155,7 +3155,7 @@ static void print_verilog_primitive(FILE *fd, int inst) /* netlist switch level } } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ - if(strstr(result, "expr(") ) { + if(strstr(result, "expr(") == result) { result = eval_expr(result); } dbg(1, "print_verilog_primitive(): after translate3() result=%s\n", result); @@ -4652,7 +4652,7 @@ const char *translate(int inst, const char* s) * can be calculated */ my_strdup2(_ALLOC_ID_, &result, spice_get_node(tcl_hook2(result))); - if(strstr(result, "expr(")) { + if(strstr(result, "expr(") == result) { dbg(1, "translate(): expr():%s\n", result); my_strdup2(_ALLOC_ID_, &result, eval_expr( translate3(result, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, From b449bb27285095d93209905d270d63ac84adf366 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 4 Mar 2025 09:07:55 +0100 Subject: [PATCH 37/67] revert: better detection of expr() in netlister functions --- src/token.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/token.c b/src/token.c index 469e06c1..e8f04ea7 100644 --- a/src/token.c +++ b/src/token.c @@ -1199,7 +1199,7 @@ static void print_vhdl_primitive(FILE *fd, int inst) /* netlist primitives, 200 } } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ - if(strstr(result, "expr(") == result) { + if(strstr(result, "expr(")) { result = eval_expr(result); } dbg(1, "print_vhdl_primitive(): after translate3() result=%s\n", result); @@ -2463,7 +2463,7 @@ int print_spice_element(FILE *fd, int inst) value = spiceprefixtag; } - if(strstr(value, "expr(") == value) { + if(strstr(value, "expr(")) { value = eval_expr(value); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ @@ -2518,7 +2518,7 @@ int print_spice_element(FILE *fd, int inst) if(result) { my_strdup(_ALLOC_ID_, &result, tcl_hook2(result)); } - if(strstr(result, "expr(") == result) { + if(strstr(result, "expr(")) { result = eval_expr(result); } if(result) fprintf(fd, "%s", result); @@ -3155,7 +3155,7 @@ static void print_verilog_primitive(FILE *fd, int inst) /* netlist switch level } } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ - if(strstr(result, "expr(") == result) { + if(strstr(result, "expr(")) { result = eval_expr(result); } dbg(1, "print_verilog_primitive(): after translate3() result=%s\n", result); @@ -4652,7 +4652,7 @@ const char *translate(int inst, const char* s) * can be calculated */ my_strdup2(_ALLOC_ID_, &result, spice_get_node(tcl_hook2(result))); - if(strstr(result, "expr(") == result) { + if(strstr(result, "expr(")) { dbg(1, "translate(): expr():%s\n", result); my_strdup2(_ALLOC_ID_, &result, eval_expr( translate3(result, 1, xctx->inst[inst].prop_ptr, xctx->sym[xctx->inst[inst].ptr].templ, From 56e0410047d08ae4da2051b8df08bdb0294aac2a Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 4 Mar 2025 09:43:26 +0100 Subject: [PATCH 38/67] fix a bug in eval_expr() usage in token.c (duplicate return string to avoid double free) --- src/token.c | 11 ++++++----- xschem_library/devices/res.sym | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/token.c b/src/token.c index e8f04ea7..d11b7990 100644 --- a/src/token.c +++ b/src/token.c @@ -1200,7 +1200,7 @@ static void print_vhdl_primitive(FILE *fd, int inst) /* netlist primitives, 200 } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ if(strstr(result, "expr(")) { - result = eval_expr(result); + my_strdup2(_ALLOC_ID_, &result, eval_expr(result)); } dbg(1, "print_vhdl_primitive(): after translate3() result=%s\n", result); } @@ -2153,7 +2153,8 @@ int print_spice_element(FILE *fd, int inst) const char *str_ptr=NULL; register int c, state=TOK_BEGIN, space; char *template=NULL,*format=NULL, *s, *name=NULL, *token=NULL; - const char *lab, *value = NULL; + const char *lab; + char *value = NULL; /* char *translatedvalue = NULL; */ size_t sizetok=0; size_t token_pos=0; @@ -2464,7 +2465,7 @@ int print_spice_element(FILE *fd, int inst) } if(strstr(value, "expr(")) { - value = eval_expr(value); + my_strdup2(_ALLOC_ID_, &value, eval_expr(value)); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ if(!token_exists && token[0] =='%') { @@ -2519,7 +2520,7 @@ int print_spice_element(FILE *fd, int inst) my_strdup(_ALLOC_ID_, &result, tcl_hook2(result)); } if(strstr(result, "expr(")) { - result = eval_expr(result); + my_strdup2(_ALLOC_ID_, &result, eval_expr(result)); } if(result) fprintf(fd, "%s", result); dbg(1, "print_spice_element(): returning |%s|\n", result); @@ -3156,7 +3157,7 @@ static void print_verilog_primitive(FILE *fd, int inst) /* netlist switch level } my_strdup2(_ALLOC_ID_, &result, tcl_hook2(result)); /* tcl evaluation if tcleval(....) */ if(strstr(result, "expr(")) { - result = eval_expr(result); + my_strdup2(_ALLOC_ID_, &result, eval_expr(result)); } dbg(1, "print_verilog_primitive(): after translate3() result=%s\n", result); } diff --git a/xschem_library/devices/res.sym b/xschem_library/devices/res.sym index b03f4db0..6abdfa63 100644 --- a/xschem_library/devices/res.sym +++ b/xschem_library/devices/res.sym @@ -25,8 +25,8 @@ K {type=resistor function0="1" function1="0" -format="@name @pinlist @value m=@m" - +format="@name @@P @@M R=expr( 100 * (@L - 0.1u) / (@W - 0.1u)) SUB=$XX" + verilog_format="tran @name (@@P\\\\, @@M\\\\);" tedax_format="footprint @name @footprint From fb9189018d580efdcdd9bf9fc11568e73869d40a Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 4 Mar 2025 09:58:56 +0100 Subject: [PATCH 39/67] avoid strdup on value in print_spice_element() --- src/token.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/token.c b/src/token.c index d11b7990..74a8bf9b 100644 --- a/src/token.c +++ b/src/token.c @@ -2154,7 +2154,7 @@ int print_spice_element(FILE *fd, int inst) register int c, state=TOK_BEGIN, space; char *template=NULL,*format=NULL, *s, *name=NULL, *token=NULL; const char *lab; - char *value = NULL; + const char *value = NULL; /* char *translatedvalue = NULL; */ size_t sizetok=0; size_t token_pos=0; @@ -2465,7 +2465,7 @@ int print_spice_element(FILE *fd, int inst) } if(strstr(value, "expr(")) { - my_strdup2(_ALLOC_ID_, &value, eval_expr(value)); + value = eval_expr(value); } /* token=%xxxx and xxxx is not defined in prop_ptr or template: return xxxx */ if(!token_exists && token[0] =='%') { From 72527804e003a863cfae6f7625568ccff59eb3a4 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Tue, 4 Mar 2025 10:01:29 +0100 Subject: [PATCH 40/67] revert res.sym --- xschem_library/devices/res.sym | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xschem_library/devices/res.sym b/xschem_library/devices/res.sym index 6abdfa63..b03f4db0 100644 --- a/xschem_library/devices/res.sym +++ b/xschem_library/devices/res.sym @@ -25,8 +25,8 @@ K {type=resistor function0="1" function1="0" -format="@name @@P @@M R=expr( 100 * (@L - 0.1u) / (@W - 0.1u)) SUB=$XX" - +format="@name @pinlist @value m=@m" + verilog_format="tran @name (@@P\\\\, @@M\\\\);" tedax_format="footprint @name @footprint From eb99123a49d147816a608a93dc2b842bc6cace6e Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 6 Mar 2025 20:47:49 +0100 Subject: [PATCH 41/67] `xschem load` command: allow multiple files to be loaded --- src/scheduler.c | 63 ++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/scheduler.c b/src/scheduler.c index bcbd4fd3..eb50f476 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -2915,11 +2915,10 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[1], "load") ) { int load_symbols = 1, force = 1, undo_reset = 1, nofullzoom = 0, nodraw = 0; - int keep_symbols = 0; - size_t i; + int keep_symbols = 0, first; + int i; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} - for(i = 2; i < argc; i++) { if(argv[i][0] == '-') { if(!strcmp(argv[i], "-nosymbols")) { @@ -2939,8 +2938,11 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg break; } } - - if(argc>i) { + first = i; + if(argc==2) { + ask_new_file(); + } else + for(i = first; i < argc; i++) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[i], home_dir); tcleval(f); @@ -2967,34 +2969,41 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg unselect_all(1); /* no implicit undo: if needed do it before loading */ /* if(!undo_reset) xctx->push_undo(); */ - if(undo_reset) xctx->currsch = 0; - if(!keep_symbols) remove_symbols(); - if(!nofullzoom) { - xctx->zoom=CADINITIALZOOM; - xctx->mooz=1/CADINITIALZOOM; - xctx->xorigin=CADINITIALX; - xctx->yorigin=CADINITIALY; + if(i == first) { + if(undo_reset) xctx->currsch = 0; + if(!keep_symbols) remove_symbols(); + if(!nofullzoom) { + xctx->zoom=CADINITIALZOOM; + xctx->mooz=1/CADINITIALZOOM; + xctx->xorigin=CADINITIALX; + xctx->yorigin=CADINITIALY; + } } dbg(1, "scheduler: undo_reset=%d\n", undo_reset); - ret = load_schematic(load_symbols, f, undo_reset, !force); - dbg(1, "xschem load: ret=%d\n", ret); - if(undo_reset) { - tclvareval("update_recent_file {", f, "}", NULL); - my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch], "."); - if(xctx->portmap[xctx->currsch].table) str_hash_free(&xctx->portmap[xctx->currsch]); - str_hash_init(&xctx->portmap[xctx->currsch], HASHSIZE); - xctx->sch_path_hash[xctx->currsch] = 0; - xctx->sch_inst_number[xctx->currsch] = 1; + + if(i > first) { + ret = new_schematic("create", "noconfirm", f, 1); + if(undo_reset) { + tclvareval("update_recent_file {", f, "}", NULL); + } + } else { + ret = load_schematic(load_symbols, f, undo_reset, !force); + dbg(1, "xschem load: f=%s, ret=%d\n", f, ret); + if(undo_reset) { + tclvareval("update_recent_file {", f, "}", NULL); + my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch], "."); + if(xctx->portmap[xctx->currsch].table) str_hash_free(&xctx->portmap[xctx->currsch]); + str_hash_init(&xctx->portmap[xctx->currsch], HASHSIZE); + xctx->sch_path_hash[xctx->currsch] = 0; + xctx->sch_inst_number[xctx->currsch] = 1; + } + if(nofullzoom) { + if(!nodraw) draw(); + } else zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); } - if(nofullzoom) { - if(!nodraw) draw(); - } else zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); } } } - else if(argc==2) { - ask_new_file(); - } Tcl_SetResult(interp, xctx->sch[xctx->currsch], TCL_STATIC); } From fafd48eb31cb33fa09defcebea6f5d4e2a492ce8 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Mar 2025 14:45:13 +0100 Subject: [PATCH 42/67] new tcl variable grid_point_size to set size of grid points (for 4K+ displays) --- src/draw.c | 29 +++++++++++++++++---- src/xschem.tcl | 71 ++++++++++++++++++++++++-------------------------- src/xschemrc | 3 +++ 3 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/draw.c b/src/draw.c index c6af613f..f5bb1c9e 100644 --- a/src/draw.c +++ b/src/draw.c @@ -930,10 +930,14 @@ static void drawgrid() double mult; #if DRAW_ALL_CAIRO==0 int i=0; + const char *psize_ptr; int big_gr = tclgetboolvar("big_grid_points"); + int grid_point_size = -1; char dash_arr[2]; int axes = tclgetboolvar("draw_grid_axes"); + psize_ptr = tclgetvar("grid_point_size"); + if(psize_ptr[0]) grid_point_size = atoi(psize_ptr); if(axes) { dash_arr[0] = dash_arr[1] = (char) 3; XSetDashes(display, xctx->gc[GRIDLAYER], 0, dash_arr, 1); @@ -959,9 +963,8 @@ static void drawgrid() delta = delta * pow(CADGRIDMULTIPLY, mult); } - /* while(delta < CADGRIDTHRESHOLD) delta *= CADGRIDMULTIPLY; */ /* <-- to be improved,but works */ - + /* ************************ Draw axes ****************** */ #if DRAW_ALL_CAIRO==1 xax =floor(xctx->xorigin*xctx->mooz) + 0.5; yax = floor(xctx->yorigin*xctx->mooz) + 0.5; #else @@ -1007,26 +1010,36 @@ static void drawgrid() #endif } } + /* ************************ /Draw axes ****************** */ + #if DRAW_ALL_CAIRO==0 - if(axes) { + if(grid_point_size != -1) { + XSetLineAttributes (display, xctx->gc[GRIDLAYER], + grid_point_size, LineSolid, LINECAP, LINEJOIN); + } else if(!big_gr) { + XSetLineAttributes (display, xctx->gc[GRIDLAYER], + 0, LineSolid, LINECAP, LINEJOIN); + } else { XSetLineAttributes (display, xctx->gc[GRIDLAYER], XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN); } #endif + if(grid_point_size >= 0) big_gr = 1; + tmp = floor((xctx->areay1+1)/delta)*delta-fmod(-xctx->yorigin*xctx->mooz, delta); for(x=floor((xctx->areax1+1)/delta)*delta-fmod(-xctx->xorigin*xctx->mooz, delta); x < xctx->areax2; x += delta) { xx = x; #if DRAW_ALL_CAIRO==1 xx = floor(x) + 0.5; #endif - if((int)xx == (int)xax) continue; + if(axes && (int)xx == (int)xax) continue; for(y=tmp; y < xctx->areay2; y += delta) { yy = y; #if DRAW_ALL_CAIRO==1 yy = floor(y) + 0.5; #endif - if((int)yy == (int)yax) continue; + if(axes && (int)yy == (int)yax) continue; #if DRAW_ALL_CAIRO==1 if(xctx->draw_window) { cairo_move_to(xctx->cairo_ctx, xx, yy) ; @@ -1087,6 +1100,12 @@ static void drawgrid() if(xctx->draw_pixmap) cairo_stroke(xctx->cairo_save_ctx); if(xctx->draw_window) cairo_stroke(xctx->cairo_ctx); #endif + + #if DRAW_ALL_CAIRO==0 + XSetLineAttributes (display, xctx->gc[GRIDLAYER], + XLINEWIDTH(xctx->lw), LineSolid, LINECAP, LINEJOIN); + #endif + } #if !defined(__unix__) && HAS_CAIRO==1 diff --git a/src/xschem.tcl b/src/xschem.tcl index d0803710..02a702fd 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -7863,42 +7863,34 @@ proc no_open_dialogs {} { ## "file_dialog_*" only one load_file_dialog window is allowed set tctx::global_list { - PDK_ROOT PDK SKYWATER_MODELS SKYWATER_STDCELLS - INITIALINSTDIR INITIALLOADDIR INITIALPROPDIR INITIALTEXTDIR XSCHEM_LIBRARY_PATH - add_all_windows_drives auto_hilight auto_hilight_graph_nodes autofocus_mainwindow - autotrim_wires bespice_listen_port big_grid_points bus_replacement_char cadgrid cadlayers - cadsnap cairo_font_name cairo_font_scale change_lw color_ps tctx::colors compare_sch constr_mv - copy_cell crosshair_layer crosshair_size cursor_2_hook custom_label_prefix custom_token - dark_colors dark_colorscheme dark_gui_colorscheme delay_flag - dim_bg dim_value disable_unique_names do_all_inst draw_crosshair - draw_grid draw_grid_axes draw_window edit_prop_pos edit_prop_size - edit_symbol_prop_new_sel editprop_sympath en_hilight_conn_inst enable_dim_bg enable_stretch - enter_text_default_geometry filetmp fix_broken_tiled_fill flat_netlist fullscreen - gaw_fd gaw_tcp_address graph_autoload graph_bus - graph_change_done graph_digital graph_dialog_default_geometry - graph_legend graph_linewidth_mult graph_logx - graph_logy graph_private_cursor graph_rainbow graph_schname graph_sel_color graph_sel_wave - graph_selected graph_sort graph_unlocked graph_use_ctrl_key - hide_empty_graphs hide_symbols tctx::hsize - incr_hilight incremental_select infix_interface infowindow_text intuitive_interface - keep_symbols launcher_default_program - light_colors line_width live_cursor2_backannotate local_netlist_dir lvs_ignore - lvs_netlist measure_text netlist_dir netlist_show netlist_type no_ask_save - no_change_attrs nolist_libs noprint_libs old_selected_tok only_probes path pathlist - persistent_command preserve_unchanged_attrs prev_symbol ps_colors ps_paper_size rainbow_colors - tctx::rcode recentfile - retval retval_orig rotated_text search_case search_exact search_found search_schematic - search_select search_value select_touch selected_tok show_hidden_texts show_infowindow - show_infowindow_after_netlist - simconf_default_geometry simconf_vpos simulate_bg spiceprefix split_files svg_colors - svg_font_name sym_txt symbol symbol_width tabstop tclcmd_txt tclstop text_line_default_geometry - text_replace_selection text_tabs_setting textwindow_fileid textwindow_filename textwindow_w - toolbar_horiz toolbar_list - toolbar_visible top_is_subckt transparent_svg undo_type use_lab_wire unselect_partial_sel_wires - use_label_prefix use_tclreadline - user_wants_copy_cell verilog_2001 verilog_bitblast viewdata_fileid viewdata_filename viewdata_w - tctx::vsize xschem_libs xschem_listen_port zoom_full_center orthogonal_wiring snap_cursor - snap_cursor_size cadence_compat use_cursor_for_selection + INITIALINSTDIR INITIALLOADDIR INITIALPROPDIR INITIALTEXTDIR PDK PDK_ROOT SKYWATER_MODELS + SKYWATER_STDCELLS XSCHEM_LIBRARY_PATH add_all_windows_drives auto_hilight + auto_hilight_graph_nodes autofocus_mainwindow autotrim_wires bespice_listen_port big_grid_points + bus_replacement_char cadence_compat cadgrid cadlayers cadsnap cairo_font_name cairo_font_scale + change_lw color_ps compare_sch constr_mv copy_cell crosshair_layer crosshair_size cursor_2_hook + custom_label_prefix custom_token dark_colors dark_colorscheme dark_gui_colorscheme delay_flag + dim_bg dim_value disable_unique_names do_all_inst draw_crosshair draw_grid draw_grid_axes + draw_window edit_prop_pos edit_prop_size edit_symbol_prop_new_sel editprop_sympath + en_hilight_conn_inst enable_dim_bg enable_stretch enter_text_default_geometry filetmp + fix_broken_tiled_fill flat_netlist fullscreen gaw_fd gaw_tcp_address graph_autoload graph_bus + graph_change_done graph_dialog_default_geometry graph_digital graph_legend graph_linewidth_mult + graph_logx graph_logy graph_private_cursor graph_rainbow graph_schname graph_sel_color + graph_sel_wave graph_selected graph_sort graph_unlocked graph_use_ctrl_key hide_empty_graphs + hide_symbols incr_hilight incremental_select infix_interface infowindow_text intuitive_interface + keep_symbols launcher_default_program light_colors line_width live_cursor2_backannotate + local_netlist_dir lvs_ignore lvs_netlist measure_text netlist_dir netlist_show netlist_type + no_ask_save no_change_attrs nolist_libs noprint_libs old_selected_tok only_probes + orthogonal_wiring path pathlist persistent_command preserve_unchanged_attrs prev_symbol ps_colors + ps_paper_size rainbow_colors recentfile retval retval_orig rotated_text search_case search_exact + search_found search_schematic search_select search_value select_touch selected_tok + show_hidden_texts show_infowindow show_infowindow_after_netlist simconf_default_geometry + simconf_vpos simulate_bg snap_cursor snap_cursor_size spiceprefix split_files svg_colors + svg_font_name sym_txt symbol symbol_width tabstop tclcmd_txt tclstop tctx::colors tctx::hsize + tctx::rcode tctx::vsize text_line_default_geometry text_replace_selection text_tabs_setting + textwindow_fileid textwindow_filename textwindow_w toolbar_horiz toolbar_list toolbar_visible + top_is_subckt transparent_svg undo_type unselect_partial_sel_wires use_cursor_for_selection + use_lab_wire use_label_prefix use_tclreadline user_wants_copy_cell verilog_2001 verilog_bitblast + viewdata_fileid viewdata_filename viewdata_w xschem_libs xschem_listen_port zoom_full_center } ## list of global arrays to save/restore on context switching @@ -8283,7 +8275,7 @@ proc load_raw {{type {}}} { proc build_widgets { {topwin {} } } { global XSCHEM_SHAREDIR tabbed_interface simulate_bg OS sim - global dark_gui_colorscheme draw_crosshair + global dark_gui_colorscheme draw_crosshair grid_point_size global recentfile color_ps transparent_svg menu_debug_var enable_stretch global netlist_show flat_netlist split_files compare_sch intuitive_interface global draw_grid big_grid_points sym_txt change_lw incr_hilight symbol_width @@ -8621,6 +8613,10 @@ proc build_widgets { {topwin {} } } { set change_lw 0 input_line "Enter linewidth (float):" "xschem line_width" } + $topwin.menubar.view add command -label "Set grid point size" \ + -command { + input_line "Enter Grid point size (int or -1: $grid_point_size)" "set grid_point_size" $grid_point_size + } $topwin.menubar.view add checkbutton -label "Tabbed interface" -variable tabbed_interface \ -selectcolor $selectcolor -command setup_tabbed_interface @@ -9361,6 +9357,7 @@ set_ne select_touch 1 set_ne draw_grid 1 set_ne big_grid_points 0 +set_ne grid_point_size -1 ;# grid point size (>=0) or unspecified (-1) set_ne draw_grid_axes 1 set_ne persistent_command 0 set_ne intuitive_interface 1 diff --git a/src/xschemrc b/src/xschemrc index b472f338..64304589 100644 --- a/src/xschemrc +++ b/src/xschemrc @@ -289,6 +289,9 @@ #### enable to scale grid point size as done with lines at close zoom, default: 0 # set big_grid_points 0 +#### explicitly set grid point size in pixels. Default: -1 (not set) +# set grid_point_size 2 + #### enable drawing grid axes. Default: enabled (1) # set draw_grid_axes 1 From 96eeef5a95fd06e7ad89a458ce9d866231831f7f Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Mar 2025 15:34:19 +0100 Subject: [PATCH 43/67] raw_read(): cleanup allocated `rawptr` if nothing loaded due to missing file --- src/save.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/save.c b/src/save.c index d36cd80f..50803498 100644 --- a/src/save.c +++ b/src/save.c @@ -1014,17 +1014,16 @@ int raw_read(const char *f, Raw **rawptr, const char *type, int no_warning, doub return res; } dbg(1, "raw_read(): type=%s\n", type ? type : ""); - *rawptr = my_calloc(_ALLOC_ID_, 1, sizeof(Raw)); - raw = *rawptr; - raw->level = -1; - raw->annot_p = -1; - raw->sweep1 = sweep1; - raw->sweep2 = sweep2; - raw->annot_sweep_idx = -1; - - int_hash_init(&raw->table, HASHSIZE); fd = my_fopen(f, fopen_read_mode); if(fd) { + *rawptr = my_calloc(_ALLOC_ID_, 1, sizeof(Raw)); + raw = *rawptr; + raw->level = -1; + raw->annot_p = -1; + raw->sweep1 = sweep1; + raw->sweep2 = sweep2; + raw->annot_sweep_idx = -1; + int_hash_init(&raw->table, HASHSIZE); if((res = read_dataset(fd, rawptr, type, no_warning)) == 1) { int i; set_modify(-2); /* clear text floater caches */ @@ -1051,7 +1050,6 @@ int raw_read(const char *f, Raw **rawptr, const char *type, int no_warning, doub } } } - } else { /* free_rawfile(rawptr, 0, 0); */ /* do not free: already done in read_dataset()->extra_rawfile() */ if(!no_warning) { @@ -1208,6 +1206,7 @@ int extra_rawfile(int what, const char *file, const char *type, double sweep1, d if(what == 0) return 0; /* if not already done insert base raw file (if there is one) into xctx->extra_raw_arr[0] */ if(xctx->raw && xctx->extra_raw_n == 0) { + dbg(1, "insert extra_raw_arr[0]\n"); xctx->extra_raw_arr[xctx->extra_raw_n] = xctx->raw; xctx->extra_raw_n++; } @@ -1279,7 +1278,8 @@ int extra_rawfile(int what, const char *file, const char *type, double sweep1, d if(!no_warning) { dbg(0, "extra_rawfile() read: %s not found or no \"%s\" analysis\n", f, type ? type : ""); } - if(xctx->extra_raw_n) { /* only restore if raw wiles were not deleted due to a failure in read_raw() */ + if(xctx->extra_raw_n) { /* only restore if raw files were not deleted due to a failure in read_raw() */ + dbg(1, "extra_rawfile(): read: restore previous, extra_idx=%d\n", xctx->extra_idx); xctx->raw = save; /* restore */ xctx->extra_prev_idx = xctx->extra_idx; } From d7d812c02337783f664f5555b59a94fffbca8cc2 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 7 Mar 2025 15:59:43 +0100 Subject: [PATCH 44/67] square grid points if grid_point_size is specified --- src/draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.c b/src/draw.c index f5bb1c9e..73dcb5e0 100644 --- a/src/draw.c +++ b/src/draw.c @@ -1015,7 +1015,7 @@ static void drawgrid() #if DRAW_ALL_CAIRO==0 if(grid_point_size != -1) { XSetLineAttributes (display, xctx->gc[GRIDLAYER], - grid_point_size, LineSolid, LINECAP, LINEJOIN); + grid_point_size, LineSolid, CapProjecting, LINEJOIN); } else if(!big_gr) { XSetLineAttributes (display, xctx->gc[GRIDLAYER], 0, LineSolid, LINECAP, LINEJOIN); From f1901e055c6577245cfa8bfccdbcf50fbe541d76 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sat, 8 Mar 2025 10:53:21 +0100 Subject: [PATCH 45/67] clip graph point[].x values to prevent aliasing at absurdly deep zoom in values --- src/draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/draw.c b/src/draw.c index 73dcb5e0..fb0c1ddc 100644 --- a/src/draw.c +++ b/src/draw.c @@ -4107,7 +4107,7 @@ void draw_graph(int i, int flags, Graph_ctx *gr, void *ct) if((gr->mode == 2) || (xxfollowing >= start && xxprevious <= end)) { if(first == -1) first = p; /* Build poly x array. Translate from graph coordinates to screen coords */ - point[poly_npoints].x = (short)S_X(xx); + point[poly_npoints].x = (short)CLIP(S_X(xx), -30000, 30000); if(dataset == -1 || dataset == sweepvar_wrap) { /* cursor1: show measurements on nodes in graph */ if(flags & 2 && measure_p == -1 && cnt) { From 2f1643368e58b2943abfe98752152526af278063 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Sat, 8 Mar 2025 21:14:46 +0100 Subject: [PATCH 46/67] add command `xschem raw rename old_node new_node` --- doc/xschem_man/developer_info.html | 10 +++++++--- src/save.c | 16 ++++++++++++++++ src/scheduler.c | 19 +++++++++++++------ src/xschem.h | 1 + 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html index 844379c7..25369cc1 100644 --- a/doc/xschem_man/developer_info.html +++ b/doc/xschem_man/developer_info.html @@ -1200,8 +1200,9 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
  • push_undo
  •     Push current state on undo stack 
  • raw what ...
  • -     what = add | clear | datasets | index | info | loaded | list | new | points | rawfile | del |
    -            read | set | sim_type | switch | switch_back | table_read | value | values | pos_at | vars |
    +     what = add | clear | datasets | index | info | loaded | list |
    +            new | points | rawfile | del | read | set | rename |
    +            sim_type | switch | switch_back | table_read | value | values | pos_at | vars |
           
        xschem raw read filename [type [sweep1 sweep2]]
          if sweep1, sweep2 interval is given in 'read' subcommand load only the interval
    @@ -1216,6 +1217,9 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        xschem raw del name
          delete named vector from current raw file
           
    +   xschem raw rename old_name new_name
    +     rename a node in the loaded raw file.
    +      
        xschem raw info
          print information about loaded raw files and show the currently active one.
           
    @@ -1292,7 +1296,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
          new dataset do not start with a header row.
          Lines beginning with '#' are comments and ignored
           
    -        time    var_a   var_b   var_c
    +        time    var_a   var_b   var_cnode in the loaded raw file.
          # this is a comment, ignored
              0.0     0.0     1.8    0.3
            <single empty line: ignored>
    diff --git a/src/save.c b/src/save.c
    index 50803498..a3c039eb 100644
    --- a/src/save.c
    +++ b/src/save.c
    @@ -1074,6 +1074,22 @@ int raw_read(const char *f, Raw **rawptr, const char *type, int no_warning, doub
       return 0;
     }
     
    +int raw_renamevar(const char *old_name, const char *new_name)
    +{       
    +  int n, ret = 0;
    +  Raw *raw = xctx->raw; 
    +  Int_hashentry *entry;
    +      
    +  n = get_raw_index(old_name, &entry);
    +  if(n < 0) return ret;
    +  dbg(1, "n=%d, %s \n", n, entry->token);
    +  int_hash_lookup(&raw->table, entry->token, 0, XDELETE);
    +  my_strdup2(_ALLOC_ID_, &raw->names[n], new_name);
    +  int_hash_lookup(&raw->table, raw->names[n], n, XINSERT); /* update hash table */
    +  ret = 1;
    +  return ret; 
    +}         
    +
     int raw_deletevar(const char *name)
     {
       int ret = 0;
    diff --git a/src/scheduler.c b/src/scheduler.c
    index eb50f476..0fc3adc4 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -4004,8 +4004,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
         case 'r': /*----------------------------------------------*/
     
         /* raw what ...
    -     *     what = add | clear | datasets | index | info | loaded | list | new | points | rawfile | del |
    -     *            read | set | sim_type | switch | switch_back | table_read | value | values | pos_at | vars |
    +     *     what = add | clear | datasets | index | info | loaded | list |
    +     *            new | points | rawfile | del | read | set | rename |
    +     *            sim_type | switch | switch_back | table_read | value | values | pos_at | vars |
          *
          *   xschem raw read filename [type [sweep1 sweep2]]
          *     if sweep1, sweep2 interval is given in 'read' subcommand load only the interval
    @@ -4020,6 +4021,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
          *   xschem raw del name
          *     delete named vector from current raw file
          *
    +     *   xschem raw rename old_name new_name
    +     *     rename a node in the loaded raw file.
    +     *
          *   xschem raw info
          *     print information about loaded raw files and show the currently active one.
          *
    @@ -4096,7 +4100,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
          *     new dataset do not start with a header row.
          *     Lines beginning with '#' are comments and ignored
          *
    -     *        time    var_a   var_b   var_c
    +     *        time    var_a   var_b   var_cnode in the loaded raw file.
          *     # this is a comment, ignored
          *         0.0     0.0     1.8    0.3
          *       
    @@ -4164,9 +4168,6 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
               update_op();
             }
             Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE);
    -      } else if(argc > 3 && !strcmp(argv[2], "del")) {
    -        ret = raw_deletevar(argv[3]);
    -        Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE);
           } else if(argc > 2 && !strcmp(argv[2], "clear")) {
             if(argc > 4)  {
               ret = extra_rawfile(3, argv[3], argv[4], -1.0, -1.0);
    @@ -4199,6 +4200,12 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
                   Tcl_SetResult(interp, dtoa(val), TCL_VOLATILE);
                 }
               }
    +        } else if(argc > 3 && !strcmp(argv[2], "del")) {
    +          ret = raw_deletevar(argv[3]);
    +          Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE);
    +        } else if(argc > 4 && !strcmp(argv[2], "rename")) {
    +          ret = raw_renamevar(argv[3], argv[4]);
    +          Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE);
             } else if(argc > 3 && !strcmp(argv[2], "index")) {
               /* xschem raw index v(ldcp) */
               int idx;
    diff --git a/src/xschem.h b/src/xschem.h
    index 60a192ef..4989f3fa 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -1259,6 +1259,7 @@ extern int embed_rawfile(const char *rawfile);
     extern int read_rawfile_from_attr(const char *b64s, size_t length, const char *type);
     extern int raw_read_from_attr(Raw **rawptr, const char *type, double sweep1, double sweep2);
     extern int raw_add_vector(const char *varname, const char *expr, int sweep_idx);
    +extern int raw_renamevar(const char *old_name, const char *new_name);
     extern int raw_deletevar(const char *name);
     extern int new_rawfile(const char *name, const char *type, const char *sweepvar,
                            double start, double end, double step);
    
    From de6434c94174c7a63cc632fdcf19e1f6e5389b25 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sun, 9 Mar 2025 02:46:58 +0100
    Subject: [PATCH 47/67] allow multi-selections when loading a file; limit max
     line width in change_linewidth() to avoid strange artifacts at extreme
     zoom-ins
    
    ---
     src/callback.c  |  3 +++
     src/scheduler.c | 50 ++++++++++++++++++++++++++++++++++---------------
     src/xinit.c     |  1 +
     src/xschem.tcl  | 28 +++++++++++++++++++++++++--
     4 files changed, 65 insertions(+), 17 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index 7b9dea45..9fe4a3c1 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -3395,6 +3395,9 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m
        {
         if(xctx->semaphore >= 2) return;
         ask_new_file();
    +    xctx->semaphore--;
    +    tcleval("load_additional_files");
    +    xctx->semaphore++;
         return;
        }
        if(key=='S' && rstate == 0)   /* change element order */
    diff --git a/src/scheduler.c b/src/scheduler.c
    index 0fc3adc4..84f94fa5 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -2941,6 +2941,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           first = i;
           if(argc==2) {
             ask_new_file();
    +        tcleval("load_additional_files");
           } else
           for(i = first; i < argc; i++) {
             char f[PATH_MAX + 100];
    @@ -3016,13 +3017,32 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           int cancel = 0;
           if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;}
           if(argc > 2) {
    -        if(!is_from_web(argv[2])) {
    -          my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir);
    -          tcleval(f);
    -          /* tclvareval("file normalize {", tclresult(), "}", NULL); */
    -          my_strncpy(f, abs_sym_path(tclresult(), ""), S(f));
    -        } else {
    -          my_strncpy(f, argv[2], S(f));
    +        int i;
    +        for(i = 2; i < argc; i++) {
    +          if(!is_from_web(argv[i])) {
    +            my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[i], home_dir);
    +            tcleval(f);
    +            /* tclvareval("file normalize {", tclresult(), "}", NULL); */
    +            my_strncpy(f, abs_sym_path(tclresult(), ""), S(f));
    +          } else {
    +            my_strncpy(f, argv[i], S(f));
    +          }
    +          if(f[0]) {
    +           char win_path[WINDOW_PATH_SIZE];
    +           dbg(1, "f=%s\n", f);
    +           if(check_loaded(f, win_path)) {
    +             char msg[PATH_MAX + 100];
    +             my_snprintf(msg, S(msg),
    +                "tk_messageBox -type okcancel -icon warning -parent [xschem get topwindow] "
    +                "-message {Warning: %s already open.}", f);
    +             tcleval(msg);
    +             if(strcmp(tclresult(), "ok")) continue;
    +           }
    +           new_schematic("create", "noconfirm", f, 1);
    +           tclvareval("update_recent_file {", f, "}", NULL);
    +          } else {
    +            new_schematic("create", NULL, NULL, 1);
    +          }
             }
           } else {
             tcleval("load_file_dialog {Load file} *.\\{sch,sym,tcl\\} INITIALLOADDIR");
    @@ -3031,14 +3051,14 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             } else {
               cancel = 1;
             }
    -      }
    -      if(!cancel) {
    -        if(f[0]) {
    -         dbg(1, "f=%s\n", f);
    -         new_schematic("create", "noconfirm", f, 1);
    -         tclvareval("update_recent_file {", f, "}", NULL);
    -        } else {
    -          new_schematic("create", NULL, NULL, 1);
    +        if(!cancel) {
    +          if(f[0]) {
    +           dbg(1, "f=%s\n", f);
    +           new_schematic("create", "noconfirm", f, 1);
    +           tclvareval("update_recent_file {", f, "}", NULL);
    +          } else {
    +            new_schematic("create", NULL, NULL, 1);
    +          }
             }
           }
           Tcl_ResetResult(interp);
    diff --git a/src/xinit.c b/src/xinit.c
    index 288b4e8c..192ae975 100644
    --- a/src/xinit.c
    +++ b/src/xinit.c
    @@ -2060,6 +2060,7 @@ void change_linewidth(double w)
         double cs = tclgetdoublevar("cadsnap");
         if(tclgetboolvar("change_lw"))  {
           xctx->lw=xctx->mooz * 0.09 * cs;
    +      if(xctx->lw > 100.) xctx->lw = 100.;
           xctx->cadhalfdotsize = CADHALFDOTSIZE * (cs < 20. ? cs : 20.) / 10.;
         }
       /* explicitly set line width */
    diff --git a/src/xschem.tcl b/src/xschem.tcl
    index 02a702fd..8c8b17f1 100644
    --- a/src/xschem.tcl
    +++ b/src/xschem.tcl
    @@ -4369,8 +4369,10 @@ proc file_dialog_display_preview {f} {
     proc file_dialog_right_listboxselect {dirselect} {
         global file_dialog_yview file_dialog_dir1 file_dialog_dir2  file_dialog_retval file_dialog_sel
         global OS file_dialog_loadfile file_dialog_index1 file_dialog_files1 file_dialog_globfilter
    +    global file_dialog_others
         set file_dialog_yview [.load.l.paneright.f.list yview] 
    -    set file_dialog_sel [.load.l.paneright.f.list curselection]
    +    set file_dialog_sel [lindex [.load.l.paneright.f.list curselection] 0]
    +    
         if { $file_dialog_sel ne {} } {
           set curr_dir [abs_sym_path [lindex $file_dialog_files1 $file_dialog_index1]]
           set curr_item [.load.l.paneright.f.list get $file_dialog_sel]
    @@ -4403,6 +4405,17 @@ proc file_dialog_right_listboxselect {dirselect} {
     
           set file_dialog_dir1 $curr_dir
           set file_dialog_dir2 $curr_item
    +
    +      set file_dialog_others {}
    +      if {$file_dialog_loadfile == 1} {
    +        foreach i [lrange [.load.l.paneright.f.list curselection] 1 end] {
    +          set file_dialog_retval [.load.l.paneright.f.list get $i]
    +          lappend file_dialog_others [file_dialog_getresult 1 0]
    +        }
    +      }
    +      set file_dialog_retval {} ;# we used this variable above to communicate with file_dialog_getresult
    +
    +
           if { [file isdirectory $file_dialog_d]} {
             bind .load.l.paneright.draw  {}
             bind .load.l.paneright.draw  {}
    @@ -4425,6 +4438,16 @@ proc file_dialog_right_listboxselect {dirselect} {
         }
     }
     
    +proc load_additional_files {} {
    +  global file_dialog_others
    +
    +  if {$file_dialog_others ne {} } {
    +    foreach i $file_dialog_others {
    +      xschem load_new_window $i
    +    }
    +  }
    +}
    +
     # global_initdir: name of global variable containing the initial directory
     # loadfile: set to 0 if calling for saving instead of loading a file
     #           set to 2 for non blocking operation (symbol insertion)
    @@ -4514,7 +4537,7 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
     
       listbox .load.l.paneright.f.list  -background {grey90} -listvariable file_dialog_files2 -width 20 -height 12\
         -fg black -highlightcolor red -highlightthickness 2 \
    -    -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode browse \
    +    -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode extended \
         -xscrollcommand ".load.l.paneright.f.xscroll set" -exportselection 0
       scrollbar .load.l.paneright.f.yscroll -command ".load.l.paneright.f.list yview" -takefocus 0
       scrollbar .load.l.paneright.f.xscroll -command ".load.l.paneright.f.list xview" -orient horiz -takefocus 0
    @@ -9348,6 +9371,7 @@ set_ne enable_stretch 0
     set_ne constr_mv 0
     set_ne unselect_partial_sel_wires 0
     set_ne load_file_dialog_fullpath 1
    +set_ne file_dialog_others {} ;# contains 2nd, 3rd, ... selected filenames on mult. selections in load file
     
     # if set show selected elements while dragging the selection rectangle.
     # once selected these can not be unselected by retracting the selection rectangle
    
    From 7e5a36531b6ce2cad6c17d8ab26b4de5b2a4f530 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sun, 9 Mar 2025 10:15:50 +0100
    Subject: [PATCH 48/67] reduce max zoom in/out factors
    
    ---
     src/xschem.h | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/src/xschem.h b/src/xschem.h
    index 4989f3fa..eb62ea35 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -163,8 +163,8 @@ extern char win_temp_dir[PATH_MAX];
     #define CADINITIALY -870
     #define CADZOOMSTEP 1.2
     #define CADMOVESTEP 200
    -#define CADMAXZOOM 1000000.0
    -#define CADMINZOOM 0.000001
    +#define CADMAXZOOM 10000.0
    +#define CADMINZOOM 0.005
     #define CADHALFDOTSIZE 3.7
     #define CADNULLNODE -1      /*  no valid node number */
     #define CADWIREMINDIST 8.0
    
    From d582fc98d8c902671850f049519b6ed54c5f69e4 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Sun, 9 Mar 2025 13:53:18 +0100
    Subject: [PATCH 49/67] proc load_file_dialog: allow multi selection (extended)
     only for load file, not for place instance
    
    ---
     src/xschem.tcl | 8 ++++++--
     1 file changed, 6 insertions(+), 2 deletions(-)
    
    diff --git a/src/xschem.tcl b/src/xschem.tcl
    index 8c8b17f1..1b0cfef2 100644
    --- a/src/xschem.tcl
    +++ b/src/xschem.tcl
    @@ -4533,11 +4533,15 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
       eval .load.l.paneright paneconfigure .load.l.paneright.f $optnever
       eval .load.l.paneright paneconfigure .load.l.paneright.draw $optalways
     
    -
    +  if {$global_initdir eq {INITIALINSTDIR}} {
    +    set selmode browse
    +  } else {
    +    set selmode extended
    +  }
     
       listbox .load.l.paneright.f.list  -background {grey90} -listvariable file_dialog_files2 -width 20 -height 12\
         -fg black -highlightcolor red -highlightthickness 2 \
    -    -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode extended \
    +    -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode $selmode \
         -xscrollcommand ".load.l.paneright.f.xscroll set" -exportselection 0
       scrollbar .load.l.paneright.f.yscroll -command ".load.l.paneright.f.list yview" -takefocus 0
       scrollbar .load.l.paneright.f.xscroll -command ".load.l.paneright.f.list xview" -orient horiz -takefocus 0
    
    From c69c1b26874f771369d930c788a8bb51ce872555 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Mon, 10 Mar 2025 01:03:15 +0100
    Subject: [PATCH 50/67] better handling of snap cursor and snap wire creation
     (no cadence emulation)
    
    ---
     src/callback.c | 35 +++++++++++++++++++++--------------
     1 file changed, 21 insertions(+), 14 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index 9fe4a3c1..f05ccbb1 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -1514,6 +1514,7 @@ static void find_snap_position(double *x, double *y, int pos_changed) {
     /* action == 3 : delete and draw
      * action == 1 : delete
      * action == 2 : draw
    + * action == 5 : delete even if pos not changed
      */
     static void draw_snap_cursor(int action) {
       int snapcursor_size;
    @@ -1527,7 +1528,7 @@ static void draw_snap_cursor(int action) {
       /* Save current drawing context */
       xctx->draw_pixmap = 0;
       xctx->draw_window = 1;
    -  if(pos_changed) {
    +  if(pos_changed || action == 5) {
         /* Erase the cursor */
         if (action & 1) {
           erase_snap_cursor(xctx->prev_snapx, xctx->prev_snapy, snapcursor_size);
    @@ -1617,7 +1618,7 @@ void draw_crosshair(int what, int state)
             dbg(1, "find\n");
           }
         } else {
    -      draw_snap_cursor(what);
    +      /* draw_snap_cursor(what); */
         }
       }
       
    @@ -1685,7 +1686,7 @@ static void snapped_wire(double c_snap)
       }
     }
     
    -static int check_menu_start_commands(double c_snap, int mx, int my)
    +static int check_menu_start_commands(int state, double c_snap, int mx, int my)
     {
       dbg(1, "check_menu_start_commands(): ui_state=%x, ui_state2=%x last_command=%d\n", 
           xctx->ui_state, xctx->ui_state2, xctx->last_command);
    @@ -1726,10 +1727,14 @@ static int check_menu_start_commands(double c_snap, int mx, int my)
       else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTWIRE)) {
         int prev_state = xctx->ui_state;
         if(xctx->semaphore >= 2) return 0;
    -    start_wire(xctx->mousex_snap, xctx->mousey_snap);
    -    if(prev_state == STARTWIRE) {
    -      tcleval("set constr_mv 0" );
    -      xctx->constr_mv=0;
    +    if( state & ShiftMask) {
    +      snapped_wire(c_snap);
    +    } else {
    +      start_wire(xctx->mousex_snap, xctx->mousey_snap);
    +      if(prev_state == STARTWIRE) {
    +        tcleval("set constr_mv 0" );
    +        xctx->constr_mv=0;
    +      }
         }
     
         /* 
    @@ -2481,7 +2486,7 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i
         if(draw_xhair) {
           draw_crosshair(1, state); /* when moving mouse: first action is delete crosshair, will be drawn later */
         }
    -    if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* clear */
    +    if(snap_cursor) draw_snap_cursor(1); /* clear */
         /* pan schematic */
         if(xctx->ui_state & STARTPAN) pan(RUBBER, mx, my);
     
    @@ -2489,7 +2494,7 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i
           if(draw_xhair) {
             draw_crosshair(2, state); /* locked UI: draw new crosshair and break out */
           }
    -      if(snap_cursor && wire_draw_active) draw_snap_cursor(2); /* redraw */
    +      if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(2); /* redraw */
           return;
         }
     
    @@ -2590,7 +2595,7 @@ static void handle_motion_notify(int event, KeySym key, int state, int rstate, i
         if(draw_xhair) {
           draw_crosshair(2, state); /* what = 2(draw) */
         }
    -    if(snap_cursor && wire_draw_active) draw_snap_cursor(2); /* redraw */
    +    if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(2); /* redraw */
     }
     
     static void handle_key_press(int event, KeySym key, int state, int rstate, int mx, int my, 
    @@ -2884,7 +2889,7 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m
         if(xctx->ui_state2 & MENUSTARTWIRE) {
           xctx->ui_state2 &= ~MENUSTARTWIRE;
         }
    -    if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* erase */
    +    if(snap_cursor) draw_snap_cursor(1); /* erase */
         if(tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE) && cadence_compat) {
           xctx->last_command &= ~STARTWIRE;
         }
    @@ -4196,7 +4201,7 @@ static void handle_button_press(int event, int state, int rstate, KeySym key, in
             return;
          }
          /* handle all object insertions started from Tools/Edit menu */
    -     if(check_menu_start_commands(c_snap, mx, my)) return;
    +     if(check_menu_start_commands(state, c_snap, mx, my)) return;
     
          /* complete the pending STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */
          if(end_place_move_copy_zoom()) return;
    @@ -4427,7 +4432,7 @@ static void handle_button_release(int event, KeySym key, int state, int button,
          return;
        }
        if(draw_xhair) draw_crosshair(3, state); /* restore crosshair when selecting / unselecting */
    -   if(snap_cursor && wire_draw_active) draw_snap_cursor(3); /* erase & redraw */
    +   if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(3); /* erase & redraw */
     }
     
     static void handle_double_click(int event, int state, KeySym key, int button,
    @@ -4614,7 +4619,7 @@ int wire_draw_active = (xctx->ui_state & STARTWIRE) ||
     
       case LeaveNotify:
         if(draw_xhair) draw_crosshair(1, state); /* clear crosshair when exiting window */
    -    if(snap_cursor && wire_draw_active) draw_snap_cursor(1); /* erase */
    +    if(snap_cursor) draw_snap_cursor(1); /* erase */
         tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL);
         xctx->mouse_inside = 0;
         break;
    @@ -4657,6 +4662,8 @@ int wire_draw_active = (xctx->ui_state & STARTWIRE) ||
         break;
     
       case KeyRelease:
    +    /* force clear (even if mouse pos not changed) */
    +    /* if(snap_cursor && (key == XK_Shift_L || key == XK_Shift_R) ) draw_snap_cursor(5); */
         break;
     
       case KeyPress:
    
    From 5e6ce2dce8bfa628b6b6d04d907f5e69185373b8 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Mon, 10 Mar 2025 17:02:46 +0100
    Subject: [PATCH 51/67] fix xschem.tcl proc traversal
    
    ---
     src/xschem.tcl | 11 ++++++++---
     1 file changed, 8 insertions(+), 3 deletions(-)
    
    diff --git a/src/xschem.tcl b/src/xschem.tcl
    index 1b0cfef2..8c42401b 100644
    --- a/src/xschem.tcl
    +++ b/src/xschem.tcl
    @@ -1840,6 +1840,7 @@ proc simconf_add {tool} {
     ############ cellview
     # proc cellview prints symbol bindings (default binding or "schematic" attr in symbol)
     # of all symbols used in current and sub schematics.
    +# the derived_symbols parameter of cellview should be either empty or 'derived_symbols'
     proc cellview_setlabels {w symbol derived_symbol} {
       global dark_gui_colorscheme netlist_type
       if {$dark_gui_colorscheme} {
    @@ -1941,6 +1942,8 @@ proc cellview_edit_sym {w} {
       xschem load_new_window $sym
     }
     
    +# derived_symbols: empty or 'derived_symbols'
    +# upd: never set by caller (used iinternally to update)
     proc cellview { {derived_symbols {}} {upd 0}} {
       global keep_symbols nolist_libs dark_gui_colorscheme netlist_type
     
    @@ -2099,10 +2102,9 @@ proc traversal_setlabels {w parent_sch instname inst_sch sym_sch default_sch
       set save_netlist_type [xschem get netlist_type]
       # puts "traversal_setlabels: $w parent: |$parent_sch| inst: $instname def: $sym_sch $inst_sch --> [$w get]"
       # update schematic
    -  if {$parent_sch ne {}} {
    +  if {$parent_sch ne {} && $sym_spice_sym_def eq {} &&  $inst_spice_sym_def eq {} } {
         set current [xschem get current_name]
         if { $inst_sch ne [$w get] } {
    -      puts "update attr"
           xschem load -undoreset -nodraw $parent_sch 
           if { [$w get] eq  $sym_sch} {
             xschem setprop -fast instance $instname schematic  ;# remove schematic attr on instance
    @@ -2228,6 +2230,7 @@ proc hier_traversal {{level 0} {only_subckts 0} {all_hierarchy 1}} {
       set instances  [xschem get instances]
       set current_level [xschem get currsch]
       for {set i 0} { $i < $instances} { incr i} {
    +    # puts "hier_traversal: i=$i, current_level=$current_level, parent_sch=$parent_sch"
         set instname [xschem getprop instance $i name]
         set symbol [xschem getprop instance $i cell::name]
         set default_sch [add_ext $symbol .sch]
    @@ -2305,8 +2308,10 @@ proc hier_traversal {{level 0} {only_subckts 0} {all_hierarchy 1}} {
           if {$descended} {
             incr level
             set dp [hier_traversal $level $only_subckts 1]
    -        xschem go_back 1
    +        xschem go_back 2
             incr level -1
    +      } else { ;# descended into a blank schematic. Go back.
    +        xschem go_back 2
           }
         }
       }
    
    From 57a9348cf1cb5d265e29160ad817554c10ca9a2b Mon Sep 17 00:00:00 2001
    From: Chayan Deb 
    Date: Tue, 11 Mar 2025 11:51:17 +0530
    Subject: [PATCH 52/67] [Major Refactor]: The 'handle_key_press()' function
     responsible for processing keyboard inputs has been refactored to use
     switch-case statements intead of an excessively long chain of if-else
     statements. Due to huge number of possible inputs, this approach should
     improve performance (jump-tables) of the application, while increasing
     readability and maintainability of the codebase in the future. Custom
     keybinds can also be easily worked on and implemented in the near future
     thanks to this approach.
    
    ---
     src/callback.c | 2970 ++++++++++++++++++++++++------------------------
     1 file changed, 1483 insertions(+), 1487 deletions(-)
    
    diff --git a/src/callback.c b/src/callback.c
    index 504a19eb..44ab24cb 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -2605,1502 +2605,1498 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m
                                  int button, int aux, int infix_interface, int enable_stretch, const char *win_path, double c_snap,
                                  int cadence_compat, int wire_draw_active, int snap_cursor)
     {
    -   char str[PATH_MAX + 100];
    -   if(key==' ') {
    -     if(xctx->ui_state & STARTWIRE) { /*  & instead of == 20190409 */
    -       new_wire(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap);
    -       xctx->manhattan_lines++;
    -       xctx->manhattan_lines %=3;
    -       new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -
    -     } else if(xctx->ui_state & STARTLINE) {
    -       new_line(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap);
    -       xctx->manhattan_lines++;
    -       xctx->manhattan_lines %=3;
    -       new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -     } else {
    -       if(xctx->semaphore<2) {
    -         rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */
    -       }
    -       pan(START, mx, my);
    -       xctx->ui_state |= STARTPAN;
    -     }
    -     return;
    -   }
    -   if(key == '_' )              /* toggle change line width */
    -   {
    -    if(!tclgetboolvar("change_lw")) {
    -        tcleval("alert_ { enabling change line width} {}");
    -        tclsetvar("change_lw","1");
    -    }
    -    else {
    -        tcleval("alert_ { disabling change line width} {}");
    -        tclsetvar("change_lw","0");
    -    }
    -    return;
    -   }
    -   if(key == 'b' && rstate==ControlMask)         /* toggle show text in symbol */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }     
    -    xctx->sym_txt =!xctx->sym_txt;
    -    if(xctx->sym_txt) {
    -        /* tcleval("alert_ { enabling text in symbol} {}"); */
    -        tclsetvar("sym_txt","1");
    -        draw();
    -    }
    -    else {
    -        /* tcleval("alert_ { disabling text in symbol} {}"); */
    -        tclsetvar("sym_txt","0");
    -        draw();
    -    }
    -    return;
    -   }
    -   if(key == '%' )              /* toggle draw grid */
    -   {
    -    int dr_gr;
    -    dr_gr = tclgetboolvar("draw_grid");
    -    dr_gr =!dr_gr;
    -    if(dr_gr) {
    -        /* tcleval("alert_ { enabling draw grid} {}"); */
    -        tclsetvar("draw_grid","1");
    -        draw();
    -    }
    -    else {
    -        /* tcleval("alert_ { disabling draw grid} {}"); */
    -        tclsetvar("draw_grid","0");
    -        draw();
    -    }
    -    return;
    -   }
    -   if(key == 'j'  && rstate==0 )                 /* print list of highlight nets */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     print_hilight_net(1);
    -     return;
    -   }
    -   if(key == 'j'  && rstate==ControlMask)        /* create ipins from highlight nets */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     print_hilight_net(0);
    -     return;
    -   }
    -   if(key == 'j'  && EQUAL_MODMASK)   /* create labels without i prefix from hilight nets */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     print_hilight_net(4);
    -     return;
    -   }
    -   if(key == 'J'  && SET_MODMASK ) /* create labels with i prefix from hilight nets */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     print_hilight_net(2);
    -     return;
    -   }
    -   if(key == 'h'  && rstate==ControlMask )       /* go to http link */
    -   {
    -     int savesem = xctx->semaphore;
    -     xctx->semaphore = 0;
    -     launcher();
    -     xctx->semaphore = savesem;
    -     return;
    -   }
    -   if(key == 'h'  && EQUAL_MODMASK)   /* create symbol pins from schematic pins 20171208 */
    -   {
    -     tcleval("schpins_to_sympins");
    -     return;
    -   }
    -   if(key == 'h' && rstate == 0) {
    -     /* horizontally constrained drag 20171023 */
    -     if ( xctx->constr_mv == 1 ) {
    -       tcleval("set constr_mv 0" );
    -       xctx->constr_mv = 0;
    -     } else {
    -       tcleval("set constr_mv 1" );
    -       xctx->constr_mv = 1;
    -     }
    -     if(xctx->ui_state & STARTWIRE) {
    -       if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    -       if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    -       new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -     }
    -     if(xctx->ui_state & STARTLINE) {
    -       if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    -       if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    -       new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -     }
    -     return;
    -   }
    -   if(key=='H' && rstate == 0) {           /* attach labels to selected instances */
    -     attach_labels_to_inst(1);
    -     return;
    -   }
    -   if (key == 'H' && rstate == ControlMask) { /* create schematic and symbol from selected components */
    -     make_schematic_symbol_from_sel();
    -     return;
    -   }
    -   if(key == 'v' && rstate==0) {
    -     /* vertically constrained drag 20171023 */
    -     if ( xctx->constr_mv == 2 ) {
    -       tcleval("set constr_mv 0" );
    -       xctx->constr_mv = 0;
    -     } else {
    -       tcleval("set constr_mv 2" );
    -       xctx->constr_mv = 2;
    -     }
    -     if(xctx->ui_state & STARTWIRE) {
    -       if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    -       if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    -       new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -     }
    -     if(xctx->ui_state & STARTLINE) {
    -       if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    -       if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    -       new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    -     }
    -     return;
    -   }
    -   if(key == 'j'  && SET_MODMASK && (state & ControlMask) )  /* print list of highlight net with label expansion */
    -   {
    -     print_hilight_net(3);
    -     return;
    -   }
    -   if(key == 'J' && rstate == 0) {
    -    create_plot_cmd();
    -    return;
    -   }
    -   if(key == '$' &&  rstate == 0 )            /* toggle pixmap  saving */
    -   {
    -    xctx->draw_pixmap =!xctx->draw_pixmap;
    -    if(xctx->draw_pixmap) tcleval("alert_ { enabling draw pixmap} {}");
    -    else tcleval("alert_ { disabling draw pixmap} {}");
    -    return;
    -   }
    -   if(key == '$' && (state &ControlMask) )              /* toggle window  drawing */
    -   {
    -    xctx->draw_window =!xctx->draw_window;
    -    if(xctx->draw_window) {
    -      tcleval("alert_ { enabling draw window} {}");
    -      tclsetvar("draw_window","1");
    -    } else {
    -      tcleval("alert_ { disabling draw window} {}");
    -      tclsetvar("draw_window","0");
    -    }
    -    return;
    -   }
    -   if(key == '='  && (state &ControlMask))              /* toggle fill rectangles */
    -   {
    -    int x;
    -    xctx->fill_pattern++;
    -    if(xctx->fill_pattern==2) xctx->fill_pattern=0;
    -
    -    if(xctx->fill_pattern==1) {
    -     tcleval("alert_ { Stippled pattern fill} {}");
    -     for(x=0;xfill_type[x]==2) XSetFillStyle(display,xctx->gcstipple[x],FillSolid);
    -       else XSetFillStyle(display,xctx->gcstipple[x],FillStippled);
    -     }
    -    }
    -    else if(xctx->fill_pattern==2) {
    -     tcleval("alert_ { solid pattern fill} {}");
    -     for(x=0;xgcstipple[x],FillSolid);
    -    }
    -    else  {
    -     tcleval("alert_ { No pattern fill} {}");
    -     for(x=0;xgcstipple[x],FillStippled);
    -    }
    -
    -    draw();
    -    return;
    -   }
    -   if(key == '+'  && state & ControlMask)         /* change line width */
    -   {
    -    xctx->lw = round_to_n_digits(xctx->lw + 0.5, 2);
    -    change_linewidth(xctx->lw);
    -    tclsetboolvar("change_lw", 0);
    -    draw();
    -    return;
    -   }
    -
    -   if(key == '-'  && state & ControlMask)         /* change line width */
    -   {
    -    xctx->lw = round_to_n_digits(xctx->lw - 0.5, 2);
    -    if(xctx->lw < 0.0) xctx->lw = 0.0;
    -    change_linewidth(xctx->lw);
    -    tclsetboolvar("change_lw", 0);
    -    draw();
    -    return;
    -   }
    -   /* highlight discrepanciens between selected instance pin and net names */
    -   if(key == 'X' && rstate == 0)
    -   {
    -     hilight_net_pin_mismatches();
    -     return;
    -   }
    -   /* create wire snapping to closest instance pin */
    -   if(key== 'W' /* && !xctx->ui_state */ && rstate == 0 && !cadence_compat) {
    -     if(xctx->semaphore >= 2) return;
    -     snapped_wire(c_snap);
    -     return;
    -   }
    -   /* create wire snapping to closest instance pin (cadence keybind) */
    -   if(key== 's' /* && !xctx->ui_state */ && rstate == 0 && cadence_compat) {
    -    if(xctx->semaphore >= 2) return;
    -    snapped_wire(c_snap);
    -    return;
    -   }
    -   if(key == 'w' /* && !xctx->ui_state */ && rstate==0)    /* place wire. */
    -   {
    -     int prev_state = xctx->ui_state;
    -     if(xctx->semaphore >= 2) return;
    -
    -     if(infix_interface) {
    -       start_wire(xctx->mousex_snap, xctx->mousey_snap);
    -       if(prev_state == STARTWIRE) {
    -         tcleval("set constr_mv 0" );
    -         xctx->constr_mv=0;
    -       }
    -     } else {
    -       xctx->last_command = 0;
    -       xctx->ui_state |= MENUSTART;
    -       xctx->ui_state2 = MENUSTARTWIRE;
    -       if(prev_state & STARTWIRE) start_wire(xctx->mousex_snap, xctx->mousey_snap);
    -     }
    -     return;
    -   }
    -   if(key == XK_Return && (state == 0 ) && xctx->ui_state & STARTPOLYGON) { /* close polygon */
    -    new_polygon(ADD|END, xctx->mousex_snap, xctx->mousey_snap);
    -    return;
    -   }
    -   if(key == XK_Escape) /* abort & redraw */
    -   {
    -    if(xctx->semaphore < 2) {
    -      abort_operation();
    -    }
    -    /* stuff that can be done reentrantly ... */
    -    tclsetvar("tclstop", "1"); /* stop simulation if any running */
    -    if(xctx->ui_state2 & MENUSTARTWIRE) {
    -      xctx->ui_state2 &= ~MENUSTARTWIRE;
    -    }
    -    if(snap_cursor) draw_snap_cursor(1); /* erase */
    -    if(tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE) && cadence_compat) {
    -      xctx->last_command &= ~STARTWIRE;
    -    }
    -    return;
    -   }
    -   if(key=='z' && rstate == 0 &&
    -     !(xctx->ui_state & (STARTRECT | STARTLINE | STARTWIRE | STARTPOLYGON | STARTARC))) /* zoom box */
    -   {
    -    dbg(1, "callback(): zoom_rectangle call\n");
    -    zoom_rectangle(START);return;
    -   }
    -   if(key=='Z' && rstate == 0)                   /* zoom in */
    -   {
    -    view_zoom(0.0); return;
    -   }
    -   if(key=='z' && EQUAL_MODMASK && cadence_compat) /* toggle snap-cursor option */
    -   {
    -     if(tclgetboolvar("snap_cursor")) {
    -       tclsetvar("snap_cursor", "0");
    -       draw_snap_cursor(1);
    -       xctx->closest_pin_found = 0;
    -       xctx->prev_snapx = 0.0;
    -       xctx->prev_snapy = 0.0;
    -     } else {
    -       tclsetvar("snap_cursor", "1");
    -       if(wire_draw_active) draw_snap_cursor(3);
    -     }
    -   }
    -   if(key=='p' && EQUAL_MODMASK)                           /* add symbol pin */
    -   {
    -    xctx->push_undo();
    -    unselect_all(1);
    -    storeobject(-1, xctx->mousex_snap-2.5, xctx->mousey_snap-2.5, xctx->mousex_snap+2.5, xctx->mousey_snap+2.5,
    -                xRECT, PINLAYER, SELECTED, "name=XXX\ndir=inout");
    -    xctx->need_reb_sel_arr=1;
    -    rebuild_selected_array();
    -    move_objects(START,0,0,0);
    -    xctx->ui_state |= START_SYMPIN;
    -    return;
    -   }
    -   if(key=='p' && !(xctx->ui_state & STARTPOLYGON) && rstate==0)              /* start polygon */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     dbg(1, "callback(): start polygon\n");
    -     if(infix_interface) {
    -       xctx->mx_double_save=xctx->mousex_snap;
    -       xctx->my_double_save=xctx->mousey_snap;
    -       xctx->last_command = 0;
    -       new_polygon(PLACE, xctx->mousex_snap, xctx->mousey_snap);
    -     } else {
    -       xctx->ui_state |= MENUSTART;
    -       xctx->ui_state2 = MENUSTARTPOLYGON;
    -     }
    -     return;
    -   }
    -   if(key=='P' && rstate == 0)                   /* pan, other way to. */
    -   {
    -    xctx->xorigin=-xctx->mousex_snap+xctx->areaw*xctx->zoom/2.0;
    -    xctx->yorigin=-xctx->mousey_snap+xctx->areah*xctx->zoom/2.0;
    -    draw();
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key=='5' && rstate == 0) { /* 20110112 display only probes */
    -    xctx->only_probes = !xctx->only_probes;
    -    tclsetboolvar("only_probes", xctx->only_probes);
    -    toggle_only_probes();
    -    return;
    -   }  /* /20110112 */
    -   if(key<='9' && key >='0' && state==ControlMask)              /* choose layer */
    -   {
    -    char n[30];
    -    xctx->rectcolor = (int)key - '0'+4;
    -    my_snprintf(n, S(n), "%d", xctx->rectcolor);
    -    tclvareval("xschem set rectcolor ", n, NULL);
    -
    -    if(has_x) {
    -      if(!strcmp(win_path, ".drw")) {
    -        tclvareval("reconfigure_layers_button {}", NULL);
    -      } else {
    -        tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL);
    +  char str[PATH_MAX + 100];
    +  switch (key) {
    +    case '0' ... '4': 
    +      if(state == 0) {                                  /* toggle pin logic level */
    +        if(xctx->semaphore >= 2) break;
    +        if(key == '4') logic_set(-1, 1, NULL);
    +        else logic_set((int)key - '0', 1, NULL);
           }
    -    }
    -    dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]);
    -    return;
    -   }
    -   if(key==XK_Delete && (xctx->ui_state & SELECTION) ) /* delete selection */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     delete(1/* to_push_undo */);return;
    -   }
    -   if(key==XK_Right && state == ControlMask) {
    -     int save = xctx->semaphore;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     if(xctx->semaphore >= 2) return;
    -     xctx->semaphore = 0;
    -     tcleval("next_tab");
    -     xctx->semaphore = save;
    -     return;
    -   }
    -   if(key==XK_Left && state == ControlMask) {
    -     int save = xctx->semaphore;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     if(xctx->semaphore >= 2) return;
    -     xctx->semaphore = 0;
    -     tcleval("prev_tab");
    -     xctx->semaphore = save;
    -     return;
    -   }
    -   if(key==XK_Right && !(state & ControlMask))   /* left */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    xctx->xorigin+=-CADMOVESTEP*xctx->zoom;
    -    draw();
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key==XK_Left && !(state & ControlMask))   /* right */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    xctx->xorigin-=-CADMOVESTEP*xctx->zoom;
    -    draw();
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key==XK_Down)                     /* down */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    xctx->yorigin+=-CADMOVESTEP*xctx->zoom;
    -    draw();
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key==XK_Up)                       /* up */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    xctx->yorigin-=-CADMOVESTEP*xctx->zoom;
    -    draw();
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key=='w' && rstate == ControlMask) /* close current schematic */
    -   {
    -     int save_sem;
    -     if(xctx->semaphore >= 2) return;
    -     save_sem = xctx->semaphore;
    -     tcleval("xschem exit");
    -     xctx->semaphore = save_sem;
    -     return;
    -   }
    -   /* toggle spice_ignore, verilog_ignore, ... flag on selected instances. */
    -   if(key == 'T' && rstate == 0) {
    -     toggle_ignore();
    -   }
    -   if(key=='t' && rstate == 0)                        /* place text */
    -   {
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     if(xctx->semaphore >= 2) return;
    -     xctx->last_command = 0;
    -     xctx->mx_double_save = xctx->mousex_snap;
    -     xctx->my_double_save = xctx->mousey_snap;
    -     if(place_text(0, xctx->mousex_snap, xctx->mousey_snap)) { /* 1 = draw text 24122002 */
    -       xctx->mousey_snap = xctx->my_double_save;
    -       xctx->mousex_snap = xctx->mx_double_save;
    -       move_objects(START,0,0,0);
    -       xctx->ui_state |= PLACE_TEXT;
    -     }
    -     return;
    -   }
    -   if(key=='t' && (rstate & ControlMask)) 
    -   {
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return; 
    -     }
    -     return;
    -   }  
    -   if(key=='r' /* && !xctx->ui_state */ && rstate==0)              /* start rect */
    -   {
    -    dbg(1, "callback(): start rect\n");
    -    if(xctx->semaphore >= 2) return;
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      xctx->last_command = 0;
    -      new_rect(PLACE,xctx->mousex_snap, xctx->mousey_snap);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTRECT;
    -    }
    -    return;
    -   }
    -   if(key=='V' && rstate == ControlMask)                     /* toggle spice/vhdl netlist  */
    -   {
    -    xctx->netlist_type++; if(xctx->netlist_type==6) xctx->netlist_type=1;
    -    set_tcl_netlist_type();
    -    draw(); /* needed to ungrey or grey out  components due to *_ignore attribute */
    -    return;
    -   }
    -   if((key=='s' && rstate == 0) && !cadence_compat)      /* simulate (original keybind) */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    -             "-message {Run circuit simulation?}");
    -     if(strcmp(tclresult(),"ok")==0) {
    -       tcleval("[xschem get top_path].menubar invoke Simulate");
    -     }
    -     return;
    -   }
    -   if((key=='r' && rstate == ControlMask) && cadence_compat)      /* simulate (for cadence users) */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    -             "-message {Run circuit simulation?}");
    -     if(strcmp(tclresult(),"ok")==0) {
    -       tcleval("[xschem get top_path].menubar invoke Simulate");
    -     }
    -     return;
    -   }
    -   if(key=='s' && rstate == ControlMask )      /* save 20121201 */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }     
    -     /* check if unnamed schematic, use saveas in this case */
    -     if(!strcmp(xctx->sch[xctx->currsch],"") || strstr(xctx->sch[xctx->currsch], "untitled")) {
    -       saveas(NULL, SCHEMATIC);
    -     } else {
    -       save(1, 0);
    -     }
    -     return;
    -   }
    -   if(key=='s' && SET_MODMASK && (state & ControlMask) )           /* save as symbol */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     saveas(NULL, SYMBOL);
    -     return;
    -   }
    -   if(key=='S' && rstate == ControlMask) /* save as schematic */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     saveas(NULL, SCHEMATIC);
    -     return;
    -   }
    -   if(key=='e' && rstate == 0)           /* descend to schematic */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    descend_schematic(0, 1, 1, 1);return;
    -   }
    -   if(key=='e' && EQUAL_MODMASK)            /* edit schematic in new window */
    -   {
    -    int save = xctx->semaphore;
    -    xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    -    /*  schematic_in_new_window(0, 1, 0); */
    -    tcleval("open_sub_schematic");
    -    xctx->semaphore = save;
    -    return;
    -   }
    +      else if(state==ControlMask) {                     /* choose layer */
    +        char n[30];
    +        xctx->rectcolor = (int)key - '0'+4;
    +        my_snprintf(n, S(n), "%d", xctx->rectcolor);
    +        tclvareval("xschem set rectcolor ", n, NULL);
     
    -   if(key=='E' && EQUAL_MODMASK)          /* edit schematic in new window - new xschem process */
    -   { 
    -    int save = xctx->semaphore;
    -    xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    -    schematic_in_new_window(1, 1, 0);
    -    xctx->semaphore = save;
    -    return;
    -   }
    -
    -   if(key=='i' && EQUAL_MODMASK)            /* edit symbol in new window */
    -   {
    -    int save =  xctx->semaphore;
    -    xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    -    symbol_in_new_window(0);
    -    xctx->semaphore = save;
    -    return;
    -   }
    -
    -   if(key=='I' && EQUAL_MODMASK)            /* edit symbol in new window - new xschem process */
    -   {
    -    int save =  xctx->semaphore;
    -    xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    -    symbol_in_new_window(1);
    -    xctx->semaphore = save;
    -    return;
    -   }
    -
    -
    -   if( (key=='e' && rstate == ControlMask) || (key==XK_BackSpace))  /* back */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    go_back(1);return;
    -   }
    -
    -   if(key=='a' && rstate == 0)   /* make symbol */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    -            "-message {do you want to make symbol view ?}");
    -    if(strcmp(tclresult(),"ok")==0)
    -    {
    -     save_schematic(xctx->sch[xctx->currsch], 0);
    -     make_symbol();
    -    }
    -    return;
    -   }
    -   if(key=='a' && rstate == ControlMask)         /* select all */
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }     
    -    select_all();
    -    return;
    -   }
    -   if(key=='y' && rstate == 0)                           /* toggle stretching */
    -   {
    -    enable_stretch = !enable_stretch;
    -    tclsetboolvar("enable_stretch", enable_stretch);
    -    return;
    -   }
    -   if(key=='x' && EQUAL_MODMASK) /* toggle draw crosshair at mouse pos */
    -   {
    -     if(tclgetboolvar("draw_crosshair")) {
    -       tclsetvar("draw_crosshair", "0");
    -     } else {
    -       tclsetvar("draw_crosshair", "1");
    -     }
    -     draw();
    -   }
    -   if(key=='x' && rstate == ControlMask) /* cut selection into clipboard */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    rebuild_selected_array();
    -    if(xctx->lastsel) {  /* 20071203 check if something selected */
    -      save_selection(2);
    -      delete(1/* to_push_undo */);
    -    }
    -    return;
    -   }
    -   if(key=='c' && rstate == ControlMask) /* copy selection into clipboard */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     rebuild_selected_array();
    -     if(xctx->lastsel) {  /* 20071203 check if something selected */
    -       save_selection(2);
    -     }
    -    return;
    -   }
    -   if(key=='C' /* && !xctx->ui_state */ && rstate == 0) /* place arc */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     if(infix_interface) {
    -       xctx->mx_double_save=xctx->mousex_snap;
    -       xctx->my_double_save=xctx->mousey_snap;
    -       xctx->last_command = 0;
    -       new_arc(PLACE, 180., xctx->mousex_snap, xctx->mousey_snap);
    -     } else {
    -       xctx->ui_state |= MENUSTART;
    -       xctx->ui_state2 = MENUSTARTARC;
    -     }
    -     return;
    -   }
    -   if(key=='C' /* && !xctx->ui_state */ && rstate == ControlMask) /* place circle */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     if(infix_interface) {
    -       xctx->mx_double_save=xctx->mousex_snap;
    -       xctx->my_double_save=xctx->mousey_snap;
    -       xctx->last_command = 0;
    -       new_arc(PLACE, 360., xctx->mousex_snap, xctx->mousey_snap);
    -     } else {
    -       xctx->ui_state |= MENUSTART;
    -       xctx->ui_state2 = MENUSTARTCIRCLE;
    -     }
    -     return;
    -   }
    -   if(key=='O' && rstate == ControlMask )   /* load most recent tile */
    -   {
    -     tclvareval("xschem load -gui [lindex $recentfile 0]", NULL);
    -     return;
    -   }
    -   if(key=='O' && rstate == 0)   /* toggle light/dark colorscheme 20171113 */
    -   {
    -     int d_c;
    -     d_c = tclgetboolvar("dark_colorscheme");
    -     d_c = !d_c;
    -     tclsetboolvar("dark_colorscheme", d_c);
    -     tclsetdoublevar("dim_value", 0.0);
    -     tclsetdoublevar("dim_bg", 0.0);
    -     build_colors(0.0, 0.0);
    -     draw();
    -     return;
    -   }
    -   if(key=='v' && rstate == ControlMask)   /* paste from clipboard */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    merge_file(2,".sch");
    -    return;
    -   }
    -   if(key=='Q' && rstate == ControlMask ) /* view attributes */
    -   {
    -    edit_property(2);return;
    -   }
    -   if(key=='q' && rstate==ControlMask) /* quit xschem */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    /* must be set to zero, otherwise switch_tab/switch_win does not proceed
    -     * and these are necessary when closing tabs/windows */
    -    xctx->semaphore = 0;
    -    tcleval("quit_xschem");
    -    return;
    -   }
    -   if(key=='q' && rstate==0) /* edit attributes */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    edit_property(0);
    -    return;
    -   }
    -   if(key=='q' && EQUAL_MODMASK)                      /* edit .sch file (DANGER!!) */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    rebuild_selected_array();
    -    if(xctx->lastsel==0 ) {
    -      my_snprintf(str, S(str), "edit_file {%s}", abs_sym_path(xctx->sch[xctx->currsch], ""));
    -      tcleval(str);
    -    }
    -    else if(xctx->sel_array[0].type==ELEMENT) {
    -      my_snprintf(str, S(str), "edit_file {%s}",
    -         abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""));
    -      tcleval(str);
    -
    -    }
    -    return;
    -   }
    -   #if defined(__unix__) && HAS_CAIRO==1
    -   if(key == XK_Print) {
    -     xctx->ui_state |= GRABSCREEN;
    -     tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL);
    -     tclvareval("grab set -global ", xctx->top_path, ".drw", NULL);
    -     return;
    -   }
    -   #endif
    -   if(key=='Q' && rstate == 0) /* edit attributes in editor */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    edit_property(1);return;
    -   }
    -   if(key=='i' && rstate==0) /* descend to  symbol */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    descend_symbol();return;
    -   }
    -   if((key==XK_Insert && state == ShiftMask) ||  (key == 'i' && rstate == ControlMask)) /* insert sym */
    -   {
    -     tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2");
    -     return;
    -   }
    -   if((key==XK_Insert) || (key == 'I' && rstate == 0) ) /* insert sym */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    start_place_symbol();
    -
    -    return;
    -   }
    -   if(key=='s' && SET_MODMASK)                     /* reload */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -     tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    -             "-message {Are you sure you want to reload from disk?}");
    -     if(strcmp(tclresult(),"ok")==0) {
    -        char filename[PATH_MAX];
    -        unselect_all(1);
    -        remove_symbols();
    -        my_strncpy(filename, abs_sym_path(xctx->sch[xctx->currsch], ""), S(filename));
    -        load_schematic(1, filename, 1, 1);
    -        draw();
    -     }
    -     return;
    -   }
    -   if(key=='o' && rstate == ControlMask)   /* load */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    ask_new_file();
    -    xctx->semaphore--;
    -    tcleval("load_additional_files");
    -    xctx->semaphore++;
    -    return;
    -   }
    -   if(key=='S' && rstate == 0)   /* change element order */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    change_elem_order(-1);
    -    return;
    -   }
    -   if(key=='k' && EQUAL_MODMASK)        /* select whole net (all attached wires/labels/pins) */
    -   {
    -     select_hilight_net();
    -     return;
    -   }
    -   if(key=='k' && rstate==ControlMask)                           /* unhilight net */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    unhilight_net();
    -    return;
    -   }
    -   if(key=='K' && rstate == ControlMask)       /* hilight net drilling thru elements  */
    -                                                        /* with 'propag=' prop set on pins */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->enable_drill=1;
    -    hilight_net(0);
    -    redraw_hilights(0);
    -    /* draw_hilight_net(1); */
    -    return;
    -   }
    -   if(key=='k' && rstate==0)                             /* hilight net */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->enable_drill=0;
    -    hilight_net(0);
    -    redraw_hilights(0);
    -    /* draw_hilight_net(1); */
    -    return;
    -   }
    -   if(key=='K' && rstate == 0)                           /* delete hilighted nets */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->enable_drill=0;
    -    clear_all_hilights();
    -    /* undraw_hilight_net(1); */
    -    draw();
    -    return;
    -   }
    -   if(key=='g' && EQUAL_MODMASK) { /* highlight net and send to viewer */
    -     int tool = 0;
    -     int exists = 0;
    -     char *tool_name = NULL;
    -
    -     if(xctx->semaphore >= 2) return;
    -     tcleval("winfo exists .graphdialog");
    -     if(tclresult()[0] == '1') tool = XSCHEM_GRAPH;
    -     else if(xctx->graph_lastsel >=0 &&
    -         xctx->rects[GRIDLAYER] > xctx->graph_lastsel &&
    -         xctx->rect[GRIDLAYER][xctx->graph_lastsel].flags & 1) {
    -       tool = XSCHEM_GRAPH;
    -     }
    -     tcleval("info exists sim");
    -     if(tclresult()[0] == '1') exists = 1;
    -     xctx->enable_drill = 0;
    -     if(exists) {
    -       if(!tool) {
    -         tool = tclgetintvar("sim(spicewave,default)");
    -         my_snprintf(str, PATH_MAX + 100, "sim(spicewave,%d,name)", tool);
    -         my_strdup(_ALLOC_ID_, &tool_name, tclgetvar(str));
    -         dbg(1,"callback(): tool_name=%s\n", tool_name);
    -         if(strstr(tool_name, "Gaw")) tool=GAW;
    -         else if(strstr(tool_name, "Bespice")) tool=BESPICE;
    -         my_free(_ALLOC_ID_, &tool_name);
    -       }
    -     }
    -     if(tool) {
    -       hilight_net(tool);
    -       redraw_hilights(0);
    -     }
    -     Tcl_ResetResult(interp);
    -     return;
    -   }
    -   if(key=='g' && rstate==0)                         /* half snap factor */
    -   {
    -    set_snap(c_snap / 2.0);
    -    change_linewidth(-1.);
    -    draw();
    -    return;
    -   }
    -   if(key=='g' && rstate==ControlMask)              /* set snap factor 20161212 */
    -   {
    -    my_snprintf(str,  S(str),
    -     "input_line {Enter snap value (default: %.16g current: %.16g)}  {xschem set cadsnap} {%g} 10",
    -     CADSNAP, c_snap, c_snap);
    -    tcleval(str);
    -    return;
    -   }
    -   if(key=='G' && rstate == 0)                                    /* double snap factor */
    -   {
    -    set_snap(c_snap * 2.0);
    -    change_linewidth(-1.);
    -    draw();
    -    return;
    -   }
    -   if(key=='*' && EQUAL_MODMASK)         /* svg print , 20121108 */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    svg_draw();
    -    return;
    -   }
    -   if(key=='*' && rstate == 0 )                    /* postscript print */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    ps_draw(7, 0, 0);
    -    return;
    -   }
    -   if(key=='*' && rstate == ControlMask)      /* xpm print */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    print_image();
    -    return;
    -   }
    -   if(key=='u' && EQUAL_MODMASK)                      /* align to grid */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->push_undo();
    -    round_schematic_to_grid(c_snap);
    -    set_modify(1);
    -    if(tclgetboolvar("autotrim_wires")) trim_wires();
    -    xctx->prep_hash_inst=0;
    -    xctx->prep_hash_wires=0;
    -    xctx->prep_net_structs=0;
    -    xctx->prep_hi_structs=0;
    -
    -    draw();
    -    return;
    -   }
    -   if(key=='u' && rstate==ControlMask) /* unselect attached floater elements */
    -   {
    -     unselect_attached_floaters();
    -   }
    -   if(0 && (key=='|') && rstate==ControlMask)                   /* testmode */
    -   {
    -    static int x = 0;
    -
    -    if(x == 0) {
    -      int i;
    -      XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
    -            xctx->areaw, xctx->areah);
    -      XFlush(display);
    -      sleep_ms(400);
    -      for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) {
    -        XDrawLine(display, xctx->window,  xctx->gctiled,
    -           i, xctx->xrect[0].y, i, xctx->xrect[0].height);
    -        XFlush(display);
    -        sleep_ms(4);
    +        if(has_x) {
    +          if(!strcmp(win_path, ".drw")) {
    +            tclvareval("reconfigure_layers_button {}", NULL);
    +          } else {
    +            tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL);
    +          }
    +        }
    +        dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]);
           }
    -    } else if(x == 1) {
    -      int i;
    -      XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
    -            xctx->areaw, xctx->areah);
    -      XFlush(display);
    -      sleep_ms(400);
    -      for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) {
    -        XDrawLine(display, xctx->window,  xctx->gctiled,
    -           i, xctx->xrect[0].y, i+1, xctx->xrect[0].height);
    -        XFlush(display);
    -        sleep_ms(4);
    -      }
    -    }
    -    x++;
    -    x %= 2;
    -    return;
    -   }
    -   if(key=='u' && rstate==0)                             /* undo */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->pop_undo(0, 1);  /* 2nd parameter: set_modify_status */
    -    draw();
    -    return;
    -   }
    -   if(key=='U' && rstate == 0)                     /* redo */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->pop_undo(1, 1); /* 2nd parameter: set_modify_status */
    -    draw();
    -    return;
    -   }
    -   if(key=='&')                         /* check wire connectivity */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->push_undo();
    -    trim_wires();
    -    draw();
    -    return;
    -   }
    -   if(key=='l' && rstate == ControlMask) { /* create schematic from selected symbol 20171004 */
    -     if(xctx->semaphore >= 2) return;
    -     create_sch_from_sym();
    -     return;
    -   }
    -   if(key=='l' /* && !xctx->ui_state */ && rstate == 0) /* start line */
    -   {
    -     int prev_state = xctx->ui_state;
    -     if(xctx->semaphore >= 2) return;
    -     if(infix_interface) {
    -       start_line(xctx->mousex_snap, xctx->mousey_snap);
    -       if(prev_state == STARTLINE) {
    -         tcleval("set constr_mv 0" );
    -         xctx->constr_mv=0;
    -       }
    -     } else {
    -       xctx->last_command = 0;
    -       xctx->ui_state |= MENUSTART;
    -       xctx->ui_state2 = MENUSTARTLINE;
    -     }
    -     return;
    -   }
    -   if(key=='l' && EQUAL_MODMASK) {                         /* add pin label*/
    -     place_net_label(1);
    -     return;
    -   }
    -   if(key >= '0' && key <= '4' && state == 0) {  /* toggle pin logic level */
    -     if(xctx->semaphore >= 2) return;
    -     if(key == '4') logic_set(-1, 1, NULL);
    -     else logic_set((int)key - '0', 1, NULL);
    -     return;
    -   }
    -   if(key=='L' && EQUAL_MODMASK ) {                         /* add pin label*/
    -    place_net_label(0);
    -    return;
    -   }
    -   if(key=='L' && rstate == 0) {                         /* toggle orthogonal routing */
    -    if(tclgetboolvar("orthogonal_wiring")){
    -      tclsetboolvar("orthogonal_wiring", 0);
    -      xctx->manhattan_lines = 0;
    -    } else {
    -      tclsetboolvar("orthogonal_wiring", 1);
    -    }
    -    redraw_w_a_l_r_p_z_rubbers(1);
    -    return;
    -   }
    -   if(key=='F' && rstate == 0)                     /* flip */
    -   {
    -    if(xctx->ui_state & STARTMOVE) move_objects(FLIP,0,0,0);
    -    else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP);
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -      move_objects(FLIP,0,0,0);
    -      move_objects(END,0,0,0);
    -    }
    -    return;
    -   }
    -   if(key=='V' && rstate == 0)                     /* vertical flip */
    -   {
    -    if(xctx->ui_state & STARTMOVE) {
    -      move_objects(ROTATE,0,0,0);
    -      move_objects(ROTATE,0,0,0);
    -      move_objects(FLIP,0,0,0);
    -    }
    -    else if(xctx->ui_state & STARTCOPY) {
    -      copy_objects(ROTATE);
    -      copy_objects(ROTATE);
    -      copy_objects(FLIP);
    -    }
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;    
    -      move_objects(START,0,0,0);
    -      move_objects(ROTATE,0,0,0);
    -      move_objects(ROTATE,0,0,0);
    -      move_objects(FLIP,0,0,0);
    -      move_objects(END,0,0,0);
    -    } 
    -    return;
    -   }
    -   if(key=='v' && EQUAL_MODMASK)  /* vertical flip objects around their anchor points */
    -   {
    -    if(xctx->ui_state & STARTMOVE) {
    -      move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -      move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -      move_objects(FLIP|ROTATELOCAL,0,0,0);
    -    }
    -    else if(xctx->ui_state & STARTCOPY) {
    -      copy_objects(ROTATE|ROTATELOCAL);
    -      copy_objects(ROTATE|ROTATELOCAL);
    -      copy_objects(FLIP|ROTATELOCAL);
    -    }
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -      move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -      move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -      move_objects(FLIP|ROTATELOCAL,0,0,0);
    -      move_objects(END,0,0,0);
    -    }
    -    return;
    -   }
    +      break;
     
    -   if(key=='\\' && state==0)          /* fullscreen */
    -   {
    +    case '5':
    +      if(rstate == 0) {                               /* 20110112 display only probes */
    +        xctx->only_probes = !xctx->only_probes;
    +        tclsetboolvar("only_probes", xctx->only_probes);
    +        toggle_only_probes();
    +      }  /* /20110112 */
    +      break;
    +
    +    case '6' ... '9': 
    +      if(state==ControlMask) {                        /* choose layer */
    +        char n[30];
    +        xctx->rectcolor = (int)key - '0'+4;
    +        my_snprintf(n, S(n), "%d", xctx->rectcolor);
    +        tclvareval("xschem set rectcolor ", n, NULL);
    +
    +        if(has_x) {
    +          if(!strcmp(win_path, ".drw")) {
    +            tclvareval("reconfigure_layers_button {}", NULL);
    +          } else {
    +            tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL);
    +          }
    +        }
    +        dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]);
    +      }
    +      break;
         
    -    dbg(1, "callback(): toggle fullscreen, win_path=%s\n", win_path);
    -    toggle_fullscreen(win_path);
    -    return;
    -   }
    -   if(key=='f' && EQUAL_MODMASK)              /* flip objects around their anchor points 20171208 */
    -   {
    -    if(xctx->ui_state & STARTMOVE) move_objects(FLIP|ROTATELOCAL,0,0,0);
    -    else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP|ROTATELOCAL);
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -      move_objects(FLIP|ROTATELOCAL,0,0,0);
    -      move_objects(END,0,0,0);
    -    }
    -    return;
    -   }
    -   if(key=='R' && rstate == 0)             /* rotate */
    -   {
    -    if(xctx->ui_state & STARTMOVE) move_objects(ROTATE,0,0,0);
    -    else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE);
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -      move_objects(ROTATE,0,0,0);
    -      move_objects(END,0,0,0);
    -    }
    -
    -    return;
    -   }
    -   if(key=='r' && EQUAL_MODMASK)              /* rotate objects around their anchor points 20171208 */
    -   {
    -    if(xctx->ui_state & STARTMOVE) move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -    else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE|ROTATELOCAL);
    -    else {
    -      rebuild_selected_array();
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -      move_objects(ROTATE|ROTATELOCAL,0,0,0);
    -      move_objects(END,0,0,0);
    -    }
    -    return;
    -   }
    -   /* Move selection */
    -   if(key=='m' && rstate==0 && !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    if(enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTMOVE;
    -    }
    -    return;
    -   }
    -   /* Move selection adding wires to moved pins */
    -   if(((key == 'M' && rstate == 0) || (key == 'm' && EQUAL_MODMASK)) &&
    -     !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {
    -    xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    -    if(infix_interface) {
    -      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 */
    -      move_objects(START,0,0,0);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTMOVE;
    -    }
    -    return;
    -   }
    -   /* move selection stretching attached nets */
    -   if(key=='m' && rstate == ControlMask &&
    -       !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {        
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }     
    -    if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTMOVE;
    -    }
    -    return;
    -   }
    -   
    -   /* move selection, stretch attached nets, create new wires on pin-to-moved-pin connections */
    -   if(key=='M' && rstate == ControlMask &&  
    -       !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {
    -    if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    -    xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      move_objects(START,0,0,0);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTMOVE;
    -    }
    -    return;
    -   }
    -
    -   if(key=='c' && EQUAL_MODMASK &&           /* duplicate selection */
    -     !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      copy_objects(START);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTCOPY;
    -    }
    -    return;
    -   }
    -
    -   if(key=='c' && rstate==0 &&           /* duplicate selection */
    -     !(xctx->ui_state & (STARTMOVE | STARTCOPY)))
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    if(infix_interface) {
    -      xctx->mx_double_save=xctx->mousex_snap;
    -      xctx->my_double_save=xctx->mousey_snap;
    -      copy_objects(START);
    -    } else {
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTCOPY;
    -    }
    -    return;
    -   }
    -   if(key=='n' && rstate == ControlMask)              /* clear schematic */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     tcleval("xschem clear SCHEMATIC");
    -     return;
    -   }
    -   if(key=='N' && rstate == ControlMask )    /* clear symbol */
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     tcleval("xschem clear SYMBOL");
    -     return;
    -   }
    -   if(key=='n' && rstate==0)              /* hierarchical netlist */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    tcleval("xschem netlist -erc");
    -    return;
    -   }
    -   if(key=='N' && rstate == 0)              /* current level only netlist */
    -   {
    -    int err = 0;
    -    yyparse_error = 0;
    -    if(xctx->semaphore >= 2) return;
    -    unselect_all(1);
    -    if( set_netlist_dir(0, NULL) ) {
    -      dbg(1, "callback(): -------------\n");
    -      if(xctx->netlist_type == CAD_SPICE_NETLIST)
    -        err = global_spice_netlist(0, 1);
    -      else if(xctx->netlist_type == CAD_VHDL_NETLIST)
    -        err = global_vhdl_netlist(0, 1);
    -      else if(xctx->netlist_type == CAD_VERILOG_NETLIST)
    -        err = global_verilog_netlist(0, 1);
    -      else if(xctx->netlist_type == CAD_TEDAX_NETLIST)
    -        err = global_tedax_netlist(0, 1);
    -      else
    -        tcleval("tk_messageBox -type ok -parent [xschem get topwindow] "
    -                "-message {Please Set netlisting mode (Options menu)}");
    -      dbg(1, "callback(): -------------\n");
    -    }
    -    else {
    -       if(has_x) tcleval("alert_ {Can not write into the netlist directory. Please check} {}");
    -       else dbg(0, "Can not write into the netlist directory. Please check");
    -       err = 1;
    -    }
    -    if(err) {
    -      if(has_x) {
    -        tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background red", NULL);
    -        tclvareval("set tctx::", xctx->current_win_path, "_netlist red", NULL);
    +    case 'a':
    +      if(rstate == 0) {                               /* make symbol */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    +                "-message {do you want to make symbol view ?}");
    +        if(strcmp(tclresult(),"ok")==0)
    +        {
    +         save_schematic(xctx->sch[xctx->currsch], 0);
    +         make_symbol();
    +        }
           }
    -    } else {
    -      if(has_x) {
    -        tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background Green", NULL);
    -        tclvareval("set tctx::", xctx->current_win_path, "_netlist Green", NULL);
    +      else if(rstate == ControlMask) {                    /* select all */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }     
    +        select_all();
           }
    -    }
    +      break;
     
    -    return;
    -   }
    -   if(key=='A' && rstate == ControlMask) /* only for graph (toggle hcursor1 if graph_use_ctrl_key set) */
    -   {
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     return;
    -   }
    -   if(key=='A' && rstate == 0)                             /* toggle show netlist */
    -   {
    -     int net_s;
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     net_s = tclgetboolvar("netlist_show");
    -     net_s = !net_s;
    -     if(net_s) {
    -       tcleval("alert_ { enabling show netlist window} {}");
    -       tclsetvar("netlist_show","1");
    -     }
    -     else {
    -       tcleval("alert_ { disabling show netlist window } {}");
    -       tclsetvar("netlist_show","0");
    -     }
    -     return;
    -   }
    -   if(key=='>') {
    -     if(xctx->semaphore >= 2) return;
    -     if(xctx->draw_single_layer< cadlayers-1) xctx->draw_single_layer++;
    -     draw();
    -     return;
    -   }
    -   if(key=='<') {
    -     if(xctx->semaphore >= 2) return;
    -     if(xctx->draw_single_layer>=0 ) xctx->draw_single_layer--;
    -     draw();
    -     return;
    -   }
    -   if(key==':')                         /* toggle flat netlist (only spice)  */
    -   {
    -    if(!tclgetboolvar("flat_netlist")) {
    -        tcleval("alert_ { enabling flat netlist} {}");
    -        tclsetvar("flat_netlist","1");
    -    }
    -    else {
    -        tcleval("alert_ { set hierarchical netlist } {}");
    -        tclsetvar("flat_netlist","0");
    -    }
    -    return;
    -   }
    -   if(key=='B' && rstate == ControlMask) /* only for graph (toggle hcursor2 if graph_use_ctrl_key set) */
    -   { 
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     } 
    -     return;
    -   }
    -   if(key=='B' && rstate == 0)    /* edit schematic header/license */
    -   {
    -     if(waves_selected(event, key, state, button)) {
    -       waves_callback(event, mx, my, key, button, aux, state);
    -       return;
    -     }
    -     tcleval("update_schematic_header");
    -   }
    -   if(key=='b' && rstate==0)                     /* merge schematic */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    merge_file(0, ""); /* 2nd parameter not used any more for merge 25122002 */
    -    return;
    -   }
    -   if(key=='b' && EQUAL_MODMASK)                     /* hide/show instance details */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    xctx->hide_symbols++;
    -    if(xctx->hide_symbols >= 3) xctx->hide_symbols = 0;
    -    tclsetintvar("hide_symbols", xctx->hide_symbols);
    -    draw();
    -    return;
    -   }
    -   if(key=='d' && rstate == 0) /* unselect object under the mouse */
    -   {
    -     if(infix_interface) {
    -       unselect_at_mouse_pos(mx, my);
    -     } else {
    -       xctx->ui_state |= (MENUSTART | DESEL_CLICK);
    -       xctx->ui_state2 = MENUSTARTDESEL;
    -     }
    -     return;
    -   }
    -   if(key=='D' && rstate == 0)          /* unselect by area */
    -   {
    -       if( !(xctx->ui_state & STARTPAN) && !xctx->shape_point_selected &&
    -         !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT)) && !(xctx->ui_state & STARTSELECT)) {
    -         if(infix_interface) {
    -           xctx->mx_save = mx; xctx->my_save = my;
    -           xctx->mx_double_save=xctx->mousex;
    -           xctx->my_double_save=xctx->mousey;
    -           xctx->ui_state |= DESEL_AREA;
    -         } else {
    -           xctx->ui_state |= MENUSTART;
    -           xctx->ui_state2 = MENUSTARTDESEL;
    +    case 'A':
    +      if(rstate == 0) {                           /* toggle show netlist */
    +        int net_s;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        net_s = tclgetboolvar("netlist_show");
    +        net_s = !net_s;
    +        if(net_s) {
    +          tcleval("alert_ { enabling show netlist window} {}");
    +          tclsetvar("netlist_show","1");
    +        }
    +        else {
    +          tcleval("alert_ { disabling show netlist window } {}");
    +          tclsetvar("netlist_show","0");
    +        }
    +      }
    +      else if(rstate == ControlMask) {                      /* only for graph (toggle hcursor1 if graph_use_ctrl_key set) */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +      }
    +      break;
    +
    +    case 'b':
    +      if(rstate==0) {                                               /* merge schematic */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        merge_file(0, ""); /* 2nd parameter not used any more for merge 25122002 */
    +      }
    +      else if(rstate==ControlMask) {                            /* toggle show text in symbol */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }     
    +        xctx->sym_txt =!xctx->sym_txt;
    +        if(xctx->sym_txt) {
    +            /* tcleval("alert_ { enabling text in symbol} {}"); */
    +            tclsetvar("sym_txt","1");
    +            draw();
    +        }
    +        else {
    +            /* tcleval("alert_ { disabling text in symbol} {}"); */
    +            tclsetvar("sym_txt","0");
    +            draw();
    +        }
    +      }
    +      else if(EQUAL_MODMASK) {                                /* hide/show instance details */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->hide_symbols++;
    +        if(xctx->hide_symbols >= 3) xctx->hide_symbols = 0;
    +        tclsetintvar("hide_symbols", xctx->hide_symbols);
    +        draw();
    +      }
    +      break;
    +
    +    case 'B':
    +      if(rstate == 0) {                                       /* edit schematic header/license */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        tcleval("update_schematic_header");
    +      }
    +      else if(rstate == ControlMask) {                      /* only for graph (toggle hcursor2 if graph_use_ctrl_key set) */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        } 
    +      }
    +      break;
    +
    +    case 'c':
    +      /* duplicate selection */
    +      if(rstate==0 && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        if(xctx->semaphore >= 2) break;
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          copy_objects(START);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTCOPY;
    +        }
    +      }
    +      /* copy selection into clipboard */
    +      else if(rstate == ControlMask) {
    +        if(xctx->semaphore >= 2) break;
    +        rebuild_selected_array();
    +        if(xctx->lastsel) {  /* 20071203 check if something selected */
    +          save_selection(2);
    +        }
    +      }
    +      /* duplicate selection */
    +      else if(EQUAL_MODMASK && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        if(xctx->semaphore >= 2) break;
    +        xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          copy_objects(START);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTCOPY;
    +        }
    +      }
    +      break;
    +
    +    case 'C':
    +      if(/* !xctx->ui_state && */ rstate == 0) {                              /* place arc */
    +        if(xctx->semaphore >= 2) break;
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_arc(PLACE, 180., xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTARC;
    +        }
    +      }
    +      else if(/* !xctx->ui_state && */ rstate == ControlMask) {             /* place circle */
    +        if(xctx->semaphore >= 2) break;
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_arc(PLACE, 360., xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTCIRCLE;
    +        }
    +      }
    +      break;
    +
    +    case 'd':
    +      if(rstate == 0) {                                           /* unselect object under the mouse */
    +        if(infix_interface) {
    +          unselect_at_mouse_pos(mx, my);
    +        } else {
    +          xctx->ui_state |= (MENUSTART | DESEL_CLICK);
    +          xctx->ui_state2 = MENUSTARTDESEL;
    +        }
    +      }
    +      else if(rstate == ControlMask) {                                      /* delete files */
    +        if(xctx->semaphore >= 2) break;
    +        delete_files();
    +      }
    +      break;
    +
    +    case 'D':
    +      if(rstate == 0) {                                                     /* unselect by area */
    +        if( !(xctx->ui_state & STARTPAN) && !xctx->shape_point_selected &&
    +          !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT)) && !(xctx->ui_state & STARTSELECT)) {
    +          if(infix_interface) {
    +            xctx->mx_save = mx; xctx->my_save = my;
    +            xctx->mx_double_save=xctx->mousex;
    +            xctx->my_double_save=xctx->mousey;
    +            xctx->ui_state |= DESEL_AREA;
    +          } else {
    +            xctx->ui_state |= MENUSTART;
    +            xctx->ui_state2 = MENUSTARTDESEL;
    +          }
    +        }
    +      }
    +      break;
    +
    +    case 'e':
    +      if(rstate == 0) {                                                 /* descend to schematic */
    +        if(xctx->semaphore >= 2) break;
    +        descend_schematic(0, 1, 1, 1);
    +      }
    +      else if(rstate == ControlMask) {
    +        if(xctx->semaphore >= 2) break;
    +        go_back(1);
    +      }
    +      else if(EQUAL_MODMASK) {                                        /* edit schematic in new window */
    +        int save = xctx->semaphore;
    +        xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    +        /*  schematic_in_new_window(0, 1, 0); */
    +        tcleval("open_sub_schematic");
    +        xctx->semaphore = save;
    +      }
    +      break;
    +
    +    case 'E':
    +      if(EQUAL_MODMASK) {                                   /* edit schematic in new window - new xschem process */
    +        int save = xctx->semaphore;
    +        xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    +        schematic_in_new_window(1, 1, 0);
    +        xctx->semaphore = save;
    +      }
    +      break;
    +
    +    case 'f':
    +      if(rstate == 0) {                                                   /* full zoom */
    +        int flags = 1;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        if(tclgetboolvar("zoom_full_center")) flags |= 2;
    +        zoom_full(1, 0, flags, 0.97);
    +      }
    +      else if(rstate == ControlMask) {                                    /* search */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }     
    +        tcleval("property_search");
    +      }
    +      else if(EQUAL_MODMASK) {                                /* flip objects around their anchor points 20171208 */
    +        if(xctx->ui_state & STARTMOVE) move_objects(FLIP|ROTATELOCAL,0,0,0);
    +        else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP|ROTATELOCAL);
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +          move_objects(FLIP|ROTATELOCAL,0,0,0);
    +          move_objects(END,0,0,0);
    +        }
    +      }
    +      break;
    +
    +    case 'F':
    +      if(rstate == 0) {                                                   /* flip */
    +        if(xctx->ui_state & STARTMOVE) move_objects(FLIP,0,0,0);
    +        else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP);
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +          move_objects(FLIP,0,0,0);
    +          move_objects(END,0,0,0);
    +        }
    +      }
    +      else if(rstate == ControlMask ) {                               /* full zoom selection */
    +        if(xctx->ui_state == SELECTION) {
    +          zoom_full(1, 1, 3, 0.97);
    +        }
    +      }
    +      break;
    +
    +    case 'g':
    +      if(rstate==0) {                                                 /* half snap factor */
    +        set_snap(c_snap / 2.0);
    +        change_linewidth(-1.);
    +        draw();
    +      }
    +      else if(rstate==ControlMask) {                              /* set snap factor 20161212 */
    +        my_snprintf(str,  S(str), 
    +                    "input_line {Enter snap value (default: %.16g current: %.16g)}  {xschem set cadsnap} {%g} 10",
    +                    CADSNAP, c_snap, c_snap);
    +        tcleval(str);
    +      }
    +      else if(EQUAL_MODMASK) {                                /* highlight net and send to viewer */
    +        int tool = 0;
    +        int exists = 0;
    +        char *tool_name = NULL;
    +
    +        if(xctx->semaphore >= 2) break;
    +        tcleval("winfo exists .graphdialog");
    +        if(tclresult()[0] == '1') tool = XSCHEM_GRAPH;
    +        else if(xctx->graph_lastsel >=0 &&
    +            xctx->rects[GRIDLAYER] > xctx->graph_lastsel &&
    +            xctx->rect[GRIDLAYER][xctx->graph_lastsel].flags & 1) {
    +          tool = XSCHEM_GRAPH;
    +        }
    +        tcleval("info exists sim");
    +        if(tclresult()[0] == '1') exists = 1;
    +        xctx->enable_drill = 0;
    +        if(exists) {
    +          if(!tool) {
    +            tool = tclgetintvar("sim(spicewave,default)");
    +            my_snprintf(str, PATH_MAX + 100, "sim(spicewave,%d,name)", tool);
    +            my_strdup(_ALLOC_ID_, &tool_name, tclgetvar(str));
    +            dbg(1,"callback(): tool_name=%s\n", tool_name);
    +            if(strstr(tool_name, "Gaw")) tool=GAW;
    +            else if(strstr(tool_name, "Bespice")) tool=BESPICE;
    +            my_free(_ALLOC_ID_, &tool_name);
    +          }
    +        }
    +        if(tool) {
    +          hilight_net(tool);
    +          redraw_hilights(0);
    +        }
    +        Tcl_ResetResult(interp);
    +      }
    +      break;
    +      
    +    case 'G':
    +      if(rstate == 0) {                                             /* double snap factor */
    +        set_snap(c_snap * 2.0);
    +        change_linewidth(-1.);
    +        draw();
    +      }
    +      break;
    +
    +    case 'h': 
    +      if(rstate==ControlMask ) {                                    /* go to http link */
    +        int savesem = xctx->semaphore;
    +        xctx->semaphore = 0;
    +        launcher();
    +        xctx->semaphore = savesem;
    +      }
    +      else if (rstate == 0) {                             /* horizontally constrained drag 20171023 */
    +        if ( xctx->constr_mv == 1 ) {
    +          tcleval("set constr_mv 0" );
    +          xctx->constr_mv = 0;
    +        } else {
    +          tcleval("set constr_mv 1" );
    +          xctx->constr_mv = 1;
    +        }
    +        if(xctx->ui_state & STARTWIRE) {
    +          if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    +          if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    +          new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +        }
    +        if(xctx->ui_state & STARTLINE) {
    +          if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    +          if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    +          new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +        }
    +      }
    +      else if (EQUAL_MODMASK) {
    +        tcleval("schpins_to_sympins");
    +      }
    +      break;
    +
    +    case 'H': 
    +      if(rstate == 0) {                                     /* attach labels to selected instances */
    +        attach_labels_to_inst(1);
    +      }
    +      else if (rstate == ControlMask) {               /* create schematic and symbol from selected components */
    +        make_schematic_symbol_from_sel();
    +      }
    +      break;
    +
    +    case 'i':
    +      if(rstate==0) {                                             /* descend to  symbol */
    +        if(xctx->semaphore >= 2) break;
    +        descend_symbol();
    +      }
    +      else if(rstate == ControlMask) {                                                  /* insert sym */
    +        tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2");
    +      }
    +      else if(EQUAL_MODMASK) {                                  /* edit symbol in new window */
    +        int save =  xctx->semaphore;
    +        xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    +        symbol_in_new_window(0);
    +        xctx->semaphore = save;
    +      }
    +      break;
    +
    +    case 'I':
    +      if(rstate == 0) {                                             /* insert sym */
    +        if(xctx->semaphore >= 2) break;
    +        start_place_symbol();
    +      }
    +      else if(EQUAL_MODMASK) {                          /* edit symbol in new window - new xschem process */
    +        int save =  xctx->semaphore;
    +        xctx->semaphore--; /* so semaphore for current context wll be saved correctly */
    +        symbol_in_new_window(1);
    +        xctx->semaphore = save;
    +      }
    +      break;
    +
    +    case 'j':
    +      if(rstate==0 ) {                                      /* print list of highlight nets */
    +        if(xctx->semaphore >= 2) break;
    +        print_hilight_net(1);
    +      }
    +      else if(rstate==ControlMask) {                    /* create ipins from highlight nets */
    +        if(xctx->semaphore >= 2) break;
    +        print_hilight_net(0);
    +      }
    +      else if(EQUAL_MODMASK) {                      /* create labels without i prefix from hilight nets */
    +        if(xctx->semaphore >= 2) break;
    +        print_hilight_net(4);
    +      }
    +      else if( SET_MODMASK && (state & ControlMask) ) {   /* print list of highlight net with label expansion */
    +        print_hilight_net(3);
    +      }
    +      break;
    +
    +    case 'J':
    +      if(rstate == 0) {
    +        create_plot_cmd();
    +      }
    +      else if(SET_MODMASK ) {                           /* create labels with i prefix from hilight nets */
    +        if(xctx->semaphore >= 2) break;
    +        print_hilight_net(2);
    +      }
    +      break;
    +
    +    case 'k':
    +      if(rstate==0) {                                             /* hilight net */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->enable_drill=0;
    +        hilight_net(0);
    +        redraw_hilights(0);
    +        /* draw_hilight_net(1); */
    +      }
    +      else if(EQUAL_MODMASK) {                        /* select whole net (all attached wires/labels/pins) */
    +        select_hilight_net();
    +      }
    +      else if(rstate==ControlMask) {                            /* unhilight net */
    +        if(xctx->semaphore >= 2) break;
    +        unhilight_net();
    +      }
    +      break;
    +
    +    case 'K':
    +      if(rstate == 0) {                                     /* delete hilighted nets */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->enable_drill=0;
    +        clear_all_hilights();
    +        /* undraw_hilight_net(1); */
    +        draw();
    +      }
    +      else if(rstate == ControlMask) {            /* hilight net drilling thru elements with 'propag=' prop set on pins */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->enable_drill=1;
    +        hilight_net(0);
    +        redraw_hilights(0);
    +        /* draw_hilight_net(1); */
    +      }
    +      break;
    +
    +    case 'l':
    +      if(/* !xctx->ui_state && */ rstate == 0) {                /* start line */
    +        int prev_state = xctx->ui_state;
    +        if(xctx->semaphore >= 2) break;
    +        if(infix_interface) {
    +          start_line(xctx->mousex_snap, xctx->mousey_snap);
    +          if(prev_state == STARTLINE) {
    +            tcleval("set constr_mv 0" );
    +            xctx->constr_mv=0;
    +          }
    +        } else {
    +          xctx->last_command = 0;
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTLINE;
    +        }
    +      }
    +      else if(rstate == ControlMask) {                /* create schematic from selected symbol 20171004 */
    +        if(xctx->semaphore >= 2) break;
    +        create_sch_from_sym();
    +      }
    +      else if(EQUAL_MODMASK) {                                /* add pin label*/
    +        place_net_label(1);
    +      }
    +      break;
    +  
    +    case 'L':
    +      if(rstate == 0) {                                   /* toggle orthogonal routing */
    +        if(tclgetboolvar("orthogonal_wiring")){
    +          tclsetboolvar("orthogonal_wiring", 0);
    +          xctx->manhattan_lines = 0;
    +        } else {
    +          tclsetboolvar("orthogonal_wiring", 1);
    +        }
    +        redraw_w_a_l_r_p_z_rubbers(1);
    +      }
    +      else if(EQUAL_MODMASK ) {                               /* add pin label*/
    +        place_net_label(0);
    +      }
    +      break;
    +
    +    case 'm':
    +      /* Move selection */
    +      if(rstate==0 && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        if(enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTMOVE;
    +        }
    +      }
    +      /* move selection stretching attached nets */
    +      else if(rstate == ControlMask && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }     
    +        if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTMOVE;
    +        }
    +      }
    +      /* Move selection adding wires to moved pins */
    +      else if(EQUAL_MODMASK && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    +        if(infix_interface) {
    +          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 */
    +          move_objects(START,0,0,0);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTMOVE;
    +        }
    +      }
    +      break;
    +
    +    case 'M':
    +      /* Move selection adding wires to moved pins */
    +      if((rstate == 0) && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    +        if(infix_interface) {
    +          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 */
    +          move_objects(START,0,0,0);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTMOVE;
    +        }
    +      }
    +      /* move selection, stretch attached nets, create new wires on pin-to-moved-pin connections */
    +      else if(rstate == ControlMask && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) {
    +        if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */
    +        xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTMOVE;
    +        }
    +      }
    +      break;
    +
    +    case 'n':
    +      if(rstate==0) {                                   /* hierarchical netlist */
    +        if(xctx->semaphore >= 2) break;
    +        tcleval("xschem netlist -erc");
    +      }
    +      else if(rstate == ControlMask) {                  /* clear schematic */
    +        if(xctx->semaphore >= 2) break;
    +        tcleval("xschem clear SCHEMATIC");
    +      }
    +      break;
    +
    +    case 'N':
    +      if(rstate == 0) {                             /* current level only netlist */
    +        int err = 0;
    +        yyparse_error = 0;
    +        if(xctx->semaphore >= 2) break;
    +        unselect_all(1);
    +        if( set_netlist_dir(0, NULL) ) {
    +          dbg(1, "callback(): -------------\n");
    +          if(xctx->netlist_type == CAD_SPICE_NETLIST)
    +            err = global_spice_netlist(0, 1);
    +          else if(xctx->netlist_type == CAD_VHDL_NETLIST)
    +            err = global_vhdl_netlist(0, 1);
    +          else if(xctx->netlist_type == CAD_VERILOG_NETLIST)
    +            err = global_verilog_netlist(0, 1);
    +          else if(xctx->netlist_type == CAD_TEDAX_NETLIST)
    +            err = global_tedax_netlist(0, 1);
    +          else
    +            tcleval("tk_messageBox -type ok -parent [xschem get topwindow] "
    +                    "-message {Please Set netlisting mode (Options menu)}");
    +          dbg(1, "callback(): -------------\n");
    +        }
    +        else {
    +           if(has_x) tcleval("alert_ {Can not write into the netlist directory. Please check} {}");
    +           else dbg(0, "Can not write into the netlist directory. Please check");
    +           err = 1;
    +        }
    +        if(err) {
    +          if(has_x) {
    +            tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background red", NULL);
    +            tclvareval("set tctx::", xctx->current_win_path, "_netlist red", NULL);
    +          }
    +        } else {
    +          if(has_x) {
    +            tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background Green", NULL);
    +            tclvareval("set tctx::", xctx->current_win_path, "_netlist Green", NULL);
    +          }
    +        }
    +
    +      }
    +      else if(rstate == ControlMask ) {                 /* clear symbol */
    +        if(xctx->semaphore >= 2) break;
    +        tcleval("xschem clear SYMBOL");
    +      }
    +      break;
    +
    +    case 'o':
    +      if(rstate == ControlMask) {                           /* load */
    +        if(xctx->semaphore >= 2) break;
    +        ask_new_file();
    +        xctx->semaphore--;
    +        tcleval("load_additional_files");
    +        xctx->semaphore++;
    +      }
    +      break;
    +
    +    case 'O':
    +      if(rstate == ControlMask ) {                    /* load most recent tile */
    +        tclvareval("xschem load -gui [lindex $recentfile 0]", NULL);
    +      }
    +      else if(rstate == 0) {                    /* toggle light/dark colorscheme 20171113 */
    +        int d_c;
    +        d_c = tclgetboolvar("dark_colorscheme");
    +        d_c = !d_c;
    +        tclsetboolvar("dark_colorscheme", d_c);
    +        tclsetdoublevar("dim_value", 0.0);
    +        tclsetdoublevar("dim_bg", 0.0);
    +        build_colors(0.0, 0.0);
    +        draw();
    +      }
    +      break;
    +
    +    case 'p':
    +      if(EQUAL_MODMASK) {                               /* add symbol pin */
    +        xctx->push_undo();
    +        unselect_all(1);
    +        storeobject(-1, xctx->mousex_snap-2.5, xctx->mousey_snap-2.5, xctx->mousex_snap+2.5, xctx->mousey_snap+2.5,
    +                    xRECT, PINLAYER, SELECTED, "name=XXX\ndir=inout");
    +        xctx->need_reb_sel_arr=1;
    +        rebuild_selected_array();
    +        move_objects(START,0,0,0);
    +        xctx->ui_state |= START_SYMPIN;
    +      }
    +      else if( !(xctx->ui_state & STARTPOLYGON) && rstate==0) {          /* start polygon */
    +        if(xctx->semaphore >= 2) break;
    +        dbg(1, "callback(): start polygon\n");
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_polygon(PLACE, xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTPOLYGON;
    +        }
    +      }
    +      break;
    +
    +    case 'P':
    +      if(rstate == 0) {                                               /* pan, other way to. */
    +        xctx->xorigin=-xctx->mousex_snap+xctx->areaw*xctx->zoom/2.0;
    +        xctx->yorigin=-xctx->mousey_snap+xctx->areah*xctx->zoom/2.0;
    +        draw();
    +        redraw_w_a_l_r_p_z_rubbers(1);
    +      }
    +      break;
    +
    +    case 'q':
    +      if(rstate==ControlMask) {                                           /* quit xschem */
    +        if(xctx->semaphore >= 2) break;
    +        /* must be set to zero, otherwise switch_tab/switch_win does not proceed
    +         * and these are necessary when closing tabs/windows */
    +        xctx->semaphore = 0;
    +        tcleval("quit_xschem");
    +      }
    +      else if(rstate==0) {                                            /* edit attributes */
    +        if(xctx->semaphore >= 2) break;
    +        edit_property(0);
    +      }
    +      else if(EQUAL_MODMASK) {                                      /* edit .sch file (DANGER!!) */
    +        if(xctx->semaphore >= 2) break;
    +        rebuild_selected_array();
    +        if(xctx->lastsel==0 ) {
    +          my_snprintf(str, S(str), "edit_file {%s}", abs_sym_path(xctx->sch[xctx->currsch], ""));
    +          tcleval(str);
    +        }
    +        else if(xctx->sel_array[0].type==ELEMENT) {
    +          my_snprintf(str, S(str), "edit_file {%s}",
    +             abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""));
    +          tcleval(str);
    +
    +        }
    +      }
    +      break;
    +
    +    case 'Q':
    +      if(rstate == 0) {                                       /* edit attributes in editor */
    +        if(xctx->semaphore >= 2) break;
    +        edit_property(1);
    +      }
    +      else if(rstate == ControlMask) {                           /* view attributes */
    +        edit_property(2);
    +      }
    +      break;
    +
    +    case 'r':
    +      if(/* !xctx->ui_state && */ rstate==0) {             /* start rect */
    +        dbg(1, "callback(): start rect\n");
    +        if(xctx->semaphore >= 2) break;
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_rect(PLACE,xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTRECT;
    +        }
    +      }
    +      else if((rstate == ControlMask) && cadence_compat) {    /* simulate (for cadence users) */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    +                "-message {Run circuit simulation?}");
    +        if(strcmp(tclresult(),"ok")==0) {
    +          tcleval("[xschem get top_path].menubar invoke Simulate");
    +        }
    +      }
    +      else if(SET_MODMASK) {                                        /* reload */
    +        if(xctx->semaphore >= 2) break;
    +        tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    +                 "-message {Are you sure you want to reload from disk?}");
    +        if(strcmp(tclresult(),"ok")==0) {
    +          char filename[PATH_MAX];
    +          unselect_all(1);
    +          remove_symbols();
    +          my_strncpy(filename, abs_sym_path(xctx->sch[xctx->currsch], ""), S(filename));
    +          load_schematic(1, filename, 1, 1);
    +          draw();
    +        }
    +      }
    +      else if(EQUAL_MODMASK) {                        /* rotate objects around their anchor points 20171208 */
    +        if(xctx->ui_state & STARTMOVE) move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +        else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE|ROTATELOCAL);
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +          move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +          move_objects(END,0,0,0);
    +        }
    +      }
    +      break;
    +
    +    case 'R':
    +      if(rstate == 0) {                                                 /* rotate */
    +        if(xctx->ui_state & STARTMOVE) move_objects(ROTATE,0,0,0);
    +        else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE);
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +          move_objects(ROTATE,0,0,0);
    +          move_objects(END,0,0,0);
    +        }
    +
    +      }
    +      break;
    +
    +    case 's':
    +      if((rstate == 0) && !cadence_compat) {                   /* simulate (original keybind) */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] "
    +                "-message {Run circuit simulation?}");
    +        if(strcmp(tclresult(),"ok")==0) {
    +          tcleval("[xschem get top_path].menubar invoke Simulate");
    +        }
    +      }
    +      else if(/* !xctx->ui_state && */ (rstate == 0) && cadence_compat) {       /* create wire snapping to closest instance pin (cadence keybind) */
    +        if(xctx->semaphore >= 2) break;
    +        snapped_wire(c_snap);
    +      }
    +      else if(rstate == ControlMask ){                              /* save 20121201 */
    +        if(xctx->semaphore >= 2) break;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }     
    +        /* check if unnamed schematic, use saveas in this case */
    +        if(!strcmp(xctx->sch[xctx->currsch],"") || strstr(xctx->sch[xctx->currsch], "untitled")) {
    +          saveas(NULL, SCHEMATIC);
    +        } else {
    +          save(1, 0);
    +        }
    +      }
    +      else if(SET_MODMASK && (state & ControlMask) ) {          /* save as symbol */
    +        if(xctx->semaphore >= 2) break;
    +        saveas(NULL, SYMBOL);
    +      }
    +      break;
    +
    +    case 'S':
    +      if(rstate == 0) {                                   /* change element order */
    +        if(xctx->semaphore >= 2) break;
    +        change_elem_order(-1);
    +      }
    +      else if(rstate == ControlMask) {                    /* save as schematic */
    +        if(xctx->semaphore >= 2) break;
    +        saveas(NULL, SCHEMATIC);
    +      }
    +      break;
    +
    +    case 't':
    +      if(rstate == 0) {                                     /* place text */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        if(xctx->semaphore >= 2) break;
    +        xctx->last_command = 0;
    +        xctx->mx_double_save = xctx->mousex_snap;
    +        xctx->my_double_save = xctx->mousey_snap;
    +        if(place_text(0, xctx->mousex_snap, xctx->mousey_snap)) {   /* 1 = draw text 24122002 */
    +          xctx->mousey_snap = xctx->my_double_save;
    +          xctx->mousex_snap = xctx->mx_double_save;
    +          move_objects(START,0,0,0);
    +          xctx->ui_state |= PLACE_TEXT;
    +        }
    +      }
    +      else if(rstate & ControlMask) {
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break; 
    +        }
    +      }
    +      break;
    +
    +    case 'T':
    +      if(rstate == 0) {                   /* toggle spice_ignore, verilog_ignore, ... flag on selected instances. */
    +        toggle_ignore();
    +      }
    +      break;
    +
    +    case 'u':
    +      if(rstate==0) {                                                       /* undo */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->pop_undo(0, 1);  /* 2nd parameter: set_modify_status */
    +        draw();
    +      }
    +      else if(EQUAL_MODMASK) {                                        /* align to grid */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->push_undo();
    +        round_schematic_to_grid(c_snap);
    +        set_modify(1);
    +        if(tclgetboolvar("autotrim_wires")) trim_wires();
    +        xctx->prep_hash_inst=0;
    +        xctx->prep_hash_wires=0;
    +        xctx->prep_net_structs=0;
    +        xctx->prep_hi_structs=0;
    +
    +        draw();
    +      }
    +      else if(rstate==ControlMask) { /* testmode */
    +        unselect_attached_floaters();
    +      }
    +      
    +      break;
    +  
    +    case 'U':
    +      if(rstate == 0) {                                                 /* redo */
    +        if(xctx->semaphore >= 2) break;
    +        xctx->pop_undo(1, 1); /* 2nd parameter: set_modify_status */
    +        draw();
    +      }
    +      break;
    +
    +    case 'v':
    +      if(rstate==0) {                                 /* vertically constrained drag 20171023 */
    +        if ( xctx->constr_mv == 2 ) {
    +          tcleval("set constr_mv 0" );
    +          xctx->constr_mv = 0;
    +        } else {
    +          tcleval("set constr_mv 2" );
    +          xctx->constr_mv = 2;
    +        }
    +        if(xctx->ui_state & STARTWIRE) {
    +          if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    +          if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    +          new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +        }
    +        if(xctx->ui_state & STARTLINE) {
    +          if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save;
    +          if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save;
    +          new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +        }
    +      }
    +      else if(rstate == ControlMask) {                      /* paste from clipboard */
    +        if(xctx->semaphore >= 2) break;
    +        merge_file(2,".sch");
    +      }
    +      else if(EQUAL_MODMASK) {                      /* vertical flip objects around their anchor points */
    +        if(xctx->ui_state & STARTMOVE) {
    +          move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +          move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +          move_objects(FLIP|ROTATELOCAL,0,0,0);
    +        }
    +        else if(xctx->ui_state & STARTCOPY) {
    +          copy_objects(ROTATE|ROTATELOCAL);
    +          copy_objects(ROTATE|ROTATELOCAL);
    +          copy_objects(FLIP|ROTATELOCAL);
    +        }
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          move_objects(START,0,0,0);
    +          move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +          move_objects(ROTATE|ROTATELOCAL,0,0,0);
    +          move_objects(FLIP|ROTATELOCAL,0,0,0);
    +          move_objects(END,0,0,0);
    +        }
    +      }
    +      break;
    +
    +    case 'V':
    +      if(rstate == 0) {                                       /* vertical flip */
    +        if(xctx->ui_state & STARTMOVE) {
    +          move_objects(ROTATE,0,0,0);
    +          move_objects(ROTATE,0,0,0);
    +          move_objects(FLIP,0,0,0);
    +        }
    +        else if(xctx->ui_state & STARTCOPY) {
    +          copy_objects(ROTATE);
    +          copy_objects(ROTATE);
    +          copy_objects(FLIP);
    +        }
    +        else {
    +          rebuild_selected_array();
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;    
    +          move_objects(START,0,0,0);
    +          move_objects(ROTATE,0,0,0);
    +          move_objects(ROTATE,0,0,0);
    +          move_objects(FLIP,0,0,0);
    +          move_objects(END,0,0,0);
    +        } 
    +      }
    +      else if(rstate == ControlMask) {                   /* toggle spice/vhdl netlist */
    +        xctx->netlist_type++;
    +        if(xctx->netlist_type==6) xctx->netlist_type=1;
    +        set_tcl_netlist_type();
    +        draw(); /* needed to ungrey or grey out  components due to *_ignore attribute */
    +      }
    +      break;
    +
    +    case 'w':
    +      if(/* !xctx->ui_state && */ rstate==0) {                  /* place wire. */
    +        int prev_state = xctx->ui_state;
    +        if(xctx->semaphore >= 2) break;
    +
    +        if(infix_interface) {
    +          start_wire(xctx->mousex_snap, xctx->mousey_snap);
    +          if(prev_state == STARTWIRE) {
    +            tcleval("set constr_mv 0" );
    +            xctx->constr_mv=0;
    +          }
    +        } else {
    +          xctx->last_command = 0;
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTWIRE;
    +          if(prev_state & STARTWIRE) start_wire(xctx->mousex_snap, xctx->mousey_snap);
    +        }
    +      }
    +      else if(rstate == ControlMask) {                    /* close current schematic */
    +        int save_sem;
    +        if(xctx->semaphore >= 2) break;
    +        save_sem = xctx->semaphore;
    +        tcleval("xschem exit");
    +        xctx->semaphore = save_sem;
    +      }
    +      break;
    +
    +    case 'W':
    +      if(/* !xctx->ui_state && */ (rstate == 0) && !cadence_compat) {  /* create wire snapping to closest instance pin (original keybind) */
    +        if(xctx->semaphore >= 2) break;
    +        snapped_wire(c_snap);
    +      }
    +      break;
    +
    +    case 'x':
    +      if(rstate == 0) {                                           /* new cad session */
    +        new_xschem_process(NULL ,0);
    +      }
    +      else if(EQUAL_MODMASK) {                          /* toggle draw crosshair at mouse pos */
    +        if(tclgetboolvar("draw_crosshair")) {
    +          tclsetvar("draw_crosshair", "0");
    +        } else {
    +          tclsetvar("draw_crosshair", "1");
    +        }
    +        draw();
    +      }
    +      else if(rstate == ControlMask) {                      /* cut selection into clipboard */
    +        if(xctx->semaphore >= 2) break;
    +        rebuild_selected_array();
    +        if(xctx->lastsel) {  /* 20071203 check if something selected */
    +          save_selection(2);
    +          delete(1/* to_push_undo */);
    +        }
    +      }
    +      break;
    +
    +    case 'X':
    +      if(rstate == 0) {                         /* highlight discrepanciens between selected instance pin and net names */
    +        hilight_net_pin_mismatches();
    +      }
    +      break;
    +
    +    case 'y':
    +      if(rstate == 0) {                                       /* toggle stretching */
    +        enable_stretch = !enable_stretch;
    +        tclsetboolvar("enable_stretch", enable_stretch);
    +      }
    +      break;
    +
    +    case 'z':
    +      if(rstate == 0 && !(xctx->ui_state & (STARTRECT | STARTLINE | STARTWIRE | STARTPOLYGON | STARTARC))) { /* zoom box */
    +        dbg(1, "callback(): zoom_rectangle call\n");
    +        zoom_rectangle(START);
    +      }
    +      else if(rstate==ControlMask) {                            /* zoom out */
    +        view_unzoom(0.0);
    +      }
    +      else if(EQUAL_MODMASK && cadence_compat) {              /* toggle snap-cursor option */
    +        if(tclgetboolvar("snap_cursor")) {
    +          tclsetvar("snap_cursor", "0");
    +          draw_snap_cursor(1);
    +          xctx->closest_pin_found = 0;
    +          xctx->prev_snapx = 0.0;
    +          xctx->prev_snapy = 0.0;
    +        } else {
    +          tclsetvar("snap_cursor", "1");
    +          if(wire_draw_active) draw_snap_cursor(3);
    +        }
    +      }
    +      break;
    +
    +    case 'Z': 
    +      if(rstate == 0) {                                       /* zoom in */
    +        view_zoom(0.0);
    +      }
    +      break;
    +
    +    case ' ':
    +      if(xctx->ui_state & STARTWIRE) {  /*  & instead of == 20190409 */
    +        new_wire(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap);
    +        xctx->manhattan_lines++;
    +        xctx->manhattan_lines %=3;
    +        new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +
    +      } else if(xctx->ui_state & STARTLINE) {
    +        new_line(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap);
    +        xctx->manhattan_lines++;
    +        xctx->manhattan_lines %=3;
    +        new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap);
    +      } else {
    +        if(xctx->semaphore<2) {
    +          rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */
    +        }
    +        pan(START, mx, my);
    +        xctx->ui_state |= STARTPAN;
    +      }
    +      break;
    +
    +    case '_':                                         /* toggle change line width */
    +      if(!tclgetboolvar("change_lw")) {
    +          tcleval("alert_ { enabling change line width} {}");
    +          tclsetvar("change_lw","1");
    +      }
    +      else {
    +          tcleval("alert_ { disabling change line width} {}");
    +          tclsetvar("change_lw","0");
    +      }
    +      break;
    +
    +    case '%':                                           /* toggle draw grid */
    +      int dr_gr;
    +      dr_gr = tclgetboolvar("draw_grid");
    +      dr_gr =!dr_gr;
    +      if(dr_gr) {
    +          /* tcleval("alert_ { enabling draw grid} {}"); */
    +          tclsetvar("draw_grid","1");
    +          draw();
    +      }
    +      else {
    +          /* tcleval("alert_ { disabling draw grid} {}"); */
    +          tclsetvar("draw_grid","0");
    +          draw();
    +      }
    +      break;
    +      
    +    case '$': 
    +      if( rstate == 0 ) {                             /* toggle pixmap  saving */
    +        xctx->draw_pixmap =!xctx->draw_pixmap;
    +        if(xctx->draw_pixmap) tcleval("alert_ { enabling draw pixmap} {}");
    +        else tcleval("alert_ { disabling draw pixmap} {}");
    +      }
    +      else if(state & ControlMask) {                  /* toggle window  drawing */
    +        xctx->draw_window =!xctx->draw_window;
    +        if(xctx->draw_window) {
    +          tcleval("alert_ { enabling draw window} {}");
    +          tclsetvar("draw_window","1");
    +        } else {
    +          tcleval("alert_ { disabling draw window} {}");
    +          tclsetvar("draw_window","0");
    +        }
    +      }
    +      break;
    +
    +    case '=': 
    +      if(state & ControlMask) {                     /* toggle fill rectangles */
    +        int x;
    +        xctx->fill_pattern++;
    +        if(xctx->fill_pattern==2) xctx->fill_pattern=0;
    +
    +        if(xctx->fill_pattern==1) {
    +         tcleval("alert_ { Stippled pattern fill} {}");
    +         for(x=0;xfill_type[x]==2) XSetFillStyle(display,xctx->gcstipple[x],FillSolid);
    +           else XSetFillStyle(display,xctx->gcstipple[x],FillStippled);
              }
    -       }
    -     return;
    -   }
    -   if(key=='d' && rstate == ControlMask)          /* delete files */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    delete_files();
    -    return;
    -   }
    -   if(key=='x' && rstate == 0 )                  /* new cad session */
    -   {
    -    new_xschem_process(NULL ,0);
    -    return;
    -   }
    -   if((key=='#') && !(state & ControlMask) )
    -   {
    -    check_unique_names(0);
    -    return;
    -   }
    -   if((key=='#') && (state & ControlMask) )
    -   {
    -    check_unique_names(1);
    -    return;
    -   }
    -   if(key=='f' && rstate == ControlMask)         /* search */
    -   {
    -    if(xctx->semaphore >= 2) return;
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }     
    -    tcleval("property_search");
    -    return;
    -   }
    -   if(key=='F' && rstate == ControlMask )   /* full zoom selection */
    -   {
    -    if(xctx->ui_state == SELECTION) {
    -      zoom_full(1, 1, 3, 0.97);
    -    }
    -    return;
    -   }
    -   if(key=='f' && rstate == 0 )                  /* full zoom */
    -   { 
    -    int flags = 1;
    -    if(waves_selected(event, key, state, button)) {
    -      waves_callback(event, mx, my, key, button, aux, state);
    -      return;
    -    }
    -    if(tclgetboolvar("zoom_full_center")) flags |= 2;
    -    zoom_full(1, 0, flags, 0.97);
    -    return;
    -   }
    -   if((key=='z' && rstate==ControlMask))                         /* zoom out */
    -   {
    -     view_unzoom(0.0);
    -     return;
    -   }
    -   if(key=='!' && !(state & ControlMask))
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     break_wires_at_pins(0);
    -     return;
    -   }
    -   if(key=='!' && (state & ControlMask))
    -   {
    -     if(xctx->semaphore >= 2) return;
    -     break_wires_at_pins(1);
    -     return;
    -   }
    -   
    -   return;
    +        }
    +        else if(xctx->fill_pattern==2) {
    +         tcleval("alert_ { solid pattern fill} {}");
    +         for(x=0;xgcstipple[x],FillSolid);
    +        }
    +        else  {
    +         tcleval("alert_ { No pattern fill} {}");
    +         for(x=0;xgcstipple[x],FillStippled);
    +        }
    +
    +        draw();
    +      }
    +      break;
    +
    +    case '+': 
    +      if(state & ControlMask) {                           /* change line width */
    +        xctx->lw = round_to_n_digits(xctx->lw + 0.5, 2);
    +        change_linewidth(xctx->lw);
    +        tclsetboolvar("change_lw", 0);
    +        draw();
    +      }
    +      break;
    +
    +    case '-':
    +      if(state & ControlMask) {                           /* change line width */
    +        xctx->lw = round_to_n_digits(xctx->lw - 0.5, 2);
    +        if(xctx->lw < 0.0) xctx->lw = 0.0;
    +        change_linewidth(xctx->lw);
    +        tclsetboolvar("change_lw", 0);
    +        draw();
    +      }
    +      break;
    +    
    +    case XK_Return: 
    +      if((state == 0 ) && xctx->ui_state & STARTPOLYGON) {            /* close polygon */
    +        new_polygon(ADD|END, xctx->mousex_snap, xctx->mousey_snap);
    +      }
    +      break;
    +
    +    case XK_Escape:                                       /* abort & redraw */
    +      if(xctx->semaphore < 2) {
    +        abort_operation();
    +      }
    +      /* stuff that can be done reentrantly ... */
    +      tclsetvar("tclstop", "1"); /* stop simulation if any running */
    +      if(xctx->ui_state2 & MENUSTARTWIRE) {
    +        xctx->ui_state2 &= ~MENUSTARTWIRE;
    +      }
    +      if(snap_cursor) draw_snap_cursor(1); /* erase */
    +      if(tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE) && cadence_compat) {
    +        xctx->last_command &= ~STARTWIRE;
    +      }
    +      break;
    +
    +    case XK_Delete:
    +      if(xctx->ui_state & SELECTION) {              /* delete selection */
    +        if(xctx->semaphore >= 2) break;
    +        delete(1/* to_push_undo */);
    +      }
    +      break;
    +
    +    case XK_Right:
    +      if(state == ControlMask) {
    +        int save = xctx->semaphore;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        if(xctx->semaphore >= 2) break;
    +        xctx->semaphore = 0;
    +        tcleval("next_tab");
    +        xctx->semaphore = save;
    +      }
    +      else {   /* left */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        xctx->xorigin+=-CADMOVESTEP*xctx->zoom;
    +        draw();
    +        redraw_w_a_l_r_p_z_rubbers(1);
    +      }
    +      break;
    +
    +    case XK_Left:
    +      if(state == ControlMask) {
    +        int save = xctx->semaphore;
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        if(xctx->semaphore >= 2) break;
    +        xctx->semaphore = 0;
    +        tcleval("prev_tab");
    +        xctx->semaphore = save;
    +      }
    +      else {  /* right */
    +        if(waves_selected(event, key, state, button)) {
    +          waves_callback(event, mx, my, key, button, aux, state);
    +          break;
    +        }
    +        xctx->xorigin-=-CADMOVESTEP*xctx->zoom;
    +        draw();
    +        redraw_w_a_l_r_p_z_rubbers(1);
    +      }
    +      break;
    +
    +    case XK_Down:          /* down */
    +      if(waves_selected(event, key, state, button)) {
    +        waves_callback(event, mx, my, key, button, aux, state);
    +        break;
    +      }
    +      xctx->yorigin+=-CADMOVESTEP*xctx->zoom;
    +      draw();
    +      redraw_w_a_l_r_p_z_rubbers(1);
    +      break;
    +
    +    case XK_Up:           /* up */
    +      if(waves_selected(event, key, state, button)) {
    +        waves_callback(event, mx, my, key, button, aux, state);
    +        break;
    +      }
    +      xctx->yorigin-=-CADMOVESTEP*xctx->zoom;
    +      draw();
    +      redraw_w_a_l_r_p_z_rubbers(1);
    +      break;
    +    
    +    case XK_BackSpace:  /* back */
    +      if(xctx->semaphore >= 2) break;
    +      go_back(1);
    +      break;
    +    
    +#if defined(__unix__) && HAS_CAIRO==1
    +    case XK_Print:
    +      xctx->ui_state |= GRABSCREEN;
    +      tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL);
    +      tclvareval("grab set -global ", xctx->top_path, ".drw", NULL);
    +      break;
    +#endif
    +    
    +    case XK_Insert:
    +      if(state == ShiftMask) { /* insert sym */
    +        tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2");
    +      }
    +      else {
    +        if(xctx->semaphore >= 2) break;
    +        start_place_symbol();
    +      }
    +      break;
    +
    +    case '*':
    +      if(rstate == 0 ) { /* postscript print */
    +        if(xctx->semaphore >= 2) break;
    +        ps_draw(7, 0, 0);
    +      }
    +      else if(rstate == ControlMask) {/* xpm print */
    +        if(xctx->semaphore >= 2) break;
    +        print_image();
    +      }
    +      else if(EQUAL_MODMASK) { /* svg print , 20121108 */
    +        if(xctx->semaphore >= 2) break;
    +        svg_draw();
    +      }
    +      break;
    +
    +    case '&':                               /* check wire connectivity */
    +      if(xctx->semaphore >= 2) break;
    +      xctx->push_undo();
    +      trim_wires();
    +      draw();
    +      break;
    +    
    +    case '\\':
    +      if(state==0) {                            /* fullscreen */
    +        dbg(1, "callback(): toggle fullscreen, win_path=%s\n", win_path);
    +        toggle_fullscreen(win_path);
    +      }
    +      break;
    +    
    +    case '>':
    +      if(xctx->semaphore >= 2) break;
    +      if(xctx->draw_single_layer< cadlayers-1) xctx->draw_single_layer++;
    +      draw();
    +      break;
    +
    +    case '<':
    +      if(xctx->semaphore >= 2) break;
    +      if(xctx->draw_single_layer>=0 ) xctx->draw_single_layer--;
    +      draw();
    +      break;
    +
    +    /* toggle flat netlist (only spice)  */
    +    case ':':
    +      if(!tclgetboolvar("flat_netlist")) {
    +          tcleval("alert_ { enabling flat netlist} {}");
    +          tclsetvar("flat_netlist","1");
    +      }
    +      else {
    +          tcleval("alert_ { set hierarchical netlist } {}");
    +          tclsetvar("flat_netlist","0");
    +      }
    +      break;
    +    
    +    case '#':
    +      if((state & ControlMask)) {
    +        check_unique_names(1);
    +      }
    +      else {
    +        check_unique_names(0);
    +      }
    +      break;
    +
    +    case ';':
    +      if(0 && (state & ControlMask)) {   /* testmode */
    +      }
    +      break;
    +
    +    case '~':
    +      if(0 && (state & ControlMask)) {  /* testmode */
    +      }
    +      break;
    +
    +    case '|':
    +      if(0 && (state & ControlMask)) {  /* testmode */
    +        static int x = 0;
    +
    +        if(x == 0) {
    +          int i;
    +          XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
    +                xctx->areaw, xctx->areah);
    +          XFlush(display);
    +          sleep_ms(400);
    +          for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) {
    +            XDrawLine(display, xctx->window,  xctx->gctiled,
    +               i, xctx->xrect[0].y, i, xctx->xrect[0].height);
    +            XFlush(display);
    +            sleep_ms(4);
    +          }
    +        } else if(x == 1) {
    +          int i;
    +          XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1,
    +                xctx->areaw, xctx->areah);
    +          XFlush(display);
    +          sleep_ms(400);
    +          for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) {
    +            XDrawLine(display, xctx->window,  xctx->gctiled,
    +               i, xctx->xrect[0].y, i+1, xctx->xrect[0].height);
    +            XFlush(display);
    +            sleep_ms(4);
    +          }
    +        }
    +        x++;
    +        x %= 2;
    +      }
    +      break;
    +    
    +    case '!':
    +      if((state & ControlMask)) {
    +        if(xctx->semaphore >= 2) break;
    +        break_wires_at_pins(1);
    +      }
    +      else {
    +        if(xctx->semaphore >= 2) break;
    +        break_wires_at_pins(0);
    +      }
    +      break;
    +
    +    default:
    +      break;
    +  }
    +
    +  return;
     }
     
     static void handle_button_press(int event, int state, int rstate, KeySym key, int button, int mx, int my,
    
    From e1488da40791d3befebd0b678cd555ebf07b59cc Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Tue, 11 Mar 2025 20:47:00 +0100
    Subject: [PATCH 53/67] add `xschem rect gui`, `xschem line gui`, `xschem
     polygon gui`, `xschem wire gui` to start placement of these objects with 1st
     point set to mouse coordinates
    
    ---
     doc/xschem_man/developer_info.html |  41 +++--
     src/callback.c                     |   4 +-
     src/scheduler.c                    | 105 ++++++++++---
     src/xinit.c                        |   2 +-
     src/xschem.h                       |   2 +
     src/xschem.tcl                     | 231 +++++++++++++++++++++--------
     6 files changed, 291 insertions(+), 94 deletions(-)
    
    diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html
    index 25369cc1..06d9e035 100644
    --- a/doc/xschem_man/developer_info.html
    +++ b/doc/xschem_man/developer_info.html
    @@ -989,13 +989,17 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        
  • is_generator symbol
  •     tell if 'symbol' is a generator (symbol(param1,param2,...) 
  • line [x1 y1 x2 y2] [pos] [propstring] [draw]
  • +       line
    +       line gui
        if 'x1 y1 x2 y2'is given place line on current
        layer (rectcolor) at indicated coordinates.
    -   if 'pos' is given insert at given position in rectangle array.
    +   if 'pos' is given insert at given position in line array.
        if 'pos' set to -1 append to last element in line array.
        'propstring' is the attribute string. Set to empty if not given.
        if 'draw' is set to 1 (default) draw the new object, else don't
    -   If no coordinates are given start a GUI operation of line placement 
    + If no coordinates are given start a GUI operation of line placement + if `gui` argument is given start a line GUI placement with 1st point + set to current mouse coordinates
  • line_width n
  •     set line width to floating point number 'n' 
  • list_hierarchy
  • @@ -1161,8 +1165,10 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        If not given take from symbol template attribute.
  • place_text
  •     Start a GUI placement of a text object 
    -
  • polygon
  • -   Start a GUI placement of a polygon 
    +
  • polygon [gui]
  • +   Start a GUI placement of a polygon
    +   if `gui` argument is given start a polygon GUI placement with 1st point
    +   set to current mouse coordinates 
  • preview_window create|draw|destroy|close [win_path] [file]
  •     destroy: will delete preview schematic data and destroy container window
        close: same as destroy but leave the container window.
    @@ -1337,14 +1343,19 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        Rebuild selection list
  • record_global_node n node
  •     call the record_global_node function (list of netlist global nodes) 
    -
  • rect [x1 y1 x2 y2] [pos] [propstring] [draw]
  • -   if 'x1 y1 x2 y2'is given place recangle on current
    -   layer (rectcolor) at indicated coordinates.
    -   if 'pos' is given insert at given position in rectangle array.
    -   if 'pos' set to -1 append rectangle to last element in rectangle array.
    -   'propstring' is the attribute string. Set to empty if not given.
    -   if 'draw' is set to 1 (default) draw the new object, else don't
    -   If no coordinates are given start a GUI operation of rectangle placement 
    +
  • rect ...
  • +   rect [x1 y1 x2 y2] [pos] [propstring] [draw]
    +     if 'x1 y1 x2 y2'is given place recangle on current
    +     layer (rectcolor) at indicated coordinates.
    +     if 'pos' is given insert at given position in rectangle array.
    +     if 'pos' set to -1 append rectangle to last element in rectangle array.
    +     'propstring' is the attribute string. Set to empty if not given.
    +     if 'draw' is set to 1 (default) draw the new object, else don't
    +   rect
    +     If no coordinates are given start a GUI operation of rectangle placement
    +   rect gui
    +     if `gui` argument is given start a GUI placement of a rectangle with 1st
    +     point starting from current mouse coordinates 
  • redo
  •     Redo last undone action 
  • redraw
  • @@ -1664,8 +1675,12 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        
  • wire_coord n
  •     return 4 coordinates of wire[n] 
  • wire [x1 y1 x2 y2] [pos] [prop] [sel]
  • +   wire
    +   wire gui
        Place a new wire
    -   if no coordinates are given start a GUI wire placement 
    + if no coordinates are given start a GUI wire placement + if `gui` argument is given start a GUI placement of a wire with 1st point + starting from current mouse coordinates
  • wire_cut [x y] [noalign]
  •     start a wire cut operation. Point the mouse in the middle of a wire and
        Alt-click right button.
    diff --git a/src/callback.c b/src/callback.c
    index f05ccbb1..c95383e6 100644
    --- a/src/callback.c
    +++ b/src/callback.c
    @@ -212,7 +212,7 @@ static void start_place_symbol(void)
         }
     }
     
    -static void start_line(double mx, double my)
    +void start_line(double mx, double my)
     {
         xctx->last_command = STARTLINE;
         if(xctx->ui_state & STARTLINE) {
    @@ -232,7 +232,7 @@ static void start_line(double mx, double my)
         new_line(PLACE, mx, my);
     }
     
    -static void start_wire(double mx, double my)
    +void start_wire(double mx, double my)
     {
       dbg(1, "start_wire(): ui_state=%d, ui_state2=%d last_command=%d\n",
           xctx->ui_state, xctx->ui_state2, xctx->last_command);
    diff --git a/src/scheduler.c b/src/scheduler.c
    index 84f94fa5..c1e66f13 100644
    --- a/src/scheduler.c
    +++ b/src/scheduler.c
    @@ -2787,13 +2787,17 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
         break;
         case 'l': /*----------------------------------------------*/
         /* line [x1 y1 x2 y2] [pos] [propstring] [draw]
    +     * line
    +     * line gui
          *   if 'x1 y1 x2 y2'is given place line on current
          *   layer (rectcolor) at indicated coordinates.
    -     *   if 'pos' is given insert at given position in rectangle array.
    +     *   if 'pos' is given insert at given position in line array.
          *   if 'pos' set to -1 append to last element in line array.
          *   'propstring' is the attribute string. Set to empty if not given.
          *   if 'draw' is set to 1 (default) draw the new object, else don't
    -     *   If no coordinates are given start a GUI operation of line placement */
    +     *   If no coordinates are given start a GUI operation of line placement
    +     *   if `gui` argument is given start a line GUI placement with 1st point
    +     *   set to current mouse coordinates */
         if(!strcmp(argv[1], "line"))
         {
           double x1,y1,x2,y2;
    @@ -2819,6 +2823,21 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             }
             set_modify(1);
           }
    +      else if(argc == 3 && !strcmp(argv[2], "gui")) {
    +        int prev_state = xctx->ui_state;
    +        int infix_interface = tclgetboolvar("infix_interface");
    +        if(infix_interface) {
    +          start_line(xctx->mousex_snap, xctx->mousey_snap);
    +          if(prev_state == STARTLINE) {
    +            tcleval("set constr_mv 0" );
    +            xctx->constr_mv=0;
    +          }
    +        } else {
    +          xctx->last_command = 0;
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTLINE;
    +        }
    +      }
           else {
             xctx->last_command = 0;
             xctx->ui_state |= MENUSTART;
    @@ -3701,13 +3720,28 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           Tcl_ResetResult(interp);
         }
     
    -    /* polygon
    -     *   Start a GUI placement of a polygon */
    +    /* polygon [gui]
    +     *   Start a GUI placement of a polygon
    +     *   if `gui` argument is given start a polygon GUI placement with 1st point
    +     *   set to current mouse coordinates */
         else if(!strcmp(argv[1], "polygon"))
         {
           if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;}
    -      xctx->ui_state |= MENUSTART;
    -      xctx->ui_state2 = MENUSTARTPOLYGON;
    +      if(argc > 2 && !strcmp(argv[2], "gui")) {
    +        int infix_interface = tclgetboolvar("infix_interface");
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_polygon(PLACE, xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTPOLYGON;
    +        }
    +      } else {
    +        xctx->ui_state |= MENUSTART;
    +        xctx->ui_state2 = MENUSTARTPOLYGON;
    +      }
         }
     
         /* preview_window create|draw|destroy|close [win_path] [file]
    @@ -4434,7 +4468,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
         }
     
         /* rebuild_connectivity
    -         Rebuild logical connectivity abstraction of schematic */
    +     *   Rebuild logical connectivity abstraction of schematic */
         else if(!strcmp(argv[1], "rebuild_connectivity"))
         {
           int err = 0;
    @@ -4473,14 +4507,19 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE);
         }
     
    -    /* rect [x1 y1 x2 y2] [pos] [propstring] [draw]
    -     *   if 'x1 y1 x2 y2'is given place recangle on current
    -     *   layer (rectcolor) at indicated coordinates.
    -     *   if 'pos' is given insert at given position in rectangle array.
    -     *   if 'pos' set to -1 append rectangle to last element in rectangle array.
    -     *   'propstring' is the attribute string. Set to empty if not given.
    -     *   if 'draw' is set to 1 (default) draw the new object, else don't
    -     *   If no coordinates are given start a GUI operation of rectangle placement */
    +    /* rect ...
    +     *   rect [x1 y1 x2 y2] [pos] [propstring] [draw]
    +     *     if 'x1 y1 x2 y2'is given place recangle on current
    +     *     layer (rectcolor) at indicated coordinates.
    +     *     if 'pos' is given insert at given position in rectangle array.
    +     *     if 'pos' set to -1 append rectangle to last element in rectangle array.
    +     *     'propstring' is the attribute string. Set to empty if not given.
    +     *     if 'draw' is set to 1 (default) draw the new object, else don't
    +     *   rect
    +     *     If no coordinates are given start a GUI operation of rectangle placement
    +     *   rect gui
    +     *     if `gui` argument is given start a GUI placement of a rectangle with 1st
    +     *     point starting from current mouse coordinates */
         else if(!strcmp(argv[1], "rect"))
         {
           double x1,y1,x2,y2;
    @@ -4510,8 +4549,18 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
               xctx->draw_window = save;
             }
             set_modify(1);
    -      }
    -      else {
    +      } else if(argc > 2 && !strcmp(argv[2], "gui")) {
    +        int infix_interface = tclgetboolvar("infix_interface");
    +        if(infix_interface) {
    +          xctx->mx_double_save=xctx->mousex_snap;
    +          xctx->my_double_save=xctx->mousey_snap;
    +          xctx->last_command = 0;
    +          new_rect(PLACE,xctx->mousex_snap, xctx->mousey_snap);
    +        } else {
    +          xctx->ui_state |= MENUSTART;
    +          xctx->ui_state2 = MENUSTARTRECT;
    +        } 
    +      } else {
             xctx->ui_state |= MENUSTART;
             xctx->ui_state2 = MENUSTARTRECT;
           }
    @@ -6374,8 +6423,12 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
           }
         }
         /* wire [x1 y1 x2 y2] [pos] [prop] [sel]
    +     *   wire
    +     *   wire gui
          *   Place a new wire
    -     *   if no coordinates are given start a GUI wire placement */
    +     *   if no coordinates are given start a GUI wire placement
    +     *   if `gui` argument is given start a GUI placement of a wire with 1st point
    +     *   starting from current mouse coordinates */
         else if(!strcmp(argv[1], "wire"))
         {
           double x1,y1,x2,y2;
    @@ -6403,7 +6456,21 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg
             if(tclgetboolvar("autotrim_wires")) trim_wires();
             set_modify(1);
           }
    -      else {
    +      else if(argc > 2 && !strcmp(argv[2], "gui")) {
    +        int prev_state = xctx->ui_state;
    +        int infix_interface = tclgetboolvar("infix_interface");
    +        if(infix_interface) {
    +          start_wire(xctx->mousex_snap, xctx->mousey_snap);
    +          if(prev_state == STARTWIRE) {
    +            tcleval("set constr_mv 0" );
    +            xctx->constr_mv=0;
    +          }
    +        } else {
    +          xctx->last_command = 0;
    +          xctx->ui_state |= MENUSTART;                   
    +          xctx->ui_state2 = MENUSTARTWIRE;
    +        }
    +      } else {
             xctx->last_command = 0;
             xctx->ui_state |= MENUSTART;
             xctx->ui_state2 = MENUSTARTWIRE;
    diff --git a/src/xinit.c b/src/xinit.c
    index 192ae975..8ffe0f5c 100644
    --- a/src/xinit.c
    +++ b/src/xinit.c
    @@ -1179,7 +1179,7 @@ static int source_tcl_file(char *s)
         fprintf(errfp, "Tcl_AppInit() error: can not execute %s, please fix:\n", s);
         fprintf(errfp, "%s", tclresult());
         #if TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >=6
    -    fprintf(errfp, "Line No: %d\n", Tcl_GetErrorLine(interp));
    +    fprintf(errfp, "\nLine No: %d\n", Tcl_GetErrorLine(interp));
         #endif
         fprintf(errfp, "\n");
         #if TCL_MAJOR_VERSION >= 8 && TCL_MINOR_VERSION >=6
    diff --git a/src/xschem.h b/src/xschem.h
    index eb62ea35..93c93b09 100644
    --- a/src/xschem.h
    +++ b/src/xschem.h
    @@ -1401,6 +1401,8 @@ extern void tclmainloop(void);
     extern int Tcl_AppInit(Tcl_Interp *interp);
     extern void abort_operation(void);
     extern void draw_crosshair(int what, int state);
    +extern void start_line(double mx, double my);
    +extern void start_wire(double mx, double my);
     extern void backannotate_at_cursor_b_pos(xRect *r, Graph_ctx *gr);
     /* extern void snapped_wire(double c_snap); */
     extern void unselect_attached_floaters(void);
    diff --git a/src/xschem.tcl b/src/xschem.tcl
    index 8c42401b..ba2cbee2 100644
    --- a/src/xschem.tcl
    +++ b/src/xschem.tcl
    @@ -4490,7 +4490,8 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
       set_ne file_dialog_files2 {}
       panedwindow  .load.l -orient horizontal -height 8c
       if { $loadfile == 2} {frame .load.l.recent -takefocus 0}
    -  frame .load.l.paneleft -takefocus 0 -highlightcolor red -highlightthickness 2 -bg {grey90}
    +  frame .load.l.paneleft -takefocus 0 -highlightcolor red -highlightthickness 2 -bg {grey90} \
    +    -highlightbackground [option get . background {}]
       eval [subst {listbox .load.l.paneleft.list -listvariable file_dialog_names1 -width 40 -height 12 \
         -fg black -background {grey90} -highlightthickness 0 -relief flat -borderwidth 0 \
         -yscrollcommand ".load.l.paneleft.yscroll set" -selectmode browse \
    @@ -4518,14 +4519,12 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
         }
       }
     
    -
       panedwindow .load.l.paneright -orient vertical 
       frame .load.l.paneright.f -takefocus 0
       frame .load.l.paneright.draw -background white -height 3.8c -takefocus 0
       .load.l.paneright add .load.l.paneright.f
       .load.l.paneright add .load.l.paneright.draw -minsize 150
     
    -
       if { ![catch {.load.l.paneright  panecget .load.l.paneright.f -stretch}]} {
         set optnever {-stretch never}
         set optalways {-stretch always}
    @@ -4546,6 +4545,7 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
     
       listbox .load.l.paneright.f.list  -background {grey90} -listvariable file_dialog_files2 -width 20 -height 12\
         -fg black -highlightcolor red -highlightthickness 2 \
    +    -highlightbackground [option get . background {}] \
         -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode $selmode \
         -xscrollcommand ".load.l.paneright.f.xscroll set" -exportselection 0
       scrollbar .load.l.paneright.f.yscroll -command ".load.l.paneright.f.list yview" -takefocus 0
    @@ -4596,10 +4596,12 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
         .load.l.paneleft.list selection set $file_dialog_index1
       }
       label .load.buttons_bot.label  -text { File:}
    -  entry .load.buttons_bot.entry -highlightcolor red -highlightthickness 2
    +  entry .load.buttons_bot.entry -highlightcolor red -highlightthickness 2 \
    +    -highlightbackground [option get . background {}]
       entry_replace_selection .load.buttons_bot.entry
       label .load.buttons_bot.srclab  -text { Search:}
    -  entry .load.buttons_bot.src -width 18 -highlightcolor red -highlightthickness 2
    +  entry .load.buttons_bot.src -width 18 -highlightcolor red -highlightthickness 2 \
    +    -highlightbackground [option get . background {}]
       entry_replace_selection .load.buttons_bot.src
       .load.buttons_bot.src delete 0 end
       .load.buttons_bot.src insert 0 $file_dialog_globfilter
    @@ -4627,28 +4629,6 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
         set file_dialog_retval {   }
       }
     
    -  # radiobutton .load.buttons_bot.all -text All -variable file_dialog_globfilter -value {*} -takefocus 0 \
    -  #    -command {
    -  #       set file_dialog_ext $file_dialog_globfilter
    -  #       setglob $file_dialog_dir1
    -  #       .load.buttons_bot.src delete 0 end
    -  #       .load.buttons_bot.src insert 0 $file_dialog_globfilter
    -  #     }
    -  # radiobutton .load.buttons_bot.sym -text .sym -variable file_dialog_globfilter -value {*.sym} -takefocus 0 \
    -  #    -command {
    -  #       set file_dialog_ext $file_dialog_globfilter
    -  #       setglob $file_dialog_dir1
    -  #       .load.buttons_bot.src delete 0 end
    -  #       .load.buttons_bot.src insert 0 $file_dialog_globfilter
    -  #     }
    -  # radiobutton .load.buttons_bot.sch -text .sch -variable file_dialog_globfilter -value {*.sch} -takefocus 0 \
    -  #    -command {
    -  #       set file_dialog_ext $file_dialog_globfilter
    -  #       setglob $file_dialog_dir1
    -  #       .load.buttons_bot.src delete 0 end
    -  #       .load.buttons_bot.src insert 0 $file_dialog_globfilter
    -  #     }
    -
       button .load.buttons.up -width 5 -text Up -command {load_file_dialog_up  $file_dialog_dir1} -takefocus 0
       label .load.buttons.mkdirlab -text { New dir: } 
       entry .load.buttons.newdir -width 16 -takefocus 0
    @@ -4717,7 +4697,8 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
         destroy .load
         set $global_initdir \"\$file_dialog_dir1\"
       "
    -
    +  bind .load  {.load.buttons.home invoke }
    +  bind .load  {.load.buttons.up invoke }
       ### update
     
       if { [info exists file_dialog_v_sp0] } {
    @@ -4806,6 +4787,133 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
       return [file_dialog_getresult $loadfile $confirm_overwrt]
     }
     
    +
    +
    +#### Display preview of selected symbol
    +proc insert_symbol_focusin {} {
    +  puts insert_symbol_focusin
    +  set sel [.ins.center.left.l curselection]
    +  if {$sel eq {}} {
    +    set sel [.ins.center.left.l index active]
    +    .ins.center.left.l selection set active
    +  } 
    +}
    +
    +proc insert_symbol_preview {} {
    +  puts insert_symbol_preview
    +  global insert_symbol
    +  xschem preview_window close .ins.center.right {}
    +  .ins.center.right configure -bg white
    +  bind .ins.center.right  {}
    +  bind .ins.center.right  {}
    +  set sel [.ins.center.left.l curselection]
    +  if {$sel eq {}} {
    +    set sel [.ins.center.left.l index active]
    +    .ins.center.left.l selection set active
    +  }
    +  if {$sel ne {}} {
    +    set f [lindex $insert_symbol(fullpathlist) $sel 0]
    +    if {$f ne {}} {
    +      set type [is_xschem_file $f]
    +      if {$type ne {0}} {
    +        .ins.center.right configure -bg {}
    +        xschem preview_window create .ins.center.right {}
    +        xschem preview_window draw .ins.center.right [list $f]
    +        bind .ins.center.right  "xschem preview_window draw .ins.center.right [list $f]"
    +        bind .ins.center.right  "xschem preview_window draw .ins.center.right [list $f]"
    +      }
    +    }
    +  }
    +}
    +
    +# new alternate insert_symbol browser
    +#### fill list of files matching pattern
    +proc insert_symbol_filelist {paths {maxdepth 1}} {
    +  puts insert_symbol_filelist
    +  global insert_symbol
    +  set insert_symbol(regex) [.ins.top.pat_e get]
    +  set f [match_file $insert_symbol(regex) $paths $maxdepth]
    +  set insert_symbol(list) {}
    +  set insert_symbol(fullpathlist) {}
    +  .ins.center.left.l activate 0
    +  foreach i $f {
    +    set fname [rel_sym_path $i]
    +    lappend insert_symbol(list) $fname
    +    lappend insert_symbol(fullpathlist) $i
    +  }
    +}
    +
    +proc insert_symbol_place {} {
    +  puts insert_symbol_place
    +  global insert_symbol
    +  set sel [.ins.center.left.l curselection]
    +  if {$sel eq {}} {
    +    set sel [.ins.center.left.l index active]
    +    .ins.center.left.l selection set active
    +  }
    +  if {$sel ne {}} {
    +    set f [lindex $insert_symbol(fullpathlist) $sel 0]
    +    if {$f ne {}} {
    +      set type [is_xschem_file $f]
    +      if {$type ne {0}} {
    +        xschem place_symbol $f
    +      }
    +    }
    +  }
    +}
    +
    +#### paths: list of paths to use for listing symbols
    +#### maxdepth: how many levels to descend for each $paths directory (-1: no limit)
    +proc insert_symbol {{paths {}} {maxdepth 1}} {
    +  global insert_symbol
    +  # xschem set semaphore [expr {[xschem get semaphore] +1}]
    +  toplevel .ins
    +  frame .ins.top -takefocus 0
    +  panedwindow  .ins.center -orient horizontal -height 8c
    +  frame .ins.center.left  -takefocus 0
    +  frame .ins.center.right -width 300 -height 250 -bg white -takefocus 0
    +  .ins.center add .ins.center.left
    +  .ins.center add .ins.center.right
    +  frame .ins.bottom  -takefocus 0
    +  pack .ins.top -side top -fill x
    +  pack .ins.center -side top -expand 1 -fill both
    +  pack .ins.bottom -side top -fill x
    +  listbox .ins.center.left.l -listvariable insert_symbol(list) -width 50 -height 20 \
    +    -yscrollcommand ".ins.center.left.s set" -highlightcolor red -highlightthickness 2 \
    +    -activestyle underline -highlightbackground [option get . background {}] \
    +    -exportselection 0
    +  scrollbar .ins.center.left.s -command ".ins.center.left.l yview" -takefocus 0
    +  pack .ins.center.left.l -expand 1 -fill both -side left
    +  pack .ins.center.left.s -fill y -side left
    +  label .ins.top.pat_l -text Pattern:
    +  entry .ins.top.pat_e -width 20 -highlightcolor red -highlightthickness 2 \
    +     -highlightbackground [option get . background {}]
    +  if {[info exists insert_symbol(regex)]} { 
    +    .ins.top.pat_e insert 0 $insert_symbol(regex)
    +  }
    +  bind .ins.top.pat_e  "
    +    if {{%K} ne {Tab}} {
    +      insert_symbol_filelist [list $paths] [list $maxdepth]
    +    }
    +  "
    +  bind .ins.center.left.l <> { insert_symbol_preview }
    +  bind .ins.center.left.l  { insert_symbol_focusin }
    +  button .ins.bottom.dismiss -takefocus 0 -text Dismiss -command {
    +    xschem preview_window close .ins.center.right {}
    +    destroy .ins
    +  }
    +  button .ins.bottom.insert -takefocus 0 -text Insert -command {
    +    insert_symbol_place
    +  }
    +  pack .ins.bottom.insert .ins.bottom.dismiss -side left
    +  pack .ins.top.pat_l -side left
    +  pack .ins.top.pat_e -side left
    +  insert_symbol_filelist $paths $maxdepth
    +  # tkwait window .ins
    +  # xschem set semaphore [expr {[xschem get semaphore] -1}]
    +}
    +# /new alternate insert_symbol browser
    +
     # get last n path components: example , n=1 --> /aaa/bbb/ccc/ddd.sch -> ccc/ddd.sch
     proc get_cell {s n } {
       set slist [file split $s]
    @@ -6608,24 +6716,24 @@ proc viewdata {data {ro {}} {win .view}} {
       return $tctx::rcode
     }
     
    -proc sub_match_file { f {paths {}} } {
    -  global pathlist match_file_dir_arr
    +proc sub_match_file { f {paths {}} {maxdepth -1} } {
    +  global pathlist match_file_dir_arr match_file_level
       set res {}
       if {$paths eq {}} {set paths $pathlist}
       foreach i $paths {
         foreach j [glob -nocomplain -directory $i *] {
    -      # puts "--> $j  $f"
    -      # set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
           if {[file isdirectory $j] && [file readable $j]} {
    -        set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
    -        if {[array names match_file_dir_arr -exact $jj] == {}} {
    -          set match_file_dir_arr($jj) 1
    -          # puts "********** directory $jj"
    -          set sub_res [sub_match_file $f $j] ;# recursive call
    -          if {$sub_res != {} } {set res [concat $res $sub_res]}
    +        if { $maxdepth == -1 || $match_file_level < $maxdepth} {
    +          set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
    +          if {[array names match_file_dir_arr -exact $jj] == {}} {
    +            set match_file_dir_arr($jj) 1
    +            incr match_file_level
    +            set sub_res [sub_match_file $f $j $maxdepth] ;# recursive call
    +            incr match_file_level -1
    +            if {$sub_res != {} } {set res [concat $res $sub_res]}
    +          }
             }
           } else {
    -        # set fname [file tail $j]
             set fname $j
             if {[regexp $f $fname]} {
               lappend res $j
    @@ -6639,30 +6747,33 @@ proc sub_match_file { f {paths {}} } {
     # find files into $paths directories matching $f
     # use $pathlist global search path if $paths empty
     # recursively descend directories
    -proc match_file  { f {paths {}} } {
    -  global match_file_dir_arr
    +proc match_file  { f {paths {}} {maxdepth -1}  } {
    +  global match_file_dir_arr match_file_level
    +  set match_file_level 0
       catch {unset match_file_dir_arr}
    -  set res  [sub_match_file $f $paths]
    +  set res  [sub_match_file $f $paths $maxdepth]
       catch {unset match_file_dir_arr}
       return $res
     }
     
    -proc sub_find_file { f {paths {}} {first 0} } {
    -  global pathlist match_file_dir_arr
    +proc sub_find_file { f {paths {}} {first 0} {maxdepth -1}} {
    +  global pathlist match_file_dir_arr match_file_level
       set res {}
       if {$paths eq {}} {set paths $pathlist}
       foreach i $paths {
         foreach j [glob -nocomplain -directory $i *] {
    -      # puts "--> $j  $f"
           if {[file isdirectory $j]  && [file readable $j]} {
    -        set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
    -        if {[array names match_file_dir_arr -exact $jj] == {}} {
    -          set match_file_dir_arr($jj) 1
    -          # puts "********** directory $jj"
    -          set sub_res [sub_find_file $f $j $first] ;# recursive call
    -          if {$sub_res != {} } {
    -            set res [concat $res $sub_res]
    -            if {$first} {return $res}
    +        if { $maxdepth == -1 || $match_file_level < $maxdepth} {
    +          set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
    +          if {[array names match_file_dir_arr -exact $jj] == {}} {
    +            set match_file_dir_arr($jj) 1
    +            incr match_file_level
    +            set sub_res [sub_find_file $f $j $first $maxdepth] ;# recursive call
    +            incr match_file_level -1
    +            if {$sub_res != {} } {
    +              set res [concat $res $sub_res]
    +              if {$first} {return $res}
    +            }
               }
             }
           } else {
    @@ -6680,10 +6791,11 @@ proc sub_find_file { f {paths {}} {first 0} } {
     # find given file $f into $paths directories 
     # use $pathlist global search path if $paths empty
     # recursively descend directories
    -proc find_file  { f {paths {}} } {
    -  global match_file_dir_arr
    +proc find_file { f {paths {}} {maxdepth -1}} {
    +  global match_file_dir_arr match_file_level
    +  set match_file_level 0
       catch {unset match_file_dir_arr}
    -  set res  [sub_find_file $f $paths 0]
    +  set res  [sub_find_file $f $paths 0 $maxdepth]
       catch {unset match_file_dir_arr}
       return $res
     }
    @@ -6692,10 +6804,11 @@ proc find_file  { f {paths {}} } {
     # use $pathlist global search path if $paths empty
     # recursively descend directories
     # only return FIRST FOUND
    -proc find_file_first  { f {paths {}} } {
    -  global match_file_dir_arr
    +proc find_file_first  { f {paths {}} {maxdepth -1}} {
    +  global match_file_dir_arr match_file_level
    +  set match_file_level 0
       catch {unset match_file_dir_arr}
    -  set res  [sub_find_file $f $paths 1] 
    +  set res  [sub_find_file $f $paths 1 $maxdepth] 
       catch {unset match_file_dir_arr}
       return $res
     }        
    
    From 0f5c554e334201b1a995858adb5dfe767f72d791 Mon Sep 17 00:00:00 2001
    From: stefan schippers 
    Date: Wed, 12 Mar 2025 02:00:45 +0100
    Subject: [PATCH 54/67] add command `xschem get ui_state`, improvements in proc
     insert_symbol
    
    ---
     doc/xschem_man/developer_info.html |   1 +
     src/scheduler.c                    |   6 ++
     src/xschem.tcl                     | 102 +++++++++++++++++++++--------
     3 files changed, 83 insertions(+), 26 deletions(-)
    
    diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html
    index 06d9e035..e5676ad7 100644
    --- a/doc/xschem_man/developer_info.html
    +++ b/doc/xschem_man/developer_info.html
    @@ -813,6 +813,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
          
  • textlayer layer number for texts
  • top_path get top hier path of current window (always "" for tabbed if)
  • topwindow same as top_path but main window returned as "."
  • +
  • ui_state return UI state
  • version return xschem version
  • wirelayer layer used for wires
  • wires number of wires
  • diff --git a/src/scheduler.c b/src/scheduler.c index c1e66f13..f25ead3d 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1725,6 +1725,12 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_SetResult(interp, top_path,TCL_VOLATILE); } break; + case 'u': + if(!strcmp(argv[2], "ui_state")) { /* return UI state */ + if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} + Tcl_SetResult(interp, my_itoa(xctx->ui_state), TCL_VOLATILE); + } + break; case 'v': if(!strcmp(argv[2], "version")) { /* return xschem version */ Tcl_SetResult(interp, XSCHEM_VERSION, TCL_VOLATILE); diff --git a/src/xschem.tcl b/src/xschem.tcl index ba2cbee2..10b4df34 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -4791,16 +4791,17 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} #### Display preview of selected symbol proc insert_symbol_focusin {} { - puts insert_symbol_focusin + # puts insert_symbol_focusin set sel [.ins.center.left.l curselection] if {$sel eq {}} { set sel [.ins.center.left.l index active] .ins.center.left.l selection set active + insert_symbol_preview } } proc insert_symbol_preview {} { - puts insert_symbol_preview + # puts insert_symbol_preview global insert_symbol xschem preview_window close .ins.center.right {} .ins.center.right configure -bg white @@ -4816,35 +4817,48 @@ proc insert_symbol_preview {} { if {$f ne {}} { set type [is_xschem_file $f] if {$type ne {0}} { + .ins.top.dir_e configure -state normal + .ins.top.dir_e delete 0 end + .ins.top.dir_e insert 0 [file dirname [rel_sym_path $f]] + .ins.top.dir_e configure -state readonly .ins.center.right configure -bg {} xschem preview_window create .ins.center.right {} xschem preview_window draw .ins.center.right [list $f] bind .ins.center.right "xschem preview_window draw .ins.center.right [list $f]" bind .ins.center.right "xschem preview_window draw .ins.center.right [list $f]" } + insert_symbol_place } } } # new alternate insert_symbol browser #### fill list of files matching pattern -proc insert_symbol_filelist {paths {maxdepth 1}} { - puts insert_symbol_filelist +proc insert_symbol_filelist {paths {maxdepth -1}} { + # puts insert_symbol_filelist global insert_symbol set insert_symbol(regex) [.ins.top.pat_e get] + + #check if regex is valid + set err [catch {regexp $insert_symbol(regex) {12345}} res] + if {$err} {return} + set f [match_file $insert_symbol(regex) $paths $maxdepth] set insert_symbol(list) {} set insert_symbol(fullpathlist) {} .ins.center.left.l activate 0 foreach i $f { - set fname [rel_sym_path $i] - lappend insert_symbol(list) $fname - lappend insert_symbol(fullpathlist) $i + set type [regexp $insert_symbol(ext) $i] + if {$type} { + set fname [rel_sym_path $i] + lappend insert_symbol(list) $fname + lappend insert_symbol(fullpathlist) $i + } } } proc insert_symbol_place {} { - puts insert_symbol_place + # puts insert_symbol_place global insert_symbol set sel [.ins.center.left.l curselection] if {$sel eq {}} { @@ -4856,6 +4870,7 @@ proc insert_symbol_place {} { if {$f ne {}} { set type [is_xschem_file $f] if {$type ne {0}} { + xschem abort_operation xschem place_symbol $f } } @@ -4864,9 +4879,10 @@ proc insert_symbol_place {} { #### paths: list of paths to use for listing symbols #### maxdepth: how many levels to descend for each $paths directory (-1: no limit) -proc insert_symbol {{paths {}} {maxdepth 1}} { +proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { global insert_symbol # xschem set semaphore [expr {[xschem get semaphore] +1}] + set insert_symbol(ext) $ext toplevel .ins frame .ins.top -takefocus 0 panedwindow .ins.center -orient horizontal -height 8c @@ -4888,26 +4904,34 @@ proc insert_symbol {{paths {}} {maxdepth 1}} { label .ins.top.pat_l -text Pattern: entry .ins.top.pat_e -width 20 -highlightcolor red -highlightthickness 2 \ -highlightbackground [option get . background {}] + label .ins.top.dir_l -text Dir: + entry .ins.top.dir_e -width 50 -takefocus 0 -state readonly if {[info exists insert_symbol(regex)]} { .ins.top.pat_e insert 0 $insert_symbol(regex) } + + bind .ins {.ins.bottom.dismiss invoke} bind .ins.top.pat_e " if {{%K} ne {Tab}} { insert_symbol_filelist [list $paths] [list $maxdepth] + } else { } " bind .ins.center.left.l <> { insert_symbol_preview } - bind .ins.center.left.l { insert_symbol_focusin } + bind .ins.center.left.l { insert_symbol_focusin } button .ins.bottom.dismiss -takefocus 0 -text Dismiss -command { - xschem preview_window close .ins.center.right {} - destroy .ins + if { [xschem get ui_state] & 8192 } { + xschem abort_operation + } else { + xschem preview_window close .ins.center.right {} + destroy .ins + } } - button .ins.bottom.insert -takefocus 0 -text Insert -command { - insert_symbol_place - } - pack .ins.bottom.insert .ins.bottom.dismiss -side left + pack .ins.bottom.dismiss -side left pack .ins.top.pat_l -side left pack .ins.top.pat_e -side left + pack .ins.top.dir_l -side left + pack .ins.top.dir_e -side left insert_symbol_filelist $paths $maxdepth # tkwait window .ins # xschem set semaphore [expr {[xschem get semaphore] -1}] @@ -6717,15 +6741,25 @@ proc viewdata {data {ro {}} {win .view}} { } proc sub_match_file { f {paths {}} {maxdepth -1} } { - global pathlist match_file_dir_arr match_file_level + global pathlist match_file_dir_arr match_file_level nolist_libs set res {} if {$paths eq {}} {set paths $pathlist} foreach i $paths { foreach j [glob -nocomplain -directory $i *] { + set skip 0 + foreach k $nolist_libs { + if {[regexp $k $j]} { + set skip 1 + break + } + } + if {$skip} { continue } + if {[file isdirectory $j] && [file readable $j]} { if { $maxdepth == -1 || $match_file_level < $maxdepth} { set jj [regsub {/ $} [file normalize ${j}/\ ] {}] - if {[array names match_file_dir_arr -exact $jj] == {}} { + # if {[array names match_file_dir_arr -exact $jj] == {}} { ... } + if {![info exists match_file_dir_arr($jj)]} { set match_file_dir_arr($jj) 1 incr match_file_level set sub_res [sub_match_file $f $j $maxdepth] ;# recursive call @@ -6734,9 +6768,11 @@ proc sub_match_file { f {paths {}} {maxdepth -1} } { } } } else { - set fname $j - if {[regexp $f $fname]} { - lappend res $j + if {![info exists match_file_dir_arr($j)]} { + set match_file_dir_arr($j) 1 + if {[regexp $f $j]} { + lappend res $j + } } } } @@ -6757,15 +6793,26 @@ proc match_file { f {paths {}} {maxdepth -1} } { } proc sub_find_file { f {paths {}} {first 0} {maxdepth -1}} { - global pathlist match_file_dir_arr match_file_level + global pathlist match_file_dir_arr match_file_level nolist_libs set res {} if {$paths eq {}} {set paths $pathlist} foreach i $paths { foreach j [glob -nocomplain -directory $i *] { + + set skip 0 + foreach k $nolist_libs { + if {[regexp $k $j]} { + set skip 1 + break + } + } + if {$skip} { continue } + if {[file isdirectory $j] && [file readable $j]} { if { $maxdepth == -1 || $match_file_level < $maxdepth} { set jj [regsub {/ $} [file normalize ${j}/\ ] {}] - if {[array names match_file_dir_arr -exact $jj] == {}} { + # if {[array names match_file_dir_arr -exact $jj] == {}} { ... } + if {![info exists match_file_dir_arr($jj)]} { set match_file_dir_arr($jj) 1 incr match_file_level set sub_res [sub_find_file $f $j $first $maxdepth] ;# recursive call @@ -6778,9 +6825,12 @@ proc sub_find_file { f {paths {}} {first 0} {maxdepth -1}} { } } else { set fname [file tail $j] - if {$fname == $f} { - lappend res $j - if {$first} {return $res} + if {![info exists match_file_dir_arr($j)]} { + set match_file_dir_arr($j) 1 + if {$fname == $f} { + lappend res $j + if {$first} {return $res} + } } } } From 2a4ebe4b770f8ff98eb72253ae3a638fe561f055 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Wed, 12 Mar 2025 16:59:45 +0100 Subject: [PATCH 55/67] new (optional) symbol placement browser --- src/callback.c | 13 +++- src/xschem.tcl | 172 +++++++++++++++++++++++++++++++++++-------------- src/xschemrc | 13 ++++ 3 files changed, 146 insertions(+), 52 deletions(-) diff --git a/src/callback.c b/src/callback.c index c95383e6..cf4c644c 100644 --- a/src/callback.c +++ b/src/callback.c @@ -3371,14 +3371,21 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m } if((key==XK_Insert && state == ShiftMask) || (key == 'i' && rstate == ControlMask)) /* insert sym */ { - tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + } return; } if((key==XK_Insert) || (key == 'I' && rstate == 0) ) /* insert sym */ { if(xctx->semaphore >= 2) return; - start_place_symbol(); - + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + start_place_symbol(); + } return; } if(key=='s' && SET_MODMASK) /* reload */ diff --git a/src/xschem.tcl b/src/xschem.tcl index 10b4df34..f7107593 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -3982,6 +3982,7 @@ proc open_sub_schematic {{inst {}} {inst_number 0}} { proc is_xschem_file {f} { + # puts "is_xschem_file $f" regsub {\(.*} $f {} f ;# remove trailing generator args (gen.tcl(....)) if any if { ![file exists $f] } { return 0 } elseif { [file isdirectory $f] } { return 0 } @@ -4789,18 +4790,19 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} +####################################################################### +##### new alternate insert_symbol browser +####################################################################### + #### Display preview of selected symbol -proc insert_symbol_focusin {} { - # puts insert_symbol_focusin - set sel [.ins.center.left.l curselection] - if {$sel eq {}} { - set sel [.ins.center.left.l index active] - .ins.center.left.l selection set active - insert_symbol_preview - } +proc insert_symbol_focusin {{paths {}} {maxdepth -1}} { + global insert_symbol new_symbol_browser_ext + xschem abort_operation + set insert_symbol(ext) $new_symbol_browser_ext + insert_symbol_filelist $paths $maxdepth } -proc insert_symbol_preview {} { +proc insert_symbol_preview {{paths {}}} { # puts insert_symbol_preview global insert_symbol xschem preview_window close .ins.center.right {} @@ -4808,19 +4810,26 @@ proc insert_symbol_preview {} { bind .ins.center.right {} bind .ins.center.right {} set sel [.ins.center.left.l curselection] - if {$sel eq {}} { - set sel [.ins.center.left.l index active] - .ins.center.left.l selection set active - } + # if {$sel eq {}} { + # set sel [.ins.center.left.l index active] + # .ins.center.left.l selection set active + # } if {$sel ne {}} { set f [lindex $insert_symbol(fullpathlist) $sel 0] if {$f ne {}} { set type [is_xschem_file $f] if {$type ne {0}} { + set dir [rel_sym_path $f $paths] .ins.top.dir_e configure -state normal .ins.top.dir_e delete 0 end - .ins.top.dir_e insert 0 [file dirname [rel_sym_path $f]] + .ins.top.dir_e insert 0 $dir .ins.top.dir_e configure -state readonly + + + .ins.top2.dir_e configure -state normal + .ins.top2.dir_e delete 0 end + .ins.top2.dir_e insert 0 $f + .ins.top2.dir_e configure -state readonly .ins.center.right configure -bg {} xschem preview_window create .ins.center.right {} xschem preview_window draw .ins.center.right [list $f] @@ -4831,11 +4840,9 @@ proc insert_symbol_preview {} { } } } - -# new alternate insert_symbol browser #### fill list of files matching pattern proc insert_symbol_filelist {paths {maxdepth -1}} { - # puts insert_symbol_filelist + # puts "insert_symbol_filelist: paths=$paths" global insert_symbol set insert_symbol(regex) [.ins.top.pat_e get] @@ -4844,17 +4851,38 @@ proc insert_symbol_filelist {paths {maxdepth -1}} { if {$err} {return} set f [match_file $insert_symbol(regex) $paths $maxdepth] - set insert_symbol(list) {} + set filelist {} set insert_symbol(fullpathlist) {} .ins.center.left.l activate 0 foreach i $f { - set type [regexp $insert_symbol(ext) $i] - if {$type} { - set fname [rel_sym_path $i] - lappend insert_symbol(list) $fname + + set err [catch {regexp $insert_symbol(ext) $i} type] + if {!$err && $type} { + set fname [rel_sym_path $i $paths] + lappend filelist $fname lappend insert_symbol(fullpathlist) $i } } + # sort lists using filelist as key + set files {} + foreach f $filelist ff $insert_symbol(fullpathlist) { + lappend files [list $f $ff] + } + set files [lsort -dictionary -index 0 $files] + set filelist {} + set insert_symbol(fullpathlist) {} + + foreach f $files { + lassign $f ff fff + lappend filelist $ff + lappend insert_symbol(fullpathlist) $fff + } + + # assign listbox variable all at the end, it is faster... + set insert_symbol(list) $filelist + set insert_symbol(nitems) [llength $filelist] + # .ins.center.left.l selection clear 0 end + # .ins.center.left.l selection set 0 } proc insert_symbol_place {} { @@ -4881,10 +4909,16 @@ proc insert_symbol_place {} { #### maxdepth: how many levels to descend for each $paths directory (-1: no limit) proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { global insert_symbol + set paths [cleanup_paths $paths] ;# remove ~ and other strange path combinations # xschem set semaphore [expr {[xschem get semaphore] +1}] set insert_symbol(ext) $ext + if {[winfo exists .ins]} { + raise .ins + return + } toplevel .ins frame .ins.top -takefocus 0 + frame .ins.top2 -takefocus 0 panedwindow .ins.center -orient horizontal -height 8c frame .ins.center.left -takefocus 0 frame .ins.center.right -width 300 -height 250 -bg white -takefocus 0 @@ -4892,6 +4926,7 @@ proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { .ins.center add .ins.center.right frame .ins.bottom -takefocus 0 pack .ins.top -side top -fill x + pack .ins.top2 -side top -fill x pack .ins.center -side top -expand 1 -fill both pack .ins.bottom -side top -fill x listbox .ins.center.left.l -listvariable insert_symbol(list) -width 50 -height 20 \ @@ -4901,24 +4936,32 @@ proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { scrollbar .ins.center.left.s -command ".ins.center.left.l yview" -takefocus 0 pack .ins.center.left.l -expand 1 -fill both -side left pack .ins.center.left.s -fill y -side left + label .ins.top2.dir_l -text {Full path:} + entry .ins.top2.dir_e -width 60 -state readonly \ + -readonlybackground [option get . background {}] -takefocus 0 label .ins.top.pat_l -text Pattern: entry .ins.top.pat_e -width 20 -highlightcolor red -highlightthickness 2 \ -highlightbackground [option get . background {}] - label .ins.top.dir_l -text Dir: - entry .ins.top.dir_e -width 50 -takefocus 0 -state readonly + label .ins.top.dir_l -text { Symbol ref: } + entry .ins.top.dir_e -width 40 -state readonly \ + -readonlybackground [option get . background {}] -takefocus 0 + label .ins.top.ext_l -text Ext: + entry .ins.top.ext_e -width 15 -takefocus 0 -state normal -textvariable new_symbol_browser_ext if {[info exists insert_symbol(regex)]} { .ins.top.pat_e insert 0 $insert_symbol(regex) } bind .ins {.ins.bottom.dismiss invoke} - bind .ins.top.pat_e " - if {{%K} ne {Tab}} { + bind .ins " + if {{%K} eq {Tab} && {%W} eq {.ins.center.left.l}} { insert_symbol_filelist [list $paths] [list $maxdepth] - } else { + insert_symbol_preview [list $paths] } " - bind .ins.center.left.l <> { insert_symbol_preview } - bind .ins.center.left.l { insert_symbol_focusin } + bind .ins.center.left.l <> "insert_symbol_preview [list $paths]" + bind .ins.center.left.l "insert_symbol_focusin [list $paths] [list $maxdepth]" + label .ins.bottom.n -text { N. of items:} + label .ins.bottom.nitems -textvariable insert_symbol(nitems) button .ins.bottom.dismiss -takefocus 0 -text Dismiss -command { if { [xschem get ui_state] & 8192 } { xschem abort_operation @@ -4928,15 +4971,24 @@ proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { } } pack .ins.bottom.dismiss -side left + pack .ins.bottom.n -side left + pack .ins.bottom.nitems -side left + pack .ins.top2.dir_l -side left + pack .ins.top2.dir_e -side left -fill x -expand 1 pack .ins.top.pat_l -side left pack .ins.top.pat_e -side left pack .ins.top.dir_l -side left pack .ins.top.dir_e -side left + pack .ins.top.ext_l -side left + pack .ins.top.ext_e -side left insert_symbol_filelist $paths $maxdepth # tkwait window .ins # xschem set semaphore [expr {[xschem get semaphore] -1}] + return {} } -# /new alternate insert_symbol browser +####################################################################### +##### /new alternate insert_symbol browser +####################################################################### # get last n path components: example , n=1 --> /aaa/bbb/ccc/ddd.sch -> ccc/ddd.sch proc get_cell {s n } { @@ -6785,6 +6837,8 @@ proc sub_match_file { f {paths {}} {maxdepth -1} } { # recursively descend directories proc match_file { f {paths {}} {maxdepth -1} } { global match_file_dir_arr match_file_level + set err [catch {regexp $f {12345}} res] + if {$err} {return {}} set match_file_level 0 catch {unset match_file_dir_arr} set res [sub_match_file $f $paths $maxdepth] @@ -6944,9 +6998,11 @@ proc try_download_url {dirname sch_or_sym} { # if file is in a library directory (a $pathlist dir) # Example: rel_sym_path /home/schippes/share/xschem/xschem_library/devices/iopin.sym # devices/iopin.sym -proc rel_sym_path {symbol} { +proc rel_sym_path {symbol {paths {}} } { global OS pathlist env + # puts "rel_sym_path: $symbol $paths" + if { $paths eq {}} {set paths $pathlist} regsub {^~/} $symbol ${env(HOME)}/ symbol if {$OS eq "Windows"} { if {![regexp {^[A-Za-z]\:/} $symbol]} { @@ -6959,7 +7015,7 @@ proc rel_sym_path {symbol} { } set curr_dirname [pwd] set name {} - foreach path_elem $pathlist { + foreach path_elem $paths { if { ![string compare $path_elem .] && [info exist curr_dirname]} { set path_elem $curr_dirname } @@ -6993,9 +7049,10 @@ proc rel_sym_path {symbol} { # given a symbol reference 'sym' return its absolute path # Example: % abs_sym_path devices/iopin.sch # /home/schippes/share/xschem/xschem_library/devices/iopin.sym -proc abs_sym_path {fname {ext {} } } { +proc abs_sym_path {fname {ext {} } {paths {}}} { global pathlist OS + if { $paths eq {}} {set paths $pathlist} set curr_dirname [pwd] ## empty: do nothing if {$fname eq {} } return {} @@ -7055,9 +7112,9 @@ proc abs_sym_path {fname {ext {} } } { ## if file does not exists but directory does return anyway if { [file exists [file dirname "$tmpfname"]] } { return "$tmpfname" } } - ## if fname is present in one of the pathlist paths get the absolute path + ## if fname is present in one of the paths paths get the absolute path set name {} - foreach path_elem $pathlist { + foreach path_elem $paths { if { ![string compare $path_elem .] && [info exist curr_dirname]} { set path_elem $curr_dirname } @@ -8074,6 +8131,7 @@ set tctx::global_list { hide_symbols incr_hilight incremental_select infix_interface infowindow_text intuitive_interface keep_symbols launcher_default_program light_colors line_width live_cursor2_backannotate local_netlist_dir lvs_ignore lvs_netlist measure_text netlist_dir netlist_show netlist_type + new_symbol_browser new_symbol_browser_depth new_symbol_browser_ext new_symbol_browser_paths no_ask_save no_change_attrs nolist_libs noprint_libs old_selected_tok only_probes orthogonal_wiring path pathlist persistent_command preserve_unchanged_attrs prev_symbol ps_colors ps_paper_size rainbow_colors recentfile retval retval_orig rotated_text search_case search_exact @@ -8534,7 +8592,11 @@ proc build_widgets { {topwin {} } } { } $topwin.menubar.file add command -label "Component browser" -accelerator {Shift-Ins, Ctrl-I} \ -command { - load_file_dialog {Insert symbol} *.sym INITIALINSTDIR 2 + if {$new_symbol_browser} { + insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext + } else { + load_file_dialog {Insert symbol} *.sym INITIALINSTDIR 2 + } } $topwin.menubar.file add command -label "Open" -command "xschem load" -accelerator {Ctrl+O} $topwin.menubar.file add command -label "Open Most Recent" \ @@ -9160,12 +9222,27 @@ proc trace_set_paths {varname idxname op} { uplevel #0 set_paths } } +proc cleanup_paths {paths} { + global env + set pathlist {} + foreach i $paths { + regsub {^~$} $i ${env(HOME)} i + regsub {^~/} $i ${env(HOME)}/ i + if { ![string compare $i .] } { + lappend pathlist $i + } elseif { [ regexp {\.\.\/} $i] } { + lappend pathlist [file normalize $i] + } elseif { [ file exists $i] } { + lappend pathlist $i + } + } + return $pathlist +} # when XSCHEM_LIBRARY_PATH is changed call this function to refresh and cache # new library search path. proc set_paths {} { - global XSCHEM_LIBRARY_PATH env pathlist OS add_all_windows_drives file_dialog_names1 - set pathlist {} + global XSCHEM_LIBRARY_PATH pathlist OS add_all_windows_drives # puts stderr "caching search paths" if { [info exists XSCHEM_LIBRARY_PATH] } { if {$OS == "Windows"} { @@ -9179,17 +9256,8 @@ proc set_paths {} { } else { set pathlist_orig [split $XSCHEM_LIBRARY_PATH :] } - foreach i $pathlist_orig { - regsub {^~$} $i ${env(HOME)} i - regsub {^~/} $i ${env(HOME)}/ i - if { ![string compare $i .] } { - lappend pathlist $i - } elseif { [ regexp {\.\.\/} $i] } { - lappend pathlist [file normalize $i] - } elseif { [ file exists $i] } { - lappend pathlist $i - } - } + + set pathlist [cleanup_paths $pathlist_orig] } if {$pathlist eq {}} { set pathlist [pwd] } set_initial_dirs @@ -9614,6 +9682,12 @@ set_ne tabbed_interface 1 ## case insensitive symbol lookup (on case insensitive filesystems only!) set_ne case_insensitive 0 +## New alternate symbol placement browser (default: not enabled). +set_ne new_symbol_browser 0 +set_ne new_symbol_browser_paths {} ;# if empty use xschem search paths +set_ne new_symbol_browser_depth 2 ;# depth to descend into each dir of the search paths +set_ne new_symbol_browser_ext {\.(sym|tcl)$} ;# file extensions (a regex) to look for + set_ne file_dialog_ext {*} ## remember edit_prop widget size diff --git a/src/xschemrc b/src/xschemrc index 64304589..8bcacf06 100644 --- a/src/xschemrc +++ b/src/xschemrc @@ -566,6 +566,19 @@ #### or tab is open. # set tabbed_interface 0 +########################################################################### +#### ALTERNATE SYMBOL PLACEMENT BROWSER +########################################################################### +#### uses a new symbol placement widget. Default: not enabled (0) +# set new_symbol_browser 1 +#### defines a list of paths to search for. If empty uses all xschem +#### XSCHEM_LIBRARY_PATH paths +# set new_symbol_browser_paths [list ... ... ... ] +#### defines the depth to descend into each path. default: 2 +# set new_symbol_browser_depth 3 +#### defines the extensions to search for. Default: {\.(sym|tcl)$} +# set new_symbol_browser_ext {\.(sch|sym|tcl)$} + ########################################################################### #### CASE INSENSITIVE SYMBOL LOOKUP ########################################################################### From 0c4c8f4d9f3f6ae225e6e76665bc24eebd0e18fa Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 00:48:38 +0100 Subject: [PATCH 56/67] proc insert_symbol: select active listbox cell so symbol placement can start right away --- src/xschem.tcl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index f7107593..97db9639 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -4810,10 +4810,10 @@ proc insert_symbol_preview {{paths {}}} { bind .ins.center.right {} bind .ins.center.right {} set sel [.ins.center.left.l curselection] - # if {$sel eq {}} { - # set sel [.ins.center.left.l index active] - # .ins.center.left.l selection set active - # } + if {$sel eq {}} { + set sel [.ins.center.left.l index active] + .ins.center.left.l selection set active + } if {$sel ne {}} { set f [lindex $insert_symbol(fullpathlist) $sel 0] if {$f ne {}} { From 514b1f3789773171c021563b312ef05f2fae7f76 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 01:45:40 +0100 Subject: [PATCH 57/67] insert_symbol: keep selected listbox item when leaving/entering the widget --- src/xschem.tcl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 97db9639..cfb56305 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -4853,7 +4853,9 @@ proc insert_symbol_filelist {paths {maxdepth -1}} { set f [match_file $insert_symbol(regex) $paths $maxdepth] set filelist {} set insert_symbol(fullpathlist) {} - .ins.center.left.l activate 0 + set sel [.ins.center.left.l curselection] + if {$sel eq {}} { set sel 0} + .ins.center.left.l activate $sel foreach i $f { set err [catch {regexp $insert_symbol(ext) $i} type] From 9808dd14ccbe49c8956a6c1c3b23490cdb7f41a8 Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Thu, 13 Mar 2025 12:54:56 +0530 Subject: [PATCH 58/67] [Minor Addition to .gitignore]: Added an entry to ignore locally used config file for auto-formatting C-code. This helps reduce confusion when reading existing code and makes future contributions easier for newcomer developers. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 9d85221b..01c9e727 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ src/eval_expr.c src/parselabel.c src/parselabel.h +# Config file for C/C++ code formatter (clang-format) +.clang-format + # Executables *.exe src/rawtovcd From 3a9b478a8db3ac74c5291ff47e2ff72f5a1ecb4b Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Thu, 13 Mar 2025 13:08:49 +0530 Subject: [PATCH 59/67] [Resolve Merge Conflicts - 2]: Second stage of changes. --- src/callback.c | 4722 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4722 insertions(+) create mode 100644 src/callback.c diff --git a/src/callback.c b/src/callback.c new file mode 100644 index 00000000..4dd7d3bf --- /dev/null +++ b/src/callback.c @@ -0,0 +1,4722 @@ +/* File: callback.c + * + * This file is part of XSCHEM, + * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit + * simulation. + * Copyright (C) 1998-2024 Stefan Frederik Schippers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xschem.h" + +/* allow to use the Windows keys as alternate for Alt */ +#define SET_MODMASK ( (rstate & Mod1Mask) || (rstate & Mod4Mask) ) +#define EQUAL_MODMASK ( (rstate == Mod1Mask) || (rstate == Mod4Mask) ) + +static int waves_selected(int event, KeySym key, int state, int button) +{ + int rstate; /* state without ShiftMask */ + int i, check; + int graph_use_ctrl_key = tclgetboolvar("graph_use_ctrl_key"); + int is_inside = 0, skip = 0; + static unsigned int excl = STARTZOOM | STARTRECT | STARTLINE | STARTWIRE | + STARTPAN | STARTSELECT | STARTMOVE | STARTCOPY; + int draw_xhair = tclgetboolvar("draw_crosshair"); + rstate = state; /* rstate does not have ShiftMask bit, so easier to test for KeyPress events */ + rstate &= ~ShiftMask; /* don't use ShiftMask, identifying characters is sufficient */ + if(xctx->ui_state & excl) skip = 1; + /* else if(event != -3 && sch_waves_loaded() < 0 ) skip = 1; */ + /* allow to work on graphs even if ctrl released while in GRAPHPAN mode + * This is useful on touchpads with TappingDragLock enabled */ + else if(graph_use_ctrl_key && !(state & ControlMask) && !(xctx->ui_state & GRAPHPAN)) skip = 1; + else if(SET_MODMASK) skip = 1; + else if(event == MotionNotify && (state & Button2Mask)) skip = 1; + else if(event == MotionNotify && (state & Button1Mask) && (state & ShiftMask)) skip = 1; + else if(event == ButtonPress && button == Button2) skip = 1; + else if(event == ButtonPress && button == Button1 && (state & ShiftMask) ) skip = 1; + else if(event == ButtonRelease && button == Button2) skip = 1; + /* else if(event == KeyPress && (state & ShiftMask)) skip = 1; */ + else if(!skip) for(i=0; i< xctx->rects[GRIDLAYER]; ++i) { + double lmargin; + xRect *r; + r = &xctx->rect[GRIDLAYER][i]; + lmargin = (r->x2 - r->x1) / 20.; + lmargin = lmargin < 3. ? 3. : lmargin; + lmargin = lmargin > 20. ? 20. : lmargin; + if(!(r->flags & 1) ) continue; + if( !graph_use_ctrl_key && !(state & ControlMask) && + !strboolcmp(get_tok_value(xctx->rect[GRIDLAYER][i].prop_ptr, "lock", 0), "true")) continue; + + check = + (xctx->ui_state & GRAPHPAN) || + (event != -3 && + ( + POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + lmargin, r->y1 + 8, r->x2 - 20, r->y2 - 8) || + POINTINSIDE(xctx->mousex, xctx->mousey, r->x1, r->y1, r->x1 + 20, r->y1 + 8) || + POINTINSIDE(xctx->mousex, xctx->mousey, r->x2 - 20, r->y2 - 8, r->x2, r->y2) + ) + ) || + ( event == -3 && + (POINTINSIDE(xctx->mousex, xctx->mousey, r->x1, r->y1, r->x2 - 40, r->y1 + 20) || + POINTINSIDE(xctx->mousex, xctx->mousey, r->x1 + 20, r->y1, r->x2 - 30, r->y2 - 10)) + ); + + if(check) { + is_inside = 1; + if(! (xctx->ui_state & GRAPHPAN) ) { + xctx->graph_master = i; + } + if(draw_xhair) draw_crosshair(1, 0); /* remove crosshair, re-enable mouse cursor */ + tclvareval(xctx->top_path, ".drw configure -cursor tcross" , NULL); + break; + } + } + if(!is_inside) { + xctx->graph_master = -1; + xctx->ui_state &= ~GRAPHPAN; /* terminate ongoing GRAPHPAN to avoid deadlocks */ + if(draw_xhair) { + if(tclgetintvar("crosshair_size") == 0) { + tclvareval(xctx->top_path, ".drw configure -cursor none" , NULL); + } else { + tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); + } + } else + tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); + if(xctx->graph_flags & 64) { + tcleval("graph_show_measure stop"); + } + } + return is_inside; +} + +/* do nothing if coordinates not changed unless force is given */ +void redraw_w_a_l_r_p_z_rubbers(int force) +{ + double mx = xctx->mousex_snap; + double my = xctx->mousey_snap; + double origin_shifted_x2, origin_shifted_y2; + + if(!force && xctx->mousex_snap == xctx->prev_rubberx && xctx->mousey_snap == xctx->prev_rubbery) return; + + if(xctx->ui_state & STARTZOOM) zoom_rectangle(RUBBER); + if(xctx->ui_state & STARTWIRE) { + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + if(tclgetboolvar("orthogonal_wiring")) { + new_wire(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap); + /* Origin shift the cartesian coordinate p2(x2,y2) w.r.t. p1(x1,y1) */ + origin_shifted_x2 = xctx->nl_x2 - xctx->nl_x1; + origin_shifted_y2 = xctx->nl_y2 - xctx->nl_y1; + /* Draw whichever component of the resulting orthogonal-wire is bigger (either horizontal or vertical), first */ + if(origin_shifted_x2*origin_shifted_x2 > origin_shifted_y2*origin_shifted_y2){ + xctx->manhattan_lines = 1; + } else { + xctx->manhattan_lines = 2; + } + } + new_wire(RUBBER, mx, my); + } + if(xctx->ui_state & STARTARC) { + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + new_arc(RUBBER, 0, mx, my); + } + if(xctx->ui_state & STARTLINE) { + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + new_line(RUBBER, mx, my); + } + if(xctx->ui_state & STARTRECT) new_rect(RUBBER,mx, my); + if(xctx->ui_state & STARTPOLYGON) { + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + new_polygon(RUBBER, mx, my); + } + xctx->prev_rubberx = xctx->mousex_snap; + xctx->prev_rubbery = xctx->mousey_snap; +} + +/* resets UI state, unselect all and abort any pending operation */ +void abort_operation(void) +{ + xctx->no_draw = 0; + tcleval("set constr_mv 0" ); + dbg(1, "abort_operation(): Escape: ui_state=%d, last_command=%d\n", xctx->ui_state, xctx->last_command); + xctx->constr_mv=0; + + if(xctx->ui_state & STARTPOLYGON) new_polygon(END, xctx->mousex_snap, xctx->mousey_snap); + if(xctx->last_command && xctx->ui_state & (STARTWIRE | STARTLINE)) { + if(xctx->ui_state & STARTWIRE) new_wire(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap); + if(xctx->ui_state & STARTLINE) new_line(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap); + if(tclgetboolvar("draw_crosshair")) draw_crosshair(2, 0); + xctx->ui_state = 0; + return; + } + xctx->last_command=0; + xctx->manhattan_lines = 0; + if(xctx->ui_state & STARTMOVE) + { + move_objects(ABORT,0,0,0); + if(xctx->ui_state & (START_SYMPIN | PLACE_SYMBOL | PLACE_TEXT)) { + int save; + save = xctx->modified; + delete(1/* to_push_undo */); + set_modify(save); /* aborted placement: no change, so reset modify flag set by delete() */ + xctx->ui_state &= ~START_SYMPIN; + xctx->ui_state &= ~PLACE_SYMBOL; + xctx->ui_state &= ~PLACE_TEXT; + } + return; + } + if(xctx->ui_state & STARTCOPY) + { + copy_objects(ABORT); + return; + } + if(xctx->ui_state & STARTMERGE) { + delete(1/* to_push_undo */); + set_modify(0); /* aborted merge: no change, so reset modify flag set by delete() */ + } + xctx->ui_state = 0; + unselect_all(1); + draw(); +} + +static void start_place_symbol(void) +{ + xctx->last_command = 0; + rebuild_selected_array(); + if(xctx->lastsel && xctx->sel_array[0].type==ELEMENT) { + tclvareval("set INITIALINSTDIR [file dirname {", + abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""), "}]", NULL); + } + xctx->mx_double_save = xctx->mousex_snap; + xctx->my_double_save = xctx->mousey_snap; + if(place_symbol(-1,NULL,xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1/* to_push_undo */) ) { + xctx->mousey_snap = xctx->my_double_save; + xctx->mousex_snap = xctx->mx_double_save; + move_objects(START,0,0,0); + xctx->ui_state |= PLACE_SYMBOL; + } +} + +void start_line(double mx, double my) +{ + xctx->last_command = STARTLINE; + if(xctx->ui_state & STARTLINE) { + if(xctx->constr_mv != 2) { + xctx->mx_double_save=mx; + } + if(xctx->constr_mv != 1) { + xctx->my_double_save=my; + } + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + } else { + xctx->manhattan_lines = 0; + xctx->mx_double_save=mx; + xctx->my_double_save=my; + } + new_line(PLACE, mx, my); +} + +void start_wire(double mx, double my) +{ + dbg(1, "start_wire(): ui_state=%d, ui_state2=%d last_command=%d\n", + xctx->ui_state, xctx->ui_state2, xctx->last_command); + xctx->last_command = STARTWIRE; + if(xctx->ui_state & STARTWIRE) { + if(tclgetboolvar("orthogonal_wiring") && !tclgetboolvar("constr_mv")){ + xctx->constr_mv = xctx->manhattan_lines; + new_wire(CLEAR, mx, my); + redraw_w_a_l_r_p_z_rubbers(1); + } + if(xctx->constr_mv != 2) { + xctx->mx_double_save = mx; + } + if(xctx->constr_mv != 1) { + xctx->my_double_save = my; + } + if(xctx->constr_mv == 1) my = xctx->my_double_save; + if(xctx->constr_mv == 2) mx = xctx->mx_double_save; + } else { + xctx->mx_double_save=mx; + xctx->my_double_save=my; + } + new_wire(PLACE,mx, my); + if(tclgetboolvar("orthogonal_wiring") && !tclgetboolvar("constr_mv")){ + xctx->constr_mv = 0; + } +} + +static double interpolate_yval(int idx, int p, double x, int sweep_idx, int point_not_last) +{ + double val = xctx->raw->values[idx][p]; + /* not operating point, annotate from 'b' cursor */ + if(point_not_last && (xctx->raw->allpoints > 1) && sweep_idx >= 0) { + Raw *raw = xctx->raw; + SPICE_DATA *sweep_gv = raw->values[sweep_idx]; + SPICE_DATA *gv = raw->values[idx]; + double dx = sweep_gv[p + 1] - sweep_gv[p]; + double dy = gv[p + 1] - gv[p]; + double offset = x - sweep_gv[p]; + double interp = dx != 0.0 ? offset * dy / dx : 0.0; + val += interp; + } + return val; +} + +void backannotate_at_cursor_b_pos(xRect *r, Graph_ctx *gr) +{ + tcleval("catch {eval $cursor_2_hook}"); + if(sch_waves_loaded() >= 0) { + int dset, first = -1, last, dataset = gr->dataset, i, p, ofs = 0, ofs_end; + double start, end; + int sweepvar_wrap = 0, sweep_idx; + double xx, cursor2; /* xx is the p-th sweep variable value, cursor2 is cursor 'b' x position */ + Raw *raw = xctx->raw; + int save_datasets = -1, save_npoints = -1; + /* transform multiple OP points into a dc sweep */ + if(raw->sim_type && !strcmp(raw->sim_type, "op") && raw->datasets > 1 && raw->npoints[0] == 1) { + save_datasets = raw->datasets; + raw->datasets = 1; + save_npoints = raw->npoints[0]; + raw->npoints[0] = raw->allpoints; + } + sweep_idx = get_raw_index(find_nth(get_tok_value(r->prop_ptr, "sweep", 0), ", ", "\"", 0, 1), NULL); + if(sweep_idx < 0) sweep_idx = 0; + 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); + } else { + cursor2 = xctx->graph_cursor2_x; + } + } else { + cursor2 = xctx->graph_cursor2_x; + } + start = (gr->gx1 <= gr->gx2) ? gr->gx1 : gr->gx2; + end = (gr->gx1 <= gr->gx2) ? gr->gx2 : gr->gx1; + dbg(1, "start=%g, end=%g\n", start, end); + if(gr->logx) { + start = pow(10, start); + end = pow(10, end); + } + dbg(1, "cursor b pos: %g dataset=%d\n", cursor2, gr->dataset); + if(dataset < 0) dataset = 0; /* if all datasets are plotted use first for backannotation */ + dbg(1, "dataset=%d\n", dataset); + ofs = 0; + for(dset = 0 ; dset < raw->datasets; dset++) { + double prev_x, prev_prev_x; + int cnt=0, wrap; + register SPICE_DATA *gv = raw->values[sweep_idx]; + int s=0; + ofs_end = ofs + raw->npoints[dset]; + first = -1; + prev_prev_x = prev_x = 0; + last = ofs; + + /* optimization: skip unwanted datasets, if no dc no need to detect sweep variable wraps */ + if(dataset >= 0 && strcmp(xctx->raw->sim_type, "dc") && dataset != sweepvar_wrap) goto done; + + for(p = ofs ; p < ofs_end; p++) { + xx = gv[p]; + wrap = ( cnt > 1 && XSIGN(xx - prev_x) != XSIGN(prev_x - prev_prev_x)); + if(wrap) { + sweepvar_wrap++; + cnt = 0; + } + if(xx >= start && xx <= end) { + if(dataset == sweepvar_wrap) { + dbg(1, "xx=%g cursor2=%g first=%d last=%d start=%g end=%g p=%d wrap=%d sweepvar_wrap=%d ofs=%d\n", + xx, cursor2, first, last, start, end, p, wrap, sweepvar_wrap, ofs); + if(first == -1) first = p; + if(p == first) { + if(xx == cursor2) {goto found;} + s = XSIGN0(xx - cursor2); + dbg(1, "s=%d\n", s); + } else { + int ss = XSIGN0(xx - cursor2); + dbg(1, "s=%d, ss=%d\n", s, ss); + if(ss != s) {goto found;} + } + last = p; + } + ++cnt; + } /* if(xx >= start && xx <= end) */ + prev_prev_x = prev_x; + prev_x = xx; + } /* for(p = ofs ; p < ofs + raw->npoints[dset]; p++) */ + /* offset pointing to next dataset */ + + done: + + ofs = ofs_end; + sweepvar_wrap++; + } /* for(dset...) */ + found: + if(first != -1) { + if(p > last) { + double sweep0, sweep1; + p = last; + sweep0 = raw->values[sweep_idx][first]; + sweep1 = raw->values[sweep_idx][p]; + if(fabs(sweep0 - cursor2) < fabs(sweep1 - cursor2)) { + p = first; + } + } + dbg(1, "xx=%g, p=%d\n", xx, p); + Tcl_UnsetVar(interp, "ngspice::ngspice_data", TCL_GLOBAL_ONLY); + raw->annot_p = p; + raw->annot_x = cursor2; + raw->annot_sweep_idx = sweep_idx; + for(i = 0; i < raw->nvars; ++i) { + char s[100]; + raw->cursor_b_val[i] = interpolate_yval(i, p, cursor2, sweep_idx, (p < ofs_end)); + my_snprintf(s, S(s), "%.5g", raw->cursor_b_val[i]); + /* tclvareval("array set ngspice::ngspice_data [list {", raw->names[i], "} ", s, "]", NULL); */ + Tcl_SetVar2(interp, "ngspice::ngspice_data", raw->names[i], s, TCL_GLOBAL_ONLY); + } + Tcl_SetVar2(interp, "ngspice::ngspice_data", "n\\ vars", my_itoa( raw->nvars), TCL_GLOBAL_ONLY); + Tcl_SetVar2(interp, "ngspice::ngspice_data", "n\\ points", "1", TCL_GLOBAL_ONLY); + } + if(save_npoints != -1) { /* restore multiple OP points from artificial dc sweep */ + raw->datasets = save_datasets; + raw->npoints[0] = save_npoints; + } + } +} + +/* process user input (arrow keys for now) when only graphs are selected */ + +/* xctx->graph_flags: + * 1: dnu, reserved, used in draw_graphs() + * 2: draw x-cursor1 + * 4: draw x-cursor2 + * 8: dnu, reserved, used in draw_graphs() + * 16: move x-cursor1 + * 32: move x-cursor2 + * 64: show measurement tooltip + * 128: draw y-cursor1 (hcursor) + * 256: draw y-cursor2 (hcursor) + * 512: move y-cursor1 + * 1024: move y-cursor2 + */ +static int waves_callback(int event, int mx, int my, KeySym key, int button, int aux, int state) +{ + Graph_ctx *gr; + int rstate; /* reduced state wit ShiftMask bit filtered out */ + int graph_use_ctrl_key = tclgetboolvar("graph_use_ctrl_key"); + int i, dataset = 0; + int need_fullredraw = 0, need_all_redraw = 0, need_redraw = 0, need_redraw_master = 0; + double xx1 = 0.0, xx2 = 0.0, yy1, yy2; + double delta_threshold = 0.25; + double zoom_m = 0.5; + int save_mouse_at_end = 0, clear_graphpan_at_end = 0; + int track_dset = -2; /* used to find dataset of closest wave to mouse if 't' is pressed */ + xRect *r = NULL; + int access_cond = !graph_use_ctrl_key || (state & ControlMask); + + dbg(1, "uistate=%d, graph_flags=%d\n", xctx->ui_state, xctx->graph_flags); + /* if(event != -3 && !xctx->raw) return 0; */ + rstate = state; /* rstate does not have ShiftMask bit, so easier to test for KeyPress events */ + rstate &= ~ShiftMask; /* don't use ShiftMask, identifying characters is sufficient */ + #if HAS_CAIRO==1 + cairo_save(xctx->cairo_ctx); + cairo_save(xctx->cairo_save_ctx); + xctx->cairo_font = + cairo_toy_font_face_create("Sans-Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_face(xctx->cairo_ctx, xctx->cairo_font); + cairo_set_font_face(xctx->cairo_save_ctx, xctx->cairo_font); + cairo_font_face_destroy(xctx->cairo_font); + #endif + gr = &xctx->graph_struct; + if((i = xctx->graph_master) >= 0 && ((r = &xctx->rect[GRIDLAYER][i])->flags & 1)) { + /* check if this is the master graph (the one containing the mouse pointer) */ + /* determine if mouse pointer is below xaxis or left of yaxis in some graph */ + setup_graph_data(i, 0, gr); + + /* check if user clicked on a wave label -> draw wave in bold */ + if(event == ButtonPress && button == Button3 && + edit_wave_attributes(2, i, gr)) { + draw_graph(i, 1 + 8 + (xctx->graph_flags & (2 | 4 | 128 | 256)), gr, NULL); /* draw data in graph box */ + return 0; + } + + /* destroy / show measurement widget */ + if(xctx->graph_flags & 64) { + char sx[100], sy[100]; + double xval, yval; + if(gr->digital) { + double deltag = gr->gy2 - gr->gy1; + double s1 = DIG_NWAVES; /* 1/DIG_NWAVES waveforms fit in graph if unscaled vertically */ + double s2 = DIG_SPACE; /* (DIG_NWAVES - DIG_SPACE) spacing between traces */ + double c = s1 * deltag; + deltag = deltag * s1 / s2; + yval=(DG_Y(xctx->mousey) - c) / s2; + yval=fmod(yval, deltag ) + gr->gy1; + if(yval > gr->gy2 + deltag * (s1 + s2) * 0.5) yval -= deltag; + } else { + yval = G_Y(xctx->mousey); + } + + xval = G_X(xctx->mousex); + if(gr->logx) xval = pow(10, xval); + if(gr->logy) yval = pow(10, yval); + if(gr->unitx != 1.0) + my_snprintf(sx, S(sx), "%.5g%c", gr->unitx * xval, gr->unitx_suffix); + else + my_strncpy(sx, dtoa_eng(xval), S(sx)); + + if(gr->unitx != 1.0) + my_snprintf(sy, S(sy), "%.4g%c", gr->unity * yval, gr->unity_suffix); + else + my_strncpy(sy, dtoa_eng(yval), S(sy)); + + tclvareval("set measure_text \"y=", sy, "\nx=", sx, "\"", NULL); + tcleval("graph_show_measure"); + } /* if(xctx->graph_flags & 64) */ + + gr->master_gx1 = gr->gx1; + gr->master_gx2 = gr->gx2; + gr->master_gw = gr->gw; + gr->master_cx = gr->cx; + /* move hcursor1 */ + if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 512 )) { + double c; + + c = G_Y(xctx->mousey); + if(gr->logy) c = pow(10, c); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", dtoa(c))); + need_redraw_master = 1; + } + + /* move hcursor2 */ + else if(event == MotionNotify && (state & Button1Mask) && (xctx->graph_flags & 1024 )) { + double c; + + c = G_Y(xctx->mousey); + if(gr->logy) c = pow(10, c); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", dtoa(c))); + 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; + } else { + xctx->graph_top = 0; + } + if(xctx->mousex_snap < W_X(gr->gx1)) { + xctx->graph_left = 1; + } else { + xctx->graph_left = 0; + } + if(xctx->mousey_snap > W_Y(gr->gy1)) { + xctx->graph_bottom = 1; + } else { + xctx->graph_bottom = 0; + } + zoom_m = (xctx->mousex - gr->x1) / gr->w; + if(event == ButtonPress && button == Button1) { + /* dragging cursors when mouse is very close */ + if(xctx->graph_flags & 128) { /* hcursor1 */ + double cursor; + cursor = gr->hcursor1_y; + if(gr->logy ) { + cursor = mylog10(cursor); + } + if(fabs(xctx->mousey - W_Y(cursor)) < 10) { + xctx->graph_flags |= 512; /* Start move hcursor1 */ + } + } + if(xctx->graph_flags & 256) { /* hcursor2 */ + double cursor; + cursor = gr->hcursor2_y; + if(gr->logy ) { + cursor = mylog10(cursor); + } + if(fabs(xctx->mousey - W_Y(cursor)) < 10) { + xctx->graph_flags |= 1024; /* Start move hcursor2 */ + } + } + if(xctx->graph_flags & 2) { /* cursor1 */ + double cursor1; + if(r->flags & 4) { /* private_cursor */ + const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0); + if(s[0]) { + cursor1 = atof_eng(s); + } else { + cursor1 = xctx->graph_cursor1_x; + } + } else { + cursor1 = xctx->graph_cursor1_x; + } + if(gr->logx ) { + cursor1 = mylog10(cursor1); + } + if(fabs(xctx->mousex - W_X(cursor1)) < 10) { + xctx->graph_flags |= 16; /* Start move cursor1 */ + } + } + if(xctx->graph_flags & 4) { /* cursor2 */ + double cursor2; + if(r->flags & 4) { /* private_cursor */ + const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0); + if(s[0]) { + cursor2 = atof_eng(s); + } else { + cursor2 = xctx->graph_cursor2_x; + } + } else { + cursor2 = xctx->graph_cursor2_x; + } + if(gr->logx) { + cursor2 = mylog10(cursor2); + } + if(fabs(xctx->mousex - W_X(cursor2)) < 10) { + xctx->graph_flags |= 32; /* Start move cursor2 */ + } + } + } + else if(event == ButtonPress && button == Button3) { + /* Numerically set cursor position */ + if(xctx->graph_flags & 2) { + double logcursor, cursor; + if(r->flags & 4) { /* private_cursor */ + const char *s = get_tok_value(r->prop_ptr, "cursor1_x", 0); + if(s[0]) { + cursor = atof_spice(s); + } else { + cursor = xctx->graph_cursor1_x; + } + } else { + cursor = xctx->graph_cursor1_x; + } + logcursor = cursor; + if(gr->logx ) { + logcursor = mylog10(cursor); + } + 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(cursor))); + } else { + xctx->graph_cursor1_x = cursor; + } + event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */ + } + need_all_redraw = 1; + } + /* Numerically set cursor position *** DO NOT PUT AN `else if` BELOW *** */ + if(xctx->graph_flags & 4) { + double logcursor, cursor; + int floaters = there_are_floaters(); + if(r->flags & 4) { /* private_cursor */ + const char *s = get_tok_value(r->prop_ptr, "cursor2_x", 0); + if(s[0]) { + cursor = atof_spice(s); + } else { + cursor = xctx->graph_cursor2_x; + } + } else { + cursor = xctx->graph_cursor2_x; + } + logcursor = cursor; + if(gr->logx) { + logcursor = mylog10(cursor); + } + 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(cursor))); + } else { + xctx->graph_cursor2_x = cursor; + } + event = 0; button = 0; /* avoid further processing ButtonPress that might set GRAPHPAN */ + } + 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; + } + } + /* 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)) { + tclvareval("graph_edit_properties ", my_itoa(i), NULL); + } + } + /* x cursor1 toggle */ + else if(key == 'a' && access_cond) { + xctx->graph_flags ^= 2; + need_all_redraw = 1; + if(xctx->graph_flags & 2) { + double c = G_X(xctx->mousex); + + if(gr->logx) c = pow(10, c); + if(r->flags & 4) { + if(!get_tok_value(r->prop_ptr, "cursor1_x", 0)[0]) { + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(c))); + } + } else { + xctx->graph_cursor1_x = c; + } + } + } + /* x cursor2 toggle */ + else if(key == 'b' && access_cond) { + int floaters = there_are_floaters(); + + xctx->graph_flags ^= 4; + if(xctx->graph_flags & 4) { + double c = G_X(xctx->mousex); + + if(gr->logx) c = pow(10, c); + if(r->flags & 4) { + if(!get_tok_value(r->prop_ptr, "cursor2_x", 0)[0]) { + 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(xctx->raw) { + xctx->raw->annot_p = -1; + xctx->raw->annot_sweep_idx = -1; + /* need_all_redraw = 1; */ + need_fullredraw = 1; + } + } + /* swap cursors */ + else if((key == 's' && access_cond) ) { + double tmp, cursor1, cursor2; + int floaters = there_are_floaters(); + + 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); + } else { + cursor1 = xctx->graph_cursor1_x; + } + } else { + cursor1 = xctx->graph_cursor1_x; + } + + 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); + } else { + cursor2 = xctx->graph_cursor2_x; + } + } else { + cursor2 = xctx->graph_cursor2_x; + } + + tmp = cursor2; + cursor2 = cursor1; + cursor1 = tmp; + + if(r->flags & 4) { + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor1_x", dtoa(cursor1))); + } else { + xctx->graph_cursor1_x = cursor1; + } + if(r->flags & 4) { + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "cursor2_x", dtoa(cursor2))); + } else { + xctx->graph_cursor2_x = cursor2; + } + 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; + } + /* measurement tooltip */ + else if((key == 'm') && access_cond) { + xctx->graph_flags ^= 64; + if(!(xctx->graph_flags & 64)) { + tcleval("graph_show_measure stop"); + } + } + else if(key == 't' && access_cond) { + if(!gr->digital) { + const char *d = get_tok_value(r->prop_ptr, "dataset", 0); + if(d[0]) { + track_dset = atoi(d); + } else { + track_dset = -1; /* all datasets */ + } + if(track_dset < 0) { + track_dset = find_closest_wave(i, gr); + } else { + track_dset = -1; /* all datasets */ + } + } + } /* key == 't' */ + } /* if((i = xctx->graph_master) >= 0 && ((r = &xctx->rect[GRIDLAYER][i])->flags & 1)) */ + + /* save mouse position when doing pan operations */ + if( + ( event == ButtonPress && (button == Button1 || button == Button3)) && + !(xctx->ui_state & GRAPHPAN) && + !xctx->graph_top /* && !xctx->graph_bottom */ + ) { + xctx->ui_state |= GRAPHPAN; + if(!xctx->graph_left) xctx->mx_double_save = xctx->mousex_snap; + if(xctx->graph_left) xctx->my_double_save = xctx->mousey_snap; + } + dbg(1, "graph_master=%d\n", xctx->graph_master); + + finish: + + /* parameters for absolute positioning by mouse drag in bottom graph area */ + if( xctx->raw && event == MotionNotify && (state & Button1Mask) && xctx->graph_bottom ) { + int idx; + int dset; + double wwx1, wwx2, pp, delta, ccx, ddx; + + char *rawfile = NULL; + char *sim_type = NULL; + int switched = 0; + + my_strdup2(_ALLOC_ID_, &rawfile, get_tok_value(r->prop_ptr, "rawfile", 0)); + my_strdup2(_ALLOC_ID_, &sim_type, get_tok_value(r->prop_ptr, "sim_type", 0)); + if(rawfile[0] && sim_type[0]) switched = extra_rawfile(2, rawfile, sim_type, -1.0, -1.0); + my_free(_ALLOC_ID_, &rawfile); + my_free(_ALLOC_ID_, &sim_type); + + idx = get_raw_index(find_nth(get_tok_value(r->prop_ptr, "sweep", 0), ", ", "\"", 0, 1), NULL); + dset = dataset == -1 ? 0 : dataset; + + if(idx < 0 ) idx = 0; + delta = gr->gw; + wwx1 = get_raw_value(dset, idx, 0); + wwx2 = get_raw_value(dset, idx, xctx->raw->npoints[dset] - 1); + if(wwx1 == wwx2) wwx2 += 1e-6; + if(gr->logx) { + wwx1 = mylog10(wwx1); + wwx2 = mylog10(wwx2); + } + ccx = (gr->x2 - gr->x1) / (wwx2 - wwx1); + ddx = gr->x1 - wwx1 * ccx; + pp = (xctx->mousex_snap - ddx) / ccx; + xx1 = pp - delta / 2.0; + xx2 = pp + delta / 2.0; + if(switched) extra_rawfile(5, NULL, NULL, -1.0, -1.0); /* switch back to previous raw file */ + } + else if(button == Button3 && (xctx->ui_state & GRAPHPAN) && !xctx->graph_left && !xctx->graph_top) { + /* parameters for zoom area by mouse drag */ + xx1 = G_X(xctx->mx_double_save); + xx2 = G_X(xctx->mousex_snap); + if(state & ShiftMask) { + if(xx1 < xx2) { double tmp; tmp = xx1; xx1 = xx2; xx2 = tmp; } + } else { + if(xx2 < xx1) { double tmp; tmp = xx1; xx1 = xx2; xx2 = tmp; } + } + + if(xx1 == xx2) xx2 += 1e-6; + } + /* loop: after having operated on the master graph do the others */ + for(i=0; i< xctx->rects[GRIDLAYER]; ++i) { + int same_sim_type = 0; + char *curr_sim_type = NULL; + r = &xctx->rect[GRIDLAYER][i]; + my_strdup2(_ALLOC_ID_, &curr_sim_type, get_tok_value(r->prop_ptr, "sim_type", 0)); + need_redraw = 0; + if( !(r->flags & 1) ) continue; /* 1: graph; 3: graph_unlocked */ + gr->gx1 = gr->master_gx1; + gr->gx2 = gr->master_gx2; + gr->gw = gr->master_gw; + setup_graph_data(i, 1, gr); /* skip flag set, no reload x1 and x2 fields */ + if(gr->dataset >= 0 /* && gr->dataset < xctx->raw->datasets */) dataset =gr->dataset; + else dataset = -1; + + /* if master graph has unlocked X axis do not zoom/pan any other graphs: same_sim_type = 0 */ + if(!(xctx->rect[GRIDLAYER][xctx->graph_master].flags & 2) && + !strcmp(curr_sim_type, + get_tok_value(xctx->rect[GRIDLAYER][xctx->graph_master].prop_ptr, "sim_type", 0))) { + same_sim_type = 1; + } + my_free(_ALLOC_ID_, &curr_sim_type); + + if(event == MotionNotify && (state & Button1Mask) && !xctx->graph_bottom && + !(xctx->graph_flags & (16 | 32 | 512 | 1024))) { + double delta; + /* vertical move of waveforms */ + if(xctx->graph_left) { + if(i == xctx->graph_master) { + if(gr->digital) { + delta = gr->posh; + delta_threshold = 0.01; + if(fabs(xctx->my_double_save - xctx->mousey_snap) > fabs(gr->dcy * delta) * delta_threshold) { + yy1 = gr->ypos1 + (xctx->my_double_save - xctx->mousey_snap) / gr->dcy; + yy2 = gr->ypos2 + (xctx->my_double_save - xctx->mousey_snap) / gr->dcy; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + xctx->my_double_save = xctx->mousey_snap; + need_redraw = 1; + } + } else { + delta = gr->gh / gr->divy; + delta_threshold = 0.01; + if(fabs(xctx->my_double_save - xctx->mousey_snap) > fabs(gr->cy * delta) * delta_threshold) { + yy1 = gr->gy1 + (xctx->my_double_save - xctx->mousey_snap) / gr->cy; + yy2 = gr->gy2 + (xctx->my_double_save - xctx->mousey_snap) / gr->cy; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + xctx->my_double_save = xctx->mousey_snap; + need_redraw = 1; + } + } + } + } + /* horizontal move of waveforms */ + else { + save_mouse_at_end = 1; + delta = gr->gw; + delta_threshold = 0.01; + /* selected or locked or master */ + if( r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + dbg(1, "moving waves: %d\n", i); + if(fabs(xctx->mx_double_save - xctx->mousex_snap) > fabs(gr->cx * delta) * delta_threshold) { + xx1 = gr->gx1 + (xctx->mx_double_save - xctx->mousex_snap) / gr->cx; + xx2 = gr->gx2 + (xctx->mx_double_save - xctx->mousex_snap) / gr->cx; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + } + + else if(event == ButtonPress && button == Button5 && !(state & ShiftMask)) { + double delta; + /* vertical move of waveforms with mouse wheel */ + if(xctx->graph_left) { + if(i == xctx->graph_master) { + if(gr->digital) { + delta = gr->posh * 0.05; + yy1 = gr->ypos1 + delta; + yy2 = gr->ypos2 + delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + need_redraw = 1; + } else { + delta = gr->gh/ gr->divy; + delta_threshold = 1.0; + yy1 = gr->gy1 + delta * delta_threshold; + yy2 = gr->gy2 + delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } + } + /* horizontal move of waveforms with mouse wheel */ + else { + /* selected or locked or master */ + if( r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 - delta * delta_threshold; + xx2 =gr->gx2 - delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == ButtonPress && button == Button4 && !(state & ShiftMask)) { + double delta; + /* vertical move of waveforms with mouse wheel */ + if(xctx->graph_left) { + if(i == xctx->graph_master) { + if(gr->digital) { + delta = gr->posh * 0.05; + yy1 = gr->ypos1 - delta; + yy2 = gr->ypos2 - delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + need_redraw = 1; + } else { + delta = gr->gh / gr->divy; + delta_threshold = 1.0; + yy1 = gr->gy1 - delta * delta_threshold; + yy2 = gr->gy2 - delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } + } + /* horizontal move of waveforms with mouse wheel */ + else { + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 + delta * delta_threshold; + xx2 = gr->gx2 + delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == ButtonPress && button == Button5 && (state & ShiftMask)) { + if(xctx->graph_left) { + if(i == xctx->graph_master) { + if(gr->digital) { + double m = DG_Y(xctx->mousey); + double a = m - gr->ypos1; + double b = gr->ypos2 -m; + double delta = gr->posh; + double var = delta * 0.05; + yy2 = gr->ypos2 + var * b / delta; + yy1 = gr->ypos1 - var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + need_redraw = 1; + + } else { + double m = G_Y(xctx->mousey); + double a = m - gr->gy1; + double b = gr->gy2 -m; + double delta = (gr->gy2 - gr->gy1); + double var = delta * 0.2; + yy2 = gr->gy2 + var * b / delta; + yy1 = gr->gy1 - var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } + } else { + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + double var = 0.2 * gr->gw; + xx2 = gr->gx2 + var * (1 - zoom_m); + xx1 = gr->gx1 - var * zoom_m; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == ButtonPress && button == Button4 && (state & ShiftMask)) { + if(xctx->graph_left) { + if(i == xctx->graph_master) { + if(gr->digital) { + double m = DG_Y(xctx->mousey); + double a = m - gr->ypos1; + double b = gr->ypos2 -m; + double delta = (gr->ypos2 - gr->ypos1); + double var = delta * 0.05; + yy2 = gr->ypos2 - var * b / delta; + yy1 = gr->ypos1 + var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + need_redraw = 1; + } else { + double m = G_Y(xctx->mousey); + double a = m - gr->gy1; + double b = gr->gy2 -m; + double delta = (gr->gy2 - gr->gy1); + double var = delta * 0.2; + yy2 = gr->gy2 - var * b / delta; + yy1 = gr->gy1 + var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } + } else { + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + double var = 0.2 * gr->gw; + xx2 = gr->gx2 - var * (1 - zoom_m); + xx1 = gr->gx1 + var * zoom_m; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + /* y hcursor1 toggle */ + else if(event == KeyPress && key == 'A' && access_cond && i == xctx->graph_master) { + xctx->graph_flags ^= 128; + need_redraw = 1; + if(xctx->graph_flags & 128) { + double c = G_Y(xctx->mousey); + if(gr->logy) c = pow(10, c); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", dtoa(c))); + } else { + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor1_y", NULL)); + } + } + /* y hcursor2 toggle */ + else if(event == KeyPress && key == 'B' && access_cond && i == xctx->graph_master) { + xctx->graph_flags ^= 256; + need_redraw = 1; + if(xctx->graph_flags & 256) { + double c = G_Y(xctx->mousey); + if(gr->logy) c = pow(10, c); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", dtoa(c))); + } else { + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "hcursor2_y", NULL)); + } + } + else if(event == KeyPress && key == 't' && access_cond ) { + if(track_dset != -2) { /* -2 means no dataset selection ('t' key) was started */ + /* + const char *unlocked = strstr(get_tok_value(r->prop_ptr, "flags", 0), "unlocked"); + */ + int unlocked = r->flags & 2; + int floaters = there_are_floaters(); + if(i == xctx->graph_master || !unlocked) { + gr->dataset = track_dset; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "dataset", my_itoa(track_dset))); + + } + /* do this here to update texts printing current dataset in graph + * tcleval([xschem getprop rect 2 n dataset]) */ + if(i == xctx->graph_master && floaters) { + set_modify(-2); /* update floater caches to reflect actual backannotation */ + need_fullredraw = 1; + } + if((xctx->graph_flags & 4) && tclgetboolvar("live_cursor2_backannotate")) { + if(i == xctx->graph_master) { + backannotate_at_cursor_b_pos(r, gr); + } + need_fullredraw = 1; + } else { + if(!need_fullredraw) need_redraw = 1; + } + + } + } /* key == 't' */ + else if(event == KeyPress && key == XK_Left) { + double delta; + if(xctx->graph_left) { + if(!gr->digital && i == xctx->graph_master) { + double m = G_Y(xctx->mousey); + double a = m - gr->gy1; + double b = gr->gy2 -m; + double delta = gr->gh; + double var = delta * 0.2; + yy2 = gr->gy2 + var * b / delta; + yy1 = gr->gy1 - var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } else { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 - delta * delta_threshold; + xx2 = gr->gx2 - delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == KeyPress && key == XK_Right) { + double delta; + if(xctx->graph_left) { + if(!gr->digital && i == xctx->graph_master) { + double m = G_Y(xctx->mousey); + double a = m - gr->gy1; + double b = gr->gy2 -m; + double delta = gr->gh; + double var = delta * 0.2; + yy2 = gr->gy2 - var * b / delta; + yy1 = gr->gy1 + var * a / delta; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + need_redraw = 1; + } + } else { + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + delta = gr->gw; + delta_threshold = 0.05; + xx1 = gr->gx1 + delta * delta_threshold; + xx2 = gr->gx2 + delta * delta_threshold; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == KeyPress && key == XK_Down) { + if(!xctx->graph_left) { + /* selected or locked or master */ + if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + double var = 0.2 * gr->gw; + xx2 = gr->gx2 + var * (1 - zoom_m); + xx1 = gr->gx1 - var * zoom_m; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == KeyPress && key == XK_Up) { + if(!xctx->graph_left) { + /* selected or locked or master */ + if(r->sel || !(r->flags & 2) || i == xctx->graph_master) { + double var = 0.2 * gr->gw; + xx2 = gr->gx2 - var * (1 - zoom_m); + xx1 = gr->gx1 + var * zoom_m; + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + else if(event == KeyPress && key == 'f' && access_cond) { + if(xctx->raw && xctx->raw->values) { + if(xctx->graph_left) { /* full Y zoom*/ + if(i == xctx->graph_master) { + need_redraw = graph_fullyzoom(r, gr, dataset); + } /* graph_master */ + } else { /* not graph_left, full X zoom*/ + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + need_redraw = graph_fullxzoom(i, gr, dataset); + } + } + } /* raw->values */ + } /* key == 'f' */ + /* absolute positioning by mouse drag in bottom graph area */ + else if(event == MotionNotify && (state & Button1Mask) && xctx->graph_bottom ) { + if(xctx->raw && xctx->raw->values) { + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + + /* xx1 and xx2 calculated for master graph above */ + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } + } + } + + + else if(event == ButtonRelease) { + if(button != Button3) { + xctx->ui_state &= ~GRAPHPAN; + xctx->graph_flags &= ~(16 | 32 | 512 | 1024); /* clear move cursor flags */ + } + /* zoom X area by mouse drag */ + else if(button == Button3 && (xctx->ui_state & GRAPHPAN) && + !xctx->graph_left && !xctx->graph_top) { + /* selected or locked or master */ + if(r->sel || (same_sim_type && !(r->flags & 2)) || i == xctx->graph_master) { + if(xctx->mx_double_save != xctx->mousex_snap) { + clear_graphpan_at_end = 1; + + /* xx1 and xx2 calculated for master graph above */ + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x1", dtoa(xx1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "x2", dtoa(xx2))); + need_redraw = 1; + } else if(i == xctx->graph_master) { + clear_graphpan_at_end = 1; + } + } + } + /* zoom Y area by mouse drag */ + else if(button == Button3 && (xctx->ui_state & GRAPHPAN) && + xctx->graph_left && !xctx->graph_top) { + /* Only on master */ + if(i == xctx->graph_master) { + if(xctx->my_double_save != xctx->mousey_snap) { + double yy1, yy2; + clear_graphpan_at_end = 1; + if(!gr->digital) { + yy1 = G_Y(xctx->my_double_save); + yy2 = G_Y(xctx->mousey_snap); + if(state & ShiftMask) { + if(yy1 < yy2) { double tmp; tmp = yy1; yy1 = yy2; yy2 = tmp; } + } else { + if(yy2 < yy1) { double tmp; tmp = yy1; yy1 = yy2; yy2 = tmp; } + } + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "y2", dtoa(yy2))); + } else { + yy1 = DG_Y(xctx->my_double_save); + yy2 = DG_Y(xctx->mousey_snap); + if(state & ShiftMask) { + if(yy1 < yy2) { double tmp; tmp = yy1; yy1 = yy2; yy2 = tmp; } + } else { + if(yy2 < yy1) { double tmp; tmp = yy1; yy1 = yy2; yy2 = tmp; } + } + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos1", dtoa(yy1))); + my_strdup(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, "ypos2", dtoa(yy2))); + } + need_redraw = 1; + } else if(i == xctx->graph_master) { + clear_graphpan_at_end = 1; + } + } + } + } /* else if( event == ButtonRelease) */ + if(need_redraw || need_all_redraw || ( i == xctx->graph_master && need_redraw_master) ) { + setup_graph_data(i, 0, gr); + draw_graph(i, 1 + 8 + (xctx->graph_flags & (4 | 2 | 128 | 256)), gr, NULL); /* draw data in each graph box */ + } + } /* for(i=0; i< xctx->rects[GRIDLAYER]; i++ */ + + if(need_fullredraw ==1) { + draw(); + need_fullredraw = 0; + } + if(clear_graphpan_at_end) xctx->ui_state &= ~GRAPHPAN; + /* update saved mouse position after processing all graphs */ + if(save_mouse_at_end) { + if( fabs(xctx->mx_double_save - xctx->mousex_snap) > fabs(gr->master_cx * gr->master_gw) * delta_threshold) { + dbg(1, "save mouse pos\n"); + xctx->mx_double_save = xctx->mousex_snap; + xctx->my_double_save = xctx->mousey_snap; + } + } + + + draw_selection(xctx->gc[SELLAYER], 0); + #if HAS_CAIRO==1 + cairo_restore(xctx->cairo_ctx); + cairo_restore(xctx->cairo_save_ctx); + #endif + return 0; +} + +/* complete the STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */ +static int end_place_move_copy_zoom() +{ + if(xctx->ui_state & STARTZOOM) { + zoom_rectangle(END); + if( xctx->nl_x1 == xctx->nl_x2 && xctx->nl_y1 == xctx->nl_y2) { + return 0; + } + return 1; + } + else if(xctx->ui_state & STARTWIRE) { + if(tclgetboolvar("persistent_command")) { + if(xctx->constr_mv != 2) { + xctx->mx_double_save=xctx->mousex_snap; + } + if(xctx->constr_mv != 1) { + xctx->my_double_save=xctx->mousey_snap; + } + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_wire(PLACE, xctx->mousex_snap, xctx->mousey_snap); + + } else { + new_wire(PLACE|END, xctx->mousex_snap, xctx->mousey_snap); + } + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + return 0; + } + else if(xctx->ui_state & STARTARC) { + new_arc(SET, 0, xctx->mousex_snap, xctx->mousey_snap); + return 0; + } + else if(xctx->ui_state & STARTLINE) { + if(tclgetboolvar("persistent_command")) { + if(xctx->constr_mv != 2) { + xctx->mx_double_save=xctx->mousex_snap; + } + if(xctx->constr_mv == 1) { + xctx->my_double_save=xctx->mousey_snap; + } + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_line(PLACE, xctx->mousex_snap, xctx->mousey_snap); + } else { + new_line(PLACE|END, xctx->mousex_snap, xctx->mousey_snap); + } + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + return 0; + } + else if(xctx->ui_state & STARTRECT) { + new_rect(PLACE|END,xctx->mousex_snap, xctx->mousey_snap); + return 0; + } + else if(xctx->ui_state & STARTPOLYGON) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_polygon(ADD, xctx->mousex_snap, xctx->mousey_snap); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + return 0; + } + else if(xctx->ui_state & STARTMOVE) { + move_objects(END,0,0,0); + xctx->ui_state &=~START_SYMPIN; + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + return 1; + } + else if(xctx->ui_state & STARTCOPY) { + copy_objects(END); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + return 1; + } + return 0; +} + +static void draw_snap_cursor_shape(GC gc, double x, double y, int snapcursor_size) { + /* Convert coordinates to screen space */ + double screen_x = X_TO_SCREEN(x); + double screen_y = Y_TO_SCREEN(y); + double left = screen_x - snapcursor_size; + double right = screen_x + snapcursor_size; + double top = screen_y - snapcursor_size; + double bottom = screen_y + snapcursor_size; + int i; + /* Define crosshair lines */ + double lines[4][4]; + lines[0][0] = screen_x; lines[0][1] = top; lines[0][2] = right; lines[0][3] = screen_y; + lines[1][0] = right; lines[1][1] = screen_y; lines[1][2] = screen_x; lines[1][3] = bottom; + lines[2][0] = screen_x; lines[2][1] = bottom; lines[2][2] = left; lines[2][3] = screen_y; + lines[3][0] = left; lines[3][1] = screen_y; lines[3][2] = screen_x; lines[3][3] = top; + /* Draw crosshair lines */ + for (i = 0; i < 4; i++) { + draw_xhair_line(gc, snapcursor_size, lines[i][0], lines[i][1], lines[i][2], lines[i][3]); + } +} + +static void erase_snap_cursor(double prev_x, double prev_y, int snapcursor_size) { + if (fix_broken_tiled_fill || !_unix) { + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, + (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size, + 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, + 2 * INT_WIDTH(xctx->lw) + 2 * snapcursor_size, + (int)X_TO_SCREEN(prev_x) - INT_WIDTH(xctx->lw) - snapcursor_size, + (int)Y_TO_SCREEN(prev_y) - INT_WIDTH(xctx->lw) - snapcursor_size); + } else { + draw_snap_cursor_shape(xctx->gctiled, prev_x, prev_y, snapcursor_size); + } +} + +static void find_snap_position(double *x, double *y, int pos_changed) { + if (!pos_changed) { + *x = xctx->prev_snapx; + *y = xctx->prev_snapy; + } else { + xctx->closest_pin_found = find_closest_net_or_symbol_pin( + xctx->mousex, xctx->mousey, x, y); + } +} + +/* action == 3 : delete and draw + * action == 1 : delete + * action == 2 : draw + * action == 5 : delete even if pos not changed + */ +static void draw_snap_cursor(int action) { + int snapcursor_size; + int pos_changed; + int prev_draw_window = xctx->draw_window; + int prev_draw_pixmap = xctx->draw_pixmap; + + if (!xctx->mouse_inside) return; /* Early exit if mouse is outside */ + snapcursor_size = tclgetintvar("snap_cursor_size"); + pos_changed = (xctx->mousex_snap != xctx->prev_gridx) || (xctx->mousey_snap != xctx->prev_gridy); + /* Save current drawing context */ + xctx->draw_pixmap = 0; + xctx->draw_window = 1; + if(pos_changed || action == 5) { + /* Erase the cursor */ + if (action & 1) { + erase_snap_cursor(xctx->prev_snapx, xctx->prev_snapy, snapcursor_size); + draw_selection(xctx->gc[SELLAYER], 0); + } + /* Redraw the cursor */ + if (action & 2) { + double new_x, new_y; + find_snap_position(&new_x, &new_y, pos_changed); + draw_snap_cursor_shape(xctx->gc[xctx->crosshair_layer],new_x, new_y, snapcursor_size); + /* Update previous position tracking */ + xctx->prev_gridx = xctx->mousex_snap; + xctx->prev_gridy = xctx->mousey_snap; + xctx->prev_snapx = new_x; + xctx->prev_snapy = new_y; + } + } + /* Restore previous drawing context */ + xctx->draw_window = prev_draw_window; + xctx->draw_pixmap = prev_draw_pixmap; +} + +static void erase_crosshair(int size) { + + int prev_cr_x = (int)X_TO_SCREEN(xctx->prev_crossx); + int prev_cr_y = (int)Y_TO_SCREEN(xctx->prev_crossy); + int lw = INT_WIDTH(xctx->lw); + if(size) { + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size, 2 * lw + 2 * size, 2 * lw + 2 * size, + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size); + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size, 2 * lw + 2 * size, 2 * lw + 2 * size, + prev_cr_x - 1 * lw - size, prev_cr_y - 1 * lw - size); + } else { /* full screen span xhair */ + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + 0, prev_cr_y - 1 * lw, xctx->xrect[0].width, 2 * lw, 0, prev_cr_y - 1 * lw); + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + prev_cr_x - 1 * lw, 0, 2 * lw, xctx->xrect[0].height, prev_cr_x - 1 * lw, 0); + } +} + +static void draw_crosshair_shape(GC gc, double x, double y, int size) +{ + double screen_x = X_TO_SCREEN(x); + double screen_y = Y_TO_SCREEN(y); + if(size) { + draw_xhair_line(gc, size, screen_x - size, screen_y - size, screen_x + size, screen_y - size); + draw_xhair_line(gc, size, screen_x - size, screen_y + size, screen_x + size, screen_y + size); + draw_xhair_line(gc, size, screen_x - size, screen_y - size, screen_x - size, screen_y + size); + draw_xhair_line(gc, size, screen_x + size, screen_y - size, screen_x + size, screen_y + size); + } else { /* full screen span xhair */ + draw_xhair_line(gc, size, xctx->areax1, screen_y, xctx->areax2, screen_y); + draw_xhair_line(gc, size, screen_x, xctx->areay1, screen_x, xctx->areay2); + } +} + +/* what == 3 (+4) : delete and draw (force) + * what == 1 (+4) : delete (force) + * what == 2 (+4) : draw (force) + * what == 4 : force (re)clear and/or (re)draw even if on same point */ +void draw_crosshair(int what, int state) +{ + int sdw, sdp; + int xhair_size = tclgetintvar("crosshair_size"); + int snap_cursor = tclgetintvar("snap_cursor"); + double mx, my; + int changed = 0; + dbg(1, "draw_crosshair(): what=%d\n", what); + sdw = xctx->draw_window; + sdp = xctx->draw_pixmap; + + if(!xctx->mouse_inside) return; + mx = xctx->mousex_snap; + my = xctx->mousey_snap; + if( ( (xctx->ui_state & (MENUSTART | STARTWIRE) ) || xctx->ui_state == 0 ) && + (state == ShiftMask)) { + if(!snap_cursor) { + /* mouse not changed so closest net or symbol pin unchanged too */ + if(mx == xctx->prev_m_crossx && my == xctx->prev_m_crossy) { + mx = xctx->prev_crossx; /* get previous one */ + my = xctx->prev_crossy; + } else { + /* mouse position changed, so find new closest net or pin */ + find_closest_net_or_symbol_pin(xctx->mousex_snap, xctx->mousey_snap, &mx, &my); + changed = 1; /* we force a cursor redraw */ + dbg(1, "find\n"); + } + } else { + /* draw_snap_cursor(what); */ + } + } + + /* no changed closest pin/net, no force, mx,my is not changed. --> do nothing + | _____________| | + | | _____________________|____________________________ */ + if(!changed && !(what & 4) && mx == xctx->prev_crossx && my == xctx->prev_crossy) { + return; + } + dbg(1, "draw %d\n", what); + xctx->draw_pixmap = 0; + xctx->draw_window = 1; + if(what & 1) { /* delete previous */ + if(fix_broken_tiled_fill || !_unix) { + erase_crosshair(xhair_size); + } else { + draw_crosshair_shape(xctx->gctiled, xctx->prev_crossx, xctx->prev_crossy, xhair_size); + } + } + if(what & 2) { /* draw new */ + draw_crosshair_shape(xctx->gc[xctx->crosshair_layer], mx, my, xhair_size); + } + if(what) draw_selection(xctx->gc[SELLAYER], 0); + + if(what & 2) { + /* previous closest pin or net position (if snap wire or Shift pressed) */ + xctx->prev_crossx = mx; + xctx->prev_crossy = my; + /* previous mouse_snap position */ + xctx->prev_m_crossx = xctx->mousex_snap; + xctx->prev_m_crossy = xctx->mousey_snap; + } + xctx->draw_window = sdw; + xctx->draw_pixmap = sdp; +} + +static void unselect_at_mouse_pos(int mx, int my) +{ + xctx->last_command = 0; + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + select_object(xctx->mousex, xctx->mousey, 0, 0, NULL); + rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */ +} + +static void snapped_wire(double c_snap) +{ + double x, y; + if(!(xctx->ui_state & STARTWIRE)){ + find_closest_net_or_symbol_pin(xctx->mousex, xctx->mousey, &x, &y); + xctx->mx_double_save = my_round(x / c_snap) * c_snap; + xctx->my_double_save = my_round(y / c_snap) * c_snap; + xctx->manhattan_lines = 1; + new_wire(PLACE, x, y); + new_wire(RUBBER, xctx->mousex_snap,xctx->mousey_snap); + } + else { + find_closest_net_or_symbol_pin(xctx->mousex, xctx->mousey, &x, &y); + new_wire(RUBBER, x, y); + new_wire(PLACE|END, x, y); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + if((xctx->ui_state & MENUSTART) && !tclgetboolvar("persistent_command") ) xctx->ui_state &= ~MENUSTART; /*CD*/ + } +} + +static int check_menu_start_commands(int state, double c_snap, int mx, int my) +{ + dbg(1, "check_menu_start_commands(): ui_state=%x, ui_state2=%x last_command=%d\n", + xctx->ui_state, xctx->ui_state2, xctx->last_command); + + if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTDESEL) ) { + if(xctx->ui_state & DESEL_CLICK) { + unselect_at_mouse_pos(mx, my); + } else { /* unselect by area */ + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex; + xctx->my_double_save=xctx->mousey; + xctx->ui_state |= DESEL_AREA; + } + return 1; + } + if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTWIRECUT)) { + break_wires_at_point(xctx->mousex_snap, xctx->mousey_snap, 1); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTWIRECUT2)) { + break_wires_at_point(xctx->mousex_snap, xctx->mousey_snap, 0); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTMOVE)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + /* stretch nets that land on selected instance pins if connect_by_kissing == 2 */ + /* select_attached_nets(); */ + move_objects(START,0,0,0); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTCOPY)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + copy_objects(START); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTWIRE)) { + int prev_state = xctx->ui_state; + if(xctx->semaphore >= 2) return 0; + if( state & ShiftMask) { + snapped_wire(c_snap); + } else { + start_wire(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTWIRE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + } + + /* + * xctx->mx_double_save=xctx->mousex_snap; + * xctx->my_double_save=xctx->mousey_snap; + * new_wire(PLACE, xctx->mousex_snap, xctx->mousey_snap); + * xctx->ui_state &=~MENUSTART; + */ + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTSNAPWIRE)) { + snapped_wire(c_snap); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTLINE)) { + int prev_state = xctx->ui_state; + if(xctx->semaphore >= 2) return 0; + start_line(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTLINE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + + /* + * xctx->mx_double_save=xctx->mousex_snap; + * xctx->my_double_save=xctx->mousey_snap; + * new_line(PLACE, xctx->mousex_snap, xctx->mousey_snap); + * xctx->ui_state &=~MENUSTART; + */ + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTRECT)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + new_rect(PLACE,xctx->mousex_snap, xctx->mousey_snap); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTPOLYGON)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + new_polygon(PLACE, xctx->mousex_snap, xctx->mousey_snap); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTARC)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + new_arc(PLACE, 180., xctx->mousex_snap, xctx->mousey_snap); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTCIRCLE)) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + new_arc(PLACE, 360., xctx->mousex_snap, xctx->mousey_snap); + return 1; + } + else if((xctx->ui_state & MENUSTART) && (xctx->ui_state2 & MENUSTARTZOOM)) { + zoom_rectangle(START); + return 1; + } + return 0; +} + +static int add_wire_from_inst(Selected *sel, double mx, double my) +{ + int res = 0; + int prev_state = xctx->ui_state; + int i, type = sel->type; + double pinx0, piny0; + if(type == ELEMENT) { + int n = sel->n; + xSymbol *symbol = xctx->sym + xctx->inst[n].ptr; + int npin = symbol->rects[PINLAYER]; + for(i = 0; i < npin; ++i) { + get_inst_pin_coord(n, i, &pinx0, &piny0); + if(pinx0 == mx && piny0 == my) { + break; + } + } + if(i < npin) { + dbg(1, "pin: %g %g\n", pinx0, piny0); + unselect_all(1); + start_wire(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTWIRE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + res = 1; + } + } + return res; +} + +static int add_wire_from_wire(Selected *sel, double mx, double my) +{ + int res = 0; + int prev_state = xctx->ui_state; + int type = sel->type; + if(type == WIRE) { + int n = sel->n; + double x1 = xctx->wire[n].x1; + double y1 = xctx->wire[n].y1; + double x2 = xctx->wire[n].x2; + double y2 = xctx->wire[n].y2; + if( (mx == x1 && my == y1) || (mx == x2 && my == y2) ) { + unselect_all(1); + start_wire(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTWIRE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + res = 1; + } + } + return res; +} + +/* sets xctx->shape_point_selected */ +static int edit_line_point(int state) +{ + int line_n = -1, line_c = -1; + dbg(1, "1 Line selected\n"); + line_n = xctx->sel_array[0].n; + line_c = xctx->sel_array[0].col; + /* lineangle point: Check is user is clicking a control point of a lineangle */ + if(line_n >= 0) { + xLine *p = &xctx->line[line_c][line_n]; + + xctx->need_reb_sel_arr=1; + if(xctx->mousex_snap == p->x1 && xctx->mousey_snap == p->y1) { + xctx->shape_point_selected = 1; + p->sel = SELECTED1; + } + else if(xctx->mousex_snap == p->x2 && xctx->mousey_snap == p->y2) { + xctx->shape_point_selected = 1; + p->sel = SELECTED2; + } + if(xctx->shape_point_selected) { + /* move one line selected point */ + if(!(state & (ControlMask | ShiftMask))){ + /* xctx->push_undo(); */ + move_objects(START,0,0,0); + return 1; + } + } /* if(xctx->shape_point_selected) */ + } /* if(line_n >= 0) */ + return 0; +} + +/* sets xctx->shape_point_selected */ +static int edit_wire_point(int state) +{ + int wire_n = -1; + dbg(1, "edit_wire_point, ds = %g\n", xctx->cadhalfdotsize); + wire_n = xctx->sel_array[0].n; + /* wire point: Check is user is clicking a control point of a wire */ + if(wire_n >= 0) { + xWire *p = &xctx->wire[wire_n]; + + xctx->need_reb_sel_arr=1; + if(xctx->mousex_snap == p->x1 && xctx->mousey_snap == p->y1) { + xctx->shape_point_selected = 1; + p->sel = SELECTED1; + } + else if(xctx->mousex_snap == p->x2 && xctx->mousey_snap == p->y2) { + xctx->shape_point_selected = 1; + p->sel = SELECTED2; + } + if(xctx->shape_point_selected) { + /* move one wire selected point */ + if(!(state & (ControlMask | ShiftMask))){ + /* xctx->push_undo(); */ + move_objects(START,0,0,0); + return 1; + } + } /* if(xctx->shape_point_selected) */ + } /* if(wire_n >= 0) */ + return 0; +} + +/* sets xctx->shape_point_selected */ +static int edit_rect_point(int state) +{ + int rect_n = -1, rect_c = -1; + dbg(1, "1 Rectangle selected\n"); + rect_n = xctx->sel_array[0].n; + rect_c = xctx->sel_array[0].col; + /* rectangle point: Check is user is clicking a control point of a rectangle */ + if(rect_n >= 0) { + double ds = xctx->cadhalfdotsize * 2; + xRect *p = &xctx->rect[rect_c][rect_n]; + + xctx->need_reb_sel_arr=1; + if(POINTINSIDE(xctx->mousex, xctx->mousey, p->x1, p->y1, p->x1 + ds, p->y1 + ds)) { + xctx->shape_point_selected = 1; + p->sel = SELECTED1; + } + else if(POINTINSIDE(xctx->mousex, xctx->mousey, p->x2 - ds, p->y1, p->x2, p->y1 + ds)) { + xctx->shape_point_selected = 1; + p->sel = SELECTED2; + } + else if(POINTINSIDE(xctx->mousex, xctx->mousey, p->x1, p->y2 - ds, p->x1 + ds, p->y2)) { + xctx->shape_point_selected = 1; + p->sel = SELECTED3; + } + else if(POINTINSIDE(xctx->mousex, xctx->mousey, p->x2 - ds, p->y2 - ds, p->x2, p->y2)) { + xctx->shape_point_selected = 1; + p->sel = SELECTED4; + } + if(xctx->shape_point_selected) { + /* move one rectangle selected point */ + if(!(state & (ControlMask | ShiftMask))){ + /* xctx->push_undo(); */ + move_objects(START,0,0,0); + return 1; + } + } /* if(xctx->shape_point_selected) */ + } /* if(rect_n >= 0) */ + return 0; +} + +/* sets xctx->shape_point_selected */ +static int edit_polygon_point(int state) +{ + int poly_n = -1, poly_c = -1; + dbg(1, "1 Polygon selected\n"); + poly_n = xctx->sel_array[0].n; + poly_c = xctx->sel_array[0].col; + /* polygon point: Check is user is clicking a control point of a polygon */ + if(poly_n >= 0) { + int i; + double ds = xctx->cadhalfdotsize; + int c = poly_c; + int n = poly_n; + xPoly *p = &xctx->poly[c][n]; + + xctx->need_reb_sel_arr=1; + for(i = 0; i < p->points; i++) { + if( + POINTINSIDE(xctx->mousex, xctx->mousey, p->x[i] - ds, p->y[i] - ds, + p->x[i] + ds, p->y[i] + ds) + ) { + dbg(1, "selecting point %d\n", i); + p->selected_point[i] = 1; + xctx->shape_point_selected = 1; + break; + } + } + if(xctx->shape_point_selected) { + int j; + int points = p->points; + + /* add a new polygon/bezier point after selected one and start moving it*/ + if(state & ShiftMask) { + xctx->push_undo(); + points++; + my_realloc(_ALLOC_ID_, &p->x, sizeof(double) * points); + my_realloc(_ALLOC_ID_, &p->y, sizeof(double) * points); + my_realloc(_ALLOC_ID_, &p->selected_point, sizeof(unsigned short) * points); + p->selected_point[i] = 0; + for(j = points - 2; j > i; j--) { + p->x[j + 1] = p->x[j]; + p->y[j + 1] = p->y[j]; + p->selected_point[j + 1] = p->selected_point[j]; + } + p->selected_point[i + 1] = 1; + p->x[i + 1] = p->x[i]; + p->y[i + 1] = p->y[i]; + p->points = points; + p->sel = SELECTED1; + move_objects(START,0,0,0); + return 1; + } + /* delete polygon/bezier selected point */ + else if(points > 2 && state & ControlMask) { + xctx->push_undo(); + points--; + for(j = i ; j < points ; j++) { + p->x[j] = p->x[j + 1]; + p->y[j] = p->y[j + 1]; + p->selected_point[j] = p->selected_point[j + 1]; + } + my_realloc(_ALLOC_ID_, &p->x, sizeof(double) * points); + my_realloc(_ALLOC_ID_, &p->y, sizeof(double) * points); + my_realloc(_ALLOC_ID_, &p->selected_point, sizeof(unsigned short) * points); + p->points = points; + p->sel = SELECTED; + return 1; + /* move one polygon/bezier selected point */ + } else if(!(state & (ControlMask | ShiftMask))){ + /* xctx->push_undo(); */ + p->sel = SELECTED1; + move_objects(START,0,0,0); + return 1; + } + } /* if(xctx->shape_point_selected) */ + } /* if(poly_n >= 0) */ + return 0; +} + +static void context_menu_action(double mx, double my) +{ + int ret; + const char *status; + int prev_state; + xctx->semaphore++; + status = tcleval("context_menu"); + xctx->semaphore--; + if(!status) return; + ret = atoi(status); + switch(ret) { + case 1: + start_place_symbol(); + break; + case 2: + prev_state = xctx->ui_state; + start_wire(mx, my); + if(prev_state == STARTWIRE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + break; + case 3: + prev_state = xctx->ui_state; + start_line(mx, my); + if(prev_state == STARTLINE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + break; + case 4: + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_rect(PLACE,mx, my); + break; + case 5: + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_polygon(PLACE, mx, my); + break; + case 6: /* place text */ + xctx->last_command = 0; + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + if(place_text(0, mx, my)) { /* 1 = draw text */ + xctx->mousey_snap = xctx->my_double_save; + xctx->mousex_snap = xctx->mx_double_save; + move_objects(START,0,0,0); + xctx->ui_state |= PLACE_TEXT; + } + break; + case 7: /* cut selection into clipboard */ + rebuild_selected_array(); + if(xctx->lastsel) { /* 20071203 check if something selected */ + save_selection(2); + delete(1/* to_push_undo */); + } + break; + case 8: /* paste from clipboard */ + merge_file(2,".sch"); + break; + case 9: /* load most recent file */ + tclvareval("xschem load -gui [lindex $recentfile 0]", NULL); + break; + case 10: /* edit attributes */ + edit_property(0); + break; + case 11: /* edit attributes in editor */ + edit_property(1); + break; + case 12: + descend_schematic(0, 1, 1, 1); + break; + case 13: + descend_symbol(); + break; + case 14: + go_back(1); + break; + case 15: /* copy selection into clipboard */ + rebuild_selected_array(); + if(xctx->lastsel) { + save_selection(2); + } + break; + case 16: /* move selection */ + if(!(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + } + break; + case 17: /* duplicate selection */ + if(!(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + copy_objects(START); + } + break; + case 18: /* delete selection */ + if(xctx->ui_state & SELECTION) delete(1/* to_push_undo */); + break; + case 19: /* place arc */ + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_arc(PLACE, 180., mx, my); + break; + case 20: /* place circle */ + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_arc(PLACE, 360., mx, my); + break; + case 21: /* abort & redraw */ + abort_operation(); + break; + default: + break; + } +} + +/* Mouse wheel events */ +static int handle_mouse_wheel(int event, int mx, int my, KeySym key, int button, int aux, int state) +{ + int graph_use_ctrl_key = tclgetboolvar("graph_use_ctrl_key"); + if(button==Button5 && state == 0 ) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return 1; + } + view_unzoom(CADZOOMSTEP); + return 0; + } + else if(button==Button4 && state == 0 ) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return 1; + } + view_zoom(CADZOOMSTEP); + return 0; + } + if(!graph_use_ctrl_key) { + if(button==Button4 && (state & ShiftMask) && !(state & Button2Mask)) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return 1; + } + xctx->xorigin+=-CADMOVESTEP*xctx->zoom/2.; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + else if(button==Button5 && (state & ShiftMask) && !(state & Button2Mask)) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return 1; + } + xctx->xorigin-=-CADMOVESTEP*xctx->zoom/2.; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + else if(button==Button4 && (state & ControlMask) && !(state & Button2Mask)) { + xctx->yorigin+=-CADMOVESTEP*xctx->zoom/2.; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + else if(button==Button5 && (state & ControlMask) && !(state & Button2Mask)) { + xctx->yorigin-=-CADMOVESTEP*xctx->zoom/2.; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + } + return 0; +} + +static void end_shape_point_edit(double c_snap) +{ + int save = xctx->modified; + double sx, sy; + dbg(1, "%g %g %g %g\n", + xctx->mx_double_save, xctx->my_double_save, xctx->mousex_snap, xctx->mousey_snap); + if(xctx->lastsel == 1 && xctx->sel_array[0].type==POLYGON) { + int k; + int n = xctx->sel_array[0].n; + int c = xctx->sel_array[0].col; + move_objects(END,0,0,0); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->poly[c][n].sel = SELECTED; + xctx->shape_point_selected = 0; + for(k=0; kpoly[c][n].points; ++k) { + xctx->poly[c][n].selected_point[k] = 0; + } + xctx->need_reb_sel_arr=1; + } + else if(xctx->lastsel == 1 && xctx->sel_array[0].type==xRECT) { + int n = xctx->sel_array[0].n; + int c = xctx->sel_array[0].col; + move_objects(END,0,0,0); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->rect[c][n].sel = SELECTED; + xctx->shape_point_selected = 0; + xctx->need_reb_sel_arr=1; + } + else if(xctx->lastsel == 1 && xctx->sel_array[0].type==LINE) { + int n = xctx->sel_array[0].n; + int c = xctx->sel_array[0].col; + move_objects(END,0,0,0); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->line[c][n].sel = SELECTED; + xctx->shape_point_selected = 0; + xctx->need_reb_sel_arr=1; + } + else if(xctx->lastsel == 1 && xctx->sel_array[0].type==WIRE) { + int n = xctx->sel_array[0].n; + move_objects(END,0,0,0); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->wire[n].sel = SELECTED; + xctx->shape_point_selected = 0; + xctx->need_reb_sel_arr=1; + } + sx = my_round(xctx->mx_double_save / c_snap) * c_snap; + sy = my_round(xctx->my_double_save / c_snap) * c_snap; + + if(sx == xctx->mousex_snap && sy == xctx->mousey_snap) { + set_modify(save); + } +} + +#if defined(__unix__) && HAS_CAIRO==1 +static int grabscreen(const char *win_path, int event, int mx, int my, KeySym key, + int button, int aux, int state) +{ + static int grab_state = 0; + static int x1, y1, x2, y2; + int rmx, rmy, wmx, wmy; + unsigned int msq; + Window rw, cw; + XSetWindowAttributes winattr; + XGCValues gcv; + static GC gc = NULL; + static Window clientwin = 0; + static int first_motion = 1; + static int displayh = 0, displayw = 0; + static unsigned long white = 0; + + if(grab_state == 0 && event == ButtonPress && button == Button1) { + unsigned long gcvm = GCFunction | GCForeground; + + white = WhitePixel(display, screen_number); + displayh = DisplayHeight(display, screen_number); + displayw = DisplayWidth(display, screen_number); + + XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq); + gcv.function = GXxor; + gcv.foreground = white; + gc = XCreateGC(display, rw, gcvm, &gcv); + + winattr.override_redirect = True; + clientwin = XCreateWindow(display, rw, 0, 0, displayw, displayh, 0, screendepth, + InputOutput, visual, CWOverrideRedirect, &winattr); + XMapRaised(display,clientwin); + + x1 = rmx; + y1 = rmy; + dbg(1, "grabscreen(): got point1: %d %d\n", x1, y1); + grab_state = 1; + } + + if(grab_state == 1 && event == MotionNotify) { + static int xx1, xx2, yy1, yy2; + xx1 = x1; yy1 = y1; xx2 = x2; yy2 = y2; + INT_RECTORDER(xx1, yy1, xx2, yy2); + dbg(1, "Motion: %d %d %d %d\n", xx1, yy1, xx2, yy2); + if(!first_motion) { + XDrawRectangle(display, clientwin, gc, xx1 - 1, yy1 - 1, xx2 - xx1 + 2, yy2 - yy1 + 2); + } + first_motion = 0; + XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq); + x2 = xx2 = rmx; + y2 = yy2 = rmy; + xx1 = x1; yy1 = y1; + INT_RECTORDER(xx1, yy1, xx2, yy2); + XDrawRectangle(display, clientwin, gc, xx1 - 1, yy1 - 1, xx2 - xx1 + 2, yy2 - yy1 + 2); + } + + if(grab_state == 1 && event == ButtonRelease) { + int grab_w = 0, grab_h = 0; + cairo_surface_t *sfc = NULL, *subsfc = NULL; + png_to_byte_closure_t closure; + char *encoded_data = NULL; + size_t olength; + char *prop = NULL; + + + grab_state = 0; + first_motion = 1; + xctx->ui_state &= ~GRABSCREEN; + XQueryPointer(display, xctx->window, &rw, &cw , &rmx, &rmy, &wmx, &wmy, &msq); + x2 = rmx; + y2 = rmy; + INT_RECTORDER(x1, y1, x2, y2); + tclvareval("grab release ", xctx->top_path, ".drw", NULL); + if(x2 - x1 > 10 && y2 -y1 > 10) { + xctx->push_undo(); + grab_w = (x2 - x1 + 1); + grab_h = (y2 - y1 + 1); + dbg(1, "grabscreen(): grab area: %d %d - %d %d\n", x1, y1, x2, y2); + dbg(1, "grabscreen(): root w=%d, h=%d\n", displayw, displayh); + sfc = cairo_xlib_surface_create(display, rw, visual, displayw, displayh); + if(!sfc || cairo_surface_status(sfc) != CAIRO_STATUS_SUCCESS) { + dbg(0, "grabscreen(): failure creating sfc\n"); + XFreeGC(display, gc); + XDestroyWindow(display, clientwin); + return 0; + } + dbg(1, "sfc: w=%d, h=%d\n", + cairo_xlib_surface_get_width(sfc), + cairo_xlib_surface_get_height(sfc)); + subsfc = cairo_surface_create_for_rectangle(sfc, x1, y1, grab_w, grab_h); + if(!subsfc || cairo_surface_status(subsfc) != CAIRO_STATUS_SUCCESS) { + dbg(0, "grabscreen(): failure creating subsfc\n"); + cairo_surface_destroy(sfc); + XFreeGC(display, gc); + XDestroyWindow(display, clientwin); + return 0; + } + closure.buffer = NULL; + closure.size = 0; + closure.pos = 0; + cairo_surface_write_to_png_stream(subsfc, png_writer, &closure); + cairo_surface_destroy(subsfc); + cairo_surface_destroy(sfc); + closure.size = closure.pos; + dbg(1, "closure.size = %ld\n", closure.size); + encoded_data = base64_encode((unsigned char *)closure.buffer, closure.size, &olength, 0); + dbg(1, "olength = %ld\n", olength); + my_free(_ALLOC_ID_, &closure.buffer); + my_mstrcat(_ALLOC_ID_, &prop, "flags=image,unscaled\nalpha=0.8\nimage_data=", encoded_data, NULL); + my_free(_ALLOC_ID_, &encoded_data); + storeobject(-1, xctx->mousex_snap, xctx->mousey_snap, xctx->mousex_snap + grab_w, xctx->mousey_snap + grab_h, + xRECT, GRIDLAYER, SELECTED, prop); + my_free(_ALLOC_ID_, &prop); + xctx->need_reb_sel_arr=1; + rebuild_selected_array(); + move_objects(START,0,0,0); + xctx->ui_state |= START_SYMPIN; + } + XFreeGC(display, gc); + XDestroyWindow(display, clientwin); + } + return 1; +} +#endif + +void unselect_attached_floaters(void) +{ + int c, i, found = 0; + for(c = 0; c < cadlayers; c++) { + for(i = 0; i < xctx->rects[c]; i++) { + if(get_tok_value(xctx->rect[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_box(c, i, 0, 1, 1); + } + } + for(i = 0; i < xctx->lines[c]; i++) { + if(get_tok_value(xctx->line[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_line(c, i, 0, 1, 1); + } + } + + for(i = 0; i < xctx->polygons[c]; i++) { + if(get_tok_value(xctx->poly[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_polygon(c, i, 0, 1, 1); + } + } + for(i = 0; i < xctx->arcs[c]; i++) { + if(get_tok_value(xctx->arc[c][i].prop_ptr, "name", 0)[0]) { + found = 1; + select_arc(c, i, 0, 1, 1); + } + } + } + for(i = 0; i < xctx->wires; i++) { + if(get_tok_value(xctx->wire[i].prop_ptr, "name", 0)[0]) { + found = 1; + select_wire(i, 0, 1, 1); + } + } + for(i = 0; i < xctx->texts; i++) { + if(get_tok_value(xctx->text[i].prop_ptr, "name", 0)[0]) { + found = 1; + select_text(i, 0, 1, 1); + } + } + if(found) { + rebuild_selected_array(); + draw_selection(xctx->gc[SELLAYER],0); + } +} + +static void handle_enter_notify(int draw_xhair, int crosshair_size) +{ + struct stat buf; + dbg(2, "callback(): Enter event, ui_state=%d\n", xctx->ui_state); + xctx->mouse_inside = 1; + if(draw_xhair) { + if(crosshair_size == 0) { + tclvareval(xctx->top_path, ".drw configure -cursor none" , NULL); + } + } else + tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); + /* xschem window *sending* selected objects + when the pointer comes back in abort copy operation since it has been done + in another xschem xctx->window; STARTCOPY set and selection file does not exist any more */ + if(stat(sel_file, &buf) && (xctx->ui_state & STARTCOPY) ) + { + copy_objects(ABORT); + unselect_all(1); + } + /* xschem window *receiving* selected objects selection cleared --> abort */ + else if(xctx->paste_from == 1 && stat(sel_file, &buf) && (xctx->ui_state & STARTMERGE)) { + abort_operation(); + } + /*xschem window *receiving* selected objects + * no selected objects and selection file exists --> start merge */ + else if(xctx->lastsel == 0 && !stat(sel_file, &buf)) { + xctx->mousex_snap = 490; + xctx->mousey_snap = -340; + merge_file(1, ".sch"); + } + + return; +} + +static void handle_motion_notify(int event, KeySym key, int state, int rstate, int button, + int mx, int my, int aux, int draw_xhair, int enable_stretch, int snap_cursor, int wire_draw_active) +{ + char str[PATH_MAX + 100]; + if( waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return; + } + if(draw_xhair) { + draw_crosshair(1, state); /* when moving mouse: first action is delete crosshair, will be drawn later */ + } + if(snap_cursor) draw_snap_cursor(1); /* clear */ + /* pan schematic */ + if(xctx->ui_state & STARTPAN) pan(RUBBER, mx, my); + + if(xctx->semaphore >= 2) { + if(draw_xhair) { + draw_crosshair(2, state); /* locked UI: draw new crosshair and break out */ + } + if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(2); /* redraw */ + return; + } + + /* update status bar messages */ + if(xctx->ui_state) { + if(abs(mx-xctx->mx_save) > 8 || abs(my-xctx->my_save) > 8 ) { + my_snprintf(str, S(str), "mouse = %.16g %.16g - selected: %d w=%.6g h=%.6g", + xctx->mousex_snap, xctx->mousey_snap, + xctx->lastsel , + xctx->mousex_snap-xctx->mx_double_save, xctx->mousey_snap-xctx->my_double_save + ); + statusmsg(str,1); + } + } + + /* determine direction of a rectangle selection (or unselection with ALT key) */ + if(xctx->ui_state & STARTSELECT && !(xctx->ui_state & (PLACE_SYMBOL | STARTPAN | PLACE_TEXT)) ) { + /* Unselect by area : determine direction */ + if( ((state & Button1Mask) && SET_MODMASK) || (xctx->ui_state & DESEL_AREA)) { + if(mx >= xctx->mx_save) xctx->nl_dir = 0; + else xctx->nl_dir = 1; + select_rect(enable_stretch, RUBBER,0); + /* select by area : determine direction */ + } else if(state & Button1Mask) { + if(mx >= xctx->mx_save) xctx->nl_dir = 0; + else xctx->nl_dir = 1; + select_rect(enable_stretch, RUBBER,1); + } + } + /* draw objects being moved */ + if(xctx->ui_state & STARTMOVE) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + move_objects(RUBBER,0,0,0); + } + + /* draw objects being copied */ + if(xctx->ui_state & STARTCOPY) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + copy_objects(RUBBER); + } + + /* draw moving objects being inserted, wires, arcs, lines, rectangles, polygons or zoom box */ + redraw_w_a_l_r_p_z_rubbers(0); + + /* start of a mouse area select. Button1 pressed. No shift pressed + * Do not start an area select if user is dragging a polygon/bezier point */ + if(!(xctx->ui_state & STARTPOLYGON) && (state&Button1Mask) && !(xctx->ui_state & STARTWIRE) && + !(xctx->ui_state & STARTPAN) && !(SET_MODMASK) && !xctx->shape_point_selected && + !(state & ShiftMask) && !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT))) + { + if(mx != xctx->mx_save || my != xctx->my_save) { + xctx->mouse_moved = 1; + if(!xctx->drag_elements) { + if( !(xctx->ui_state & STARTSELECT)) { + select_rect(enable_stretch, START,1); + xctx->onetime=1; + } + if(abs(mx-xctx->mx_save) > 8 || + abs(my-xctx->my_save) > 8 ) { /* set reasonable threshold before unsel */ + if(xctx->onetime) { + unselect_all(1); /* 20171026 avoid multiple calls of unselect_all() */ + xctx->onetime=0; + } + xctx->ui_state|=STARTSELECT; /* set it again cause unselect_all(1) clears it... */ + } + } + } + } + /* Unselect by area */ + if( (((state & Button1Mask) && SET_MODMASK) || (xctx->ui_state & DESEL_AREA)) && + !(state & ShiftMask) && + !(xctx->ui_state & STARTPAN) && + !xctx->shape_point_selected && + !(xctx->ui_state & STARTSELECT) && + !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT))) { /* unselect area */ + select_rect(enable_stretch, START,0); + } + /* Select by area. Shift pressed */ + else if((state&Button1Mask) && (state & ShiftMask) && !(xctx->ui_state & STARTWIRE) && + !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT)) && !xctx->shape_point_selected && + !xctx->drag_elements && !(xctx->ui_state & STARTPAN) ) { + if(mx != xctx->mx_save || my != xctx->my_save) { + if( !(xctx->ui_state & STARTSELECT)) { + select_rect(enable_stretch, START,1); + } + if(abs(mx-xctx->mx_save) > 8 || + abs(my-xctx->my_save) > 8 ) { /* set reasonable threshold before unsel */ + if(!xctx->already_selected) { + select_object(X_TO_XSCHEM(xctx->mx_save), + Y_TO_XSCHEM(xctx->my_save), 0, 0, NULL); /* remove near obj if dragging */ + } + rebuild_selected_array(); + } + } + } + if(draw_xhair) { + draw_crosshair(2, state); /* what = 2(draw) */ + } + if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(2); /* redraw */ + + return; +} + +static void handle_key_press(int event, KeySym key, int state, int rstate, int mx, int my, + int button, int aux, int infix_interface, int enable_stretch, const char *win_path, double c_snap, + int cadence_compat, int wire_draw_active, int snap_cursor) +{ + char str[PATH_MAX + 100]; + switch (key) { + case '0' ... '4': + if(state == 0) { /* toggle pin logic level */ + if(xctx->semaphore >= 2) break; + if(key == '4') logic_set(-1, 1, NULL); + else logic_set((int)key - '0', 1, NULL); + } + else if(state==ControlMask) { /* choose layer */ + char n[30]; + xctx->rectcolor = (int)key - '0'+4; + my_snprintf(n, S(n), "%d", xctx->rectcolor); + tclvareval("xschem set rectcolor ", n, NULL); + + if(has_x) { + if(!strcmp(win_path, ".drw")) { + tclvareval("reconfigure_layers_button {}", NULL); + } else { + tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL); + } + } + dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]); + } + break; + + case '5': + if(rstate == 0) { /* 20110112 display only probes */ + xctx->only_probes = !xctx->only_probes; + tclsetboolvar("only_probes", xctx->only_probes); + toggle_only_probes(); + } /* /20110112 */ + break; + + case '6' ... '9': + if(state==ControlMask) { /* choose layer */ + char n[30]; + xctx->rectcolor = (int)key - '0'+4; + my_snprintf(n, S(n), "%d", xctx->rectcolor); + tclvareval("xschem set rectcolor ", n, NULL); + + if(has_x) { + if(!strcmp(win_path, ".drw")) { + tclvareval("reconfigure_layers_button {}", NULL); + } else { + tclvareval("reconfigure_layers_button [winfo parent ", win_path, "]", NULL); + } + } + dbg(1, "callback(): new color: %d\n",xctx->color_index[xctx->rectcolor]); + } + break; + + case 'a': + if(rstate == 0) { /* make symbol */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] " + "-message {do you want to make symbol view ?}"); + if(strcmp(tclresult(),"ok")==0) + { + save_schematic(xctx->sch[xctx->currsch], 0); + make_symbol(); + } + } + else if(rstate == ControlMask) { /* select all */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + select_all(); + } + break; + + case 'A': + if(rstate == 0) { /* toggle show netlist */ + int net_s; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + net_s = tclgetboolvar("netlist_show"); + net_s = !net_s; + if(net_s) { + tcleval("alert_ { enabling show netlist window} {}"); + tclsetvar("netlist_show","1"); + } + else { + tcleval("alert_ { disabling show netlist window } {}"); + tclsetvar("netlist_show","0"); + } + } + else if(rstate == ControlMask) { /* only for graph (toggle hcursor1 if graph_use_ctrl_key set) */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + } + break; + + case 'b': + if(rstate==0) { /* merge schematic */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + merge_file(0, ""); /* 2nd parameter not used any more for merge 25122002 */ + } + else if(rstate==ControlMask) { /* toggle show text in symbol */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + xctx->sym_txt =!xctx->sym_txt; + if(xctx->sym_txt) { + /* tcleval("alert_ { enabling text in symbol} {}"); */ + tclsetvar("sym_txt","1"); + draw(); + } + else { + /* tcleval("alert_ { disabling text in symbol} {}"); */ + tclsetvar("sym_txt","0"); + draw(); + } + } + else if(EQUAL_MODMASK) { /* hide/show instance details */ + if(xctx->semaphore >= 2) break; + xctx->hide_symbols++; + if(xctx->hide_symbols >= 3) xctx->hide_symbols = 0; + tclsetintvar("hide_symbols", xctx->hide_symbols); + draw(); + } + break; + + case 'B': + if(rstate == 0) { /* edit schematic header/license */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + tcleval("update_schematic_header"); + } + else if(rstate == ControlMask) { /* only for graph (toggle hcursor2 if graph_use_ctrl_key set) */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + } + break; + + case 'c': + /* duplicate selection */ + if(rstate==0 && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + if(xctx->semaphore >= 2) break; + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + copy_objects(START); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTCOPY; + } + } + /* copy selection into clipboard */ + else if(rstate == ControlMask) { + if(xctx->semaphore >= 2) break; + rebuild_selected_array(); + if(xctx->lastsel) { /* 20071203 check if something selected */ + save_selection(2); + } + } + /* duplicate selection */ + else if(EQUAL_MODMASK && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + if(xctx->semaphore >= 2) break; + xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */ + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + copy_objects(START); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTCOPY; + } + } + break; + + case 'C': + if(/* !xctx->ui_state && */ rstate == 0) { /* place arc */ + if(xctx->semaphore >= 2) break; + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_arc(PLACE, 180., xctx->mousex_snap, xctx->mousey_snap); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTARC; + } + } + else if(/* !xctx->ui_state && */ rstate == ControlMask) { /* place circle */ + if(xctx->semaphore >= 2) break; + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_arc(PLACE, 360., xctx->mousex_snap, xctx->mousey_snap); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTCIRCLE; + } + } + break; + + case 'd': + if(rstate == 0) { /* unselect object under the mouse */ + if(infix_interface) { + unselect_at_mouse_pos(mx, my); + } else { + xctx->ui_state |= (MENUSTART | DESEL_CLICK); + xctx->ui_state2 = MENUSTARTDESEL; + } + } + else if(rstate == ControlMask) { /* delete files */ + if(xctx->semaphore >= 2) break; + delete_files(); + } + break; + + case 'D': + if(rstate == 0) { /* unselect by area */ + if( !(xctx->ui_state & STARTPAN) && !xctx->shape_point_selected && + !(xctx->ui_state & (PLACE_SYMBOL | PLACE_TEXT)) && !(xctx->ui_state & STARTSELECT)) { + if(infix_interface) { + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex; + xctx->my_double_save=xctx->mousey; + xctx->ui_state |= DESEL_AREA; + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTDESEL; + } + } + } + break; + + case 'e': + if(rstate == 0) { /* descend to schematic */ + if(xctx->semaphore >= 2) break; + descend_schematic(0, 1, 1, 1); + } + else if(rstate == ControlMask) { + if(xctx->semaphore >= 2) break; + go_back(1); + } + else if(EQUAL_MODMASK) { /* edit schematic in new window */ + int save = xctx->semaphore; + xctx->semaphore--; /* so semaphore for current context wll be saved correctly */ + /* schematic_in_new_window(0, 1, 0); */ + tcleval("open_sub_schematic"); + xctx->semaphore = save; + } + break; + + case 'E': + if(EQUAL_MODMASK) { /* edit schematic in new window - new xschem process */ + int save = xctx->semaphore; + xctx->semaphore--; /* so semaphore for current context wll be saved correctly */ + schematic_in_new_window(1, 1, 0); + xctx->semaphore = save; + } + break; + + case 'f': + if(rstate == 0) { /* full zoom */ + int flags = 1; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(tclgetboolvar("zoom_full_center")) flags |= 2; + zoom_full(1, 0, flags, 0.97); + } + else if(rstate == ControlMask) { /* search */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + tcleval("property_search"); + } + else if(EQUAL_MODMASK) { /* flip objects around their anchor points 20171208 */ + if(xctx->ui_state & STARTMOVE) move_objects(FLIP|ROTATELOCAL,0,0,0); + else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP|ROTATELOCAL); + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(FLIP|ROTATELOCAL,0,0,0); + move_objects(END,0,0,0); + } + } + break; + + case 'F': + if(rstate == 0) { /* flip */ + if(xctx->ui_state & STARTMOVE) move_objects(FLIP,0,0,0); + else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP); + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(FLIP,0,0,0); + move_objects(END,0,0,0); + } + } + else if(rstate == ControlMask ) { /* full zoom selection */ + if(xctx->ui_state == SELECTION) { + zoom_full(1, 1, 3, 0.97); + } + } + break; + + case 'g': + if(rstate==0) { /* half snap factor */ + set_snap(c_snap / 2.0); + change_linewidth(-1.); + draw(); + } + else if(rstate==ControlMask) { /* set snap factor 20161212 */ + my_snprintf(str, S(str), + "input_line {Enter snap value (default: %.16g current: %.16g)} {xschem set cadsnap} {%g} 10", + CADSNAP, c_snap, c_snap); + tcleval(str); + } + else if(EQUAL_MODMASK) { /* highlight net and send to viewer */ + int tool = 0; + int exists = 0; + char *tool_name = NULL; + + if(xctx->semaphore >= 2) break; + tcleval("winfo exists .graphdialog"); + if(tclresult()[0] == '1') tool = XSCHEM_GRAPH; + else if(xctx->graph_lastsel >=0 && + xctx->rects[GRIDLAYER] > xctx->graph_lastsel && + xctx->rect[GRIDLAYER][xctx->graph_lastsel].flags & 1) { + tool = XSCHEM_GRAPH; + } + tcleval("info exists sim"); + if(tclresult()[0] == '1') exists = 1; + xctx->enable_drill = 0; + if(exists) { + if(!tool) { + tool = tclgetintvar("sim(spicewave,default)"); + my_snprintf(str, PATH_MAX + 100, "sim(spicewave,%d,name)", tool); + my_strdup(_ALLOC_ID_, &tool_name, tclgetvar(str)); + dbg(1,"callback(): tool_name=%s\n", tool_name); + if(strstr(tool_name, "Gaw")) tool=GAW; + else if(strstr(tool_name, "Bespice")) tool=BESPICE; + my_free(_ALLOC_ID_, &tool_name); + } + } + if(tool) { + hilight_net(tool); + redraw_hilights(0); + } + Tcl_ResetResult(interp); + } + break; + + case 'G': + if(rstate == 0) { /* double snap factor */ + set_snap(c_snap * 2.0); + change_linewidth(-1.); + draw(); + } + break; + + case 'h': + if(rstate==ControlMask ) { /* go to http link */ + int savesem = xctx->semaphore; + xctx->semaphore = 0; + launcher(); + xctx->semaphore = savesem; + } + else if (rstate == 0) { /* horizontally constrained drag 20171023 */ + if ( xctx->constr_mv == 1 ) { + tcleval("set constr_mv 0" ); + xctx->constr_mv = 0; + } else { + tcleval("set constr_mv 1" ); + xctx->constr_mv = 1; + } + if(xctx->ui_state & STARTWIRE) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + } + if(xctx->ui_state & STARTLINE) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + } + } + else if (EQUAL_MODMASK) { + tcleval("schpins_to_sympins"); + } + break; + + case 'H': + if(rstate == 0) { /* attach labels to selected instances */ + attach_labels_to_inst(1); + } + else if (rstate == ControlMask) { /* create schematic and symbol from selected components */ + make_schematic_symbol_from_sel(); + } + break; + + case 'i': + if(rstate==0) { /* descend to symbol */ + if(xctx->semaphore >= 2) break; + descend_symbol(); + } + else if(rstate == ControlMask) { /* insert sym */ + tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + } + else if(EQUAL_MODMASK) { /* edit symbol in new window */ + int save = xctx->semaphore; + xctx->semaphore--; /* so semaphore for current context wll be saved correctly */ + symbol_in_new_window(0); + xctx->semaphore = save; + } + break; + + case 'I': + if(rstate == 0) { /* insert sym */ + if(xctx->semaphore >= 2) break; + start_place_symbol(); + } + else if(EQUAL_MODMASK) { /* edit symbol in new window - new xschem process */ + int save = xctx->semaphore; + xctx->semaphore--; /* so semaphore for current context wll be saved correctly */ + symbol_in_new_window(1); + xctx->semaphore = save; + } + break; + + case 'j': + if(rstate==0 ) { /* print list of highlight nets */ + if(xctx->semaphore >= 2) break; + print_hilight_net(1); + } + else if(rstate==ControlMask) { /* create ipins from highlight nets */ + if(xctx->semaphore >= 2) break; + print_hilight_net(0); + } + else if(EQUAL_MODMASK) { /* create labels without i prefix from hilight nets */ + if(xctx->semaphore >= 2) break; + print_hilight_net(4); + } + else if( SET_MODMASK && (state & ControlMask) ) { /* print list of highlight net with label expansion */ + print_hilight_net(3); + } + break; + + case 'J': + if(rstate == 0) { + create_plot_cmd(); + } + else if(SET_MODMASK ) { /* create labels with i prefix from hilight nets */ + if(xctx->semaphore >= 2) break; + print_hilight_net(2); + } + break; + + case 'k': + if(rstate==0) { /* hilight net */ + if(xctx->semaphore >= 2) break; + xctx->enable_drill=0; + hilight_net(0); + redraw_hilights(0); + /* draw_hilight_net(1); */ + } + else if(EQUAL_MODMASK) { /* select whole net (all attached wires/labels/pins) */ + select_hilight_net(); + } + else if(rstate==ControlMask) { /* unhilight net */ + if(xctx->semaphore >= 2) break; + unhilight_net(); + } + break; + + case 'K': + if(rstate == 0) { /* delete hilighted nets */ + if(xctx->semaphore >= 2) break; + xctx->enable_drill=0; + clear_all_hilights(); + /* undraw_hilight_net(1); */ + draw(); + } + else if(rstate == ControlMask) { /* hilight net drilling thru elements with 'propag=' prop set on pins */ + if(xctx->semaphore >= 2) break; + xctx->enable_drill=1; + hilight_net(0); + redraw_hilights(0); + /* draw_hilight_net(1); */ + } + break; + + case 'l': + if(/* !xctx->ui_state && */ rstate == 0) { /* start line */ + int prev_state = xctx->ui_state; + if(xctx->semaphore >= 2) break; + if(infix_interface) { + start_line(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTLINE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + } else { + xctx->last_command = 0; + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTLINE; + } + } + else if(rstate == ControlMask) { /* create schematic from selected symbol 20171004 */ + if(xctx->semaphore >= 2) break; + create_sch_from_sym(); + } + else if(EQUAL_MODMASK) { /* add pin label*/ + place_net_label(1); + } + break; + + case 'L': + if(rstate == 0) { /* toggle orthogonal routing */ + if(tclgetboolvar("orthogonal_wiring")){ + tclsetboolvar("orthogonal_wiring", 0); + xctx->manhattan_lines = 0; + } else { + tclsetboolvar("orthogonal_wiring", 1); + } + redraw_w_a_l_r_p_z_rubbers(1); + } + else if(EQUAL_MODMASK ) { /* add pin label*/ + place_net_label(0); + } + break; + + case 'm': + /* Move selection */ + if(rstate==0 && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */ + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTMOVE; + } + } + /* move selection stretching attached nets */ + else if(rstate == ControlMask && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */ + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTMOVE; + } + } + /* Move selection adding wires to moved pins */ + else if(EQUAL_MODMASK && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */ + if(infix_interface) { + 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 */ + move_objects(START,0,0,0); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTMOVE; + } + } + break; + + case 'M': + /* Move selection adding wires to moved pins */ + if((rstate == 0) && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */ + if(infix_interface) { + 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 */ + move_objects(START,0,0,0); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTMOVE; + } + } + /* move selection, stretch attached nets, create new wires on pin-to-moved-pin connections */ + else if(rstate == ControlMask && !(xctx->ui_state & (STARTMOVE | STARTCOPY))) { + if(!enable_stretch) select_attached_nets(); /* stretch nets that land on selected instance pins */ + xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */ + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTMOVE; + } + } + break; + + case 'n': + if(rstate==0) { /* hierarchical netlist */ + if(xctx->semaphore >= 2) break; + tcleval("xschem netlist -erc"); + } + else if(rstate == ControlMask) { /* clear schematic */ + if(xctx->semaphore >= 2) break; + tcleval("xschem clear SCHEMATIC"); + } + break; + + case 'N': + if(rstate == 0) { /* current level only netlist */ + int err = 0; + yyparse_error = 0; + if(xctx->semaphore >= 2) break; + unselect_all(1); + if( set_netlist_dir(0, NULL) ) { + dbg(1, "callback(): -------------\n"); + if(xctx->netlist_type == CAD_SPICE_NETLIST) + err = global_spice_netlist(0, 1); + else if(xctx->netlist_type == CAD_VHDL_NETLIST) + err = global_vhdl_netlist(0, 1); + else if(xctx->netlist_type == CAD_VERILOG_NETLIST) + err = global_verilog_netlist(0, 1); + else if(xctx->netlist_type == CAD_TEDAX_NETLIST) + err = global_tedax_netlist(0, 1); + else + tcleval("tk_messageBox -type ok -parent [xschem get topwindow] " + "-message {Please Set netlisting mode (Options menu)}"); + dbg(1, "callback(): -------------\n"); + } + else { + if(has_x) tcleval("alert_ {Can not write into the netlist directory. Please check} {}"); + else dbg(0, "Can not write into the netlist directory. Please check"); + err = 1; + } + if(err) { + if(has_x) { + tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background red", NULL); + tclvareval("set tctx::", xctx->current_win_path, "_netlist red", NULL); + } + } else { + if(has_x) { + tclvareval(xctx->top_path, ".menubar entryconfigure Netlist -background Green", NULL); + tclvareval("set tctx::", xctx->current_win_path, "_netlist Green", NULL); + } + } + + } + else if(rstate == ControlMask ) { /* clear symbol */ + if(xctx->semaphore >= 2) break; + tcleval("xschem clear SYMBOL"); + } + break; + + case 'o': + if(rstate == ControlMask) { /* load */ + if(xctx->semaphore >= 2) break; + ask_new_file(); + xctx->semaphore--; + tcleval("load_additional_files"); + xctx->semaphore++; + } + break; + + case 'O': + if(rstate == ControlMask ) { /* load most recent tile */ + tclvareval("xschem load -gui [lindex $recentfile 0]", NULL); + } + else if(rstate == 0) { /* toggle light/dark colorscheme 20171113 */ + int d_c; + d_c = tclgetboolvar("dark_colorscheme"); + d_c = !d_c; + tclsetboolvar("dark_colorscheme", d_c); + tclsetdoublevar("dim_value", 0.0); + tclsetdoublevar("dim_bg", 0.0); + build_colors(0.0, 0.0); + draw(); + } + break; + + case 'p': + if(EQUAL_MODMASK) { /* add symbol pin */ + xctx->push_undo(); + unselect_all(1); + storeobject(-1, xctx->mousex_snap-2.5, xctx->mousey_snap-2.5, xctx->mousex_snap+2.5, xctx->mousey_snap+2.5, + xRECT, PINLAYER, SELECTED, "name=XXX\ndir=inout"); + xctx->need_reb_sel_arr=1; + rebuild_selected_array(); + move_objects(START,0,0,0); + xctx->ui_state |= START_SYMPIN; + } + else if( !(xctx->ui_state & STARTPOLYGON) && rstate==0) { /* start polygon */ + if(xctx->semaphore >= 2) break; + dbg(1, "callback(): start polygon\n"); + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_polygon(PLACE, xctx->mousex_snap, xctx->mousey_snap); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTPOLYGON; + } + } + break; + + case 'P': + if(rstate == 0) { /* pan, other way to. */ + xctx->xorigin=-xctx->mousex_snap+xctx->areaw*xctx->zoom/2.0; + xctx->yorigin=-xctx->mousey_snap+xctx->areah*xctx->zoom/2.0; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + break; + + case 'q': + if(rstate==ControlMask) { /* quit xschem */ + if(xctx->semaphore >= 2) break; + /* must be set to zero, otherwise switch_tab/switch_win does not proceed + * and these are necessary when closing tabs/windows */ + xctx->semaphore = 0; + tcleval("quit_xschem"); + } + else if(rstate==0) { /* edit attributes */ + if(xctx->semaphore >= 2) break; + edit_property(0); + } + else if(EQUAL_MODMASK) { /* edit .sch file (DANGER!!) */ + if(xctx->semaphore >= 2) break; + rebuild_selected_array(); + if(xctx->lastsel==0 ) { + my_snprintf(str, S(str), "edit_file {%s}", abs_sym_path(xctx->sch[xctx->currsch], "")); + tcleval(str); + } + else if(xctx->sel_array[0].type==ELEMENT) { + my_snprintf(str, S(str), "edit_file {%s}", + abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), "")); + tcleval(str); + + } + } + break; + + case 'Q': + if(rstate == 0) { /* edit attributes in editor */ + if(xctx->semaphore >= 2) break; + edit_property(1); + } + else if(rstate == ControlMask) { /* view attributes */ + edit_property(2); + } + break; + + case 'r': + if(/* !xctx->ui_state && */ rstate==0) { /* start rect */ + dbg(1, "callback(): start rect\n"); + if(xctx->semaphore >= 2) break; + if(infix_interface) { + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + xctx->last_command = 0; + new_rect(PLACE,xctx->mousex_snap, xctx->mousey_snap); + } else { + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTRECT; + } + } + else if((rstate == ControlMask) && cadence_compat) { /* simulate (for cadence users) */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] " + "-message {Run circuit simulation?}"); + if(strcmp(tclresult(),"ok")==0) { + tcleval("[xschem get top_path].menubar invoke Simulate"); + } + } + else if(SET_MODMASK) { /* reload */ + if(xctx->semaphore >= 2) break; + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] " + "-message {Are you sure you want to reload from disk?}"); + if(strcmp(tclresult(),"ok")==0) { + char filename[PATH_MAX]; + unselect_all(1); + remove_symbols(); + my_strncpy(filename, abs_sym_path(xctx->sch[xctx->currsch], ""), S(filename)); + load_schematic(1, filename, 1, 1); + draw(); + } + } + else if(EQUAL_MODMASK) { /* rotate objects around their anchor points 20171208 */ + if(xctx->ui_state & STARTMOVE) move_objects(ROTATE|ROTATELOCAL,0,0,0); + else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE|ROTATELOCAL); + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(ROTATE|ROTATELOCAL,0,0,0); + move_objects(END,0,0,0); + } + } + break; + + case 'R': + if(rstate == 0) { /* rotate */ + if(xctx->ui_state & STARTMOVE) move_objects(ROTATE,0,0,0); + else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE); + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(ROTATE,0,0,0); + move_objects(END,0,0,0); + } + + } + break; + + case 's': + if((rstate == 0) && !cadence_compat) { /* simulate (original keybind) */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] " + "-message {Run circuit simulation?}"); + if(strcmp(tclresult(),"ok")==0) { + tcleval("[xschem get top_path].menubar invoke Simulate"); + } + } + else if(/* !xctx->ui_state && */ (rstate == 0) && cadence_compat) { /* create wire snapping to closest instance pin (cadence keybind) */ + if(xctx->semaphore >= 2) break; + snapped_wire(c_snap); + } + else if(rstate == ControlMask ){ /* save 20121201 */ + if(xctx->semaphore >= 2) break; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + /* check if unnamed schematic, use saveas in this case */ + if(!strcmp(xctx->sch[xctx->currsch],"") || strstr(xctx->sch[xctx->currsch], "untitled")) { + saveas(NULL, SCHEMATIC); + } else { + save(1, 0); + } + } + else if(SET_MODMASK && (state & ControlMask) ) { /* save as symbol */ + if(xctx->semaphore >= 2) break; + saveas(NULL, SYMBOL); + } + break; + + case 'S': + if(rstate == 0) { /* change element order */ + if(xctx->semaphore >= 2) break; + change_elem_order(-1); + } + else if(rstate == ControlMask) { /* save as schematic */ + if(xctx->semaphore >= 2) break; + saveas(NULL, SCHEMATIC); + } + break; + + case 't': + if(rstate == 0) { /* place text */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(xctx->semaphore >= 2) break; + xctx->last_command = 0; + xctx->mx_double_save = xctx->mousex_snap; + xctx->my_double_save = xctx->mousey_snap; + if(place_text(0, xctx->mousex_snap, xctx->mousey_snap)) { /* 1 = draw text 24122002 */ + xctx->mousey_snap = xctx->my_double_save; + xctx->mousex_snap = xctx->mx_double_save; + move_objects(START,0,0,0); + xctx->ui_state |= PLACE_TEXT; + } + } + else if(rstate & ControlMask) { + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + } + break; + + case 'T': + if(rstate == 0) { /* toggle spice_ignore, verilog_ignore, ... flag on selected instances. */ + toggle_ignore(); + } + break; + + case 'u': + if(rstate==0) { /* undo */ + if(xctx->semaphore >= 2) break; + xctx->pop_undo(0, 1); /* 2nd parameter: set_modify_status */ + draw(); + } + else if(EQUAL_MODMASK) { /* align to grid */ + if(xctx->semaphore >= 2) break; + xctx->push_undo(); + round_schematic_to_grid(c_snap); + set_modify(1); + if(tclgetboolvar("autotrim_wires")) trim_wires(); + xctx->prep_hash_inst=0; + xctx->prep_hash_wires=0; + xctx->prep_net_structs=0; + xctx->prep_hi_structs=0; + + draw(); + } + else if(rstate==ControlMask) { /* testmode */ + unselect_attached_floaters(); + } + + break; + + case 'U': + if(rstate == 0) { /* redo */ + if(xctx->semaphore >= 2) break; + xctx->pop_undo(1, 1); /* 2nd parameter: set_modify_status */ + draw(); + } + break; + + case 'v': + if(rstate==0) { /* vertically constrained drag 20171023 */ + if ( xctx->constr_mv == 2 ) { + tcleval("set constr_mv 0" ); + xctx->constr_mv = 0; + } else { + tcleval("set constr_mv 2" ); + xctx->constr_mv = 2; + } + if(xctx->ui_state & STARTWIRE) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + } + if(xctx->ui_state & STARTLINE) { + if(xctx->constr_mv == 1) xctx->mousey_snap = xctx->my_double_save; + if(xctx->constr_mv == 2) xctx->mousex_snap = xctx->mx_double_save; + new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + } + } + else if(rstate == ControlMask) { /* paste from clipboard */ + if(xctx->semaphore >= 2) break; + merge_file(2,".sch"); + } + else if(EQUAL_MODMASK) { /* vertical flip objects around their anchor points */ + if(xctx->ui_state & STARTMOVE) { + move_objects(ROTATE|ROTATELOCAL,0,0,0); + move_objects(ROTATE|ROTATELOCAL,0,0,0); + move_objects(FLIP|ROTATELOCAL,0,0,0); + } + else if(xctx->ui_state & STARTCOPY) { + copy_objects(ROTATE|ROTATELOCAL); + copy_objects(ROTATE|ROTATELOCAL); + copy_objects(FLIP|ROTATELOCAL); + } + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(ROTATE|ROTATELOCAL,0,0,0); + move_objects(ROTATE|ROTATELOCAL,0,0,0); + move_objects(FLIP|ROTATELOCAL,0,0,0); + move_objects(END,0,0,0); + } + } + break; + + case 'V': + if(rstate == 0) { /* vertical flip */ + if(xctx->ui_state & STARTMOVE) { + move_objects(ROTATE,0,0,0); + move_objects(ROTATE,0,0,0); + move_objects(FLIP,0,0,0); + } + else if(xctx->ui_state & STARTCOPY) { + copy_objects(ROTATE); + copy_objects(ROTATE); + copy_objects(FLIP); + } + else { + rebuild_selected_array(); + xctx->mx_double_save=xctx->mousex_snap; + xctx->my_double_save=xctx->mousey_snap; + move_objects(START,0,0,0); + move_objects(ROTATE,0,0,0); + move_objects(ROTATE,0,0,0); + move_objects(FLIP,0,0,0); + move_objects(END,0,0,0); + } + } + else if(rstate == ControlMask) { /* toggle spice/vhdl netlist */ + xctx->netlist_type++; + if(xctx->netlist_type==6) xctx->netlist_type=1; + set_tcl_netlist_type(); + draw(); /* needed to ungrey or grey out components due to *_ignore attribute */ + } + break; + + case 'w': + if(/* !xctx->ui_state && */ rstate==0) { /* place wire. */ + int prev_state = xctx->ui_state; + if(xctx->semaphore >= 2) break; + + if(infix_interface) { + start_wire(xctx->mousex_snap, xctx->mousey_snap); + if(prev_state == STARTWIRE) { + tcleval("set constr_mv 0" ); + xctx->constr_mv=0; + } + } else { + xctx->last_command = 0; + xctx->ui_state |= MENUSTART; + xctx->ui_state2 = MENUSTARTWIRE; + if(prev_state & STARTWIRE) start_wire(xctx->mousex_snap, xctx->mousey_snap); + } + } + else if(rstate == ControlMask) { /* close current schematic */ + int save_sem; + if(xctx->semaphore >= 2) break; + save_sem = xctx->semaphore; + tcleval("xschem exit"); + xctx->semaphore = save_sem; + } + break; + + case 'W': + if(/* !xctx->ui_state && */ (rstate == 0) && !cadence_compat) { /* create wire snapping to closest instance pin (original keybind) */ + if(xctx->semaphore >= 2) break; + snapped_wire(c_snap); + } + break; + + case 'x': + if(rstate == 0) { /* new cad session */ + new_xschem_process(NULL ,0); + } + else if(EQUAL_MODMASK) { /* toggle draw crosshair at mouse pos */ + if(tclgetboolvar("draw_crosshair")) { + tclsetvar("draw_crosshair", "0"); + } else { + tclsetvar("draw_crosshair", "1"); + } + draw(); + } + else if(rstate == ControlMask) { /* cut selection into clipboard */ + if(xctx->semaphore >= 2) break; + rebuild_selected_array(); + if(xctx->lastsel) { /* 20071203 check if something selected */ + save_selection(2); + delete(1/* to_push_undo */); + } + } + break; + + case 'X': + if(rstate == 0) { /* highlight discrepanciens between selected instance pin and net names */ + hilight_net_pin_mismatches(); + } + break; + + case 'y': + if(rstate == 0) { /* toggle stretching */ + enable_stretch = !enable_stretch; + tclsetboolvar("enable_stretch", enable_stretch); + } + break; + + case 'z': + if(rstate == 0 && !(xctx->ui_state & (STARTRECT | STARTLINE | STARTWIRE | STARTPOLYGON | STARTARC))) { /* zoom box */ + dbg(1, "callback(): zoom_rectangle call\n"); + zoom_rectangle(START); + } + else if(rstate==ControlMask) { /* zoom out */ + view_unzoom(0.0); + } + else if(EQUAL_MODMASK && cadence_compat) { /* toggle snap-cursor option */ + if(tclgetboolvar("snap_cursor")) { + tclsetvar("snap_cursor", "0"); + draw_snap_cursor(1); + xctx->closest_pin_found = 0; + xctx->prev_snapx = 0.0; + xctx->prev_snapy = 0.0; + } else { + tclsetvar("snap_cursor", "1"); + if(wire_draw_active) draw_snap_cursor(3); + } + } + break; + + case 'Z': + if(rstate == 0) { /* zoom in */ + view_zoom(0.0); + } + break; + + case ' ': + if(xctx->ui_state & STARTWIRE) { /* & instead of == 20190409 */ + new_wire(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap); + xctx->manhattan_lines++; + xctx->manhattan_lines %=3; + new_wire(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + + } else if(xctx->ui_state & STARTLINE) { + new_line(RUBBER|CLEAR, xctx->mousex_snap, xctx->mousey_snap); + xctx->manhattan_lines++; + xctx->manhattan_lines %=3; + new_line(RUBBER, xctx->mousex_snap, xctx->mousey_snap); + } else { + if(xctx->semaphore<2) { + rebuild_selected_array(); /* sets or clears xctx->ui_state SELECTION flag */ + } + pan(START, mx, my); + xctx->ui_state |= STARTPAN; + } + break; + + case '_': /* toggle change line width */ + if(!tclgetboolvar("change_lw")) { + tcleval("alert_ { enabling change line width} {}"); + tclsetvar("change_lw","1"); + } + else { + tcleval("alert_ { disabling change line width} {}"); + tclsetvar("change_lw","0"); + } + break; + + case '%': /* toggle draw grid */ + int dr_gr; + dr_gr = tclgetboolvar("draw_grid"); + dr_gr =!dr_gr; + if(dr_gr) { + /* tcleval("alert_ { enabling draw grid} {}"); */ + tclsetvar("draw_grid","1"); + draw(); + } + else { + /* tcleval("alert_ { disabling draw grid} {}"); */ + tclsetvar("draw_grid","0"); + draw(); + } + break; + + case '$': + if( rstate == 0 ) { /* toggle pixmap saving */ + xctx->draw_pixmap =!xctx->draw_pixmap; + if(xctx->draw_pixmap) tcleval("alert_ { enabling draw pixmap} {}"); + else tcleval("alert_ { disabling draw pixmap} {}"); + } + else if(state & ControlMask) { /* toggle window drawing */ + xctx->draw_window =!xctx->draw_window; + if(xctx->draw_window) { + tcleval("alert_ { enabling draw window} {}"); + tclsetvar("draw_window","1"); + } else { + tcleval("alert_ { disabling draw window} {}"); + tclsetvar("draw_window","0"); + } + } + break; + + case '=': + if(state & ControlMask) { /* toggle fill rectangles */ + int x; + xctx->fill_pattern++; + if(xctx->fill_pattern==2) xctx->fill_pattern=0; + + if(xctx->fill_pattern==1) { + tcleval("alert_ { Stippled pattern fill} {}"); + for(x=0;xfill_type[x]==2) XSetFillStyle(display,xctx->gcstipple[x],FillSolid); + else XSetFillStyle(display,xctx->gcstipple[x],FillStippled); + } + } + else if(xctx->fill_pattern==2) { + tcleval("alert_ { solid pattern fill} {}"); + for(x=0;xgcstipple[x],FillSolid); + } + else { + tcleval("alert_ { No pattern fill} {}"); + for(x=0;xgcstipple[x],FillStippled); + } + + draw(); + } + break; + + case '+': + if(state & ControlMask) { /* change line width */ + xctx->lw = round_to_n_digits(xctx->lw + 0.5, 2); + change_linewidth(xctx->lw); + tclsetboolvar("change_lw", 0); + draw(); + } + break; + + case '-': + if(state & ControlMask) { /* change line width */ + xctx->lw = round_to_n_digits(xctx->lw - 0.5, 2); + if(xctx->lw < 0.0) xctx->lw = 0.0; + change_linewidth(xctx->lw); + tclsetboolvar("change_lw", 0); + draw(); + } + break; + + case XK_Return: + if((state == 0 ) && xctx->ui_state & STARTPOLYGON) { /* close polygon */ + new_polygon(ADD|END, xctx->mousex_snap, xctx->mousey_snap); + } + break; + + case XK_Escape: /* abort & redraw */ + if(xctx->semaphore < 2) { + abort_operation(); + } + /* stuff that can be done reentrantly ... */ + tclsetvar("tclstop", "1"); /* stop simulation if any running */ + if(xctx->ui_state2 & MENUSTARTWIRE) { + xctx->ui_state2 &= ~MENUSTARTWIRE; + } + if(snap_cursor) draw_snap_cursor(1); /* erase */ + if(tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE) && cadence_compat) { + xctx->last_command &= ~STARTWIRE; + } + break; + + case XK_Delete: + if(xctx->ui_state & SELECTION) { /* delete selection */ + if(xctx->semaphore >= 2) break; + delete(1/* to_push_undo */); + } + break; + + case XK_Right: + if(state == ControlMask) { + int save = xctx->semaphore; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(xctx->semaphore >= 2) break; + xctx->semaphore = 0; + tcleval("next_tab"); + xctx->semaphore = save; + } + else { /* left */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + xctx->xorigin+=-CADMOVESTEP*xctx->zoom; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + break; + + case XK_Left: + if(state == ControlMask) { + int save = xctx->semaphore; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + if(xctx->semaphore >= 2) break; + xctx->semaphore = 0; + tcleval("prev_tab"); + xctx->semaphore = save; + } + else { /* right */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + xctx->xorigin-=-CADMOVESTEP*xctx->zoom; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + } + break; + + case XK_Down: /* down */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + xctx->yorigin+=-CADMOVESTEP*xctx->zoom; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + break; + + case XK_Up: /* up */ + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + break; + } + xctx->yorigin-=-CADMOVESTEP*xctx->zoom; + draw(); + redraw_w_a_l_r_p_z_rubbers(1); + break; + + case XK_BackSpace: /* back */ + if(xctx->semaphore >= 2) break; + go_back(1); + break; + +#if defined(__unix__) && HAS_CAIRO==1 + case XK_Print: + xctx->ui_state |= GRABSCREEN; + tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); + tclvareval("grab set -global ", xctx->top_path, ".drw", NULL); + break; +#endif + + case XK_Insert: + if(state == ShiftMask) { /* insert sym */ + tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + } + else { + if(xctx->semaphore >= 2) break; + start_place_symbol(); + } + break; + + case '*': + if(rstate == 0 ) { /* postscript print */ + if(xctx->semaphore >= 2) break; + ps_draw(7, 0, 0); + } + else if(rstate == ControlMask) {/* xpm print */ + if(xctx->semaphore >= 2) break; + print_image(); + } + else if(EQUAL_MODMASK) { /* svg print , 20121108 */ + if(xctx->semaphore >= 2) break; + svg_draw(); + } + break; + + case '&': /* check wire connectivity */ + if(xctx->semaphore >= 2) break; + xctx->push_undo(); + trim_wires(); + draw(); + break; + + case '\\': + if(state==0) { /* fullscreen */ + dbg(1, "callback(): toggle fullscreen, win_path=%s\n", win_path); + toggle_fullscreen(win_path); + } + break; + + case '>': + if(xctx->semaphore >= 2) break; + if(xctx->draw_single_layer< cadlayers-1) xctx->draw_single_layer++; + draw(); + break; + + case '<': + if(xctx->semaphore >= 2) break; + if(xctx->draw_single_layer>=0 ) xctx->draw_single_layer--; + draw(); + break; + + /* toggle flat netlist (only spice) */ + case ':': + if(!tclgetboolvar("flat_netlist")) { + tcleval("alert_ { enabling flat netlist} {}"); + tclsetvar("flat_netlist","1"); + } + else { + tcleval("alert_ { set hierarchical netlist } {}"); + tclsetvar("flat_netlist","0"); + } + break; + + case '#': + if((state & ControlMask)) { + check_unique_names(1); + } + else { + check_unique_names(0); + } + break; + + case ';': + if(0 && (state & ControlMask)) { /* testmode */ + } + break; + + case '~': + if(0 && (state & ControlMask)) { /* testmode */ + } + break; + + case '|': + if(0 && (state & ControlMask)) { /* testmode */ + static int x = 0; + + if(x == 0) { + int i; + XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1, + xctx->areaw, xctx->areah); + XFlush(display); + sleep_ms(400); + for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) { + XDrawLine(display, xctx->window, xctx->gctiled, + i, xctx->xrect[0].y, i, xctx->xrect[0].height); + XFlush(display); + sleep_ms(4); + } + } else if(x == 1) { + int i; + XFillRectangle(display, xctx->window, xctx->gc[BACKLAYER], xctx->areax1, xctx->areay1, + xctx->areaw, xctx->areah); + XFlush(display); + sleep_ms(400); + for(i = xctx->xrect[0].x; i < xctx->xrect[0].width; i++) { + XDrawLine(display, xctx->window, xctx->gctiled, + i, xctx->xrect[0].y, i+1, xctx->xrect[0].height); + XFlush(display); + sleep_ms(4); + } + } + x++; + x %= 2; + } + break; + + case '!': + if((state & ControlMask)) { + if(xctx->semaphore >= 2) break; + break_wires_at_pins(1); + } + else { + if(xctx->semaphore >= 2) break; + break_wires_at_pins(0); + } + break; + + default: + break; + } + + return; +} + +static void handle_button_press(int event, int state, int rstate, KeySym key, int button, int mx, int my, + double c_snap, int draw_xhair, int crosshair_size, int enable_stretch, int aux) +{ + dbg(1, "callback(): ButtonPress ui_state=%d state=%d\n",xctx->ui_state,state); + int use_cursor_for_sel = tclgetintvar("use_cursor_for_selection"); + int excl = xctx->ui_state & (STARTWIRE | STARTRECT | STARTLINE | STARTPOLYGON | STARTARC); + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return; + } + /* terminate a schematic pan action */ + if(xctx->ui_state & STARTPAN) { + xctx->ui_state &=~STARTPAN; + return; + } + + /* select instance and connected nets stopping at wire junctions */ + if(!excl && button == Button3 && state == ControlMask && xctx->semaphore <2) + { + Selected sel; + sel = select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); + if(sel.type) select_connected_nets(1); + } + + /* break wire at mouse coordinates, move break point to nearest grid point */ + else if(!excl && button == Button3 && EQUAL_MODMASK && + !(state & ShiftMask) && xctx->semaphore <2) + { + break_wires_at_point(xctx->mousex_snap, xctx->mousey_snap, 1); + } + /* break wire at mouse coordinates */ + else if(!excl && button == Button3 && EQUAL_MODMASK && + (state & ShiftMask) && xctx->semaphore <2) + { + break_wires_at_point(xctx->mousex_snap, xctx->mousey_snap, 0); + } + /* select instance and connected nets NOT stopping at wire junctions */ + else if(!excl && button == Button3 && state == ShiftMask && xctx->semaphore <2) + { + Selected sel; + sel = select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); + if(sel.type) select_connected_nets(0); + } + /* moved to Button3 release */ + /* + * else if(button == Button3 && state == 0 && xctx->semaphore <2) { + * context_menu_action(xctx->mousex_snap, xctx->mousey_snap); + * } + */ + + /* zoom rectangle by right clicking and drag */ + else if(!excl && button == Button3 && state == 0 && xctx->semaphore < 2) { + zoom_rectangle(START);return; + } + + /* Mouse wheel events */ + else if(handle_mouse_wheel(event, mx, my, key, button, aux, state)) return; + + /* terminate wire placement in snap mode */ + else if(button==Button1 && (state & ShiftMask) && (xctx->ui_state & STARTWIRE) ) { + snapped_wire(c_snap); + } + /* Alt - Button1 click to unselect */ + else if(button==Button1 && (SET_MODMASK) ) { + unselect_at_mouse_pos(mx, my); + } + + /* Middle button press (Button2) will pan the schematic. */ + else if(button==Button2 && (state == 0)) { + pan(START, mx, my); + xctx->ui_state |= STARTPAN; + } + + /* button1 click to select another instance while edit prop dialog open */ + else if(button==Button1 && xctx->semaphore >= 2) { + if(tcleval("winfo exists .dialog.textinput")[0] == '1') { /* proc text_line */ + tcleval(".dialog.f1.b1 invoke"); + return; + } else if(tcleval("winfo exists .dialog.txt")[0] == '1') { /* proc enter_text */ + tcleval(".dialog.buttons.ok invoke"); + return; + } else if(state==0 && tclgetvar("edit_symbol_prop_new_sel")[0]) { + tcleval("set edit_symbol_prop_new_sel 1; .dialog.f1.b1 invoke"); /* invoke 'OK' of edit prop dialog */ + } else if((state & ShiftMask) && tclgetvar("edit_symbol_prop_new_sel")[0]) { + select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); + tclsetvar("preserve_unchanged_attrs", "1"); + rebuild_selected_array(); + } + } + + /* Handle the remaining Button1Press events */ + else if(button==Button1) /* MOD button is not pressed here. Processed above */ + { + xctx->onetime = 0; + xctx->mouse_moved = 0; + xctx->drag_elements = 0; + + /* start another wire or line in persistent mode */ + if(tclgetboolvar("persistent_command") && xctx->last_command) { + if(xctx->last_command == STARTLINE) start_line(xctx->mousex_snap, xctx->mousey_snap); + if(xctx->last_command == STARTWIRE){ + if(tclgetboolvar("snap_cursor") + && (xctx->prev_snapx == xctx->mousex_snap + && xctx->prev_snapy == xctx->mousey_snap) + && (xctx->ui_state & STARTWIRE) + && xctx->closest_pin_found){ + new_wire(PLACE|END, xctx->mousex_snap, xctx->mousey_snap); + xctx->ui_state &= ~STARTWIRE; + } + else + start_wire(xctx->mousex_snap, xctx->mousey_snap); + } + return; + } + /* handle all object insertions started from Tools/Edit menu */ + if(check_menu_start_commands(state, c_snap, mx, my)) return; + + /* complete the pending STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */ + if(end_place_move_copy_zoom()) return; + + /* Button1Press to select objects */ + if(!excl) { + Selected sel; + int already_selected = 0; + int prev_last_sel = xctx->lastsel; + int no_shift_no_ctrl = !(state & (ShiftMask | ControlMask)); + + xctx->shape_point_selected = 0; + xctx->mx_save = mx; xctx->my_save = my; + xctx->mx_double_save=xctx->mousex; + xctx->my_double_save=xctx->mousey; + + #if 0 /* disabled */ + /* Clicking and dragging from a **selected** instance pin will start a new wire + * if no other elements are selected */ + if(xctx->lastsel == 1 && xctx->sel_array[0].type==ELEMENT) { + if(add_wire_from_wire(&xctx->sel_array[0], xctx->mousex_snap, xctx->mousey_snap)) return; + if(add_wire_from_inst(&xctx->sel_array[0], xctx->mousex_snap, xctx->mousey_snap)) return; + } + #endif + + /* In *NON* intuitive interface a button1 press with no modifiers will + * first unselect everything... + * For intuitive interface unselection see below... */ + if(!xctx->intuitive_interface && no_shift_no_ctrl ) unselect_all(1); + + /* find closest object. Use snap coordinates if full crosshair is enabled + * since the mouse pointer is obscured and crosshair is snapped to grid points */ + if(draw_xhair && (use_cursor_for_sel || crosshair_size == 0)) { + sel = find_closest_obj(xctx->mousex_snap, xctx->mousey_snap, 0); + } else { + sel = find_closest_obj(xctx->mousex, xctx->mousey, 0); + } + dbg(1, "sel.type=%d\n", sel.type); + /* determine if closest object was already selected when button1 was pressed */ + switch(sel.type) { + case WIRE: if(xctx->wire[sel.n].sel) already_selected = 1; break; + case xTEXT: if(xctx->text[sel.n].sel) already_selected = 1; break; + case LINE: if(xctx->line[sel.col][sel.n].sel) already_selected = 1; break; + case POLYGON: if(xctx->poly[sel.col][sel.n].sel) already_selected = 1; break; + case xRECT: if(xctx->rect[sel.col][sel.n].sel) already_selected = 1; break; + case ARC: if(xctx->arc[sel.col][sel.n].sel) already_selected = 1; break; + case ELEMENT: if(xctx->inst[sel.n].sel) already_selected = 1; break; + default: break; + } /*end switch */ + + /* Clicking and drag on an instance pin -> drag a new wire */ + if(xctx->intuitive_interface && !already_selected) { + if(add_wire_from_inst(&sel, xctx->mousex_snap, xctx->mousey_snap)) return; + } + + /* Clicking and drag on a wire end -> drag a new wire */ + if(xctx->intuitive_interface && !already_selected) { + if(add_wire_from_wire(&sel, xctx->mousex_snap, xctx->mousey_snap)) return; + } + + /* In intuitive interface a button1 press with no modifiers will + * unselect everything... we do it here */ + if(xctx->intuitive_interface && !already_selected && no_shift_no_ctrl ) unselect_all(1); + + /* select the object under the mouse and rebuild the selected array */ + if(!already_selected) select_object(xctx->mousex, xctx->mousey, SELECTED, 0, &sel); + rebuild_selected_array(); + dbg(1, "Button1Press to select objects, lastsel = %d\n", xctx->lastsel); + + /* if clicking on some object endpoints or vertices set shape_point_selected + * this information will be used in Motion events to draw the stretched vertices */ + if(xctx->lastsel == 1 && xctx->sel_array[0].type==POLYGON) { + if(edit_polygon_point(state)) return; /* sets xctx->shape_point_selected */ + } + if(xctx->lastsel == 1 && xctx->intuitive_interface) { + int cond = already_selected; + + if(cond && xctx->sel_array[0].type==xRECT) { + if(edit_rect_point(state)) return; /* sets xctx->shape_point_selected */ + } + + if(cond && xctx->sel_array[0].type==LINE) { + if(edit_line_point(state)) return; /* sets xctx->shape_point_selected */ + } + + if(cond && xctx->sel_array[0].type==WIRE) { + if(edit_wire_point(state)) return; /* sets xctx->shape_point_selected */ + } + } + dbg(1, "shape_point_selected=%d, lastsel=%d\n", xctx->shape_point_selected, xctx->lastsel); + + /* intuitive interface: directly drag elements */ + if(sel.type && xctx->intuitive_interface && xctx->lastsel >= 1 && + !xctx->shape_point_selected) { + /* enable_stretch (from TCL variable) reverses command if enabled: + * - move --> stretch move + * - stretch move (with ctrl key) --> move + */ + int stretch = (state & ControlMask ? 1 : 0) ^ enable_stretch; + xctx->drag_elements = 1; + /* select attached nets depending on ControlMask and enable_stretch */ + if(stretch) { + select_attached_nets(); /* stretch nets that land on selected instance pins */ + } + /* if dragging instances with stretch enabled and Shift down add wires to pins + * attached to something */ + if((state & ShiftMask) && stretch) { + xctx->connect_by_kissing = 2; /* 2 will be used to reset var to 0 at end of move */ + move_objects(START,0,0,0); + } + /* dragging away an object with Shift pressed is a copy (duplicate object) */ + else if(state & ShiftMask) copy_objects(START); + /* else it is a normal move */ + else move_objects(START,0,0,0); + } + + if(tclgetboolvar("auto_hilight") && !xctx->shape_point_selected) { + if(!(state & ShiftMask) && xctx->hilight_nets && sel.type == 0 ) { + if(!prev_last_sel) { + redraw_hilights(1); /* 1: clear all hilights, then draw */ + } + } + hilight_net(0); + if(xctx->lastsel) { + redraw_hilights(0); + } + } + return; + } /* if(!excl) */ + } /* button==Button1 */ + + return; +} + +static void handle_button_release(int event, KeySym key, int state, int button, int mx, int my, + int aux, double c_snap, int enable_stretch, int draw_xhair, int snap_cursor, int wire_draw_active) +{ + char str[PATH_MAX + 100]; + if(waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return; + } + xctx->ui_state &= ~DESEL_CLICK; + dbg(1, "release: shape_point_selected=%d\n", xctx->shape_point_selected); + /* bring up context menu if no pending operation */ + if(state == Button3Mask && xctx->semaphore <2) { + if(!end_place_move_copy_zoom()) { + context_menu_action(xctx->mousex_snap, xctx->mousey_snap); + } + } + + /* launcher, no intuitive interface */ + if(!xctx->intuitive_interface && state == (Button1Mask | ControlMask) && + !xctx->shape_point_selected && xctx->mouse_moved == 0) { + int savesem = xctx->semaphore; + xctx->semaphore = 0; + launcher(); /* works only if lastsel == 1 */ + xctx->semaphore = savesem; + } + + /* launcher, intuitive_interface, only if no movement has been done */ + else if(xctx->intuitive_interface && state == (Button1Mask | ControlMask) && + !xctx->shape_point_selected && (xctx->ui_state & STARTMOVE) && xctx->mouse_moved == 0) { + int savesem = xctx->semaphore; + move_objects(ABORT, 0, 0.0, 0.0); + unselect_all(1); + xctx->drag_elements = 0; /* after ctrl-Button1Press on a launcher need to clear this */ + select_object(xctx->mousex, xctx->mousey, SELECTED, 0, NULL); + rebuild_selected_array(); + xctx->semaphore = 0; + launcher(); /* works only if lastsel == 1 */ + xctx->semaphore = savesem; + } + + /* end wire creation when dragging in intuitive interface from an inst pin or wire endpoint */ + else if(state == Button1Mask && xctx->intuitive_interface && !tclgetboolvar("persistent_command") + && (xctx->ui_state & STARTWIRE) && !(xctx->ui_state & MENUSTART)) { + if(end_place_move_copy_zoom()) return; + } + + /* end intuitive_interface copy or move */ + if(xctx->ui_state & STARTCOPY && xctx->drag_elements) { + copy_objects(END); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->drag_elements = 0; + } + else if(xctx->ui_state & STARTMOVE && xctx->drag_elements) { + move_objects(END,0,0,0); + xctx->constr_mv=0; + tcleval("set constr_mv 0" ); + xctx->drag_elements = 0; + } + + /* if a polygon/bezier/rectangle control point was clicked, end point move operation + * and set polygon state back to SELECTED from SELECTED1 */ + else if((xctx->ui_state & (STARTMOVE | SELECTION)) && xctx->shape_point_selected) { + end_shape_point_edit(c_snap); + } + + if(xctx->ui_state & STARTPAN) { + xctx->ui_state &=~STARTPAN; + /* xctx->mx_save = mx; xctx->my_save = my; */ + /* xctx->mx_double_save=xctx->mousex_snap; */ + /* xctx->my_double_save=xctx->mousey_snap; */ + redraw_w_a_l_r_p_z_rubbers(1); + return; + } + dbg(1, "callback(): ButtonRelease ui_state=%d state=%d\n",xctx->ui_state,state); + if(xctx->semaphore >= 2) return; + if(xctx->ui_state & STARTSELECT) { + if(state & ControlMask) { + select_rect(!enable_stretch, END,-1); + } else { + /* Button1 release: end of rectangle select */ + if(!(state & (Button4Mask|Button5Mask) ) ) { + select_rect(enable_stretch, END,-1); + } + } + xctx->ui_state &= ~DESEL_AREA; + rebuild_selected_array(); + my_snprintf(str, S(str), "mouse = %.16g %.16g - selected: %d path: %s", + xctx->mousex_snap, xctx->mousey_snap, xctx->lastsel, xctx->sch_path[xctx->currsch] ); + statusmsg(str,1); + } + + /* clear start from menu flag or infix_interface=0 start commands */ + if( state == Button1Mask && xctx->ui_state & MENUSTART) { + xctx->ui_state &= ~MENUSTART; + return; + } + if(draw_xhair) draw_crosshair(3, state); /* restore crosshair when selecting / unselecting */ + if(snap_cursor && ((state == ShiftMask) || wire_draw_active)) draw_snap_cursor(3); /* erase & redraw */ + return; +} + +static void handle_double_click(int event, int state, KeySym key, int button, + int mx, int my, int aux, int cadence_compat) +{ + if( waves_selected(event, key, state, button)) { + waves_callback(event, mx, my, key, button, aux, state); + return; + } else { + if(xctx->semaphore >= 2) return; + dbg(1, "callback(): DoubleClick ui_state=%d state=%d\n",xctx->ui_state,state); + if(button==Button1) { + Selected sel; + 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, NULL); + if(sel.type) { + xctx->ui_state = SELECTION; + rebuild_selected_array(); + } + } + if(xctx->ui_state == 0 || xctx->ui_state == SELECTION) { + edit_property(0); + } else { + if(xctx->ui_state & STARTWIRE) { + redraw_w_a_l_r_p_z_rubbers(1); + start_wire(mx, my); + xctx->ui_state &= ~STARTWIRE; + } + if(xctx->ui_state & STARTLINE) { + xctx->ui_state &= ~STARTLINE; + } + if( (xctx->ui_state & STARTPOLYGON) && (state ==0 ) ) { + new_polygon(SET, xctx->mousex_snap, xctx->mousey_snap); + } + } + } + } +} + + +/* main window callback */ +/* mx and my are set to the mouse coord. relative to window */ +/* win_path: set to .drw or sub windows .x1.drw, .x2.drw, ... */ +int callback(const char *win_path, int event, int mx, int my, KeySym key, + int button, int aux, int state) +{ + char str[PATH_MAX + 100]; + int redraw_only; + double c_snap; +#ifndef __unix__ + short cstate = GetKeyState(VK_CAPITAL); + short nstate = GetKeyState(VK_NUMLOCK); +#else + XKeyboardState kbdstate; +#endif +int enable_stretch = tclgetboolvar("enable_stretch"); +int draw_xhair = tclgetboolvar("draw_crosshair"); +int crosshair_size = tclgetintvar("crosshair_size"); +int infix_interface = tclgetboolvar("infix_interface"); +int rstate; /* (reduced state, without ShiftMask) */ +int snap_cursor = tclgetboolvar("snap_cursor"); +int cadence_compat = tclgetboolvar("cadence_compat"); +int wire_draw_active = (xctx->ui_state & STARTWIRE) || + ((xctx->ui_state2 & MENUSTARTWIRE) && (xctx->ui_state & MENUSTART)) || + (tclgetboolvar("persistent_command") && (xctx->last_command & STARTWIRE)); + + /* this fix uses an alternative method for getting mouse coordinates on KeyPress/KeyRelease + * events. Some remote connection softwares do not generate the correct coordinates + * on such events */ + if(fix_mouse_coord) { + if(event == KeyPress || event == KeyRelease) { + tclvareval("getmousex ", win_path, NULL); + mx = atoi(tclresult()); + tclvareval("getmousey ", win_path, NULL); + my = atoi(tclresult()); + dbg(1, "mx = %d my=%d\n", mx, my); + } + } + +#ifndef __unix__ + if(cstate & 0x0001) { /* caps lock */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state active -text {CAPS LOCK SET! }", NULL); + } else if (nstate & 0x0001) { /* num lock */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state active -text {NUM LOCK SET! }", NULL); + } else { /* normal state */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state normal -text {}", NULL); + } +#else + XGetKeyboardControl(display, &kbdstate); + if(kbdstate.led_mask & 1) { /* caps lock */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state active -text {CAPS LOCK SET! }", NULL); + } else if(kbdstate.led_mask & 2) { /* num lock */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state active -text {NUM LOCK SET! }", NULL); + } else { /* normal state */ + tclvareval(xctx->top_path, ".statusbar.8 configure -state normal -text {}", NULL); + } +#endif + + if(wire_draw_active) { + tclvareval(xctx->top_path, ".statusbar.10 configure -state active -text {DRAW WIRE! }", NULL); + } else { + tclvareval(xctx->top_path, ".statusbar.10 configure -state normal -text { }", NULL); + } + + tclvareval(xctx->top_path, ".statusbar.7 configure -text $netlist_type", NULL); + tclvareval(xctx->top_path, ".statusbar.3 delete 0 end;", + xctx->top_path, ".statusbar.3 insert 0 $cadsnap", + NULL); + tclvareval(xctx->top_path, ".statusbar.5 delete 0 end;", + xctx->top_path, ".statusbar.5 insert 0 $cadgrid", + NULL); + + #if 0 + /* exclude Motion and Expose events */ + if(event!=6 /* && event!=12 */) { + dbg(0, "callback(): state=%d event=%d, win_path=%s, old_win_path=%s, semaphore=%d\n", + state, event, win_path, old_win_path, xctx->semaphore+1); + } + #endif + + /* Schematic window context switch */ + redraw_only =0; + if(strcmp(old_win_path, win_path) ) { + if( xctx->semaphore >= 1 || event == Expose) { + dbg(1, "callback(): semaphore >=2 (or Expose) switching window context: %s --> %s\n", old_win_path, win_path); + redraw_only = 1; + new_schematic("switch_no_tcl_ctx", win_path, "", 1); + } else { + dbg(1, "callback(): switching window context: %s --> %s, semaphore=%d\n", old_win_path, win_path, xctx->semaphore); + new_schematic("switch", win_path, "", 1); + } + tclvareval("housekeeping_ctx", NULL); + } + /* artificially set semaphore to allow only redraw operations in switched schematic, + * so we don't need to switch tcl context which is costly performance-wise + */ + if(redraw_only) { + dbg(1, "callback(): incrementing semaphore for redraw_only\n"); + xctx->semaphore++; + } + + xctx->semaphore++; /* to recognize recursive callback() calls */ + + c_snap = tclgetdoublevar("cadsnap"); + #ifdef __unix__ + state &= (1 <<13) -1; /* filter out anything above bit 12 (4096) */ + #endif + state &= ~Mod2Mask; /* 20170511 filter out NumLock status */ + state &= ~LockMask; /* filter out Caps Lock */ + rstate = state; /* rstate does not have ShiftMask bit, so easier to test for KeyPress events */ + rstate &= ~ShiftMask; /* don't use ShiftMask, identifying characters is sufficient */ + rstate &= ~Button1Mask; /* ignore button-1 */ + if(xctx->semaphore >= 2) + { + if(debug_var>=2) + if(event != MotionNotify) + fprintf(errfp, "callback(): reentrant call of callback(), semaphore=%d, ev=%d, ui_state=%d\n", + xctx->semaphore, event, xctx->ui_state); + } + xctx->mousex=X_TO_XSCHEM(mx); + xctx->mousey=Y_TO_XSCHEM(my); + xctx->mousex_snap=my_round(xctx->mousex / c_snap) * c_snap; + xctx->mousey_snap=my_round(xctx->mousey / c_snap) * c_snap; + + if(abs(mx-xctx->mx_save) > 8 || abs(my-xctx->my_save) > 8 ) { + my_snprintf(str, S(str), "mouse = %.16g %.16g - selected: %d path: %s", + xctx->mousex_snap, xctx->mousey_snap, xctx->lastsel, xctx->sch_path[xctx->currsch] ); + statusmsg(str,1); + } + + dbg(1, "key=%d EQUAL_MODMASK=%d, SET_MODMASK=%d\n", key, SET_MODMASK, EQUAL_MODMASK); + + #if defined(__unix__) && HAS_CAIRO==1 + if(xctx->ui_state & GRABSCREEN) { + grabscreen(win_path, event, mx, my, key, button, aux, state); + } else + #endif + switch(event) + { + + case LeaveNotify: + if(draw_xhair) draw_crosshair(1, state); /* clear crosshair when exiting window */ + if(snap_cursor) draw_snap_cursor(1); /* erase */ + tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); + xctx->mouse_inside = 0; + break; + + case EnterNotify: + handle_enter_notify(draw_xhair, crosshair_size); + break; + + case Expose: + dbg(1, "callback: Expose, win_path=%s, %dx%d+%d+%d\n", win_path, button, aux, mx, my); + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], mx,my,button,aux,mx,my); + { + XRectangle xr[1]; + xr[0].x=(short)mx; + xr[0].y=(short)my; + xr[0].width=(unsigned short)button; + xr[0].height=(unsigned short)aux; + /* redraw selection on expose, needed if no backing store available on the server 20171112 */ + XSetClipRectangles(display, xctx->gc[SELLAYER], 0,0, xr, 1, Unsorted); + rebuild_selected_array(); + if(tclgetboolvar("compare_sch") /* && xctx->sch_to_compare[0] */){ + compare_schematics(""); + } else { + draw_selection(xctx->gc[SELLAYER],0); + } + XSetClipMask(display, xctx->gc[SELLAYER], None); + } + dbg(1, "callback(): Expose\n"); + break; + + case ConfigureNotify: + dbg(1,"callback(): ConfigureNotify event\n"); + resetwin(1, 1, 0, 0, 0); + draw(); + break; + + case MotionNotify: + handle_motion_notify(event, key, state, rstate, button, mx, my, + aux, draw_xhair, enable_stretch, + snap_cursor, wire_draw_active); + break; + + case KeyRelease: + /* force clear (even if mouse pos not changed) */ + /* if(snap_cursor && (key == XK_Shift_L || key == XK_Shift_R) ) draw_snap_cursor(5); */ + break; + + case KeyPress: + handle_key_press(event, key, state, rstate, mx, my, button, aux, + infix_interface, enable_stretch, win_path, c_snap, + cadence_compat, wire_draw_active, snap_cursor); + break; + + case ButtonPress: + handle_button_press(event, state, rstate, key, button, mx, my, + c_snap, draw_xhair, crosshair_size, enable_stretch, aux); + break; + + case ButtonRelease: + handle_button_release(event, key, state, button, mx, my, aux, c_snap, enable_stretch, + draw_xhair, snap_cursor, wire_draw_active); + break; + + case -3: /* double click : edit prop */ + handle_double_click(event, state, key, button, mx, my, aux, cadence_compat); + break; + + default: + dbg(1, "callback(): Event:%d\n",event); + break; + } /* switch(event) */ + + if(xctx->semaphore > 0) xctx->semaphore--; + if(redraw_only) { + xctx->semaphore--; /* decrement articially incremented semaphore (see above) */ + dbg(1, "callback(): semaphore >=2 restoring window context: %s <-- %s\n", old_win_path, win_path); + if(old_win_path[0]) new_schematic("switch_no_tcl_ctx", old_win_path, "", 1); + } + else + if(strcmp(old_win_path, win_path)) { + if(old_win_path[0]) dbg(1, "callback(): reset old_win_path: %s <- %s\n", old_win_path, win_path); + my_strncpy(old_win_path, win_path, S(old_win_path)); + } + return 0; +} + From afaa38e81d62e12bb000f30fae1020450cfcef60 Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Thu, 13 Mar 2025 13:17:21 +0530 Subject: [PATCH 60/67] [Resolve Merge Conflicts - 3]: Third (Final) stage of changes. All upstream changes are now merged. --- src/callback.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/callback.c b/src/callback.c index 4dd7d3bf..a9158ee3 100644 --- a/src/callback.c +++ b/src/callback.c @@ -3032,7 +3032,11 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m descend_symbol(); } else if(rstate == ControlMask) { /* insert sym */ - tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + } } else if(EQUAL_MODMASK) { /* edit symbol in new window */ int save = xctx->semaphore; @@ -3045,7 +3049,11 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m case 'I': if(rstate == 0) { /* insert sym */ if(xctx->semaphore >= 2) break; - start_place_symbol(); + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + start_place_symbol(); + } } else if(EQUAL_MODMASK) { /* edit symbol in new window - new xschem process */ int save = xctx->semaphore; @@ -3967,11 +3975,19 @@ static void handle_key_press(int event, KeySym key, int state, int rstate, int m case XK_Insert: if(state == ShiftMask) { /* insert sym */ - tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + tcleval("load_file_dialog {Insert symbol} *.\\{sym,tcl\\} INITIALINSTDIR 2"); + } } else { if(xctx->semaphore >= 2) break; - start_place_symbol(); + if(tclgetboolvar("new_symbol_browser")) { + tcleval("insert_symbol $new_symbol_browser_paths $new_symbol_browser_depth $new_symbol_browser_ext"); + } else { + start_place_symbol(); + } } break; From ac1f61803085b2a0c1eb022210642268062dd1fe Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 11:26:07 +0100 Subject: [PATCH 61/67] clean up a bit the load_file_dialog pack order --- src/xschem.tcl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index cfb56305..e1afef31 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -4503,9 +4503,6 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} file_dialog_set_colors1 scrollbar .load.l.paneleft.yscroll -command ".load.l.paneleft.list yview" -takefocus 0 scrollbar .load.l.paneleft.xscroll -command ".load.l.paneleft.list xview" -orient horiz -takefocus 0 - pack .load.l.paneleft.yscroll -side right -fill y - pack .load.l.paneleft.xscroll -side bottom -fill x - pack .load.l.paneleft.list -fill both -expand true -padx 12 bind .load.l.paneleft.list <> { set file_dialog_sel [.load.l.paneleft.list curselection] if { $file_dialog_sel ne {} } { @@ -4551,9 +4548,6 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} -xscrollcommand ".load.l.paneright.f.xscroll set" -exportselection 0 scrollbar .load.l.paneright.f.yscroll -command ".load.l.paneright.f.list yview" -takefocus 0 scrollbar .load.l.paneright.f.xscroll -command ".load.l.paneright.f.list xview" -orient horiz -takefocus 0 - pack .load.l.paneright.f.yscroll -side right -fill y - pack .load.l.paneright.f.xscroll -side bottom -fill x - pack .load.l.paneright.f.list -side bottom -fill both -expand true if { $loadfile == 2} { .load.l add .load.l.recent @@ -4598,7 +4592,7 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} } label .load.buttons_bot.label -text { File:} entry .load.buttons_bot.entry -highlightcolor red -highlightthickness 2 \ - -highlightbackground [option get . background {}] + -highlightbackground [option get . background {}] -takefocus 0 entry_replace_selection .load.buttons_bot.entry label .load.buttons_bot.srclab -text { Search:} entry .load.buttons_bot.src -width 18 -highlightcolor red -highlightthickness 2 \ @@ -4652,19 +4646,28 @@ proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}} file_dialog_set_colors1 .load.l.paneleft.list xview moveto 1 } + + pack .load.l -expand true -fill both + pack .load.l.paneleft.yscroll -side right -fill y + pack .load.l.paneleft.xscroll -side bottom -fill x + pack .load.l.paneleft.list -fill both -expand true -padx 12 + pack .load.buttons.home .load.buttons.up .load.buttons.pwd .load.buttons.path -side left pack .load.buttons.mkdirlab -side left pack .load.buttons.newdir -expand true -fill x -side left pack .load.buttons.rmdir .load.buttons.mkdir -side right - # pack .load.buttons_bot.all .load.buttons_bot.sym .load.buttons_bot.sch -side left pack .load.buttons_bot.srclab -side left pack .load.buttons_bot.src -side left pack .load.buttons_bot.label -side left pack .load.buttons_bot.entry -side left -fill x -expand true + + pack .load.l.paneright.f.yscroll -side right -fill y + pack .load.l.paneright.f.xscroll -side bottom -fill x + pack .load.l.paneright.f.list -side bottom -fill both -expand true + pack .load.buttons_bot.cancel .load.buttons_bot.ok -side left pack .load.buttons_bot -side bottom -fill x pack .load.buttons -side bottom -fill x - pack .load.l -expand true -fill both if { [info exists file_dialog_default_geometry]} { wm geometry .load "${file_dialog_default_geometry}" } From 84ca35e6f55dac57eed36429f77e00cb271fa4c6 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 11:57:09 +0100 Subject: [PATCH 62/67] add menu option Simulation->LVS->Upper case .SUBCKT and .ENDS --- src/spice.awk | 2 +- src/spice_netlist.c | 31 ++++++++++++++++++++++--------- src/xschem.tcl | 9 ++++++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/spice.awk b/src/spice.awk index 5dc7b170..07bb05e7 100755 --- a/src/spice.awk +++ b/src/spice.awk @@ -275,7 +275,7 @@ function process( i,j, iprefix, saveinstr, savetype, saveanalysis) } else if( $1 ~ /^\*\.(ipin|opin|iopin)/ ) { num=split($2,name,",") for(i=1;i<=num;i++) print $1 " " name[i] - } else if( $1 ~ /\.subckt/) { + } else if( tolower($1) ~ /\.subckt/) { # remove m=.. from subcircuit definition since m= is a multiplier not a param sub(/ m=[0-9]+/," ",$0) gsub(","," ",$0) diff --git a/src/spice_netlist.c b/src/spice_netlist.c index 663a2a7d..359a0095 100644 --- a/src/spice_netlist.c +++ b/src/spice_netlist.c @@ -173,10 +173,9 @@ static int spice_netlist(FILE *fd, int spice_stop ) int err = 0; int i, flag = 0; const char *type; - int top_sub; + int top_sub = tclgetboolvar("lvs_netlist") || tclgetboolvar("top_is_subckt"); int lvs_ignore = tclgetboolvar("lvs_ignore"); - - top_sub = tclgetboolvar("lvs_netlist") || tclgetboolvar("top_is_subckt"); + if(!spice_stop) { dbg(1, "spice_netlist(): invoke prepare_netlist_structs for %s\n", xctx->current_name); xctx->prep_net_structs = 0; @@ -278,6 +277,7 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ int found_top_symbol = 0; int npins = 0; /* top schematic number of i/o ports */ Sch_pin_record *pinnumber_list = NULL; /* list of top sch i/o ports ordered wrt sim_pinnumber attr */ + int uppercase_subckt = tclgetboolvar("uppercase_subckt"); exit_code = 0; /* reset exit code */ split_f = tclgetboolvar("split_files"); @@ -341,7 +341,10 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ } top_sub = tclgetboolvar("lvs_netlist") || tclgetboolvar("top_is_subckt"); if(!top_sub) fprintf(fd,"**"); - fprintf(fd,".subckt %s", get_cell(xctx->sch[xctx->currsch], 0)); + if(uppercase_subckt) + fprintf(fd,".SUBCKT %s", get_cell(xctx->sch[xctx->currsch], 0)); + else + fprintf(fd,".subckt %s", get_cell(xctx->sch[xctx->currsch], 0)); pinnumber_list = sort_schematic_pins(&npins); /* sort pins according to sim_pinnumber attr */ /* print top subckt ipin/opins */ @@ -405,7 +408,10 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ /* /20100217 */ if(!top_sub) fprintf(fd,"**"); - fprintf(fd, ".ends\n"); + if(uppercase_subckt) + fprintf(fd, ".ENDS\n"); + else + fprintf(fd, ".ends\n"); if(split_f) { @@ -572,7 +578,7 @@ int global_spice_netlist(int global, int alert) /* netlister driver */ /* 20150922 added split_files check */ - if(!split_f) fprintf(fd, ".end\n"); + if( !top_sub && !split_f) fprintf(fd, ".end\n"); dbg(1, "global_spice_netlist(): starting awk on netlist!\n"); @@ -612,7 +618,8 @@ int spice_block_netlist(FILE *fd, int i, int alert) char *sym_def = NULL; char *name = NULL; const char *default_schematic; - + int uppercase_subckt = tclgetboolvar("uppercase_subckt"); + split_f = tclgetboolvar("split_files"); if(!strboolcmp( get_tok_value(xctx->sym[i].prop_ptr,"spice_stop",0),"true") ) @@ -657,7 +664,10 @@ int spice_block_netlist(FILE *fd, int i, int alert) } else { const char *s = get_cell(sanitize(name), 0); fprintf(fd, "** sch_path: %s\n", sanitized_abs_sym_path(filename, "")); - fprintf(fd, ".subckt %s ", s); + if(uppercase_subckt) + fprintf(fd, ".SUBCKT %s ", s); + else + fprintf(fd, ".subckt %s ", s); print_spice_subckt_nodes(fd, i); my_strdup(_ALLOC_ID_, &extra, get_tok_value(xctx->sym[i].prop_ptr,"extra",0) ); @@ -680,7 +690,10 @@ int spice_block_netlist(FILE *fd, int i, int alert) fprintf(fd, "%s\n", xctx->schprop); fprintf(fd,"**** end user architecture code\n"); } - fprintf(fd, ".ends\n\n"); + if(uppercase_subckt) + fprintf(fd, ".ENDS\n\n"); + else + fprintf(fd, ".ends\n\n"); } if(split_f) { int save; diff --git a/src/xschem.tcl b/src/xschem.tcl index e1afef31..77d498b2 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -8146,8 +8146,9 @@ set tctx::global_list { svg_font_name sym_txt symbol symbol_width tabstop tclcmd_txt tclstop tctx::colors tctx::hsize tctx::rcode tctx::vsize text_line_default_geometry text_replace_selection text_tabs_setting textwindow_fileid textwindow_filename textwindow_w toolbar_horiz toolbar_list toolbar_visible - top_is_subckt transparent_svg undo_type unselect_partial_sel_wires use_cursor_for_selection - use_lab_wire use_label_prefix use_tclreadline user_wants_copy_cell verilog_2001 verilog_bitblast + top_is_subckt transparent_svg undo_type unselect_partial_sel_wires uppercase_subckt + use_cursor_for_selection use_lab_wire use_label_prefix use_tclreadline user_wants_copy_cell + verilog_2001 verilog_bitblast viewdata_fileid viewdata_filename viewdata_w xschem_libs xschem_listen_port zoom_full_center } @@ -9144,7 +9145,8 @@ tclcommand=\"xschem raw_read \$netlist_dir/[file tail [file rootname [xschem get xschem set format {} } } - + $topwin.menubar.simulation.lvs add checkbutton -label "Upper case .SUBCKT and .ENDS" \ + -selectcolor $selectcolor -variable uppercase_subckt $topwin.menubar.simulation.lvs add checkbutton -label "Top level is a .subckt" \ -selectcolor $selectcolor -variable top_is_subckt @@ -9582,6 +9584,7 @@ set_ne local_netlist_dir 0 ;# if set use /simulation for netlist and si set_ne bus_replacement_char {} ;# use {<>} to replace [] with <> in bussed signals set_ne lvs_netlist 0 set_ne top_is_subckt 0 +set_ne uppercase_subckt 0 set_ne lvs_ignore 0 set_ne hide_empty_graphs 0 ;# if set to 1 waveform boxes will be hidden if no raw file loaded set_ne graph_use_ctrl_key 0;# if set forces to use Control key to operate on graphs From e789988b05d5e80f27313dcd3dbff13780250656 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 20:11:18 +0100 Subject: [PATCH 63/67] add `user_startup_commands` variable that may contain tcl code to be executed after any new window creation. Useful to have custom bindings / menu / buttons in all windows. --- src/xinit.c | 2 ++ src/xschem.tcl | 9 +++++++++ src/xschemrc | 11 +++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/xinit.c b/src/xinit.c index 8ffe0f5c..ff9e9835 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -1666,6 +1666,7 @@ static void create_new_window(int *window_count, const char *noconfirm, const ch tclvareval("set_bindings ", window_path[n], NULL); tclvareval("set_replace_key_binding ", window_path[n], NULL); tclvareval("save_ctx ", window_path[n], NULL); + tcleval("eval_user_startup_commands"); /* restore previous context, * because the Expose event after new window creation does a context switch prev win -> new win * @@ -2892,6 +2893,7 @@ int Tcl_AppInit(Tcl_Interp *inter) if(has_x) { tclsetintvar("tctx::max_new_windows", MAX_NEW_WINDOWS); tcleval("pack_widgets; set_bindings .drw"); + tcleval("eval_user_startup_commands"); } fs=tclgetintvar("fullscreen"); diff --git a/src/xschem.tcl b/src/xschem.tcl index 77d498b2..3f2844f5 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -9355,6 +9355,15 @@ proc source_user_tcl_files {} { } } +proc eval_user_startup_commands {} { + global user_startup_commands + if {[info exists user_startup_commands]} { + if {[catch {uplevel #0 $user_startup_commands} res]} { + puts "executing $user_startup_commands:\n\n$res" + } + } +} + proc eval_postinit_commands {} { global postinit_commands if {[info exists postinit_commands]} { diff --git a/src/xschemrc b/src/xschemrc index 8bcacf06..41516e38 100644 --- a/src/xschemrc +++ b/src/xschemrc @@ -540,6 +540,17 @@ # } # } +########################################################################### +#### TCL COMMANDS TO BE EXECUTED AFTER ANY NEW WINDOW CREATION +########################################################################### +#### this hook is useful to execute user UI code (like event binding, +#### new buttons / menu entries etc). +# set user_startup_commands { +# bind [xschem get current_win_path] { +# puts Hello +# } +# } + ########################################################################### #### TCL COMMANDS TO BE EXECUTED AFTER GENERATING NETLIST ########################################################################### From 9cb435c43cb3a67ace38a1031724c19ed60fd6b6 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 13 Mar 2025 21:51:06 +0100 Subject: [PATCH 64/67] remove implicit [pwd] prepending to relative paths given to rel_sym_path. this assumption is incorrect in some cases --- src/xschem.tcl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/xschem.tcl b/src/xschem.tcl index 3f2844f5..39283906 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -7009,15 +7009,15 @@ proc rel_sym_path {symbol {paths {}} } { if { $paths eq {}} {set paths $pathlist} regsub {^~/} $symbol ${env(HOME)}/ symbol - if {$OS eq "Windows"} { - if {![regexp {^[A-Za-z]\:/} $symbol]} { - set symbol [pwd]/$symbol - } - } else { - if {![regexp {^/} $symbol]} { - set symbol [pwd]/$symbol - } - } + # if {$OS eq "Windows"} { + # if {![regexp {^[A-Za-z]\:/} $symbol]} { + # set symbol [pwd]/$symbol + # } + # } else { + # if {![regexp {^/} $symbol]} { + # set symbol [pwd]/$symbol + # } + # } set curr_dirname [pwd] set name {} foreach path_elem $paths { From 588ee1b8f42c5361cd5517a15d546d98a0a0be49 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Fri, 14 Mar 2025 03:29:05 +0100 Subject: [PATCH 65/67] do not call place_symbol() with relative symbol names. Initial draft fof proc get_list_of_dirs_with_symbols --- src/actions.c | 10 ++++----- src/xschem.tcl | 56 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/actions.c b/src/actions.c index d56798ac..f4c6df71 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1295,8 +1295,8 @@ void attach_labels_to_inst(int interactive) /* offloaded from callback.c 201710 int use_label_prefix; int found=0; - my_strdup(_ALLOC_ID_, &symname_pin, tcleval("rel_sym_path [find_file_first lab_pin.sym]")); - my_strdup(_ALLOC_ID_, &symname_wire, tcleval("rel_sym_path [find_file_first lab_wire.sym]")); + my_strdup(_ALLOC_ID_, &symname_pin, tcleval("find_file_first lab_pin.sym")); + my_strdup(_ALLOC_ID_, &symname_wire, tcleval("find_file_first lab_wire.sym")); if(symname_pin && symname_wire) { rebuild_selected_array(); k = xctx->lastsel; @@ -1451,10 +1451,10 @@ void delete_files(void) void place_net_label(int type) { if(type == 1) { - const char *lab = tcleval("rel_sym_path [find_file_first lab_pin.sym]"); + const char *lab = tcleval("find_file_first lab_pin.sym"); place_symbol(-1, lab, xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1/*to_push_undo*/); } else { - const char *lab = tcleval("rel_sym_path [find_file_first lab_wire.sym]"); + const char *lab = tcleval("find_file_first lab_wire.sym"); place_symbol(-1, lab, xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1/*to_push_undo*/); } move_objects(START,0,0,0); @@ -1482,7 +1482,7 @@ int place_symbol(int pos, const char *symbol_name, double x, double y, short rot tcleval("load_file_dialog {Choose symbol} *.\\{sym,tcl\\} INITIALINSTDIR"); my_strncpy(name1, tclresult(), S(name1)); } else { - my_strncpy(name1, trim_chars(symbol_name, " \t\n"), S(name1)); + my_strncpy(name1, abs_sym_path(trim_chars(symbol_name, " \t\n"), ""), S(name1)); } if(!name1[0]) return 0; dbg(1, "place_symbol(): 1: name1=%s first_call=%d\n",name1, first_call); diff --git a/src/xschem.tcl b/src/xschem.tcl index 39283906..01f727ca 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -3997,6 +3997,13 @@ proc is_xschem_file {f} { } else { fconfigure $fd -translation binary while { [gets $fd line] >=0 } { + + #### Can not use this. schematics may containg 8 bit extended characters + # if {[regexp {[^[:print:][:space:]]} $line]} { ;# line contains non ascii chars + # close $fd + # return 0 + # } + # this is a script. not an xschem file if { $nline == 0 && [regexp {^#!} $line] } { #### too dangerous executing an arbitrary script... @@ -4843,6 +4850,34 @@ proc insert_symbol_preview {{paths {}}} { } } } + +proc get_list_of_dirs_with_symbols {{paths {}} {levels -1} {level -1}} { + global pathlist + if {$level == -1} { set level 0} + if {$paths eq {}} {set paths $pathlist} + + foreach i $paths { + set filelist [glob -nocomplain -directory $i -type f *] + set there_are_symbols 0 + foreach f $filelist { + if {[regexp {\.(sch|sym|tcl)$} $f]} { + # if {[is_xschem_file $f] ne {0}} { } + set there_are_symbols 1 + break + } + } + if {$there_are_symbols} { + puts $i + } + + set dirlist [glob -nocomplain -directory $i -type d *] + if {$levels >=0 && $level + 1 > $levels} {return} + foreach d $dirlist { + get_list_of_dirs_with_symbols $d $levels [expr {$level + 1} ] + } + } +} + #### fill list of files matching pattern proc insert_symbol_filelist {paths {maxdepth -1}} { # puts "insert_symbol_filelist: paths=$paths" @@ -4925,8 +4960,10 @@ proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { frame .ins.top -takefocus 0 frame .ins.top2 -takefocus 0 panedwindow .ins.center -orient horizontal -height 8c + frame .ins.center.leftdir -takefocus 0 frame .ins.center.left -takefocus 0 - frame .ins.center.right -width 300 -height 250 -bg white -takefocus 0 + frame .ins.center.right -width 250 -height 250 -bg white -takefocus 0 + .ins.center add .ins.center.leftdir .ins.center add .ins.center.left .ins.center add .ins.center.right frame .ins.bottom -takefocus 0 @@ -4934,13 +4971,26 @@ proc insert_symbol {{paths {}} {maxdepth -1} {ext {.*}}} { pack .ins.top2 -side top -fill x pack .ins.center -side top -expand 1 -fill both pack .ins.bottom -side top -fill x - listbox .ins.center.left.l -listvariable insert_symbol(list) -width 50 -height 20 \ + + listbox .ins.center.leftdir.l -listvariable insert_symbol(dirs) -width 20 -height 20 \ + -yscrollcommand ".ins.center.leftdir.s set" -highlightcolor red -highlightthickness 2 \ + -activestyle underline -highlightbackground [option get . background {}] \ + -exportselection 0 + + listbox .ins.center.left.l -listvariable insert_symbol(list) -width 40 -height 20 \ -yscrollcommand ".ins.center.left.s set" -highlightcolor red -highlightthickness 2 \ -activestyle underline -highlightbackground [option get . background {}] \ -exportselection 0 + + scrollbar .ins.center.leftdir.s -command ".ins.center.leftdir.l yview" -takefocus 0 scrollbar .ins.center.left.s -command ".ins.center.left.l yview" -takefocus 0 + pack .ins.center.left.l -expand 1 -fill both -side left pack .ins.center.left.s -fill y -side left + + pack .ins.center.leftdir.l -expand 1 -fill both -side left + pack .ins.center.leftdir.s -fill y -side left + label .ins.top2.dir_l -text {Full path:} entry .ins.top2.dir_e -width 60 -state readonly \ -readonlybackground [option get . background {}] -takefocus 0 @@ -9115,7 +9165,7 @@ proc build_widgets { {topwin {} } } { -selectcolor $selectcolor -variable auto_hilight_graph_nodes $topwin.menubar.simulation.graph add command -label {Add waveform graph} -command {xschem add_graph} $topwin.menubar.simulation.graph add command -label {Add waveform reload launcher} -command { - xschem place_symbol [rel_sym_path [find_file_first launcher.sym]] "name=h5\ndescr=\"load waves\" + xschem place_symbol [find_file_first launcher.sym] "name=h5\ndescr=\"load waves\" tclcommand=\"xschem raw_read \$netlist_dir/[file tail [file rootname [xschem get current_name]]].raw tran\" " } From 5c44fec50654dc750835f717ae653041ac9e6ef9 Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Fri, 14 Mar 2025 14:00:36 +0530 Subject: [PATCH 66/67] [Experimental Feature Update]: Orthogonal wiring feature is now applicable to wires connected to objects, when those objects are moved to a different position by the user. This requires 'enable_stretching' to be enabled, which will stretch the connected wires orthogonally when the user drags/moves objects around in the canvas. --- src/actions.c | 60 ++++------------------------------------ src/draw.c | 39 ++++++++++++++++++++++++++ src/move.c | 76 ++++++++++++++++++++++++++++++++++++++++++++------- src/xschem.h | 1 + 4 files changed, 111 insertions(+), 65 deletions(-) diff --git a/src/actions.c b/src/actions.c index d56798ac..7a6f56e5 100644 --- a/src/actions.c +++ b/src/actions.c @@ -3183,61 +3183,11 @@ void new_wire(int what, double mx_snap, double my_snap) xctx->ui_state &= ~STARTWIRE; } if( (what & RUBBER) ) { - if(xctx->manhattan_lines & 1) { - xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1; - xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); - drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); - xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1; - xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2; - ORDER(xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gctiled, NOW, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - restore_selection(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); - xctx->nl_x2 = mx_snap; xctx->nl_y2 = my_snap; - if(!(what & CLEAR)) { - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); - drawtempline(xctx->gc[WIRELAYER], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gc[WIRELAYER], NOW, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - } - } else if(xctx->manhattan_lines & 2) { - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); - drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); - restore_selection(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); - xctx->nl_x2 = mx_snap; xctx->nl_y2 = my_snap; - if(!(what & CLEAR)) { - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); - drawtempline(xctx->gc[WIRELAYER], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gc[WIRELAYER], NOW, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); - } - } else { - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - restore_selection(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); - xctx->nl_x2 = mx_snap; xctx->nl_y2 = my_snap; - if(!(what & CLEAR)) { - xctx->nl_xx1 = xctx->nl_x1; xctx->nl_yy1 = xctx->nl_y1; - xctx->nl_xx2 = xctx->nl_x2; xctx->nl_yy2 = xctx->nl_y2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - drawtempline(xctx->gc[WIRELAYER], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - } + drawtemp_manhattanline(xctx->gctiled, NOW, xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); + restore_selection(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); + xctx->nl_x2 = mx_snap; xctx->nl_y2 = my_snap; + if(!(what & CLEAR)) { + drawtemp_manhattanline(xctx->gc[WIRELAYER], NOW, xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2); } } } diff --git a/src/draw.c b/src/draw.c index fb0c1ddc..c6bbeabe 100644 --- a/src/draw.c +++ b/src/draw.c @@ -1375,6 +1375,45 @@ void drawtempline(GC gc, int what, double linex1,double liney1,double linex2,dou } } +void drawtemp_manhattanline(GC gc, int what, double x1, double y1, double x2, double y2) +{ + double origin_shifted_x2, origin_shifted_y2; + if(tclgetboolvar("orthogonal_wiring")) { + /* Origin shift the cartesian coordinate p2(x2,y2) w.r.t. p1(x1,y1) */ + origin_shifted_x2 = x2 - x1; + origin_shifted_y2 = y2 - y1; + /* Draw whichever component of the resulting orthogonal-wire is bigger (either horizontal or vertical), first */ + if(origin_shifted_x2*origin_shifted_x2 > origin_shifted_y2*origin_shifted_y2) + xctx->manhattan_lines = 1; + else + xctx->manhattan_lines = 2; + } + if(xctx->manhattan_lines & 1) { + xctx->nl_xx1 = x1; xctx->nl_yy1 = y1; + xctx->nl_xx2 = x2; xctx->nl_yy2 = y2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); + drawtempline(gc, what, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); + xctx->nl_xx1 = x1; xctx->nl_yy1 = y1; + xctx->nl_xx2 = x2; xctx->nl_yy2 = y2; + ORDER(xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + drawtempline(gc, what, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + } else if(xctx->manhattan_lines & 2) { + xctx->nl_xx1 = x1; xctx->nl_yy1 = y1; + xctx->nl_xx2 = x2; xctx->nl_yy2 = y2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); + drawtempline(gc, what, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); + xctx->nl_xx1 = x1; xctx->nl_yy1 = y1; + xctx->nl_xx2 = x2; xctx->nl_yy2 = y2; + ORDER(xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); + drawtempline(gc, what, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); + } else { + xctx->nl_xx1 = x1; xctx->nl_yy1 = y1; + xctx->nl_xx2 = x2; xctx->nl_yy2 = y2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + drawtempline(gc, what, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + } +} + void drawtemparc(GC gc, int what, double x, double y, double r, double a, double b) { static int i=0; diff --git a/src/move.c b/src/move.c index 6ec3ba09..37db4066 100644 --- a/src/move.c +++ b/src/move.c @@ -378,25 +378,25 @@ void draw_selection(GC g, int interruptable) if(xctx->wire[n].sel==SELECTED) { if(xctx->wire[n].bus) - drawtempline(g, THICK, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, + drawtemp_manhattanline(g, THICK, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); else - drawtempline(g, ADD, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, + drawtemp_manhattanline(g, ADD, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); } else if(xctx->wire[n].sel==SELECTED1) { if(xctx->wire[n].bus) - drawtempline(g, THICK, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2, xctx->ry2); + drawtemp_manhattanline(g, THICK, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2, xctx->ry2); else - drawtempline(g, ADD, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2, xctx->ry2); + drawtemp_manhattanline(g, ADD, xctx->rx1+xctx->deltax, xctx->ry1+xctx->deltay, xctx->rx2, xctx->ry2); } else if(xctx->wire[n].sel==SELECTED2) { if(xctx->wire[n].bus) - drawtempline(g, THICK, xctx->rx1, xctx->ry1, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); + drawtemp_manhattanline(g, THICK, xctx->rx1, xctx->ry1, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); else - drawtempline(g, ADD, xctx->rx1, xctx->ry1, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); + drawtemp_manhattanline(g, ADD, xctx->rx1, xctx->ry1, xctx->rx2+xctx->deltax, xctx->ry2+xctx->deltay); } break; case LINE: @@ -1135,10 +1135,66 @@ void move_objects(int what, int merge, double dx, double dy) if(wire[n].sel == SELECTED1) wire[n].sel = SELECTED2; else if(wire[n].sel == SELECTED2) wire[n].sel = SELECTED1; } - wire[n].x1=xctx->rx1; - wire[n].y1=xctx->ry1; - wire[n].x2=xctx->rx2; - wire[n].y2=xctx->ry2; + + if(wire[n].sel & (SELECTED|SELECTED1)) + { + if(xctx->manhattan_lines & 1) xctx->manhattan_lines=2; + else if(xctx->manhattan_lines & 2) xctx->manhattan_lines=1; + } + wire[n].sel = SELECTED; + delete_wires(SELECTED); + if(xctx->manhattan_lines & 1) { + if(xctx->nl_xx2!=xctx->nl_xx1) { + xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; + xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); + storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1, 0, NULL); + } + if(xctx->nl_yy2!=xctx->nl_yy1) { + xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; + xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; + ORDER(xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + storeobject(-1, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,NOW, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); + } + } else if(xctx->manhattan_lines & 2) { + if(xctx->nl_yy2!=xctx->nl_yy1) { + xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; + xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); + storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2, 0, NULL); + } + if(xctx->nl_xx2!=xctx->nl_xx1) { + xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; + xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; + ORDER(xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); + storeobject(-1, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); + } + } else { + xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; + xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; + ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); + storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); + } + xctx->prep_hi_structs = 0; + if(tclgetboolvar("autotrim_wires")) trim_wires(); + prepare_netlist_structs(0); /* since xctx->prep_hi_structs==0, do a delete_netlist_structs() first, + * this clears both xctx->prep_hi_structs and xctx->prep_net_structs. */ + if(xctx->hilight_nets) { + propagate_hilights(1, 1, XINSERT_NOREPLACE); + } + draw(); + /* draw_hilight_net(1);*/ /* for updating connection bubbles on hilight nets */ + set_modify(1); } break; diff --git a/src/xschem.h b/src/xschem.h index 93c93b09..4d85fe43 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1428,6 +1428,7 @@ extern void filledrect(int c, int what, double rectx1,double recty1, extern void drawtempline(GC gc, int what, double x1,double y1,double x2,double y2); +extern void drawtemp_manhattanline(GC gc, int what, double x1,double y1,double x2,double y2); /* instead of doing a drawtemprect(xctx->gctiled, NOW, ....) do 4 * XCopy Area operations. Used if fix_broken_tiled_fill is set */ From a3eb1b0c17be1b7aa17a4835484a7dc49ef7a846 Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Fri, 14 Mar 2025 16:16:29 +0530 Subject: [PATCH 67/67] [Experimental Feature Update]: Orthogonal wiring feature is now applicable to wires connected to objects, when those objects are moved to a different position by the user. This requires 'enable_stretching' to be enabled, which will stretch the connected wires orthogonally when the user drags/moves objects around in the canvas. --- src/move.c | 74 ++++++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 52 deletions(-) diff --git a/src/move.c b/src/move.c index 37db4066..59893122 100644 --- a/src/move.c +++ b/src/move.c @@ -1141,60 +1141,30 @@ void move_objects(int what, int merge, double dx, double dy) if(xctx->manhattan_lines & 1) xctx->manhattan_lines=2; else if(xctx->manhattan_lines & 2) xctx->manhattan_lines=1; } - wire[n].sel = SELECTED; - delete_wires(SELECTED); - if(xctx->manhattan_lines & 1) { - if(xctx->nl_xx2!=xctx->nl_xx1) { - xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; - xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1); - storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1,WIRE,0,0,NULL); - hash_wire(XINSERT, xctx->wires-1, 1); - drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1, 0, NULL); - } - if(xctx->nl_yy2!=xctx->nl_yy1) { - xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; - xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; - ORDER(xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - storeobject(-1, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); - hash_wire(XINSERT, xctx->wires-1, 1); - drawline(WIRELAYER,NOW, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); - } - } else if(xctx->manhattan_lines & 2) { - if(xctx->nl_yy2!=xctx->nl_yy1) { - xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; - xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2); - storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2,WIRE,0,0,NULL); - hash_wire(XINSERT, xctx->wires-1, 1); - drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2, 0, NULL); - } - if(xctx->nl_xx2!=xctx->nl_xx1) { - xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; - xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; - ORDER(xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2); - storeobject(-1, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); - hash_wire(XINSERT, xctx->wires-1, 1); - drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); - } - } else { - xctx->nl_xx1 = xctx->rx1; xctx->nl_yy1 = xctx->ry1; - xctx->nl_xx2 = xctx->rx2; xctx->nl_yy2 = xctx->ry2; - ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2); - storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL); - hash_wire(XINSERT, xctx->wires-1, 1); - drawline(WIRELAYER,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2, 0, NULL); + wire[n].x1 = xctx->rx1; + wire[n].y1 = xctx->ry1; + if(xctx->manhattan_lines&1) + { + wire[n].x2 = xctx->rx2; + wire[n].y2 = xctx->ry1; + storeobject(-1, xctx->rx2,xctx->ry1,xctx->rx2,xctx->ry2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,ADD, xctx->rx2,xctx->ry1,xctx->rx2,xctx->ry2, 0, NULL); } - xctx->prep_hi_structs = 0; - if(tclgetboolvar("autotrim_wires")) trim_wires(); - prepare_netlist_structs(0); /* since xctx->prep_hi_structs==0, do a delete_netlist_structs() first, - * this clears both xctx->prep_hi_structs and xctx->prep_net_structs. */ - if(xctx->hilight_nets) { - propagate_hilights(1, 1, XINSERT_NOREPLACE); + else if(xctx->manhattan_lines&2) + { + wire[n].x2 = xctx->rx1; + wire[n].y2 = xctx->ry2; + storeobject(-1, xctx->rx1,xctx->ry2,xctx->rx2,xctx->ry2,WIRE,0,0,NULL); + hash_wire(XINSERT, xctx->wires-1, 1); + drawline(WIRELAYER,ADD, xctx->rx1,xctx->ry2,xctx->rx2,xctx->ry2, 0, NULL); } - draw(); - /* draw_hilight_net(1);*/ /* for updating connection bubbles on hilight nets */ - set_modify(1); + else + { + wire[n].x2 = xctx->rx2; + wire[n].y2 = xctx->ry2; + } + } break;